Compare commits
20 Commits
main
...
users/myst
Author | SHA1 | Date | |
---|---|---|---|
![]() |
9265309f0c | ||
![]() |
b7f8a9c8b4 | ||
![]() |
68761d9bf3 | ||
![]() |
d11cfe5d18 | ||
![]() |
435ba3597b | ||
![]() |
dff521f73d | ||
![]() |
40bafe2ceb | ||
![]() |
341dd6a3ca | ||
![]() |
75c4d315dc | ||
![]() |
904c996355 | ||
![]() |
a6220fef9b | ||
![]() |
20f2ad062c | ||
![]() |
6fc858761e | ||
![]() |
57ba236e87 | ||
![]() |
07ccb63a1b | ||
![]() |
dd58f21c9e | ||
![]() |
bd4dd9ca02 | ||
![]() |
5a056672cb | ||
![]() |
0e830ad120 | ||
![]() |
f3fdeff3e3 |
@ -49,7 +49,8 @@ DEPENDENTS_TO_TEST = {
|
||||
"flang",
|
||||
},
|
||||
"lld": {"bolt", "cross-project-tests"},
|
||||
"clang": {"clang-tools-extra", "cross-project-tests", "lldb"},
|
||||
# TODO(issues/132795): LLDB should be enabled on clang changes.
|
||||
"clang": {"clang-tools-extra", "cross-project-tests"},
|
||||
"mlir": {"flang"},
|
||||
# Test everything if ci scripts are changed.
|
||||
".ci": {
|
||||
|
@ -83,11 +83,11 @@ class TestComputeProjects(unittest.TestCase):
|
||||
)
|
||||
self.assertEqual(
|
||||
env_variables["projects_to_build"],
|
||||
"clang;clang-tools-extra;lld;lldb;llvm",
|
||||
"clang;clang-tools-extra;lld;llvm",
|
||||
)
|
||||
self.assertEqual(
|
||||
env_variables["project_check_targets"],
|
||||
"check-clang check-clang-tools check-lldb",
|
||||
"check-clang check-clang-tools",
|
||||
)
|
||||
self.assertEqual(
|
||||
env_variables["runtimes_to_build"], "compiler-rt;libcxx;libcxxabi;libunwind"
|
||||
@ -158,11 +158,11 @@ class TestComputeProjects(unittest.TestCase):
|
||||
)
|
||||
self.assertEqual(
|
||||
env_variables["projects_to_build"],
|
||||
"clang;clang-tools-extra;lld;lldb;llvm;mlir",
|
||||
"clang;clang-tools-extra;lld;llvm;mlir",
|
||||
)
|
||||
self.assertEqual(
|
||||
env_variables["project_check_targets"],
|
||||
"check-clang check-clang-cir check-clang-tools check-lldb",
|
||||
"check-clang check-clang-cir check-clang-tools",
|
||||
)
|
||||
self.assertEqual(
|
||||
env_variables["runtimes_to_build"], "compiler-rt;libcxx;libcxxabi;libunwind"
|
||||
|
@ -60,8 +60,7 @@ cmake -S "${MONOREPO_ROOT}"/llvm -B "${BUILD_DIR}" \
|
||||
-D MLIR_ENABLE_BINDINGS_PYTHON=ON \
|
||||
-D LLDB_ENABLE_PYTHON=ON \
|
||||
-D LLDB_ENFORCE_STRICT_TEST_REQUIREMENTS=ON \
|
||||
-D CMAKE_INSTALL_PREFIX="${INSTALL_DIR}" \
|
||||
-D CMAKE_EXE_LINKER_FLAGS="-no-pie"
|
||||
-D CMAKE_INSTALL_PREFIX="${INSTALL_DIR}"
|
||||
|
||||
start-group "ninja"
|
||||
|
||||
|
2
.github/workflows/pr-code-format.yml
vendored
2
.github/workflows/pr-code-format.yml
vendored
@ -70,6 +70,8 @@ jobs:
|
||||
- name: Run code formatter
|
||||
env:
|
||||
GITHUB_PR_NUMBER: ${{ github.event.pull_request.number }}
|
||||
START_REV: ${{ github.event.pull_request.base.sha }}
|
||||
END_REV: ${{ github.event.pull_request.head.sha }}
|
||||
CHANGED_FILES: ${{ steps.changed-files.outputs.all_changed_files }}
|
||||
# Create an empty comments file so the pr-write job doesn't fail.
|
||||
run: |
|
||||
|
10
.github/workflows/premerge.yaml
vendored
10
.github/workflows/premerge.yaml
vendored
@ -69,11 +69,6 @@ jobs:
|
||||
|
||||
./.ci/monolithic-linux.sh "${projects_to_build}" "${project_check_targets}" "${runtimes_to_build}" "${runtimes_check_targets}" "${runtimes_check_targets_needs_reconfig}" "${enable_cir}"
|
||||
- name: Upload Artifacts
|
||||
# In some cases, Github will fail to upload the artifact. We want to
|
||||
# continue anyways as a failed artifact upload is an infra failure, not
|
||||
# a checks failure.
|
||||
# https://github.com/actions/upload-artifact/issues/569
|
||||
continue-on-error: true
|
||||
if: '!cancelled()'
|
||||
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
|
||||
with:
|
||||
@ -119,11 +114,6 @@ jobs:
|
||||
# these environment variables.
|
||||
bash -c "export SCCACHE_GCS_BUCKET=$CACHE_GCS_BUCKET; export SCCACHE_GCS_RW_MODE=READ_WRITE; export SCCACHE_IDLE_TIMEOUT=0; sccache --start-server; .ci/monolithic-windows.sh \"${{ steps.vars.outputs.windows-projects }}\" \"${{ steps.vars.outputs.windows-check-targets }}\""
|
||||
- name: Upload Artifacts
|
||||
# In some cases, Github will fail to upload the artifact. We want to
|
||||
# continue anyways as a failed artifact upload is an infra failure, not
|
||||
# a checks failure.
|
||||
# https://github.com/actions/upload-artifact/issues/569
|
||||
continue-on-error: true
|
||||
if: '!cancelled()'
|
||||
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
|
||||
with:
|
||||
|
@ -138,12 +138,6 @@
|
||||
Dump function CFGs to graphviz format after each stage;enable '-print-loops'
|
||||
for color-coded blocks
|
||||
|
||||
- `--dump-dot-func=<func1,func2,func3...>`
|
||||
|
||||
Dump function CFGs to graphviz format for specified functions only;
|
||||
takes function name patterns (regex supported). Note: C++ function names
|
||||
must be passed using their mangled names
|
||||
|
||||
- `--dump-linux-exceptions`
|
||||
|
||||
Dump Linux kernel exception table
|
||||
|
@ -1196,6 +1196,11 @@ public:
|
||||
return getSecondaryEntryPointSymbol(BB.getLabel());
|
||||
}
|
||||
|
||||
/// Remove a label from the secondary entry point map.
|
||||
void removeSymbolFromSecondaryEntryPointMap(const MCSymbol *Label) {
|
||||
SecondaryEntryPoints.erase(Label);
|
||||
}
|
||||
|
||||
/// Return true if the basic block is an entry point into the function
|
||||
/// (either primary or secondary).
|
||||
bool isEntryPoint(const BinaryBasicBlock &BB) const {
|
||||
|
@ -241,7 +241,7 @@ private:
|
||||
|
||||
/// Adjust function sizes and set proper maximum size values after the whole
|
||||
/// symbol table has been processed.
|
||||
void adjustFunctionBoundaries(DenseMap<uint64_t, MarkerSymType> &MarkerSyms);
|
||||
void adjustFunctionBoundaries();
|
||||
|
||||
/// Make .eh_frame section relocatable.
|
||||
void relocateEHFrameSection();
|
||||
|
@ -15,12 +15,6 @@
|
||||
|
||||
#include "llvm/Support/CommandLine.h"
|
||||
|
||||
namespace llvm {
|
||||
namespace bolt {
|
||||
class BinaryFunction;
|
||||
}
|
||||
} // namespace llvm
|
||||
|
||||
namespace opts {
|
||||
|
||||
enum HeatmapModeKind {
|
||||
@ -106,9 +100,6 @@ extern llvm::cl::opt<unsigned> Verbosity;
|
||||
/// Return true if we should process all functions in the binary.
|
||||
bool processAllFunctions();
|
||||
|
||||
/// Return true if we should dump dot graphs for the given function.
|
||||
bool shouldDumpDot(const llvm::bolt::BinaryFunction &Function);
|
||||
|
||||
enum GadgetScannerKind { GS_PACRET, GS_PAUTH, GS_ALL };
|
||||
|
||||
extern llvm::cl::bits<GadgetScannerKind> GadgetScannersToRun;
|
||||
|
@ -1915,9 +1915,13 @@ void BinaryFunction::postProcessEntryPoints() {
|
||||
continue;
|
||||
|
||||
// If we have grabbed a wrong code label which actually points to some
|
||||
// constant island inside the function, ignore this label.
|
||||
if (isStartOfConstantIsland(Offset))
|
||||
// constant island inside the function, ignore this label and remove it
|
||||
// from the secondary entry point map.
|
||||
if (isStartOfConstantIsland(Offset)) {
|
||||
BC.SymbolToFunctionMap.erase(Label);
|
||||
removeSymbolFromSecondaryEntryPointMap(Label);
|
||||
continue;
|
||||
}
|
||||
|
||||
BC.errs() << "BOLT-WARNING: reference in the middle of instruction "
|
||||
"detected in function "
|
||||
|
@ -30,11 +30,6 @@ using namespace bolt;
|
||||
using namespace MCPlus;
|
||||
|
||||
namespace opts {
|
||||
cl::opt<bool>
|
||||
TerminalHLT("terminal-x86-hlt",
|
||||
cl::desc("Assume that execution stops at x86 HLT instruction"),
|
||||
cl::init(true), cl::Hidden, cl::cat(BoltCategory));
|
||||
|
||||
cl::opt<bool>
|
||||
TerminalTrap("terminal-trap",
|
||||
cl::desc("Assume that execution stops at trap instruction"),
|
||||
@ -137,13 +132,10 @@ bool MCPlusBuilder::equals(const MCSpecifierExpr &A, const MCSpecifierExpr &B,
|
||||
}
|
||||
|
||||
bool MCPlusBuilder::isTerminator(const MCInst &Inst) const {
|
||||
if (isX86HLT(Inst))
|
||||
return opts::TerminalHLT;
|
||||
|
||||
if (Info->get(Inst.getOpcode()).isTrap())
|
||||
return opts::TerminalTrap;
|
||||
|
||||
return Analysis->isTerminator(Inst);
|
||||
return (opts::TerminalTrap && Info->get(Inst.getOpcode()).isTrap()) ||
|
||||
Analysis->isTerminator(Inst)
|
||||
? !isX86HLT(Inst)
|
||||
: false;
|
||||
}
|
||||
|
||||
void MCPlusBuilder::setTailCall(MCInst &Inst) const {
|
||||
|
@ -52,7 +52,6 @@ namespace opts {
|
||||
extern cl::opt<bool> PrintAll;
|
||||
extern cl::opt<bool> PrintDynoStats;
|
||||
extern cl::opt<bool> DumpDotAll;
|
||||
extern bool shouldDumpDot(const bolt::BinaryFunction &Function);
|
||||
extern cl::opt<std::string> AsmDump;
|
||||
extern cl::opt<bolt::PLTCall::OptType> PLT;
|
||||
extern cl::opt<bolt::IdenticalCodeFolding::ICFLevel, false,
|
||||
@ -341,7 +340,7 @@ Error BinaryFunctionPassManager::runPasses() {
|
||||
|
||||
Function.print(BC.outs(), Message);
|
||||
|
||||
if (opts::shouldDumpDot(Function))
|
||||
if (opts::DumpDotAll)
|
||||
Function.dumpGraphForPass(PassIdName);
|
||||
}
|
||||
}
|
||||
|
@ -84,7 +84,6 @@ extern cl::opt<bool> KeepNops;
|
||||
extern cl::opt<bool> Lite;
|
||||
extern cl::list<std::string> ReorderData;
|
||||
extern cl::opt<bolt::ReorderFunctions::ReorderType> ReorderFunctions;
|
||||
extern cl::opt<bool> TerminalHLT;
|
||||
extern cl::opt<bool> TerminalTrap;
|
||||
extern cl::opt<bool> TimeBuild;
|
||||
extern cl::opt<bool> TimeRewrite;
|
||||
@ -115,35 +114,6 @@ cl::opt<bool> DumpDotAll(
|
||||
"enable '-print-loops' for color-coded blocks"),
|
||||
cl::Hidden, cl::cat(BoltCategory));
|
||||
|
||||
cl::list<std::string> DumpDotFunc(
|
||||
"dump-dot-func", cl::CommaSeparated,
|
||||
cl::desc(
|
||||
"dump function CFGs to graphviz format for specified functions only;"
|
||||
"takes function name patterns (regex supported)"),
|
||||
cl::value_desc("func1,func2,func3,..."), cl::Hidden, cl::cat(BoltCategory));
|
||||
|
||||
bool shouldDumpDot(const bolt::BinaryFunction &Function) {
|
||||
// If dump-dot-all is enabled, dump all functions
|
||||
if (DumpDotAll)
|
||||
return !Function.isIgnored();
|
||||
|
||||
// If no specific functions specified in dump-dot-func, don't dump any
|
||||
if (DumpDotFunc.empty())
|
||||
return false;
|
||||
|
||||
if (Function.isIgnored())
|
||||
return false;
|
||||
|
||||
// Check if function matches any of the specified patterns
|
||||
for (const std::string &Name : DumpDotFunc) {
|
||||
if (Function.hasNameRegex(Name)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static cl::list<std::string>
|
||||
ForceFunctionNames("funcs",
|
||||
cl::CommaSeparated,
|
||||
@ -910,9 +880,14 @@ void RewriteInstance::discoverFileObjects() {
|
||||
// code section (see IHI0056B). $d identifies data contents.
|
||||
// Compilers usually merge multiple data objects in a single $d-$x interval,
|
||||
// but we need every data object to be marked with $d. Because of that we
|
||||
// keep track of marker symbols with all locations of data objects.
|
||||
// create a vector of MarkerSyms with all locations of data objects.
|
||||
|
||||
DenseMap<uint64_t, MarkerSymType> MarkerSymbols;
|
||||
struct MarkerSym {
|
||||
uint64_t Address;
|
||||
MarkerSymType Type;
|
||||
};
|
||||
|
||||
std::vector<MarkerSym> SortedMarkerSymbols;
|
||||
auto addExtraDataMarkerPerSymbol = [&]() {
|
||||
bool IsData = false;
|
||||
uint64_t LastAddr = 0;
|
||||
@ -936,14 +911,14 @@ void RewriteInstance::discoverFileObjects() {
|
||||
}
|
||||
|
||||
if (MarkerType != MarkerSymType::NONE) {
|
||||
MarkerSymbols[SymInfo.Address] = MarkerType;
|
||||
SortedMarkerSymbols.push_back(MarkerSym{SymInfo.Address, MarkerType});
|
||||
LastAddr = SymInfo.Address;
|
||||
IsData = MarkerType == MarkerSymType::DATA;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (IsData) {
|
||||
MarkerSymbols[SymInfo.Address] = MarkerSymType::DATA;
|
||||
SortedMarkerSymbols.push_back({SymInfo.Address, MarkerSymType::DATA});
|
||||
LastAddr = SymInfo.Address;
|
||||
}
|
||||
}
|
||||
@ -1308,24 +1283,27 @@ void RewriteInstance::discoverFileObjects() {
|
||||
BC->setHasSymbolsWithFileName(FileSymbols.size());
|
||||
|
||||
// Now that all the functions were created - adjust their boundaries.
|
||||
adjustFunctionBoundaries(MarkerSymbols);
|
||||
adjustFunctionBoundaries();
|
||||
|
||||
// Annotate functions with code/data markers in AArch64
|
||||
for (auto &[Address, Type] : MarkerSymbols) {
|
||||
auto *BF = BC->getBinaryFunctionContainingAddress(Address, true, true);
|
||||
for (auto ISym = SortedMarkerSymbols.begin();
|
||||
ISym != SortedMarkerSymbols.end(); ++ISym) {
|
||||
|
||||
auto *BF =
|
||||
BC->getBinaryFunctionContainingAddress(ISym->Address, true, true);
|
||||
|
||||
if (!BF) {
|
||||
// Stray marker
|
||||
continue;
|
||||
}
|
||||
const auto EntryOffset = Address - BF->getAddress();
|
||||
if (Type == MarkerSymType::CODE) {
|
||||
const auto EntryOffset = ISym->Address - BF->getAddress();
|
||||
if (ISym->Type == MarkerSymType::CODE) {
|
||||
BF->markCodeAtOffset(EntryOffset);
|
||||
continue;
|
||||
}
|
||||
if (Type == MarkerSymType::DATA) {
|
||||
if (ISym->Type == MarkerSymType::DATA) {
|
||||
BF->markDataAtOffset(EntryOffset);
|
||||
BC->AddressToConstantIslandMap[Address] = BF;
|
||||
BC->AddressToConstantIslandMap[ISym->Address] = BF;
|
||||
continue;
|
||||
}
|
||||
llvm_unreachable("Unknown marker");
|
||||
@ -1854,8 +1832,7 @@ void RewriteInstance::disassemblePLT() {
|
||||
}
|
||||
}
|
||||
|
||||
void RewriteInstance::adjustFunctionBoundaries(
|
||||
DenseMap<uint64_t, MarkerSymType> &MarkerSyms) {
|
||||
void RewriteInstance::adjustFunctionBoundaries() {
|
||||
for (auto BFI = BC->getBinaryFunctions().begin(),
|
||||
BFE = BC->getBinaryFunctions().end();
|
||||
BFI != BFE; ++BFI) {
|
||||
@ -1893,15 +1870,12 @@ void RewriteInstance::adjustFunctionBoundaries(
|
||||
continue;
|
||||
}
|
||||
|
||||
auto It = MarkerSyms.find(NextSymRefI->first);
|
||||
if (It == MarkerSyms.end() || It->second != MarkerSymType::DATA) {
|
||||
// This is potentially another entry point into the function.
|
||||
uint64_t EntryOffset = NextSymRefI->first - Function.getAddress();
|
||||
LLVM_DEBUG(dbgs() << "BOLT-DEBUG: adding entry point to function "
|
||||
<< Function << " at offset 0x"
|
||||
<< Twine::utohexstr(EntryOffset) << '\n');
|
||||
Function.addEntryPointAtOffset(EntryOffset);
|
||||
}
|
||||
// This is potentially another entry point into the function.
|
||||
uint64_t EntryOffset = NextSymRefI->first - Function.getAddress();
|
||||
LLVM_DEBUG(dbgs() << "BOLT-DEBUG: adding entry point to function "
|
||||
<< Function << " at offset 0x"
|
||||
<< Twine::utohexstr(EntryOffset) << '\n');
|
||||
Function.addEntryPointAtOffset(EntryOffset);
|
||||
|
||||
++NextSymRefI;
|
||||
}
|
||||
@ -2203,9 +2177,7 @@ void RewriteInstance::adjustCommandLineOptions() {
|
||||
if (!opts::KeepNops.getNumOccurrences())
|
||||
opts::KeepNops = true;
|
||||
|
||||
// Linux kernel may resume execution after a trap or x86 HLT instruction.
|
||||
if (!opts::TerminalHLT.getNumOccurrences())
|
||||
opts::TerminalHLT = false;
|
||||
// Linux kernel may resume execution after a trap instruction in some cases.
|
||||
if (!opts::TerminalTrap.getNumOccurrences())
|
||||
opts::TerminalTrap = false;
|
||||
}
|
||||
@ -3598,7 +3570,7 @@ void RewriteInstance::postProcessFunctions() {
|
||||
if (opts::PrintAll || opts::PrintCFG)
|
||||
Function.print(BC->outs(), "after building cfg");
|
||||
|
||||
if (opts::shouldDumpDot(Function))
|
||||
if (opts::DumpDotAll)
|
||||
Function.dumpGraphForPass("00_build-cfg");
|
||||
|
||||
if (opts::PrintLoopInfo) {
|
||||
|
@ -1,38 +0,0 @@
|
||||
# This test is to ensure that we query data marker symbols to avoid
|
||||
# misidentifying constant data island symbol as extra entry point.
|
||||
|
||||
# RUN: %clang %cflags %s -o %t.so -Wl,-q -Wl,--init=_bar -Wl,--fini=_bar
|
||||
# RUN: llvm-bolt %t.so -o %t.instr.so
|
||||
|
||||
.text
|
||||
.global _start
|
||||
.type _start, %function
|
||||
_start:
|
||||
ret
|
||||
|
||||
.text
|
||||
.global _foo
|
||||
.type _foo, %function
|
||||
_foo:
|
||||
cbz x1, _foo_2
|
||||
_foo_1:
|
||||
add x1, x2, x0
|
||||
b _foo
|
||||
_foo_2:
|
||||
ret
|
||||
|
||||
# None of these constant island symbols should be identified as extra entry
|
||||
# point for function `_foo'.
|
||||
.align 4
|
||||
_const1: .short 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80
|
||||
_const2: .short 0x30, 0x40, 0x50, 0x60, 0x70, 0x80, 0x90, 0xa0
|
||||
_const3: .short 0x04, 0x08, 0x0c, 0x20, 0x60, 0x80, 0xa0, 0xc0
|
||||
|
||||
.text
|
||||
.global _bar
|
||||
.type _bar, %function
|
||||
_bar:
|
||||
ret
|
||||
|
||||
# Dummy relocation to force relocation mode
|
||||
.reloc 0, R_AARCH64_NONE
|
@ -1,24 +0,0 @@
|
||||
#include <iostream>
|
||||
|
||||
// Multiple functions to test selective dumping
|
||||
int add(int a, int b) { return a + b; }
|
||||
|
||||
int multiply(int a, int b) { return a * b; }
|
||||
|
||||
int main_helper() {
|
||||
std::cout << "Helper function" << std::endl;
|
||||
return 42;
|
||||
}
|
||||
|
||||
int main_secondary() { return add(5, 3); }
|
||||
|
||||
void other_function() { std::cout << "Other function" << std::endl; }
|
||||
|
||||
int main() {
|
||||
int result = add(10, 20);
|
||||
result = multiply(result, 2);
|
||||
main_helper();
|
||||
main_secondary();
|
||||
other_function();
|
||||
return result;
|
||||
}
|
17
bolt/test/X86/cfg_build_hlt.s
Normal file
17
bolt/test/X86/cfg_build_hlt.s
Normal file
@ -0,0 +1,17 @@
|
||||
## Check CFG for halt instruction
|
||||
|
||||
# RUN: %clang %cflags %s -static -o %t.exe -nostdlib
|
||||
# RUN: llvm-bolt %t.exe --print-cfg --print-only=main -o %t 2>&1 | FileCheck %s --check-prefix=CHECK-CFG
|
||||
# RUN: llvm-objdump -d %t --print-imm-hex | FileCheck %s --check-prefix=CHECK-BIN
|
||||
|
||||
# CHECK-CFG: BB Count : 1
|
||||
# CHECK-BIN: <main>:
|
||||
# CHECK-BIN-NEXT: f4 hlt
|
||||
# CHECK-BIN-NEXT: c3 retq
|
||||
|
||||
.global main
|
||||
.type main, %function
|
||||
main:
|
||||
hlt
|
||||
retq
|
||||
.size main, .-main
|
@ -1,24 +0,0 @@
|
||||
## Check that HLT instruction is handled differently depending on the flags.
|
||||
## It's a terminator in the user-level code, but the execution can resume in
|
||||
## ring 0.
|
||||
|
||||
# RUN: %clang %cflags %s -static -o %t.exe -nostdlib
|
||||
# RUN: llvm-bolt %t.exe --print-cfg --print-only=main --terminal-x86-hlt=0 \
|
||||
# RUN: -o %t.ring0 2>&1 | FileCheck %s --check-prefix=CHECK-RING0
|
||||
# RUN: llvm-bolt %t.exe --print-cfg --print-only=main \
|
||||
# RUN: -o %t.ring3 2>&1 | FileCheck %s --check-prefix=CHECK-RING3
|
||||
# RUN: llvm-objdump -d %t.ring0 --print-imm-hex | FileCheck %s --check-prefix=CHECK-BIN
|
||||
|
||||
# CHECK-RING0: BB Count : 1
|
||||
# CHECK-RING3: BB Count : 2
|
||||
|
||||
# CHECK-BIN: <main>:
|
||||
# CHECK-BIN-NEXT: f4 hlt
|
||||
# CHECK-BIN-NEXT: c3 retq
|
||||
|
||||
.global main
|
||||
.type main, %function
|
||||
main:
|
||||
hlt
|
||||
retq
|
||||
.size main, .-main
|
@ -1,52 +0,0 @@
|
||||
# Test the --dump-dot-func option with multiple functions
|
||||
# (includes tests for both mangled/unmangled names)
|
||||
|
||||
RUN: %clang++ %p/Inputs/multi-func.cpp -o %t.exe -Wl,-q
|
||||
|
||||
# Test 1: --dump-dot-func with specific function name (mangled)
|
||||
RUN: llvm-bolt %t.exe -o %t.bolt1 --dump-dot-func=_Z3addii -v=1 2>&1 | FileCheck %s --check-prefix=ADD
|
||||
|
||||
# Test 2: --dump-dot-func with regex pattern (main.*)
|
||||
RUN: llvm-bolt %t.exe -o %t.bolt2 --dump-dot-func="main.*" -v=1 2>&1 | FileCheck %s --check-prefix=MAIN-REGEX
|
||||
|
||||
# Test 3: --dump-dot-func with multiple specific functions (mangled names)
|
||||
RUN: llvm-bolt %t.exe -o %t.bolt3 --dump-dot-func=_Z3addii,_Z8multiplyii -v=1 2>&1 | FileCheck %s --check-prefix=MULTI
|
||||
|
||||
# Test 4: No option specified should create no dot files
|
||||
RUN: llvm-bolt %t.exe -o %t.bolt4 2>&1 | FileCheck %s --check-prefix=NONE
|
||||
|
||||
# Test 5: --dump-dot-func with non-existent function
|
||||
RUN: llvm-bolt %t.exe -o %t.bolt5 --dump-dot-func=nonexistent -v=1 2>&1 | FileCheck %s --check-prefix=NONEXISTENT
|
||||
|
||||
# Test 6: Backward compatibility - --dump-dot-all should still work
|
||||
RUN: llvm-bolt %t.exe -o %t.bolt6 --dump-dot-all -v=1 2>&1 | FileCheck %s --check-prefix=ALL
|
||||
|
||||
# Test 7: Test with unmangled function name (main function)
|
||||
RUN: llvm-bolt %t.exe -o %t.bolt7 --dump-dot-func=main -v=1 2>&1 | FileCheck %s --check-prefix=MAIN-UNMANGLED
|
||||
|
||||
# Check that specific functions are dumped
|
||||
ADD: BOLT-INFO: dumping CFG to _Z3addii-00_build-cfg.dot
|
||||
ADD-NOT: BOLT-INFO: dumping CFG to main-00_build-cfg.dot
|
||||
ADD-NOT: BOLT-INFO: dumping CFG to _Z8multiplyii-00_build-cfg.dot
|
||||
ADD-NOT: BOLT-INFO: dumping CFG to _Z11main_helperv-00_build-cfg.dot
|
||||
|
||||
MAIN-REGEX-DAG: BOLT-INFO: dumping CFG to main-00_build-cfg.dot
|
||||
MAIN-REGEX-NOT: BOLT-INFO: dumping CFG to _Z3addii-00_build-cfg.dot
|
||||
MAIN-REGEX-NOT: BOLT-INFO: dumping CFG to _Z8multiplyii-00_build-cfg.dot
|
||||
|
||||
MULTI-DAG: BOLT-INFO: dumping CFG to _Z3addii-00_build-cfg.dot
|
||||
MULTI-DAG: BOLT-INFO: dumping CFG to _Z8multiplyii-00_build-cfg.dot
|
||||
MULTI-NOT: BOLT-INFO: dumping CFG to main-00_build-cfg.dot
|
||||
MULTI-NOT: BOLT-INFO: dumping CFG to _Z11main_helperv-00_build-cfg.dot
|
||||
|
||||
# Should be no dumping messages when no option is specified
|
||||
NONE-NOT: BOLT-INFO: dumping CFG
|
||||
|
||||
# Should be no dumping messages for non-existent function
|
||||
NONEXISTENT-NOT: BOLT-INFO: dumping CFG
|
||||
|
||||
ALL: BOLT-INFO: dumping CFG to main-00_build-cfg.dot
|
||||
|
||||
MAIN-UNMANGLED: BOLT-INFO: dumping CFG to main-00_build-cfg.dot
|
||||
MAIN-UNMANGLED-NOT: BOLT-INFO: dumping CFG to _Z3addii-00_build-cfg.dot
|
||||
MAIN-UNMANGLED-NOT: BOLT-INFO: dumping CFG to _Z8multiplyii-00_build-cfg.dot
|
@ -544,7 +544,7 @@ runClangTidy(clang::tidy::ClangTidyContext &Context,
|
||||
ArrayRef<std::string> InputFiles,
|
||||
llvm::IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem> BaseFS,
|
||||
bool ApplyAnyFix, bool EnableCheckProfile,
|
||||
llvm::StringRef StoreCheckProfile, bool Quiet) {
|
||||
llvm::StringRef StoreCheckProfile) {
|
||||
ClangTool Tool(Compilations, InputFiles,
|
||||
std::make_shared<PCHContainerOperations>(), BaseFS);
|
||||
|
||||
@ -581,9 +581,8 @@ runClangTidy(clang::tidy::ClangTidyContext &Context,
|
||||
class ActionFactory : public FrontendActionFactory {
|
||||
public:
|
||||
ActionFactory(ClangTidyContext &Context,
|
||||
IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem> BaseFS,
|
||||
bool Quiet)
|
||||
: ConsumerFactory(Context, std::move(BaseFS)), Quiet(Quiet) {}
|
||||
IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem> BaseFS)
|
||||
: ConsumerFactory(Context, std::move(BaseFS)) {}
|
||||
std::unique_ptr<FrontendAction> create() override {
|
||||
return std::make_unique<Action>(&ConsumerFactory);
|
||||
}
|
||||
@ -594,8 +593,6 @@ runClangTidy(clang::tidy::ClangTidyContext &Context,
|
||||
DiagnosticConsumer *DiagConsumer) override {
|
||||
// Explicitly ask to define __clang_analyzer__ macro.
|
||||
Invocation->getPreprocessorOpts().SetUpStaticAnalyzer = true;
|
||||
if (Quiet)
|
||||
Invocation->getDiagnosticOpts().ShowCarets = false;
|
||||
return FrontendActionFactory::runInvocation(
|
||||
Invocation, Files, PCHContainerOps, DiagConsumer);
|
||||
}
|
||||
@ -614,10 +611,9 @@ runClangTidy(clang::tidy::ClangTidyContext &Context,
|
||||
};
|
||||
|
||||
ClangTidyASTConsumerFactory ConsumerFactory;
|
||||
bool Quiet;
|
||||
};
|
||||
|
||||
ActionFactory Factory(Context, std::move(BaseFS), Quiet);
|
||||
ActionFactory Factory(Context, std::move(BaseFS));
|
||||
Tool.run(&Factory);
|
||||
return DiagConsumer.take();
|
||||
}
|
||||
|
@ -94,8 +94,7 @@ runClangTidy(clang::tidy::ClangTidyContext &Context,
|
||||
ArrayRef<std::string> InputFiles,
|
||||
llvm::IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem> BaseFS,
|
||||
bool ApplyAnyFix, bool EnableCheckProfile = false,
|
||||
llvm::StringRef StoreCheckProfile = StringRef(),
|
||||
bool Quiet = false);
|
||||
llvm::StringRef StoreCheckProfile = StringRef());
|
||||
|
||||
/// Controls what kind of fixes clang-tidy is allowed to apply.
|
||||
enum FixBehaviour {
|
||||
|
@ -89,9 +89,13 @@ def write_header(
|
||||
+ check_name_camel.upper()
|
||||
+ "_H"
|
||||
)
|
||||
f.write("//===--- ")
|
||||
f.write(os.path.basename(filename))
|
||||
f.write(" - clang-tidy ")
|
||||
f.write("-" * max(0, 42 - len(os.path.basename(filename))))
|
||||
f.write("*- C++ -*-===//")
|
||||
f.write(
|
||||
"""
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
@ -141,9 +145,13 @@ def write_implementation(
|
||||
filename = os.path.join(module_path, check_name_camel) + ".cpp"
|
||||
print("Creating %s..." % filename)
|
||||
with io.open(filename, "w", encoding="utf8", newline="\n") as f:
|
||||
f.write("//===--- ")
|
||||
f.write(os.path.basename(filename))
|
||||
f.write(" - clang-tidy ")
|
||||
f.write("-" * max(0, 51 - len(os.path.basename(filename))))
|
||||
f.write("-===//")
|
||||
f.write(
|
||||
"""
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
|
@ -1575,10 +1575,6 @@ template <typename T, std::size_t N = SmallDataStructureSize>
|
||||
using ParamToSmallSetMap =
|
||||
llvm::DenseMap<const ParmVarDecl *, llvm::SmallSet<T, N>>;
|
||||
|
||||
template <typename T, std::size_t N = SmallDataStructureSize>
|
||||
using ParamToSmallPtrSetMap =
|
||||
llvm::DenseMap<const ParmVarDecl *, llvm::SmallPtrSet<T, N>>;
|
||||
|
||||
/// Returns whether the sets mapped to the two elements in the map have at
|
||||
/// least one element in common.
|
||||
template <typename MapTy, typename ElemTy>
|
||||
@ -1703,7 +1699,7 @@ public:
|
||||
/// Implements the heuristic that marks two parameters related if the same
|
||||
/// member is accessed (referred to) inside the current function's body.
|
||||
class AccessedSameMemberOf {
|
||||
ParamToSmallPtrSetMap<const Decl *> AccessedMembers;
|
||||
ParamToSmallSetMap<const Decl *> AccessedMembers;
|
||||
|
||||
public:
|
||||
void setup(const FunctionDecl *FD) {
|
||||
|
@ -188,7 +188,7 @@ static bool isKnownToHaveValue(const Expr &Cond, const ASTContext &Ctx,
|
||||
/// \return true iff all `CallExprs` visited have callees; false otherwise
|
||||
/// indicating there is an unresolved indirect call.
|
||||
static bool populateCallees(const Stmt *StmtNode,
|
||||
llvm::SmallPtrSet<const Decl *, 16> &Callees) {
|
||||
llvm::SmallSet<const Decl *, 16> &Callees) {
|
||||
if (const auto *Call = dyn_cast<CallExpr>(StmtNode)) {
|
||||
const Decl *Callee = Call->getDirectCallee();
|
||||
|
||||
@ -212,7 +212,7 @@ static bool populateCallees(const Stmt *StmtNode,
|
||||
/// returns true iff `SCC` contains `Func` and its' function set overlaps with
|
||||
/// `Callees`
|
||||
static bool overlap(ArrayRef<CallGraphNode *> SCC,
|
||||
const llvm::SmallPtrSet<const Decl *, 16> &Callees,
|
||||
const llvm::SmallSet<const Decl *, 16> &Callees,
|
||||
const Decl *Func) {
|
||||
bool ContainsFunc = false, Overlap = false;
|
||||
|
||||
@ -264,7 +264,7 @@ static bool hasRecursionOverStaticLoopCondVariables(const Expr *Cond,
|
||||
if (!hasStaticLocalVariable(Cond))
|
||||
return false;
|
||||
|
||||
llvm::SmallPtrSet<const Decl *, 16> CalleesInLoop;
|
||||
llvm::SmallSet<const Decl *, 16> CalleesInLoop;
|
||||
|
||||
if (!populateCallees(LoopStmt, CalleesInLoop)) {
|
||||
// If there are unresolved indirect calls, we assume there could
|
||||
|
@ -15,12 +15,14 @@ using namespace clang::ast_matchers;
|
||||
|
||||
namespace clang::tidy::bugprone {
|
||||
|
||||
namespace {
|
||||
|
||||
// Determine if the result of an expression is "stored" in some way.
|
||||
// It is true if the value is stored into a variable or used as initialization
|
||||
// or passed to a function or constructor.
|
||||
// For this use case compound assignments are not counted as a "store" (the 'E'
|
||||
// expression should have pointer type).
|
||||
static bool isExprValueStored(const Expr *E, ASTContext &C) {
|
||||
bool isExprValueStored(const Expr *E, ASTContext &C) {
|
||||
E = E->IgnoreParenCasts();
|
||||
// Get first non-paren, non-cast parent.
|
||||
ParentMapContext &PMap = C.getParentMapContext();
|
||||
@ -47,8 +49,6 @@ static bool isExprValueStored(const Expr *E, ASTContext &C) {
|
||||
return isa<CallExpr, CXXConstructExpr>(ParentE);
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
AST_MATCHER_P(CXXTryStmt, hasHandlerFor,
|
||||
ast_matchers::internal::Matcher<QualType>, InnerMatcher) {
|
||||
for (unsigned NH = Node.getNumHandlers(), I = 0; I < NH; ++I) {
|
||||
|
@ -14,8 +14,10 @@ using namespace clang::ast_matchers;
|
||||
|
||||
namespace clang::tidy::bugprone {
|
||||
|
||||
static bool isConcatenatedLiteralsOnPurpose(ASTContext *Ctx,
|
||||
const StringLiteral *Lit) {
|
||||
namespace {
|
||||
|
||||
bool isConcatenatedLiteralsOnPurpose(ASTContext *Ctx,
|
||||
const StringLiteral *Lit) {
|
||||
// String literals surrounded by parentheses are assumed to be on purpose.
|
||||
// i.e.: const char* Array[] = { ("a" "b" "c"), "d", [...] };
|
||||
|
||||
@ -56,8 +58,6 @@ static bool isConcatenatedLiteralsOnPurpose(ASTContext *Ctx,
|
||||
return false;
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
AST_MATCHER_P(StringLiteral, isConcatenatedLiteral, unsigned,
|
||||
MaxConcatenatedTokens) {
|
||||
return Node.getNumConcatenated() > 1 &&
|
||||
|
@ -46,9 +46,7 @@ enum class ConversionKind {
|
||||
ToLongDouble
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
static ConversionKind classifyConversionFunc(const FunctionDecl *FD) {
|
||||
ConversionKind classifyConversionFunc(const FunctionDecl *FD) {
|
||||
return llvm::StringSwitch<ConversionKind>(FD->getName())
|
||||
.Cases("atoi", "atol", ConversionKind::ToInt)
|
||||
.Case("atoll", ConversionKind::ToLongInt)
|
||||
@ -56,8 +54,8 @@ static ConversionKind classifyConversionFunc(const FunctionDecl *FD) {
|
||||
.Default(ConversionKind::None);
|
||||
}
|
||||
|
||||
static ConversionKind classifyFormatString(StringRef Fmt, const LangOptions &LO,
|
||||
const TargetInfo &TI) {
|
||||
ConversionKind classifyFormatString(StringRef Fmt, const LangOptions &LO,
|
||||
const TargetInfo &TI) {
|
||||
// Scan the format string for the first problematic format specifier, then
|
||||
// report that as the conversion type. This will miss additional conversion
|
||||
// specifiers, but that is acceptable behavior.
|
||||
@ -130,7 +128,7 @@ static ConversionKind classifyFormatString(StringRef Fmt, const LangOptions &LO,
|
||||
return H.get();
|
||||
}
|
||||
|
||||
static StringRef classifyConversionType(ConversionKind K) {
|
||||
StringRef classifyConversionType(ConversionKind K) {
|
||||
switch (K) {
|
||||
case ConversionKind::None:
|
||||
llvm_unreachable("Unexpected conversion kind");
|
||||
@ -150,7 +148,7 @@ static StringRef classifyConversionType(ConversionKind K) {
|
||||
llvm_unreachable("Unknown conversion kind");
|
||||
}
|
||||
|
||||
static StringRef classifyReplacement(ConversionKind K) {
|
||||
StringRef classifyReplacement(ConversionKind K) {
|
||||
switch (K) {
|
||||
case ConversionKind::None:
|
||||
llvm_unreachable("Unexpected conversion kind");
|
||||
@ -175,6 +173,7 @@ static StringRef classifyReplacement(ConversionKind K) {
|
||||
}
|
||||
llvm_unreachable("Unknown conversion kind");
|
||||
}
|
||||
} // unnamed namespace
|
||||
|
||||
void StrToNumCheck::check(const MatchFinder::MatchResult &Result) {
|
||||
const auto *Call = Result.Nodes.getNodeAs<CallExpr>("expr");
|
||||
|
@ -59,9 +59,7 @@ AST_MATCHER(FunctionDecl, isPlacementOverload) {
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
static OverloadedOperatorKind getCorrespondingOverload(const FunctionDecl *FD) {
|
||||
OverloadedOperatorKind getCorrespondingOverload(const FunctionDecl *FD) {
|
||||
switch (FD->getOverloadedOperator()) {
|
||||
default:
|
||||
break;
|
||||
@ -77,7 +75,7 @@ static OverloadedOperatorKind getCorrespondingOverload(const FunctionDecl *FD) {
|
||||
llvm_unreachable("Not an overloaded allocation operator");
|
||||
}
|
||||
|
||||
static const char *getOperatorName(OverloadedOperatorKind K) {
|
||||
const char *getOperatorName(OverloadedOperatorKind K) {
|
||||
switch (K) {
|
||||
default:
|
||||
break;
|
||||
@ -93,14 +91,13 @@ static const char *getOperatorName(OverloadedOperatorKind K) {
|
||||
llvm_unreachable("Not an overloaded allocation operator");
|
||||
}
|
||||
|
||||
static bool areCorrespondingOverloads(const FunctionDecl *LHS,
|
||||
const FunctionDecl *RHS) {
|
||||
bool areCorrespondingOverloads(const FunctionDecl *LHS,
|
||||
const FunctionDecl *RHS) {
|
||||
return RHS->getOverloadedOperator() == getCorrespondingOverload(LHS);
|
||||
}
|
||||
|
||||
static bool
|
||||
hasCorrespondingOverloadInBaseClass(const CXXMethodDecl *MD,
|
||||
const CXXRecordDecl *RD = nullptr) {
|
||||
bool hasCorrespondingOverloadInBaseClass(const CXXMethodDecl *MD,
|
||||
const CXXRecordDecl *RD = nullptr) {
|
||||
if (RD) {
|
||||
// Check the methods in the given class and accessible to derived classes.
|
||||
for (const auto *BMD : RD->methods())
|
||||
@ -127,6 +124,8 @@ hasCorrespondingOverloadInBaseClass(const CXXMethodDecl *MD,
|
||||
return false;
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
void NewDeleteOverloadsCheck::registerMatchers(MatchFinder *Finder) {
|
||||
// Match all operator new and operator delete overloads (including the array
|
||||
// forms). Do not match implicit operators, placement operators, or
|
||||
|
@ -29,13 +29,11 @@ void UnconventionalAssignOperatorCheck::registerMatchers(
|
||||
const auto HasGoodReturnType =
|
||||
cxxMethodDecl(returns(hasCanonicalType(lValueReferenceType(pointee(
|
||||
unless(isConstQualified()),
|
||||
anyOf(autoType(),
|
||||
hasDeclaration(declaresSameEntityAsBoundNode("class"))))))));
|
||||
anyOf(autoType(), hasDeclaration(equalsBoundNode("class"))))))));
|
||||
|
||||
const auto IsSelf = qualType(hasCanonicalType(
|
||||
anyOf(hasDeclaration(declaresSameEntityAsBoundNode("class")),
|
||||
referenceType(pointee(
|
||||
hasDeclaration(declaresSameEntityAsBoundNode("class")))))));
|
||||
anyOf(hasDeclaration(equalsBoundNode("class")),
|
||||
referenceType(pointee(hasDeclaration(equalsBoundNode("class")))))));
|
||||
const auto IsAssign =
|
||||
cxxMethodDecl(unless(anyOf(isDeleted(), isPrivate(), isImplicit())),
|
||||
hasName("operator="), ofClass(recordDecl().bind("class")))
|
||||
|
@ -395,12 +395,16 @@ void MacroToEnumCallbacks::Endif(SourceLocation Loc, SourceLocation IfLoc) {
|
||||
--CurrentFile->ConditionScopes;
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
template <size_t N>
|
||||
static bool textEquals(const char (&Needle)[N], const char *HayStack) {
|
||||
bool textEquals(const char (&Needle)[N], const char *HayStack) {
|
||||
return StringRef{HayStack, N - 1} == Needle;
|
||||
}
|
||||
|
||||
template <size_t N> static size_t len(const char (&)[N]) { return N - 1; }
|
||||
template <size_t N> size_t len(const char (&)[N]) { return N - 1; }
|
||||
|
||||
} // namespace
|
||||
|
||||
void MacroToEnumCallbacks::PragmaDirective(SourceLocation Loc,
|
||||
PragmaIntroducerKind Introducer) {
|
||||
|
@ -16,13 +16,14 @@ using namespace clang::ast_matchers;
|
||||
|
||||
namespace clang::tidy::modernize {
|
||||
|
||||
static constexpr char ConstructorCall[] = "constructorCall";
|
||||
static constexpr char ResetCall[] = "resetCall";
|
||||
static constexpr char NewExpression[] = "newExpression";
|
||||
namespace {
|
||||
|
||||
static std::string getNewExprName(const CXXNewExpr *NewExpr,
|
||||
const SourceManager &SM,
|
||||
const LangOptions &Lang) {
|
||||
constexpr char ConstructorCall[] = "constructorCall";
|
||||
constexpr char ResetCall[] = "resetCall";
|
||||
constexpr char NewExpression[] = "newExpression";
|
||||
|
||||
std::string getNewExprName(const CXXNewExpr *NewExpr, const SourceManager &SM,
|
||||
const LangOptions &Lang) {
|
||||
StringRef WrittenName = Lexer::getSourceText(
|
||||
CharSourceRange::getTokenRange(
|
||||
NewExpr->getAllocatedTypeSourceInfo()->getTypeLoc().getSourceRange()),
|
||||
@ -33,6 +34,8 @@ static std::string getNewExprName(const CXXNewExpr *NewExpr,
|
||||
return WrittenName.str();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
const char MakeSmartPtrCheck::PointerType[] = "pointerType";
|
||||
|
||||
MakeSmartPtrCheck::MakeSmartPtrCheck(StringRef Name, ClangTidyContext *Context,
|
||||
|
@ -19,7 +19,9 @@ using namespace clang::ast_matchers;
|
||||
|
||||
namespace clang::tidy::modernize {
|
||||
|
||||
static bool containsEscapes(StringRef HayStack, StringRef Escapes) {
|
||||
namespace {
|
||||
|
||||
bool containsEscapes(StringRef HayStack, StringRef Escapes) {
|
||||
size_t BackSlash = HayStack.find('\\');
|
||||
if (BackSlash == StringRef::npos)
|
||||
return false;
|
||||
@ -33,16 +35,16 @@ static bool containsEscapes(StringRef HayStack, StringRef Escapes) {
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool isRawStringLiteral(StringRef Text) {
|
||||
bool isRawStringLiteral(StringRef Text) {
|
||||
// Already a raw string literal if R comes before ".
|
||||
const size_t QuotePos = Text.find('"');
|
||||
assert(QuotePos != StringRef::npos);
|
||||
return (QuotePos > 0) && (Text[QuotePos - 1] == 'R');
|
||||
}
|
||||
|
||||
static bool containsEscapedCharacters(const MatchFinder::MatchResult &Result,
|
||||
const StringLiteral *Literal,
|
||||
const CharsBitSet &DisallowedChars) {
|
||||
bool containsEscapedCharacters(const MatchFinder::MatchResult &Result,
|
||||
const StringLiteral *Literal,
|
||||
const CharsBitSet &DisallowedChars) {
|
||||
// FIXME: Handle L"", u8"", u"" and U"" literals.
|
||||
if (!Literal->isOrdinary())
|
||||
return false;
|
||||
@ -62,12 +64,14 @@ static bool containsEscapedCharacters(const MatchFinder::MatchResult &Result,
|
||||
return containsEscapes(Text, R"('\"?x01)");
|
||||
}
|
||||
|
||||
static bool containsDelimiter(StringRef Bytes, const std::string &Delimiter) {
|
||||
bool containsDelimiter(StringRef Bytes, const std::string &Delimiter) {
|
||||
return Bytes.find(Delimiter.empty()
|
||||
? std::string(R"lit()")lit")
|
||||
: (")" + Delimiter + R"(")")) != StringRef::npos;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
RawStringLiteralCheck::RawStringLiteralCheck(StringRef Name,
|
||||
ClangTidyContext *Context)
|
||||
: ClangTidyCheck(Name, Context),
|
||||
|
@ -29,13 +29,12 @@
|
||||
using namespace clang::ast_matchers;
|
||||
|
||||
namespace clang::tidy::objc {
|
||||
namespace {
|
||||
|
||||
static constexpr StringRef WeakText = "__weak";
|
||||
static constexpr StringRef StrongText = "__strong";
|
||||
static constexpr StringRef UnsafeUnretainedText = "__unsafe_unretained";
|
||||
|
||||
namespace {
|
||||
|
||||
/// Matches ObjCIvarRefExpr, DeclRefExpr, or MemberExpr that reference
|
||||
/// Objective-C object (or block) variables or fields whose object lifetimes
|
||||
/// are not __unsafe_unretained.
|
||||
@ -50,8 +49,6 @@ AST_POLYMORPHIC_MATCHER(isObjCManagedLifetime,
|
||||
QT.getQualifiers().getObjCLifetime() > Qualifiers::OCL_ExplicitNone;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
static std::optional<FixItHint>
|
||||
fixItHintReplacementForOwnershipString(StringRef Text, CharSourceRange Range,
|
||||
StringRef Ownership) {
|
||||
@ -96,6 +93,8 @@ fixItHintForVarDecl(const VarDecl *VD, const SourceManager &SM,
|
||||
return FixItHint::CreateInsertion(Range.getBegin(), "__unsafe_unretained ");
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void NSInvocationArgumentLifetimeCheck::registerMatchers(MatchFinder *Finder) {
|
||||
Finder->addMatcher(
|
||||
traverse(
|
||||
|
@ -27,14 +27,11 @@ enum NamingStyle {
|
||||
CategoryProperty = 2,
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
/// For now we will only fix 'CamelCase' or 'abc_CamelCase' property to
|
||||
/// 'camelCase' or 'abc_camelCase'. For other cases the users need to
|
||||
/// come up with a proper name by their own.
|
||||
/// FIXME: provide fix for snake_case to snakeCase
|
||||
static FixItHint generateFixItHint(const ObjCPropertyDecl *Decl,
|
||||
NamingStyle Style) {
|
||||
FixItHint generateFixItHint(const ObjCPropertyDecl *Decl, NamingStyle Style) {
|
||||
auto Name = Decl->getName();
|
||||
auto NewName = Decl->getName().str();
|
||||
size_t Index = 0;
|
||||
@ -53,7 +50,7 @@ static FixItHint generateFixItHint(const ObjCPropertyDecl *Decl,
|
||||
return {};
|
||||
}
|
||||
|
||||
static std::string validPropertyNameRegex(bool UsedInMatcher) {
|
||||
std::string validPropertyNameRegex(bool UsedInMatcher) {
|
||||
// Allow any of these names:
|
||||
// foo
|
||||
// fooBar
|
||||
@ -75,13 +72,13 @@ static std::string validPropertyNameRegex(bool UsedInMatcher) {
|
||||
return StartMatcher + "([a-z]|[A-Z][A-Z0-9])[a-z0-9A-Z]*$";
|
||||
}
|
||||
|
||||
static bool hasCategoryPropertyPrefix(llvm::StringRef PropertyName) {
|
||||
bool hasCategoryPropertyPrefix(llvm::StringRef PropertyName) {
|
||||
auto RegexExp =
|
||||
llvm::Regex("^[a-zA-Z][a-zA-Z0-9]*_[a-zA-Z0-9][a-zA-Z0-9_]+$");
|
||||
return RegexExp.match(PropertyName);
|
||||
}
|
||||
|
||||
static bool prefixedPropertyNameValid(llvm::StringRef PropertyName) {
|
||||
bool prefixedPropertyNameValid(llvm::StringRef PropertyName) {
|
||||
size_t Start = PropertyName.find_first_of('_');
|
||||
assert(Start != llvm::StringRef::npos && Start + 1 < PropertyName.size());
|
||||
auto Prefix = PropertyName.substr(0, Start);
|
||||
@ -91,6 +88,7 @@ static bool prefixedPropertyNameValid(llvm::StringRef PropertyName) {
|
||||
auto RegexExp = llvm::Regex(llvm::StringRef(validPropertyNameRegex(false)));
|
||||
return RegexExp.match(PropertyName.substr(Start + 1));
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void PropertyDeclarationCheck::registerMatchers(MatchFinder *Finder) {
|
||||
Finder->addMatcher(objcPropertyDecl(
|
||||
|
@ -17,6 +17,7 @@
|
||||
#include <optional>
|
||||
|
||||
namespace clang::tidy::performance {
|
||||
namespace {
|
||||
|
||||
using namespace ::clang::ast_matchers;
|
||||
using llvm::StringRef;
|
||||
@ -29,8 +30,8 @@ static constexpr StringRef MethodDeclId = "methodDecl";
|
||||
static constexpr StringRef FunctionDeclId = "functionDecl";
|
||||
static constexpr StringRef OldVarDeclId = "oldVarDecl";
|
||||
|
||||
static void recordFixes(const VarDecl &Var, ASTContext &Context,
|
||||
DiagnosticBuilder &Diagnostic) {
|
||||
void recordFixes(const VarDecl &Var, ASTContext &Context,
|
||||
DiagnosticBuilder &Diagnostic) {
|
||||
Diagnostic << utils::fixit::changeVarDeclToReference(Var, Context);
|
||||
if (!Var.getType().isLocalConstQualified()) {
|
||||
if (std::optional<FixItHint> Fix = utils::fixit::addQualifierToVarDecl(
|
||||
@ -39,8 +40,8 @@ static void recordFixes(const VarDecl &Var, ASTContext &Context,
|
||||
}
|
||||
}
|
||||
|
||||
static std::optional<SourceLocation> firstLocAfterNewLine(SourceLocation Loc,
|
||||
SourceManager &SM) {
|
||||
std::optional<SourceLocation> firstLocAfterNewLine(SourceLocation Loc,
|
||||
SourceManager &SM) {
|
||||
bool Invalid = false;
|
||||
const char *TextAfter = SM.getCharacterData(Loc, &Invalid);
|
||||
if (Invalid) {
|
||||
@ -50,8 +51,8 @@ static std::optional<SourceLocation> firstLocAfterNewLine(SourceLocation Loc,
|
||||
return Loc.getLocWithOffset(TextAfter[Offset] == '\0' ? Offset : Offset + 1);
|
||||
}
|
||||
|
||||
static void recordRemoval(const DeclStmt &Stmt, ASTContext &Context,
|
||||
DiagnosticBuilder &Diagnostic) {
|
||||
void recordRemoval(const DeclStmt &Stmt, ASTContext &Context,
|
||||
DiagnosticBuilder &Diagnostic) {
|
||||
auto &SM = Context.getSourceManager();
|
||||
// Attempt to remove trailing comments as well.
|
||||
auto Tok = utils::lexer::findNextTokenSkippingComments(Stmt.getEndLoc(), SM,
|
||||
@ -73,8 +74,6 @@ static void recordRemoval(const DeclStmt &Stmt, ASTContext &Context,
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
AST_MATCHER_FUNCTION_P(StatementMatcher,
|
||||
isRefReturningMethodCallWithConstOverloads,
|
||||
std::vector<StringRef>, ExcludedContainerTypes) {
|
||||
@ -131,8 +130,6 @@ AST_MATCHER_FUNCTION_P(StatementMatcher, initializerReturnsReferenceToConst,
|
||||
hasUnaryOperand(OldVarDeclRef)))));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
// This checks that the variable itself is only used as const, and also makes
|
||||
// sure that it does not reference another variable that could be modified in
|
||||
// the BlockStmt. It does this by checking the following:
|
||||
@ -183,13 +180,13 @@ static bool isInitializingVariableImmutable(
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool isVariableUnused(const VarDecl &Var, const Stmt &BlockStmt,
|
||||
ASTContext &Context) {
|
||||
bool isVariableUnused(const VarDecl &Var, const Stmt &BlockStmt,
|
||||
ASTContext &Context) {
|
||||
return allDeclRefExprs(Var, BlockStmt, Context).empty();
|
||||
}
|
||||
|
||||
static const SubstTemplateTypeParmType *
|
||||
getSubstitutedType(const QualType &Type, ASTContext &Context) {
|
||||
const SubstTemplateTypeParmType *getSubstitutedType(const QualType &Type,
|
||||
ASTContext &Context) {
|
||||
auto Matches = match(
|
||||
qualType(anyOf(substTemplateTypeParmType().bind("subst"),
|
||||
hasDescendant(substTemplateTypeParmType().bind("subst")))),
|
||||
@ -197,9 +194,9 @@ getSubstitutedType(const QualType &Type, ASTContext &Context) {
|
||||
return selectFirst<SubstTemplateTypeParmType>("subst", Matches);
|
||||
}
|
||||
|
||||
static bool differentReplacedTemplateParams(const QualType &VarType,
|
||||
const QualType &InitializerType,
|
||||
ASTContext &Context) {
|
||||
bool differentReplacedTemplateParams(const QualType &VarType,
|
||||
const QualType &InitializerType,
|
||||
ASTContext &Context) {
|
||||
if (const SubstTemplateTypeParmType *VarTmplType =
|
||||
getSubstitutedType(VarType, Context)) {
|
||||
if (const SubstTemplateTypeParmType *InitializerTmplType =
|
||||
@ -215,8 +212,8 @@ static bool differentReplacedTemplateParams(const QualType &VarType,
|
||||
return false;
|
||||
}
|
||||
|
||||
static QualType constructorArgumentType(const VarDecl *OldVar,
|
||||
const BoundNodes &Nodes) {
|
||||
QualType constructorArgumentType(const VarDecl *OldVar,
|
||||
const BoundNodes &Nodes) {
|
||||
if (OldVar) {
|
||||
return OldVar->getType();
|
||||
}
|
||||
@ -227,6 +224,8 @@ static QualType constructorArgumentType(const VarDecl *OldVar,
|
||||
return MethodDecl->getReturnType();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
UnnecessaryCopyInitialization::UnnecessaryCopyInitialization(
|
||||
StringRef Name, ClangTidyContext *Context)
|
||||
: ClangTidyCheck(Name, Context),
|
||||
|
@ -21,14 +21,16 @@ using namespace clang::ast_matchers;
|
||||
|
||||
namespace clang::tidy::performance {
|
||||
|
||||
static std::string paramNameOrIndex(StringRef Name, size_t Index) {
|
||||
namespace {
|
||||
|
||||
std::string paramNameOrIndex(StringRef Name, size_t Index) {
|
||||
return (Name.empty() ? llvm::Twine('#') + llvm::Twine(Index + 1)
|
||||
: llvm::Twine('\'') + Name + llvm::Twine('\''))
|
||||
.str();
|
||||
}
|
||||
|
||||
static bool hasLoopStmtAncestor(const DeclRefExpr &DeclRef, const Decl &Decl,
|
||||
ASTContext &Context) {
|
||||
bool hasLoopStmtAncestor(const DeclRefExpr &DeclRef, const Decl &Decl,
|
||||
ASTContext &Context) {
|
||||
auto Matches = match(
|
||||
traverse(TK_AsIs,
|
||||
decl(forEachDescendant(declRefExpr(
|
||||
@ -39,6 +41,8 @@ static bool hasLoopStmtAncestor(const DeclRefExpr &DeclRef, const Decl &Decl,
|
||||
return Matches.empty();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
UnnecessaryValueParamCheck::UnnecessaryValueParamCheck(
|
||||
StringRef Name, ClangTidyContext *Context)
|
||||
: ClangTidyCheck(Name, Context),
|
||||
|
@ -122,15 +122,15 @@ AST_MATCHER(EnumDecl, hasSequentialInitialValues) {
|
||||
return !AllEnumeratorsArePowersOfTwo;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
static std::string getName(const EnumDecl *Decl) {
|
||||
std::string getName(const EnumDecl *Decl) {
|
||||
if (!Decl->getDeclName())
|
||||
return "<unnamed>";
|
||||
|
||||
return Decl->getQualifiedNameAsString();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
EnumInitialValueCheck::EnumInitialValueCheck(StringRef Name,
|
||||
ClangTidyContext *Context)
|
||||
: ClangTidyCheck(Name, Context),
|
||||
|
@ -144,8 +144,6 @@ struct CognitiveComplexity final {
|
||||
void account(SourceLocation Loc, unsigned short Nesting, Criteria C);
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
// All the possible messages that can be output. The choice of the message
|
||||
// to use is based of the combination of the CognitiveComplexity::Criteria.
|
||||
// It would be nice to have it in CognitiveComplexity struct, but then it is
|
||||
@ -165,27 +163,23 @@ static const std::array<const StringRef, 4> Msgs = {{
|
||||
}};
|
||||
|
||||
// Criteria is a bitset, thus a few helpers are needed.
|
||||
static CognitiveComplexity::Criteria
|
||||
operator|(CognitiveComplexity::Criteria LHS,
|
||||
CognitiveComplexity::Criteria RHS) {
|
||||
CognitiveComplexity::Criteria operator|(CognitiveComplexity::Criteria LHS,
|
||||
CognitiveComplexity::Criteria RHS) {
|
||||
return static_cast<CognitiveComplexity::Criteria>(llvm::to_underlying(LHS) |
|
||||
llvm::to_underlying(RHS));
|
||||
}
|
||||
static CognitiveComplexity::Criteria
|
||||
operator&(CognitiveComplexity::Criteria LHS,
|
||||
CognitiveComplexity::Criteria RHS) {
|
||||
CognitiveComplexity::Criteria operator&(CognitiveComplexity::Criteria LHS,
|
||||
CognitiveComplexity::Criteria RHS) {
|
||||
return static_cast<CognitiveComplexity::Criteria>(llvm::to_underlying(LHS) &
|
||||
llvm::to_underlying(RHS));
|
||||
}
|
||||
static CognitiveComplexity::Criteria &
|
||||
operator|=(CognitiveComplexity::Criteria &LHS,
|
||||
CognitiveComplexity::Criteria RHS) {
|
||||
CognitiveComplexity::Criteria &operator|=(CognitiveComplexity::Criteria &LHS,
|
||||
CognitiveComplexity::Criteria RHS) {
|
||||
LHS = operator|(LHS, RHS);
|
||||
return LHS;
|
||||
}
|
||||
static CognitiveComplexity::Criteria &
|
||||
operator&=(CognitiveComplexity::Criteria &LHS,
|
||||
CognitiveComplexity::Criteria RHS) {
|
||||
CognitiveComplexity::Criteria &operator&=(CognitiveComplexity::Criteria &LHS,
|
||||
CognitiveComplexity::Criteria RHS) {
|
||||
LHS = operator&(LHS, RHS);
|
||||
return LHS;
|
||||
}
|
||||
@ -205,8 +199,6 @@ void CognitiveComplexity::account(SourceLocation Loc, unsigned short Nesting,
|
||||
Total += Increase;
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
class FunctionASTVisitor final
|
||||
: public RecursiveASTVisitor<FunctionASTVisitor> {
|
||||
using Base = RecursiveASTVisitor<FunctionASTVisitor>;
|
||||
|
@ -41,11 +41,9 @@ AST_MATCHER(Stmt, isNULLMacroExpansion) {
|
||||
return isNULLMacroExpansion(&Node, Finder->getASTContext());
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
static StringRef getZeroLiteralToCompareWithForType(CastKind CastExprKind,
|
||||
QualType Type,
|
||||
ASTContext &Context) {
|
||||
StringRef getZeroLiteralToCompareWithForType(CastKind CastExprKind,
|
||||
QualType Type,
|
||||
ASTContext &Context) {
|
||||
switch (CastExprKind) {
|
||||
case CK_IntegralToBoolean:
|
||||
return Type->isUnsignedIntegerType() ? "0u" : "0";
|
||||
@ -64,15 +62,15 @@ static StringRef getZeroLiteralToCompareWithForType(CastKind CastExprKind,
|
||||
}
|
||||
}
|
||||
|
||||
static bool isUnaryLogicalNotOperator(const Stmt *Statement) {
|
||||
bool isUnaryLogicalNotOperator(const Stmt *Statement) {
|
||||
const auto *UnaryOperatorExpr = dyn_cast<UnaryOperator>(Statement);
|
||||
return UnaryOperatorExpr && UnaryOperatorExpr->getOpcode() == UO_LNot;
|
||||
}
|
||||
|
||||
static void fixGenericExprCastToBool(DiagnosticBuilder &Diag,
|
||||
const ImplicitCastExpr *Cast,
|
||||
const Stmt *Parent, ASTContext &Context,
|
||||
bool UseUpperCaseLiteralSuffix) {
|
||||
void fixGenericExprCastToBool(DiagnosticBuilder &Diag,
|
||||
const ImplicitCastExpr *Cast, const Stmt *Parent,
|
||||
ASTContext &Context,
|
||||
bool UseUpperCaseLiteralSuffix) {
|
||||
// In case of expressions like (! integer), we should remove the redundant not
|
||||
// operator and use inverted comparison (integer == 0).
|
||||
bool InvertComparison =
|
||||
@ -135,8 +133,8 @@ static void fixGenericExprCastToBool(DiagnosticBuilder &Diag,
|
||||
Diag << FixItHint::CreateInsertion(EndLoc, EndLocInsertion);
|
||||
}
|
||||
|
||||
static StringRef getEquivalentBoolLiteralForExpr(const Expr *Expression,
|
||||
ASTContext &Context) {
|
||||
StringRef getEquivalentBoolLiteralForExpr(const Expr *Expression,
|
||||
ASTContext &Context) {
|
||||
if (isNULLMacroExpansion(Expression, Context)) {
|
||||
return "false";
|
||||
}
|
||||
@ -163,7 +161,7 @@ static StringRef getEquivalentBoolLiteralForExpr(const Expr *Expression,
|
||||
return {};
|
||||
}
|
||||
|
||||
static bool needsSpacePrefix(SourceLocation Loc, ASTContext &Context) {
|
||||
bool needsSpacePrefix(SourceLocation Loc, ASTContext &Context) {
|
||||
SourceRange PrefixRange(Loc.getLocWithOffset(-1), Loc);
|
||||
StringRef SpaceBeforeStmtStr = Lexer::getSourceText(
|
||||
CharSourceRange::getCharRange(PrefixRange), Context.getSourceManager(),
|
||||
@ -175,10 +173,9 @@ static bool needsSpacePrefix(SourceLocation Loc, ASTContext &Context) {
|
||||
return !AllowedCharacters.contains(SpaceBeforeStmtStr.back());
|
||||
}
|
||||
|
||||
static void fixGenericExprCastFromBool(DiagnosticBuilder &Diag,
|
||||
const ImplicitCastExpr *Cast,
|
||||
ASTContext &Context,
|
||||
StringRef OtherType) {
|
||||
void fixGenericExprCastFromBool(DiagnosticBuilder &Diag,
|
||||
const ImplicitCastExpr *Cast,
|
||||
ASTContext &Context, StringRef OtherType) {
|
||||
if (!Context.getLangOpts().CPlusPlus) {
|
||||
Diag << FixItHint::CreateInsertion(Cast->getBeginLoc(),
|
||||
(Twine("(") + OtherType + ")").str());
|
||||
@ -203,9 +200,8 @@ static void fixGenericExprCastFromBool(DiagnosticBuilder &Diag,
|
||||
}
|
||||
}
|
||||
|
||||
static StringRef
|
||||
getEquivalentForBoolLiteral(const CXXBoolLiteralExpr *BoolLiteral,
|
||||
QualType DestType, ASTContext &Context) {
|
||||
StringRef getEquivalentForBoolLiteral(const CXXBoolLiteralExpr *BoolLiteral,
|
||||
QualType DestType, ASTContext &Context) {
|
||||
// Prior to C++11, false literal could be implicitly converted to pointer.
|
||||
if (!Context.getLangOpts().CPlusPlus11 &&
|
||||
(DestType->isPointerType() || DestType->isMemberPointerType()) &&
|
||||
@ -226,8 +222,8 @@ getEquivalentForBoolLiteral(const CXXBoolLiteralExpr *BoolLiteral,
|
||||
return BoolLiteral->getValue() ? "1" : "0";
|
||||
}
|
||||
|
||||
static bool isCastAllowedInCondition(const ImplicitCastExpr *Cast,
|
||||
ASTContext &Context) {
|
||||
bool isCastAllowedInCondition(const ImplicitCastExpr *Cast,
|
||||
ASTContext &Context) {
|
||||
std::queue<const Stmt *> Q;
|
||||
Q.push(Cast);
|
||||
|
||||
@ -255,6 +251,8 @@ static bool isCastAllowedInCondition(const ImplicitCastExpr *Cast,
|
||||
return false;
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
ImplicitBoolConversionCheck::ImplicitBoolConversionCheck(
|
||||
StringRef Name, ClangTidyContext *Context)
|
||||
: ClangTidyCheck(Name, Context),
|
||||
|
@ -28,11 +28,8 @@ AST_MATCHER_P(QualType, hasUnqualifiedType,
|
||||
|
||||
enum class Qualifier { Const, Volatile, Restrict };
|
||||
|
||||
} // namespace
|
||||
|
||||
static std::optional<Token>
|
||||
findQualToken(const VarDecl *Decl, Qualifier Qual,
|
||||
const MatchFinder::MatchResult &Result) {
|
||||
std::optional<Token> findQualToken(const VarDecl *Decl, Qualifier Qual,
|
||||
const MatchFinder::MatchResult &Result) {
|
||||
// Since either of the locs can be in a macro, use `makeFileCharRange` to be
|
||||
// sure that we have a consistent `CharSourceRange`, located entirely in the
|
||||
// source file.
|
||||
@ -61,7 +58,7 @@ findQualToken(const VarDecl *Decl, Qualifier Qual,
|
||||
*Result.SourceManager);
|
||||
}
|
||||
|
||||
static std::optional<SourceRange>
|
||||
std::optional<SourceRange>
|
||||
getTypeSpecifierLocation(const VarDecl *Var,
|
||||
const MatchFinder::MatchResult &Result) {
|
||||
SourceRange TypeSpecifier(
|
||||
@ -76,8 +73,8 @@ getTypeSpecifierLocation(const VarDecl *Var,
|
||||
return TypeSpecifier;
|
||||
}
|
||||
|
||||
static std::optional<SourceRange>
|
||||
mergeReplacementRange(SourceRange &TypeSpecifier, const Token &ConstToken) {
|
||||
std::optional<SourceRange> mergeReplacementRange(SourceRange &TypeSpecifier,
|
||||
const Token &ConstToken) {
|
||||
if (TypeSpecifier.getBegin().getLocWithOffset(-1) == ConstToken.getEndLoc()) {
|
||||
TypeSpecifier.setBegin(ConstToken.getLocation());
|
||||
return std::nullopt;
|
||||
@ -89,19 +86,21 @@ mergeReplacementRange(SourceRange &TypeSpecifier, const Token &ConstToken) {
|
||||
return SourceRange(ConstToken.getLocation(), ConstToken.getEndLoc());
|
||||
}
|
||||
|
||||
static bool isPointerConst(QualType QType) {
|
||||
bool isPointerConst(QualType QType) {
|
||||
QualType Pointee = QType->getPointeeType();
|
||||
assert(!Pointee.isNull() && "can't have a null Pointee");
|
||||
return Pointee.isConstQualified();
|
||||
}
|
||||
|
||||
static bool isAutoPointerConst(QualType QType) {
|
||||
bool isAutoPointerConst(QualType QType) {
|
||||
QualType Pointee =
|
||||
cast<AutoType>(QType->getPointeeType().getTypePtr())->desugar();
|
||||
assert(!Pointee.isNull() && "can't have a null Pointee");
|
||||
return Pointee.isConstQualified();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
QualifiedAutoCheck::QualifiedAutoCheck(StringRef Name,
|
||||
ClangTidyContext *Context)
|
||||
: ClangTidyCheck(Name, Context),
|
||||
|
@ -14,18 +14,19 @@ using namespace clang::ast_matchers;
|
||||
|
||||
namespace clang::tidy::readability {
|
||||
|
||||
static const char *const RedundantReturnDiag =
|
||||
"redundant return statement at the end "
|
||||
"of a function with a void return type";
|
||||
static const char *const RedundantContinueDiag =
|
||||
"redundant continue statement at the "
|
||||
"end of loop statement";
|
||||
namespace {
|
||||
|
||||
static bool isLocationInMacroExpansion(const SourceManager &SM,
|
||||
SourceLocation Loc) {
|
||||
const char *const RedundantReturnDiag = "redundant return statement at the end "
|
||||
"of a function with a void return type";
|
||||
const char *const RedundantContinueDiag = "redundant continue statement at the "
|
||||
"end of loop statement";
|
||||
|
||||
bool isLocationInMacroExpansion(const SourceManager &SM, SourceLocation Loc) {
|
||||
return SM.isMacroBodyExpansion(Loc) || SM.isMacroArgExpansion(Loc);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void RedundantControlFlowCheck::registerMatchers(MatchFinder *Finder) {
|
||||
Finder->addMatcher(
|
||||
functionDecl(isDefinition(), returns(voidType()),
|
||||
|
@ -717,7 +717,7 @@ int clangTidyMain(int argc, const char **argv) {
|
||||
EnableModuleHeadersParsing);
|
||||
std::vector<ClangTidyError> Errors =
|
||||
runClangTidy(Context, OptionsParser->getCompilations(), PathList, BaseFS,
|
||||
FixNotes, EnableCheckProfile, ProfilePrefix, Quiet);
|
||||
FixNotes, EnableCheckProfile, ProfilePrefix);
|
||||
bool FoundErrors = llvm::any_of(Errors, [](const ClangTidyError &E) {
|
||||
return E.DiagLevel == ClangTidyError::Error;
|
||||
});
|
||||
|
@ -13,14 +13,16 @@
|
||||
|
||||
namespace clang::tidy::utils::type_traits {
|
||||
|
||||
static bool classHasTrivialCopyAndDestroy(QualType Type) {
|
||||
namespace {
|
||||
|
||||
bool classHasTrivialCopyAndDestroy(QualType Type) {
|
||||
auto *Record = Type->getAsCXXRecordDecl();
|
||||
return Record && Record->hasDefinition() &&
|
||||
!Record->hasNonTrivialCopyConstructor() &&
|
||||
!Record->hasNonTrivialDestructor();
|
||||
}
|
||||
|
||||
static bool hasDeletedCopyConstructor(QualType Type) {
|
||||
bool hasDeletedCopyConstructor(QualType Type) {
|
||||
auto *Record = Type->getAsCXXRecordDecl();
|
||||
if (!Record || !Record->hasDefinition())
|
||||
return false;
|
||||
@ -31,6 +33,8 @@ static bool hasDeletedCopyConstructor(QualType Type) {
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
std::optional<bool> isExpensiveToCopy(QualType Type,
|
||||
const ASTContext &Context) {
|
||||
if (Type->isDependentType() || Type->isIncompleteType())
|
||||
|
@ -985,7 +985,7 @@ resolveForwardingParameters(const FunctionDecl *D, unsigned MaxDepth) {
|
||||
// Recurse on pack parameters
|
||||
size_t Depth = 0;
|
||||
const FunctionDecl *CurrentFunction = D;
|
||||
llvm::SmallPtrSet<const FunctionTemplateDecl *, 4> SeenTemplates;
|
||||
llvm::SmallSet<const FunctionTemplateDecl *, 4> SeenTemplates;
|
||||
if (const auto *Template = D->getPrimaryTemplate()) {
|
||||
SeenTemplates.insert(Template);
|
||||
}
|
||||
|
@ -6,7 +6,7 @@ add_subdirectory(support)
|
||||
|
||||
# Configure the Features.inc file.
|
||||
if (NOT DEFINED CLANGD_BUILD_XPC)
|
||||
if("${CMAKE_SYSTEM_NAME}" MATCHES "Darwin")
|
||||
if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
|
||||
set(CLANGD_BUILD_XPC_DEFAULT ON)
|
||||
else ()
|
||||
set(CLANGD_BUILD_XPC_DEFAULT OFF)
|
||||
@ -193,7 +193,7 @@ if(CLANGD_TIDY_CHECKS)
|
||||
endif()
|
||||
|
||||
add_subdirectory(refactor/tweaks)
|
||||
if ("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux")
|
||||
if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
|
||||
# FIXME: Make fuzzer not use linux-specific APIs, build it everywhere.
|
||||
add_subdirectory(fuzzer)
|
||||
endif()
|
||||
|
@ -833,10 +833,6 @@ bool OverlayCDB::setCompileCommand(PathRef File,
|
||||
std::unique_ptr<ProjectModules>
|
||||
OverlayCDB::getProjectModules(PathRef File) const {
|
||||
auto MDB = DelegatingCDB::getProjectModules(File);
|
||||
if (!MDB) {
|
||||
log("Failed to get compilation Database for {0}", File);
|
||||
return {};
|
||||
}
|
||||
MDB->setCommandMangler([&Mangler = Mangler](tooling::CompileCommand &Command,
|
||||
PathRef CommandPath) {
|
||||
Mangler(Command, CommandPath);
|
||||
|
@ -1876,7 +1876,7 @@ static void fillSubTypes(const SymbolID &ID,
|
||||
});
|
||||
}
|
||||
|
||||
using RecursionProtectionSet = llvm::SmallPtrSet<const CXXRecordDecl *, 4>;
|
||||
using RecursionProtectionSet = llvm::SmallSet<const CXXRecordDecl *, 4>;
|
||||
|
||||
// Extracts parents from AST and populates the type hierarchy item.
|
||||
static void fillSuperTypes(const CXXRecordDecl &CXXRD, llvm::StringRef TUPath,
|
||||
|
@ -181,7 +181,7 @@ struct ExtractionZone {
|
||||
bool requiresHoisting(const SourceManager &SM,
|
||||
const HeuristicResolver *Resolver) const {
|
||||
// First find all the declarations that happened inside extraction zone.
|
||||
llvm::SmallPtrSet<const Decl *, 1> DeclsInExtZone;
|
||||
llvm::SmallSet<const Decl *, 1> DeclsInExtZone;
|
||||
for (auto *RootStmt : RootStmts) {
|
||||
findExplicitReferences(
|
||||
RootStmt,
|
||||
|
@ -1,66 +0,0 @@
|
||||
# A smoke test to check that clangd works without compilation database
|
||||
#
|
||||
# Windows have different escaping modes.
|
||||
# FIXME: We should add one for windows.
|
||||
# UNSUPPORTED: system-windows
|
||||
#
|
||||
# RUN: rm -fr %t
|
||||
# RUN: mkdir -p %t
|
||||
# RUN: split-file %s %t
|
||||
#
|
||||
# RUN: sed -e "s|DIR|%/t|g" %t/definition.jsonrpc.tmpl > %t/definition.jsonrpc
|
||||
#
|
||||
# RUN: clangd -experimental-modules-support -lit-test < %t/definition.jsonrpc \
|
||||
# RUN: | FileCheck -strict-whitespace %t/definition.jsonrpc
|
||||
|
||||
#--- A.h
|
||||
void printA();
|
||||
|
||||
#--- Use.cpp
|
||||
#include "A.h"
|
||||
void foo() {
|
||||
print
|
||||
}
|
||||
|
||||
#--- definition.jsonrpc.tmpl
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 0,
|
||||
"method": "initialize",
|
||||
"params": {
|
||||
"processId": 123,
|
||||
"rootPath": "clangd",
|
||||
"capabilities": {
|
||||
"textDocument": {
|
||||
"completion": {
|
||||
"completionItem": {
|
||||
"snippetSupport": true
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"trace": "off"
|
||||
}
|
||||
}
|
||||
---
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"method": "textDocument/didOpen",
|
||||
"params": {
|
||||
"textDocument": {
|
||||
"uri": "file://DIR/Use.cpp",
|
||||
"languageId": "cpp",
|
||||
"version": 1,
|
||||
"text": "#include \"A.h\"\nvoid foo() {\n print\n}\n"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# CHECK: "message"{{.*}}printA{{.*}}(fix available)
|
||||
|
||||
---
|
||||
{"jsonrpc":"2.0","id":1,"method":"textDocument/completion","params":{"textDocument":{"uri":"file://DIR/Use.cpp"},"context":{"triggerKind":1},"position":{"line":2,"character":6}}}
|
||||
---
|
||||
{"jsonrpc":"2.0","id":2,"method":"shutdown"}
|
||||
---
|
||||
{"jsonrpc":"2.0","method":"exit"}
|
@ -4473,198 +4473,6 @@ TEST(CompletionTest, SkipExplicitObjectParameter) {
|
||||
snippetSuffix(""))));
|
||||
}
|
||||
}
|
||||
|
||||
TEST(CompletionTest, MemberAccessInExplicitObjMemfn) {
|
||||
Annotations Code(R"cpp(
|
||||
struct A {
|
||||
int member {};
|
||||
int memberFnA(int a);
|
||||
int memberFnA(this A&, float a);
|
||||
|
||||
void foo(this A& self) {
|
||||
// Should not offer any members here, since
|
||||
// it needs to be referenced through `self`.
|
||||
mem$c1^;
|
||||
// should offer all results
|
||||
self.mem$c2^;
|
||||
|
||||
[&]() {
|
||||
// should not offer any results
|
||||
mem$c3^;
|
||||
}();
|
||||
}
|
||||
};
|
||||
)cpp");
|
||||
|
||||
auto TU = TestTU::withCode(Code.code());
|
||||
TU.ExtraArgs = {"-std=c++23"};
|
||||
|
||||
auto Preamble = TU.preamble();
|
||||
ASSERT_TRUE(Preamble);
|
||||
|
||||
CodeCompleteOptions Opts{};
|
||||
|
||||
MockFS FS;
|
||||
auto Inputs = TU.inputs(FS);
|
||||
|
||||
{
|
||||
auto Result = codeComplete(testPath(TU.Filename), Code.point("c1"),
|
||||
Preamble.get(), Inputs, Opts);
|
||||
|
||||
EXPECT_THAT(Result.Completions, ElementsAre());
|
||||
}
|
||||
{
|
||||
auto Result = codeComplete(testPath(TU.Filename), Code.point("c2"),
|
||||
Preamble.get(), Inputs, Opts);
|
||||
|
||||
EXPECT_THAT(
|
||||
Result.Completions,
|
||||
UnorderedElementsAre(named("member"),
|
||||
AllOf(named("memberFnA"), signature("(int a)"),
|
||||
snippetSuffix("(${1:int a})")),
|
||||
AllOf(named("memberFnA"), signature("(float a)"),
|
||||
snippetSuffix("(${1:float a})"))));
|
||||
}
|
||||
{
|
||||
auto Result = codeComplete(testPath(TU.Filename), Code.point("c3"),
|
||||
Preamble.get(), Inputs, Opts);
|
||||
|
||||
EXPECT_THAT(Result.Completions, ElementsAre());
|
||||
}
|
||||
}
|
||||
|
||||
TEST(CompletionTest, ListExplicitObjectOverloads) {
|
||||
Annotations Code(R"cpp(
|
||||
struct S {
|
||||
void foo1(int a);
|
||||
void foo2(int a) const;
|
||||
void foo2(this const S& self, float a);
|
||||
void foo3(this const S& self, int a);
|
||||
void foo4(this S& self, int a);
|
||||
};
|
||||
|
||||
void S::foo1(int a) {
|
||||
this->$c1^;
|
||||
}
|
||||
|
||||
void S::foo2(int a) const {
|
||||
this->$c2^;
|
||||
}
|
||||
|
||||
void S::foo3(this const S& self, int a) {
|
||||
self.$c3^;
|
||||
}
|
||||
|
||||
void S::foo4(this S& self, int a) {
|
||||
self.$c4^;
|
||||
}
|
||||
|
||||
void test1(S s) {
|
||||
s.$c5^;
|
||||
}
|
||||
|
||||
void test2(const S s) {
|
||||
s.$c6^;
|
||||
}
|
||||
)cpp");
|
||||
|
||||
auto TU = TestTU::withCode(Code.code());
|
||||
TU.ExtraArgs = {"-std=c++23"};
|
||||
|
||||
auto Preamble = TU.preamble();
|
||||
ASSERT_TRUE(Preamble);
|
||||
|
||||
CodeCompleteOptions Opts{};
|
||||
|
||||
MockFS FS;
|
||||
auto Inputs = TU.inputs(FS);
|
||||
|
||||
{
|
||||
auto Result = codeComplete(testPath(TU.Filename), Code.point("c1"),
|
||||
Preamble.get(), Inputs, Opts);
|
||||
EXPECT_THAT(
|
||||
Result.Completions,
|
||||
UnorderedElementsAre(AllOf(named("foo1"), signature("(int a)"),
|
||||
snippetSuffix("(${1:int a})")),
|
||||
AllOf(named("foo2"), signature("(int a) const"),
|
||||
snippetSuffix("(${1:int a})")),
|
||||
AllOf(named("foo2"), signature("(float a) const"),
|
||||
snippetSuffix("(${1:float a})")),
|
||||
AllOf(named("foo3"), signature("(int a) const"),
|
||||
snippetSuffix("(${1:int a})")),
|
||||
AllOf(named("foo4"), signature("(int a)"),
|
||||
snippetSuffix("(${1:int a})"))));
|
||||
}
|
||||
{
|
||||
auto Result = codeComplete(testPath(TU.Filename), Code.point("c2"),
|
||||
Preamble.get(), Inputs, Opts);
|
||||
EXPECT_THAT(
|
||||
Result.Completions,
|
||||
UnorderedElementsAre(AllOf(named("foo2"), signature("(int a) const"),
|
||||
snippetSuffix("(${1:int a})")),
|
||||
AllOf(named("foo2"), signature("(float a) const"),
|
||||
snippetSuffix("(${1:float a})")),
|
||||
AllOf(named("foo3"), signature("(int a) const"),
|
||||
snippetSuffix("(${1:int a})"))));
|
||||
}
|
||||
{
|
||||
auto Result = codeComplete(testPath(TU.Filename), Code.point("c3"),
|
||||
Preamble.get(), Inputs, Opts);
|
||||
EXPECT_THAT(
|
||||
Result.Completions,
|
||||
UnorderedElementsAre(AllOf(named("foo2"), signature("(int a) const"),
|
||||
snippetSuffix("(${1:int a})")),
|
||||
AllOf(named("foo2"), signature("(float a) const"),
|
||||
snippetSuffix("(${1:float a})")),
|
||||
AllOf(named("foo3"), signature("(int a) const"),
|
||||
snippetSuffix("(${1:int a})"))));
|
||||
}
|
||||
{
|
||||
auto Result = codeComplete(testPath(TU.Filename), Code.point("c4"),
|
||||
Preamble.get(), Inputs, Opts);
|
||||
EXPECT_THAT(
|
||||
Result.Completions,
|
||||
UnorderedElementsAre(AllOf(named("foo1"), signature("(int a)"),
|
||||
snippetSuffix("(${1:int a})")),
|
||||
AllOf(named("foo2"), signature("(int a) const"),
|
||||
snippetSuffix("(${1:int a})")),
|
||||
AllOf(named("foo2"), signature("(float a) const"),
|
||||
snippetSuffix("(${1:float a})")),
|
||||
AllOf(named("foo3"), signature("(int a) const"),
|
||||
snippetSuffix("(${1:int a})")),
|
||||
AllOf(named("foo4"), signature("(int a)"),
|
||||
snippetSuffix("(${1:int a})"))));
|
||||
}
|
||||
{
|
||||
auto Result = codeComplete(testPath(TU.Filename), Code.point("c5"),
|
||||
Preamble.get(), Inputs, Opts);
|
||||
EXPECT_THAT(
|
||||
Result.Completions,
|
||||
UnorderedElementsAre(AllOf(named("foo1"), signature("(int a)"),
|
||||
snippetSuffix("(${1:int a})")),
|
||||
AllOf(named("foo2"), signature("(int a) const"),
|
||||
snippetSuffix("(${1:int a})")),
|
||||
AllOf(named("foo2"), signature("(float a) const"),
|
||||
snippetSuffix("(${1:float a})")),
|
||||
AllOf(named("foo3"), signature("(int a) const"),
|
||||
snippetSuffix("(${1:int a})")),
|
||||
AllOf(named("foo4"), signature("(int a)"),
|
||||
snippetSuffix("(${1:int a})"))));
|
||||
}
|
||||
{
|
||||
auto Result = codeComplete(testPath(TU.Filename), Code.point("c6"),
|
||||
Preamble.get(), Inputs, Opts);
|
||||
EXPECT_THAT(
|
||||
Result.Completions,
|
||||
UnorderedElementsAre(AllOf(named("foo2"), signature("(int a) const"),
|
||||
snippetSuffix("(${1:int a})")),
|
||||
AllOf(named("foo2"), signature("(float a) const"),
|
||||
snippetSuffix("(${1:float a})")),
|
||||
AllOf(named("foo3"), signature("(int a) const"),
|
||||
snippetSuffix("(${1:int a})"))));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace clangd
|
||||
} // namespace clang
|
||||
|
@ -731,12 +731,6 @@ TEST_F(TargetDeclTest, BuiltinTemplates) {
|
||||
using type_pack_element = [[__type_pack_element]]<N, Pack...>;
|
||||
)cpp";
|
||||
EXPECT_DECLS("TemplateSpecializationTypeLoc", );
|
||||
|
||||
Code = R"cpp(
|
||||
template <template <class...> class Templ, class... Types>
|
||||
using dedup_types = Templ<[[__builtin_dedup_pack]]<Types...>...>;
|
||||
)cpp";
|
||||
EXPECT_DECLS("TemplateSpecializationTypeLoc", );
|
||||
}
|
||||
|
||||
TEST_F(TargetDeclTest, MemberOfTemplate) {
|
||||
|
@ -119,9 +119,6 @@ Improvements to clang-tidy
|
||||
- Improved documentation of the `-line-filter` command-line flag of
|
||||
:program:`clang-tidy` and :program:`run-clang-tidy.py`.
|
||||
|
||||
- Improved :program:`clang-tidy` option `-quiet` by suppressing diagnostic
|
||||
count messages.
|
||||
|
||||
New checks
|
||||
^^^^^^^^^^
|
||||
|
||||
@ -215,8 +212,7 @@ Changes in existing checks
|
||||
|
||||
- Improved :doc:`readability-identifier-naming
|
||||
<clang-tidy/checks/readability/identifier-naming>` check by ignoring
|
||||
declarations in system headers. The documentation is also improved to
|
||||
differentiate the general options from the specific ones.
|
||||
declarations in system headers.
|
||||
|
||||
- Improved :doc:`readability-qualified-auto
|
||||
<clang-tidy/checks/readability/qualified-auto>` check by adding the option
|
||||
|
@ -1,314 +0,0 @@
|
||||
======================
|
||||
Clang-Change-Namespace
|
||||
======================
|
||||
|
||||
.. contents::
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
:program:`clang-change-namespace` can be used to change the surrounding
|
||||
namespaces of class/function definitions.
|
||||
|
||||
Classes/functions in the moved namespace will have new namespaces while
|
||||
references to symbols (e.g. types, functions) which are not defined in the
|
||||
changed namespace will be correctly qualified by prepending namespace specifiers
|
||||
before them. This will try to add shortest namespace specifiers possible.
|
||||
|
||||
When a symbol reference needs to be fully-qualified, this adds a `::` prefix to
|
||||
the namespace specifiers unless the new namespace is the global namespace. For
|
||||
classes, only classes that are declared/defined in the given namespace in
|
||||
specified files will be moved: forward declarations will remain in the old
|
||||
namespace. The will be demonstrated in the next example.
|
||||
|
||||
Example usage
|
||||
-------------
|
||||
|
||||
For example, consider this `test.cc` example here with the forward declared
|
||||
class `FWD` and the defined class `A`, both in the namespace `a`.
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
namespace a {
|
||||
class FWD;
|
||||
class A {
|
||||
FWD *fwd;
|
||||
};
|
||||
} // namespace a
|
||||
|
||||
And now let's change the namespace `a` to `x`.
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
clang-change-namespace \
|
||||
--old_namespace "a" \
|
||||
--new_namespace "x" \
|
||||
--file_pattern "test.cc" \
|
||||
--i \
|
||||
test.cc
|
||||
|
||||
Note that in the code below there's still the forward decalred class `FWD` that
|
||||
stayed in the namespace `a`. It wasn't moved to the new namespace because it
|
||||
wasn't defined/declared here in `a` but only forward declared.
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
namespace a {
|
||||
class FWD;
|
||||
} // namespace a
|
||||
namespace x {
|
||||
|
||||
class A {
|
||||
a::FWD *fwd;
|
||||
};
|
||||
} // namespace x
|
||||
|
||||
|
||||
Another example
|
||||
---------------
|
||||
|
||||
Consider this `test.cc` file:
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
namespace na {
|
||||
class X {};
|
||||
namespace nb {
|
||||
class Y {
|
||||
X x;
|
||||
};
|
||||
} // namespace nb
|
||||
} // namespace na
|
||||
|
||||
To move the definition of class `Y` from namespace `na::nb` to `x::y`, run:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
clang-change-namespace \
|
||||
--old_namespace "na::nb" \
|
||||
--new_namespace "x::y" \
|
||||
--file_pattern "test.cc" \
|
||||
--i \
|
||||
test.cc
|
||||
|
||||
This will overwrite `test.cc` to look like this:
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
namespace na {
|
||||
class X {};
|
||||
|
||||
} // namespace na
|
||||
namespace x {
|
||||
namespace y {
|
||||
class Y {
|
||||
na::X x;
|
||||
};
|
||||
} // namespace y
|
||||
} // namespace x
|
||||
|
||||
Note, that we've successfully moved the class `Y` from namespace `na::nb` to
|
||||
namespace `x::y`.
|
||||
|
||||
Caveats
|
||||
=======
|
||||
|
||||
Content already exists in new namespace
|
||||
---------------------------------------
|
||||
|
||||
Consider this `test.cc` example that defines two `class A` one inside the
|
||||
namespace `a` and one in namespace `b`:
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
namespace a {
|
||||
class A {
|
||||
int classAFromWithinNamespace_a;
|
||||
};
|
||||
} // namespace a
|
||||
|
||||
namespace b {
|
||||
class A {
|
||||
int classAFromWithinNamespace_b;
|
||||
};
|
||||
} //namespace b
|
||||
|
||||
Let's move everything from namespace `a` to namespace `b`:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
clang-change-namespace \
|
||||
--old_namespace "a" \
|
||||
--new_namespace "b" \
|
||||
--file_pattern test.cc \
|
||||
test.cc
|
||||
|
||||
As expected we now have to definitions of `class A` inside the namespace `b`:
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
namespace b {
|
||||
class A {
|
||||
int classAFromWithinNamespace_a;
|
||||
};
|
||||
} // namespace b
|
||||
|
||||
namespace b {
|
||||
class A {
|
||||
int classAFromWithinNamespace_b;
|
||||
};
|
||||
} //namespace b
|
||||
|
||||
The re-factoring looks correct but the code will not compile due to the name
|
||||
duplication. It is not up to the tool to ensure compilability in that sense.
|
||||
But one has to be aware of that.
|
||||
|
||||
Inline namespace doesn't work
|
||||
-----------------------------
|
||||
|
||||
Consider this usage of two versions of implementations for a `greet` function:
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
#include <cstdio>
|
||||
|
||||
namespace Greeter {
|
||||
inline namespace Version1 {
|
||||
const char* greet() { return "Hello from version 1!"; }
|
||||
} // namespace Version1
|
||||
namespace Version2 {
|
||||
const char* greet() { return "Hello from version 2!"; }
|
||||
} // namespace Version2
|
||||
} // namespace Greeter
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
printf("%s\n", Greeter::greet());
|
||||
return 0;
|
||||
}
|
||||
|
||||
Note, that currently `Greeter::greet()` will result in a call to
|
||||
`Greeter::Version1::greet()` because that's the inlined namespace.
|
||||
|
||||
Let's say you want to move one and make `Version2` the default now and remove
|
||||
the `inline` from the `Version1`. First let's try to turn `namespace Version2`
|
||||
into `inline namespace Version2`:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
clang-change-namespace \
|
||||
--old_namespace "Greeter::Version2" \
|
||||
--new_namespace "inline Version2" \
|
||||
--file_pattern main.cc main.cc
|
||||
|
||||
But this will put the `inline` keyword in the wrong place resulting in:
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
#include <cstdio>
|
||||
|
||||
namespace Greeter {
|
||||
inline namespace Version1 {
|
||||
const char* greet() { return "Hello from version 1!"; }
|
||||
} // namespace Version1
|
||||
|
||||
} // namespace Greeter
|
||||
namespace inline Greeter {
|
||||
namespace Version2 {
|
||||
const char *greet() { return "Hello from version 2!"; }
|
||||
} // namespace Version2
|
||||
} // namespace inline Greeter
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
printf("%s\n", Greeter::greet());
|
||||
return 0;
|
||||
}
|
||||
|
||||
One cannot use `:program:`clang-change-namespace` to inline a namespace.
|
||||
|
||||
Symbol references not updated
|
||||
-----------------------------
|
||||
|
||||
Consider this `test.cc` file:
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
namespace old {
|
||||
struct foo {};
|
||||
} // namespace old
|
||||
|
||||
namespace b {
|
||||
old::foo g_foo;
|
||||
} // namespace b
|
||||
|
||||
Notice that namespace `b` defines a global variable of type `old::foo`. If we
|
||||
now change the name of the `old` namespace to `modern`, the reference will not
|
||||
be updated:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
clang-change-namespace \
|
||||
--old_namespace "old" \
|
||||
--new_namespace "modern" \
|
||||
--file_pattern test.cc \
|
||||
test.cc
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
namespace modern {
|
||||
struct foo {};
|
||||
} // namespace modern
|
||||
|
||||
namespace b {
|
||||
old::foo g_foo;
|
||||
} // namespace b
|
||||
|
||||
`g_foo` is still of the no longer existing type `old::foo` while instead it
|
||||
should use `modern::foo`.
|
||||
|
||||
Only symbol references in the moved namespace are updated, not outside of it.
|
||||
|
||||
|
||||
:program:`clang-change-namespace` Command Line Options
|
||||
======================================================
|
||||
|
||||
.. option:: --allowed_file=<string>
|
||||
|
||||
A file containing regexes of symbol names that are not expected to be updated
|
||||
when changing namespaces around them.
|
||||
|
||||
.. option:: --dump_result
|
||||
|
||||
Dump new file contents in YAML, if specified.
|
||||
|
||||
.. option:: --extra-arg=<string>
|
||||
|
||||
Additional argument to append to the compiler command line
|
||||
|
||||
.. option:: --extra-arg-before=<string>
|
||||
|
||||
Additional argument to prepend to the compiler command line
|
||||
|
||||
.. option:: --file_pattern=<string>
|
||||
|
||||
Only rename namespaces in files that match the given regular expression
|
||||
pattern.
|
||||
|
||||
.. option:: -i
|
||||
|
||||
Inplace edit <file>s, if specified.
|
||||
|
||||
.. option:: --new_namespace=<string>
|
||||
|
||||
New namespace. Use `""` when you target the global namespace.
|
||||
|
||||
.. option:: --old_namespace=<string>
|
||||
|
||||
Old namespace.
|
||||
|
||||
.. option:: -p <string>
|
||||
|
||||
Build path
|
||||
|
||||
.. option:: --style=<string>
|
||||
|
||||
The style name used for reformatting.
|
@ -43,21 +43,14 @@ The options and their corresponding values are:
|
||||
- ``LowerCase`` - example: ``int i_Variable``
|
||||
- ``CamelCase`` - example: ``int IVariable``
|
||||
|
||||
Options summary
|
||||
---------------
|
||||
Options
|
||||
-------
|
||||
|
||||
The available options are summarized below:
|
||||
|
||||
**General options**
|
||||
|
||||
- :option:`AggressiveDependentMemberLookup`
|
||||
- :option:`CheckAnonFieldInParent`
|
||||
- :option:`GetConfigPerFile`
|
||||
- :option:`IgnoreMainLikeFunctions`
|
||||
|
||||
**Specific options**
|
||||
The following options are described below:
|
||||
|
||||
- :option:`AbstractClassCase`, :option:`AbstractClassPrefix`, :option:`AbstractClassSuffix`, :option:`AbstractClassIgnoredRegexp`, :option:`AbstractClassHungarianPrefix`
|
||||
- :option:`AggressiveDependentMemberLookup`
|
||||
- :option:`CheckAnonFieldInParent`
|
||||
- :option:`ClassCase`, :option:`ClassPrefix`, :option:`ClassSuffix`, :option:`ClassIgnoredRegexp`, :option:`ClassHungarianPrefix`
|
||||
- :option:`ClassConstantCase`, :option:`ClassConstantPrefix`, :option:`ClassConstantSuffix`, :option:`ClassConstantIgnoredRegexp`, :option:`ClassConstantHungarianPrefix`
|
||||
- :option:`ClassMemberCase`, :option:`ClassMemberPrefix`, :option:`ClassMemberSuffix`, :option:`ClassMemberIgnoredRegexp`, :option:`ClassMemberHungarianPrefix`
|
||||
@ -73,11 +66,13 @@ The available options are summarized below:
|
||||
- :option:`EnumCase`, :option:`EnumPrefix`, :option:`EnumSuffix`, :option:`EnumIgnoredRegexp`
|
||||
- :option:`EnumConstantCase`, :option:`EnumConstantPrefix`, :option:`EnumConstantSuffix`, :option:`EnumConstantIgnoredRegexp`, :option:`EnumConstantHungarianPrefix`
|
||||
- :option:`FunctionCase`, :option:`FunctionPrefix`, :option:`FunctionSuffix`, :option:`FunctionIgnoredRegexp`
|
||||
- :option:`GetConfigPerFile`
|
||||
- :option:`GlobalConstantCase`, :option:`GlobalConstantPrefix`, :option:`GlobalConstantSuffix`, :option:`GlobalConstantIgnoredRegexp`, :option:`GlobalConstantHungarianPrefix`
|
||||
- :option:`GlobalConstantPointerCase`, :option:`GlobalConstantPointerPrefix`, :option:`GlobalConstantPointerSuffix`, :option:`GlobalConstantPointerIgnoredRegexp`, :option:`GlobalConstantPointerHungarianPrefix`
|
||||
- :option:`GlobalFunctionCase`, :option:`GlobalFunctionPrefix`, :option:`GlobalFunctionSuffix`, :option:`GlobalFunctionIgnoredRegexp`
|
||||
- :option:`GlobalPointerCase`, :option:`GlobalPointerPrefix`, :option:`GlobalPointerSuffix`, :option:`GlobalPointerIgnoredRegexp`, :option:`GlobalPointerHungarianPrefix`
|
||||
- :option:`GlobalVariableCase`, :option:`GlobalVariablePrefix`, :option:`GlobalVariableSuffix`, :option:`GlobalVariableIgnoredRegexp`, :option:`GlobalVariableHungarianPrefix`
|
||||
- :option:`IgnoreMainLikeFunctions`
|
||||
- :option:`InlineNamespaceCase`, :option:`InlineNamespacePrefix`, :option:`InlineNamespaceSuffix`, :option:`InlineNamespaceIgnoredRegexp`
|
||||
- :option:`LocalConstantCase`, :option:`LocalConstantPrefix`, :option:`LocalConstantSuffix`, :option:`LocalConstantIgnoredRegexp`, :option:`LocalConstantHungarianPrefix`
|
||||
- :option:`LocalConstantPointerCase`, :option:`LocalConstantPointerPrefix`, :option:`LocalConstantPointerSuffix`, :option:`LocalConstantPointerIgnoredRegexp`, :option:`LocalConstantPointerHungarianPrefix`
|
||||
@ -110,12 +105,6 @@ The available options are summarized below:
|
||||
- :option:`VariableCase`, :option:`VariablePrefix`, :option:`VariableSuffix`, :option:`VariableIgnoredRegexp`, :option:`VariableHungarianPrefix`
|
||||
- :option:`VirtualMethodCase`, :option:`VirtualMethodPrefix`, :option:`VirtualMethodSuffix`, :option:`VirtualMethodIgnoredRegexp`
|
||||
|
||||
|
||||
Options description
|
||||
-------------------
|
||||
|
||||
A detailed description of each option is presented below:
|
||||
|
||||
.. option:: AbstractClassCase
|
||||
|
||||
When defined, the check will ensure abstract class names conform to the
|
||||
|
@ -343,107 +343,6 @@ An overview of all the command-line options:
|
||||
some-check.SomeOption: 'some value'
|
||||
...
|
||||
|
||||
Clang-Tidy Automation
|
||||
=====================
|
||||
|
||||
:program:`clang-tidy` can analyze multiple source files by specifying them on
|
||||
the command line. For larger projects, automation scripts provide additional
|
||||
functionality like parallel execution and integration with version control
|
||||
systems.
|
||||
|
||||
Running Clang-Tidy in Parallel
|
||||
-------------------------------
|
||||
|
||||
:program:`clang-tidy` can process multiple files sequentially, but for projects
|
||||
with many source files, the :program:`run-clang-tidy.py` script provides
|
||||
parallel execution to significantly reduce analysis time. This script is
|
||||
included with clang-tidy and runs :program:`clang-tidy` over all files in a
|
||||
compilation database or a specified path concurrently.
|
||||
|
||||
The script requires a compilation database (``compile_commands.json``) which
|
||||
can be generated by build systems like CMake (using
|
||||
``-DCMAKE_EXPORT_COMPILE_COMMANDS=ON``) or by tools like `Bear`_.
|
||||
|
||||
The script supports most of the same options as :program:`clang-tidy` itself,
|
||||
including ``-checks=``, ``-fix``, ``-header-filter=``, and configuration
|
||||
options. Run ``run-clang-tidy.py --help`` for a complete list of available
|
||||
options.
|
||||
|
||||
Example invocations:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
# Run clang-tidy on all files in the compilation database in parallel
|
||||
$ run-clang-tidy.py -p=build/
|
||||
|
||||
# Run with specific checks and apply fixes
|
||||
$ run-clang-tidy.py -p=build/ -fix -checks=-*,readability-*
|
||||
|
||||
# Run on specific files/directories with header filtering
|
||||
$ run-clang-tidy.py -p=build/ -header-filter=src/ src/
|
||||
|
||||
# Run with parallel execution (uses all CPU cores by default)
|
||||
$ run-clang-tidy.py -p=build/ -j 4
|
||||
|
||||
Running Clang-Tidy on Diff
|
||||
---------------------------
|
||||
|
||||
The :program:`clang-tidy-diff.py` script allows you to run
|
||||
:program:`clang-tidy` on the lines that have been modified in your working
|
||||
directory or in a specific diff. Importantly, :program:`clang-tidy-diff.py` only reports
|
||||
diagnostics for changed lines; :program:`clang-tidy` still analyzes the entire
|
||||
file and filters out unchanged lines after analysis, so this does not improve
|
||||
performance. This is particularly useful for code reviews and continuous
|
||||
integration, as it focuses analysis on the changed code rather than the entire
|
||||
codebase.
|
||||
|
||||
The script can work with various diff sources:
|
||||
|
||||
* Git working directory changes
|
||||
* Output from ``git diff``
|
||||
* Output from ``svn diff``
|
||||
* Patch files
|
||||
|
||||
Example invocations:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
# Run clang-tidy on all changes in the working directory
|
||||
$ git diff -U0 --no-color HEAD^ | clang-tidy-diff.py -p1
|
||||
|
||||
# Run with specific checks and apply fixes
|
||||
$ git diff -U0 --no-color HEAD^ | clang-tidy-diff.py -p1 -fix \
|
||||
-checks=-*,readability-*
|
||||
|
||||
# Run on staged changes
|
||||
$ git diff -U0 --no-color --cached | clang-tidy-diff.py -p1
|
||||
|
||||
# Run on changes between two commits
|
||||
$ git diff -U0 --no-color HEAD~2 HEAD | clang-tidy-diff.py -p1
|
||||
|
||||
# Run on a patch file
|
||||
$ clang-tidy-diff.py -p1 < changes.patch
|
||||
|
||||
The ``-p1`` option tells the script to strip one level of path prefix from
|
||||
the diff, which is typically needed for Git diffs. The script supports most of
|
||||
the same options as :program:`clang-tidy` itself, including ``-checks=``,
|
||||
``-fix``, ``-header-filter=``, and configuration options.
|
||||
|
||||
While :program:`clang-tidy-diff.py` is useful for focusing on recent changes,
|
||||
relying solely on it may lead to incomplete analysis. Since the script only
|
||||
reports warnings from the modified lines, it may miss issues that are caused
|
||||
by the changes but manifest elsewhere in the code. For example, changes that
|
||||
only add lines to a function may cause it to violate size limits (e.g.,
|
||||
`readability-function-size <checks/readability/function-size.html>`_), but the
|
||||
diagnostic will be reported at the function declaration, which may not be in
|
||||
the diff and thus filtered out. Modifications to header files may also affect
|
||||
many implementation files, but only warnings in the modified header lines will
|
||||
be reported.
|
||||
|
||||
For comprehensive analysis, especially before merging significant changes,
|
||||
consider running :program:`clang-tidy` on the entire affected files or the
|
||||
whole project using :program:`run-clang-tidy.py`.
|
||||
|
||||
.. _clang-tidy-nolint:
|
||||
|
||||
Suppressing Undesired Diagnostics
|
||||
@ -566,6 +465,5 @@ example, ``NOLINTBEGIN(check-name)`` can be paired with
|
||||
:program:`clang-tidy` will generate a ``clang-tidy-nolint`` error diagnostic if
|
||||
any ``NOLINTBEGIN``/``NOLINTEND`` comment violates these requirements.
|
||||
|
||||
.. _Bear: https://github.com/rizsotto/Bear
|
||||
.. _LibTooling: https://clang.llvm.org/docs/LibTooling.html
|
||||
.. _How To Setup Tooling For LLVM: https://clang.llvm.org/docs/HowToSetupToolingForLLVM.html
|
||||
|
@ -17,7 +17,6 @@ Contents
|
||||
|
||||
clang-tidy/index
|
||||
clang-include-fixer
|
||||
clang-change-namespace
|
||||
modularize
|
||||
pp-trace
|
||||
clangd <https://clangd.llvm.org/>
|
||||
|
@ -176,11 +176,3 @@ struct TemplateAssignment {
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
namespace GH153770 {
|
||||
struct A;
|
||||
struct A {
|
||||
A() = default;
|
||||
A& operator=(const A&) = default;
|
||||
};
|
||||
} // namespace GH153770
|
||||
|
@ -10,7 +10,7 @@
|
||||
// RUN: clang-tidy -checks='-*,google-explicit-constructor' -header-filter='header_alias\.h' %s -- -I %t 2>&1 | FileCheck --check-prefix=CHECK_HEADER_ALIAS %s
|
||||
// RUN: clang-tidy -checks='-*,google-explicit-constructor' -header-filter='header_alias\.h' -quiet %s -- -I %t 2>&1 | FileCheck --check-prefix=CHECK_HEADER_ALIAS %s
|
||||
// RUN: clang-tidy -checks='-*,google-explicit-constructor' -header-filter='header\.h' %s -- -I %t 2>&1 | FileCheck --check-prefix=CHECK_HEADER %s
|
||||
// RUN: clang-tidy -checks='-*,google-explicit-constructor' -header-filter='header\.h' -quiet %s -- -I %t 2>&1 | FileCheck --check-prefix=CHECK_HEADER --allow-empty %s
|
||||
// RUN: clang-tidy -checks='-*,google-explicit-constructor' -header-filter='header\.h' -quiet %s -- -I %t 2>&1 | FileCheck --check-prefix=CHECK_HEADER %s
|
||||
|
||||
// Check that `-header-filter` operates on the same file paths as paths in
|
||||
// diagnostics printed by ClangTidy.
|
||||
|
@ -1,26 +0,0 @@
|
||||
// This test ensures that the --quiet flag only suppresses the "X warnings generated"
|
||||
// message while keeping all diagnostic information including caret indicators (^).
|
||||
|
||||
// RUN: clang-tidy -checks=-*,readability-magic-numbers,clang-diagnostic-sign-compare %s -- \
|
||||
// RUN: -Wsign-compare 2>&1 | FileCheck %s --check-prefix=CHECK-NORMAL
|
||||
// RUN: clang-tidy -checks=-*,readability-magic-numbers,clang-diagnostic-sign-compare -quiet %s -- \
|
||||
// RUN: -Wsign-compare 2>&1 | FileCheck %s --check-prefix=CHECK-QUIET
|
||||
|
||||
// CHECK-NORMAL: 2 warnings generated
|
||||
// CHECK-NORMAL-DAG: warning: 42 is a magic number
|
||||
// CHECK-NORMAL-DAG: {{[ ]*\^}}
|
||||
// CHECK-NORMAL-DAG: warning: comparison of integers of different signs
|
||||
// CHECK-NORMAL-DAG: {{[ ]*~ \^ ~}}
|
||||
|
||||
// CHECK-QUIET-NOT: {{[0-9]+}} warning{{s?}} generated
|
||||
// CHECK-QUIET-DAG: warning: 42 is a magic number
|
||||
// CHECK-QUIET-DAG: {{[ ]*\^}}
|
||||
// CHECK-QUIET-DAG: warning: comparison of integers of different signs
|
||||
// CHECK-QUIET-DAG: {{[ ]*~ \^ ~}}
|
||||
|
||||
int main() {
|
||||
const int CONST_VAL = 10;
|
||||
int x = 42; // trigger 'readability-magic-numbers' with caret: ^
|
||||
unsigned int y = CONST_VAL;
|
||||
return x < y; // trigger 'clang-diagnostic-sign-compare' with caret: ^
|
||||
}
|
@ -35,7 +35,7 @@ if(WIN32)
|
||||
endif()
|
||||
|
||||
# The Python FFI interface is broken on AIX: https://bugs.python.org/issue38628.
|
||||
if("${CMAKE_SYSTEM_NAME}" MATCHES "AIX")
|
||||
if(${CMAKE_SYSTEM_NAME} MATCHES "AIX")
|
||||
set(RUN_PYTHON_TESTS FALSE)
|
||||
endif()
|
||||
|
||||
|
@ -60,10 +60,6 @@ only for the linker wrapper will be forwarded to the wrapped linker job.
|
||||
--v Display the version number and exit
|
||||
-- The separator for the wrapped linker arguments
|
||||
|
||||
The linker wrapper will generate the appropriate runtime calls to register the
|
||||
generated device binary with the offloading runtime. To do this step manually we
|
||||
provide the ``llvm-offload-wrapper`` utility.
|
||||
|
||||
Relocatable Linking
|
||||
===================
|
||||
|
||||
|
@ -9,14 +9,14 @@ Introduction
|
||||
============
|
||||
|
||||
Coroutines in C++ were introduced in C++20, and the user experience for
|
||||
debugging them can still be challenging. This document guides you on how to most
|
||||
debugging them can still be challenging. This document guides you how to most
|
||||
efficiently debug coroutines and how to navigate existing shortcomings in
|
||||
debuggers and compilers.
|
||||
|
||||
Coroutines are generally used either as generators or for asynchronous
|
||||
programming. In this document, we will discuss both use cases. Even if you are
|
||||
using coroutines for asynchronous programming, you should still read the
|
||||
generators section, as it introduces foundational debugging techniques also
|
||||
generators section, as it will introduce foundational debugging techniques also
|
||||
applicable to the debugging of asynchronous programs.
|
||||
|
||||
Both compilers (clang, gcc, ...) and debuggers (lldb, gdb, ...) are
|
||||
@ -34,15 +34,15 @@ scripting. This guide comes with a basic GDB script for coroutine debugging.
|
||||
This guide will first showcase the more polished, bleeding-edge experience, but
|
||||
will also show you how to debug coroutines with older toolchains. In general,
|
||||
the older your toolchain, the deeper you will have to dive into the
|
||||
implementation details of coroutines (such as their ABI). The further down you go in
|
||||
this document, the more low-level, technical the content will become. If
|
||||
implementation details of coroutines (such as their ABI). The further down in
|
||||
this document you go, the more low-level, technical the content will become. If
|
||||
you are on an up-to-date toolchain, you will hopefully be able to stop reading
|
||||
earlier.
|
||||
|
||||
Debugging generators
|
||||
====================
|
||||
|
||||
One of the two major use cases for coroutines in C++ is generators, i.e.,
|
||||
One of the two major use cases for coroutines in C++ are generators, i.e.,
|
||||
functions which can produce values via ``co_yield``. Values are produced
|
||||
lazily, on-demand. For this purpose, every time a new value is requested, the
|
||||
coroutine gets resumed. As soon as it reaches a ``co_yield`` and thereby
|
||||
@ -141,7 +141,7 @@ a regular function.
|
||||
|
||||
Note the two additional variables ``__promise`` and ``__coro_frame``. Those
|
||||
show the internal state of the coroutine. They are not relevant for our
|
||||
generator example but will be relevant for asynchronous programming described
|
||||
generator example, but will be relevant for asynchronous programming described
|
||||
in the next section.
|
||||
|
||||
Stepping out of a coroutine
|
||||
@ -174,7 +174,7 @@ Inspecting a suspended coroutine
|
||||
--------------------------------
|
||||
|
||||
The ``print10Elements`` function receives an opaque ``generator`` type. Let's
|
||||
assume we are suspended at the ``++gen;`` line and want to inspect the
|
||||
assume we are suspended at the ``++gen;`` line, and want to inspect the
|
||||
generator and its internal state.
|
||||
|
||||
To do so, we can simply look into the ``gen.hdl`` variable. LLDB comes with a
|
||||
@ -188,7 +188,7 @@ We can see two function pointers ``resume`` and ``destroy``. These pointers
|
||||
point to the resume / destroy functions. By inspecting those function pointers,
|
||||
we can see that our ``generator`` is actually backed by our ``fibonacci``
|
||||
coroutine. When using VS Code + lldb-dap, you can Cmd+Click on the function
|
||||
address (``0x555...`` in the screenshot) to jump directly to the function
|
||||
address (``0x555...`` in the screenshot) to directly jump to the function
|
||||
definition backing your coroutine handle.
|
||||
|
||||
Next, we see the ``promise``. In our case, this reveals the current value of
|
||||
@ -247,12 +247,12 @@ the line number of the current suspension point in the promise:
|
||||
};
|
||||
|
||||
This stores the return address of ``await_suspend`` within the promise.
|
||||
Thereby, we can read it back from the promise of a suspended coroutine and map
|
||||
Thereby, we can read it back from the promise of a suspended coroutine, and map
|
||||
it to an exact source code location. For a complete example, see the ``task``
|
||||
type used below for asynchronous programming.
|
||||
|
||||
Alternatively, we can modify the C++ code to store the line number in the
|
||||
promise type. We can use ``std::source_location`` to get the line number of
|
||||
promise type. We can use a ``std::source_location`` to get the line number of
|
||||
the await and store it inside the ``promise_type``. In the debugger, we can
|
||||
then read the line number from the promise of the suspended coroutine.
|
||||
|
||||
@ -270,7 +270,7 @@ then read the line number from the promise of the suspended coroutine.
|
||||
};
|
||||
|
||||
The downside of both approaches is that they come at the price of additional
|
||||
runtime cost. In particular, the second approach increases binary size, since it
|
||||
runtime cost. In particular the second approach increases binary size, since it
|
||||
requires additional ``std::source_location`` objects, and those source
|
||||
locations are not stripped by split-dwarf. Whether the first approach is worth
|
||||
the additional runtime cost is a trade-off you need to make yourself.
|
||||
@ -285,7 +285,7 @@ provide custom debugging support, so in addition to this guide, you might want
|
||||
to check out their documentation.
|
||||
|
||||
When using coroutines for asynchronous programming, your library usually
|
||||
provides you with some ``task`` type. This type usually looks similar to this:
|
||||
provides you some ``task`` type. This type usually looks similar to this:
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
@ -479,7 +479,7 @@ One such solution is to store the list of in-flight coroutines in a collection:
|
||||
};
|
||||
|
||||
With this in place, it is possible to inspect ``inflight_coroutines`` from the
|
||||
debugger and rely on LLDB's ``std::coroutine_handle`` pretty-printer to
|
||||
debugger, and rely on LLDB's ``std::coroutine_handle`` pretty-printer to
|
||||
inspect the coroutines.
|
||||
|
||||
This technique will track *all* coroutines, also the ones which are currently
|
||||
@ -498,8 +498,8 @@ LLDB before 21.0 did not yet show the ``__coro_frame`` inside
|
||||
``coroutine_handle``. To inspect the coroutine frame, you had to use the
|
||||
approach described in the :ref:`devirtualization` section.
|
||||
|
||||
LLDB before 18.0 hid the ``__promise`` and ``__coro_frame``
|
||||
variables by default. The variables are still present, but they need to be
|
||||
LLDB before 18.0 was hiding the ``__promise`` and ``__coro_frame``
|
||||
variable by default. The variables are still present, but they need to be
|
||||
explicitly added to the "watch" pane in VS Code or requested via
|
||||
``print __promise`` and ``print __coro_frame`` from the debugger console.
|
||||
|
||||
@ -511,9 +511,9 @@ section.
|
||||
Toolchain Implementation Details
|
||||
================================
|
||||
|
||||
This section covers the ABI as well as additional compiler-specific behavior.
|
||||
This section covers the ABI, as well as additional compiler-specific behavior.
|
||||
The ABI is followed by all compilers, on all major systems, including Windows,
|
||||
Linux, and macOS. Different compilers emit different debug information, though.
|
||||
Linux and macOS. Different compilers emit different debug information, though.
|
||||
|
||||
Ramp, resume and destroy functions
|
||||
----------------------------------
|
||||
@ -595,7 +595,7 @@ functions as their first two members. As such, we can read the function
|
||||
pointers from the coroutine frame and then obtain the function's name from its
|
||||
address.
|
||||
|
||||
The promise is guaranteed to be at a 16-byte offset from the coroutine frame.
|
||||
The promise is guaranteed to be at a 16 byte offset from the coroutine frame.
|
||||
If we have a coroutine handle at address 0x416eb0, we can hence reinterpret-cast
|
||||
the promise as follows:
|
||||
|
||||
@ -607,8 +607,8 @@ Implementation in clang / LLVM
|
||||
------------------------------
|
||||
|
||||
The C++ Coroutines feature in the Clang compiler is implemented in two parts of
|
||||
the compiler. Semantic analysis is performed in Clang, and coroutine
|
||||
construction and optimization take place in the LLVM middle-end.
|
||||
the compiler. Semantic analysis is performed in Clang, and Coroutine
|
||||
construction and optimization takes place in the LLVM middle-end.
|
||||
|
||||
For each coroutine function, the frontend generates a single corresponding
|
||||
LLVM-IR function. This function uses special ``llvm.coro.suspend`` intrinsics
|
||||
@ -622,7 +622,7 @@ points into the coroutine frame. Most of the heavy lifting to preserve debugging
|
||||
information is done in this pass. This pass needs to rewrite all variable
|
||||
locations to point into the coroutine frame.
|
||||
|
||||
Afterwards, a couple of additional optimizations are applied before code
|
||||
Afterwards, a couple of additional optimizations are applied, before code
|
||||
gets emitted, but none of them are really interesting regarding debugging
|
||||
information.
|
||||
|
||||
@ -636,8 +636,8 @@ However, this is not possible for coroutine frames because the frames are
|
||||
constructed in the LLVM middle-end.
|
||||
|
||||
To mitigate this problem, the LLVM middle end attempts to generate some debug
|
||||
information, which is unfortunately incomplete, since much of the
|
||||
language-specific information is missing in the middle end.
|
||||
information, which is unfortunately incomplete, since much of the language
|
||||
specific information is missing in the middle end.
|
||||
|
||||
.. _devirtualization:
|
||||
|
||||
@ -655,7 +655,7 @@ There are two possible approaches to do so:
|
||||
We can lookup their types and thereby get the types of promise
|
||||
and coroutine frame.
|
||||
|
||||
In gdb, one can use the following approach to devirtualize a coroutine type,
|
||||
In gdb, one can use the following approach to devirtualize coroutine type,
|
||||
assuming we have a ``std::coroutine_handle`` is at address 0x418eb0:
|
||||
|
||||
::
|
||||
@ -679,7 +679,7 @@ LLDB comes with devirtualization support out of the box, as part of the
|
||||
pretty-printer for ``std::coroutine_handle``. Internally, this pretty-printer
|
||||
uses the second approach. We look up the types in the destroy function and not
|
||||
the resume function because the resume function pointer will be set to a
|
||||
``nullptr`` as soon as a coroutine reaches its final suspension point. If we used
|
||||
nullptr as soon as a coroutine reaches its final suspension point. If we used
|
||||
the resume function, devirtualization would hence fail for all coroutines that
|
||||
have reached their final suspension point.
|
||||
|
||||
@ -687,10 +687,10 @@ Interpreting the coroutine frame in optimized builds
|
||||
----------------------------------------------------
|
||||
|
||||
The ``__coro_frame`` variable usually refers to the coroutine frame of an
|
||||
*in-flight* coroutine. This means the coroutine is currently executing.
|
||||
*in-flight* coroutine. This means, the coroutine is currently executing.
|
||||
However, the compiler only guarantees the coroutine frame to be in a consistent
|
||||
state while the coroutine is suspended. As such, the variables inside the
|
||||
``__coro_frame`` variable might be outdated, particularly when optimizations
|
||||
``__coro_frame`` variable might be outdated, in particular when optimizations
|
||||
are enabled.
|
||||
|
||||
Furthermore, when optimizations are enabled, the compiler will layout the
|
||||
@ -731,7 +731,7 @@ despite ``a`` being frequently incremented.
|
||||
|
||||
While this might be surprising, this is a result of the optimizer recognizing
|
||||
that it can eliminate most of the load/store operations.
|
||||
The above code is optimized to the equivalent of:
|
||||
The above code gets optimized to the equivalent of:
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
@ -1180,5 +1180,5 @@ The authors of the Folly libraries wrote a blog post series on how they debug co
|
||||
* `Async stack traces in folly: Improving debugging in the developer lifecycle <https://developers.facebook.com/blog/post/2021/10/21/async-stack-traces-folly-improving-debugging-developer-lifecycle/>`_
|
||||
|
||||
Besides some topics also covered here (stack traces from the debugger), Folly's blog post series also covers
|
||||
additional topics, such as capturing async stack traces in performance profiles via eBPF filters
|
||||
more additional topics, such as capturing async stack traces in performance profiles via eBPF filters
|
||||
and printing async stack traces on crashes.
|
||||
|
@ -635,12 +635,11 @@ C and C++. For example:
|
||||
return v;
|
||||
}
|
||||
|
||||
|
||||
Boolean vectors are a Clang extension of the ext vector type. Boolean vectors
|
||||
are intended, though not guaranteed, to map to vector mask registers. The size
|
||||
parameter of a boolean vector type is the number of bits in the vector. The
|
||||
boolean vector is dense and each bit in the boolean vector is one vector
|
||||
element. Query for this feature with ``__has_feature(ext_vector_type_boolean)``.
|
||||
element.
|
||||
|
||||
The semantics of boolean vectors borrows from C bit-fields with the following
|
||||
differences:
|
||||
@ -658,16 +657,6 @@ The size and alignment are both the number of bits rounded up to the next power
|
||||
of two, but the alignment is at most the maximum vector alignment of the
|
||||
target.
|
||||
|
||||
A boolean vector can be used in a ternary `?:` operator to select vector
|
||||
elements of a different type.
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
typedef int int4 __attribute__((ext_vector_type(4)));
|
||||
typedef bool bool4 __attribute__((ext_vector_type(4)));
|
||||
|
||||
int4 blend(bool4 cond, int4 a, int4 b) { return cond ? a : b; }
|
||||
|
||||
|
||||
Vector Literals
|
||||
---------------
|
||||
@ -768,12 +757,11 @@ elementwise to the input.
|
||||
|
||||
Unless specified otherwise operation(±0) = ±0 and operation(±infinity) = ±infinity
|
||||
|
||||
The elementwise intrinsics ``__builtin_elementwise_popcount``,
|
||||
The integer elementwise intrinsics, including ``__builtin_elementwise_popcount``,
|
||||
``__builtin_elementwise_bitreverse``, ``__builtin_elementwise_add_sat``,
|
||||
``__builtin_elementwise_sub_sat``, ``__builtin_elementwise_max``,
|
||||
``__builtin_elementwise_min``, ``__builtin_elementwise_abs``,
|
||||
``__builtin_elementwise_ctlz``, ``__builtin_elementwise_cttz``, and
|
||||
``__builtin_elementwise_fma`` can be called in a ``constexpr`` context.
|
||||
``__builtin_elementwise_min``, and ``__builtin_elementwise_abs``
|
||||
can be called in a ``constexpr`` context.
|
||||
|
||||
No implicit promotion of integer types takes place. The mixing of integer types
|
||||
of different sizes and signs is forbidden in binary and ternary builtins.
|
||||
@ -882,14 +870,6 @@ T __builtin_elementwise_fshr(T x, T y, T z) perform a funnel shift right. Co
|
||||
significant bits of the wide value), the combined value is shifted
|
||||
right by z, and the least significant bits are extracted to produce
|
||||
a result that is the same size as the original arguments.
|
||||
T __builtin_elementwise_ctlz(T x[, T y]) return the number of leading 0 bits in the first argument. If integer types
|
||||
the first argument is 0 and an optional second argument is provided,
|
||||
the second argument is returned. It is undefined behaviour if the
|
||||
first argument is 0 and no second argument is provided.
|
||||
T __builtin_elementwise_cttz(T x[, T y]) return the number of trailing 0 bits in the first argument. If integer types
|
||||
the first argument is 0 and an optional second argument is provided,
|
||||
the second argument is returned. It is undefined behaviour if the
|
||||
first argument is 0 and no second argument is provided.
|
||||
============================================== ====================================================================== =========================================
|
||||
|
||||
|
||||
@ -942,24 +922,6 @@ Let ``VT`` be a vector type and ``ET`` the element type of ``VT``.
|
||||
for the comparison.
|
||||
======================================= ====================================================================== ==================================
|
||||
|
||||
*Masked Builtins*
|
||||
|
||||
Each builtin accesses memory according to a provided boolean mask. These are
|
||||
provided as ``__builtin_masked_load`` and ``__builtin_masked_store``. The first
|
||||
argument is always boolean mask vector.
|
||||
|
||||
Example:
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
using v8b = bool [[clang::ext_vector_type(8)]];
|
||||
using v8i = int [[clang::ext_vector_type(8)]];
|
||||
|
||||
v8i load(v8b m, v8i *p) { return __builtin_masked_load(m, p); }
|
||||
|
||||
void store(v8b m, v8i v, v8i *p) { __builtin_masked_store(m, v, p); }
|
||||
|
||||
|
||||
Matrix Types
|
||||
============
|
||||
|
||||
@ -1763,7 +1725,6 @@ Hexadecimal floating constants (N308) C
|
||||
Compound literals (N716) C99 C89, C++
|
||||
``//`` comments (N644) C99 C89
|
||||
Mixed declarations and code (N740) C99 C89
|
||||
init-statement in for (N740) C99 C89
|
||||
Variadic macros (N707) C99 C89
|
||||
Empty macro arguments (N570) C99 C89
|
||||
Trailing comma in enum declaration C99 C89
|
||||
@ -1830,37 +1791,6 @@ __make_integer_seq
|
||||
|
||||
This alias returns ``IntSeq`` instantiated with ``IntSeqT = T``and ``Ints`` being the pack ``0, ..., N - 1``.
|
||||
|
||||
__builtin_dedup_pack
|
||||
--------------------
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
template <class... Ts>
|
||||
using __builtin_dedup_pack = ...;
|
||||
|
||||
This alias takes a template parameter pack ``Ts`` and produces a new unexpanded pack containing the unique types
|
||||
from ``Ts``, with the order of the first occurrence of each type preserved.
|
||||
It is useful in template metaprogramming to normalize type lists.
|
||||
|
||||
The resulting pack can be expanded in contexts like template argument lists or base specifiers.
|
||||
|
||||
**Example of Use**:
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
template <typename...> struct TypeList;
|
||||
|
||||
// The resulting type is TypeList<int, double, char>
|
||||
template <typename ...ExtraTypes>
|
||||
using MyTypeList = TypeList<__builtin_dedup_pack<int, double, int, char, double, ExtraTypes...>...>;
|
||||
|
||||
**Limitations**:
|
||||
|
||||
* This builtin can only be used inside a template.
|
||||
* The resulting pack is currently only supported for expansion in template argument lists and base specifiers.
|
||||
* This builtin cannot be assigned to a template template parameter.
|
||||
|
||||
|
||||
Type Trait Primitives
|
||||
=====================
|
||||
|
||||
@ -4440,7 +4370,7 @@ fall into one of the specified floating-point classes.
|
||||
|
||||
if (__builtin_isfpclass(x, 448)) {
|
||||
// `x` is positive finite value
|
||||
...
|
||||
...
|
||||
}
|
||||
|
||||
**Description**:
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -37,22 +37,6 @@ latest release, please see the `Clang Web Site <https://clang.llvm.org>`_ or the
|
||||
Potentially Breaking Changes
|
||||
============================
|
||||
|
||||
- Clang will now emit a warning if the auto-detected GCC installation
|
||||
directory (i.e. the one with the largest version number) does not
|
||||
contain libstdc++ include directories although a "complete" GCC
|
||||
installation directory containing the include directories is
|
||||
available. It is planned to change the auto-detection to prefer the
|
||||
"complete" directory in the future. The warning will disappear if
|
||||
the libstdc++ include directories are either installed or removed
|
||||
for all GCC installation directories considered by the
|
||||
auto-detection; see the output of ``clang -v`` for a list of those
|
||||
directories. If the GCC installations cannot be modified and
|
||||
maintaining the current choice of the auto-detection is desired, the
|
||||
GCC installation directory can be selected explicitly using the
|
||||
``--gcc-install-dir`` command line argument. This will silence the
|
||||
warning. It can also be disabled using the
|
||||
``-Wno-gcc-install-dir-libstdcxx`` command line flag.
|
||||
|
||||
C/C++ Language Potentially Breaking Changes
|
||||
-------------------------------------------
|
||||
|
||||
@ -153,41 +137,10 @@ Non-comprehensive list of changes in this release
|
||||
- ``__builtin_elementwise_max`` and ``__builtin_elementwise_min`` functions for integer types can
|
||||
now be used in constant expressions.
|
||||
|
||||
- A vector of booleans is now a valid condition for the ternary ``?:`` operator.
|
||||
This binds to a simple vector select operation.
|
||||
|
||||
- Added ``__builtin_masked_load`` and ``__builtin_masked_store`` for conditional
|
||||
memory loads from vectors. Binds to the LLVM intrinsic of the same name.
|
||||
|
||||
- Use of ``__has_feature`` to detect the ``ptrauth_qualifier`` and ``ptrauth_intrinsics``
|
||||
features has been deprecated, and is restricted to the arm64e target only. The
|
||||
correct method to check for these features is to test for the ``__PTRAUTH__``
|
||||
macro.
|
||||
|
||||
- Added a new builtin, ``__builtin_dedup_pack``, to remove duplicate types from a parameter pack.
|
||||
This feature is particularly useful in template metaprogramming for normalizing type lists.
|
||||
The builtin produces a new, unexpanded parameter pack that can be used in contexts like template
|
||||
argument lists or base specifiers.
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
template <typename...> struct TypeList;
|
||||
|
||||
// The resulting type is TypeList<int, double, char>
|
||||
using MyTypeList = TypeList<__builtin_dedup_pack<int, double, int, char, double>...>;
|
||||
|
||||
Currently, the use of ``__builtin_dedup_pack`` is limited to template arguments and base
|
||||
specifiers, it also must be used within a template context.
|
||||
|
||||
|
||||
New Compiler Flags
|
||||
------------------
|
||||
- New option ``-fno-sanitize-annotate-debug-info-traps`` added to disable emitting trap reasons into the debug info when compiling with trapping UBSan (e.g. ``-fsanitize-trap=undefined``).
|
||||
|
||||
Lanai Support
|
||||
^^^^^^^^^^^^^^
|
||||
- The option ``-mcmodel={small,medium,large}`` is supported again.
|
||||
|
||||
Deprecated Compiler Flags
|
||||
-------------------------
|
||||
|
||||
@ -225,9 +178,6 @@ Improvements to Clang's diagnostics
|
||||
potential misaligned members get processed before they can get discarded.
|
||||
(#GH144729)
|
||||
|
||||
- Fixed false positive in ``-Wmissing-noreturn`` diagnostic when it was requiring the usage of
|
||||
``[[noreturn]]`` on lambdas before C++23 (#GH154493).
|
||||
|
||||
Improvements to Clang's time-trace
|
||||
----------------------------------
|
||||
|
||||
@ -245,7 +195,6 @@ Bug Fixes in This Version
|
||||
cast chain. (#GH149967).
|
||||
- Fixed a crash with incompatible pointer to integer conversions in designated
|
||||
initializers involving string literals. (#GH154046)
|
||||
- Fixed scope of typedefs present inside a template class. (#GH91451)
|
||||
|
||||
Bug Fixes to Compiler Builtins
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
@ -255,8 +204,8 @@ Bug Fixes to Compiler Builtins
|
||||
Bug Fixes to Attribute Support
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
- ``[[nodiscard]]`` is now respected on Objective-C and Objective-C++ methods
|
||||
(#GH141504) and on types returned from indirect calls (#GH142453).
|
||||
- ``[[nodiscard]]`` is now respected on Objective-C and Objective-C++ methods.
|
||||
(#GH141504)
|
||||
- Fixes some late parsed attributes, when applied to function definitions, not being parsed
|
||||
in function try blocks, and some situations where parsing of the function body
|
||||
is skipped, such as error recovery and code completion. (#GH153551)
|
||||
@ -268,7 +217,6 @@ Bug Fixes to C++ Support
|
||||
- Diagnose binding a reference to ``*nullptr`` during constant evaluation. (#GH48665)
|
||||
- Suppress ``-Wdeprecated-declarations`` in implicitly generated functions. (#GH147293)
|
||||
- Fix a crash when deleting a pointer to an incomplete array (#GH150359).
|
||||
- Fixed a mismatched lambda scope bug when propagating up ``consteval`` within nested lambdas. (#GH145776)
|
||||
- Fix an assertion failure when expression in assumption attribute
|
||||
(``[[assume(expr)]]``) creates temporary objects.
|
||||
- Fix the dynamic_cast to final class optimization to correctly handle
|
||||
@ -278,8 +226,6 @@ Bug Fixes to C++ Support
|
||||
"intializing multiple members of union" coincide (#GH149985).
|
||||
- Fix a crash when using ``explicit(bool)`` in pre-C++11 language modes. (#GH152729)
|
||||
- Fix the parsing of variadic member functions when the ellipis immediately follows a default argument.(#GH153445)
|
||||
- Fixed a bug that caused ``this`` captured by value in a lambda with a dependent explicit object parameter to not be
|
||||
instantiated properly. (#GH154054)
|
||||
|
||||
Bug Fixes to AST Handling
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
@ -309,13 +255,6 @@ NVPTX Support
|
||||
|
||||
X86 Support
|
||||
^^^^^^^^^^^
|
||||
- More SSE, AVX and AVX512 intrinsics, including initializers and general
|
||||
arithmetic can now be used in C++ constant expressions.
|
||||
- Some SSE, AVX and AVX512 intrinsics have been converted to wrap
|
||||
generic __builtin intrinsics.
|
||||
- NOTE: Please avoid use of the __builtin_ia32_* intrinsics - these are not
|
||||
guaranteed to exist in future releases, or match behaviour with previous
|
||||
releases of clang or other compilers.
|
||||
|
||||
Arm and AArch64 Support
|
||||
^^^^^^^^^^^^^^^^^^^^^^^
|
||||
@ -375,9 +314,6 @@ AST Matchers
|
||||
- Add a boolean member ``IgnoreSystemHeaders`` to ``MatchFinderOptions``. This
|
||||
allows it to ignore nodes in system headers when traversing the AST.
|
||||
|
||||
- ``hasConditionVariableStatement`` now supports ``for`` loop, ``while`` loop
|
||||
and ``switch`` statements.
|
||||
|
||||
clang-format
|
||||
------------
|
||||
- Add ``SpaceInEmptyBraces`` option and set it to ``Always`` for WebKit style.
|
||||
|
@ -226,8 +226,8 @@ one-phase compilation model is simpler for build systems to implement while the
|
||||
two-phase compilation has the potential to compile faster due to higher
|
||||
parallelism. As an example, if there are two module units ``A`` and ``B``, and
|
||||
``B`` depends on ``A``, the one-phase compilation model needs to compile them
|
||||
serially, whereas the two-phase compilation model can be compiled as
|
||||
soon as ``A.pcm`` is available, and thus can be compiled simultaneously with the
|
||||
serially, whereas the two-phase compilation model is able to be compiled as
|
||||
soon as ``A.pcm`` is available, and thus can be compiled simultaneously as the
|
||||
``A.pcm`` to ``A.o`` compilation step.
|
||||
|
||||
File name requirements
|
||||
@ -391,7 +391,7 @@ And the compilation processes for module units are like:
|
||||
As the diagrams show, we need to compile the BMI from module units to object
|
||||
files and then link the object files. (However, this cannot be done for the BMI
|
||||
from header units. See the section on :ref:`header units <header-units>` for
|
||||
more details.)
|
||||
more details.
|
||||
|
||||
BMIs cannot be shipped in an archive to create a module library. Instead, the
|
||||
BMIs(``*.pcm``) are compiled into object files(``*.o``) and those object files
|
||||
@ -403,7 +403,7 @@ clang-cl
|
||||
``clang-cl`` supports the same options as ``clang++`` for modules as detailed above;
|
||||
there is no need to prefix these options with ``/clang:``. Note that ``cl.exe``
|
||||
`options to emit/consume IFC files <https://devblogs.microsoft.com/cppblog/using-cpp-modules-in-msvc-from-the-command-line-part-1/>` are *not* supported.
|
||||
The resulting precompiled modules are also not compatible for use with ``cl.exe``.
|
||||
The resultant precompiled modules are also not compatible for use with ``cl.exe``.
|
||||
|
||||
We recommend that build system authors use the above-mentioned ``clang++`` options with ``clang-cl`` to build modules.
|
||||
|
||||
@ -411,7 +411,7 @@ Consistency Requirements
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Modules can be viewed as a kind of cache to speed up compilation. Thus, like
|
||||
other caching techniques, it is important to maintain cache consistency, which
|
||||
other caching techniques, it is important to maintain cache consistency which
|
||||
is why Clang does very strict checking for consistency.
|
||||
|
||||
Options consistency
|
||||
@ -472,8 +472,8 @@ To overcome these requirements and simplify cases like distributed builds and sa
|
||||
builds, users can use the ``-fmodules-embed-all-files`` flag to embed all input files
|
||||
into the BMI so that Clang does not need to open the corresponding file on disk.
|
||||
|
||||
When the ``-fmodules-embed-all-files`` flag is enabled, Clang explicitly emits the source
|
||||
code into the BMI file; the BMI file contains a sufficiently verbose
|
||||
When the ``-fmodules-embed-all-files`` flag are enabled, Clang explicitly emits the source
|
||||
code into the BMI file, the contents of the BMI file contain a sufficiently verbose
|
||||
representation to reproduce the original source file.
|
||||
|
||||
.. [1] Input files: The source files which took part in the compilation of the BMI.
|
||||
@ -578,7 +578,7 @@ handle the dynamic initialization of non-inline variables in the module unit.
|
||||
The importable module unit has to emit the initializer even if there is no
|
||||
dynamic initialization; otherwise, the importer may call a nonexistent
|
||||
function. The initializer function emits calls to imported modules first
|
||||
followed by calls to all of the dynamic initializers in the current module
|
||||
followed by calls to all to of the dynamic initializers in the current module
|
||||
unit.
|
||||
|
||||
Translation units that explicitly or implicitly import a named module must call
|
||||
@ -689,9 +689,9 @@ ensure it is reachable, e.g. ``using N::g;``.
|
||||
|
||||
As of Clang 22.x, the Reduced BMI is enabled by default. You may still want to
|
||||
use Full BMI with ``-fno-modules-reduced-bmi`` in the following case:
|
||||
1. Your build system uses two-phase compilation, but it hasn't adjusted the
|
||||
1. Your build system uses two-phase compilation but it haven't adjusted the
|
||||
implementation for reduced BMI.
|
||||
2. You encounter a regression with Reduced BMI that you cannot work around. Please
|
||||
2. You meet a regression with Reduced BMI that you cannot work around. Please
|
||||
report an issue for this case.
|
||||
|
||||
Experimental Non-Cascading Changes
|
||||
@ -699,7 +699,7 @@ Experimental Non-Cascading Changes
|
||||
|
||||
This section is primarily for build system vendors. For end compiler users,
|
||||
if you don't want to read it all, this is helpful to reduce recompilations.
|
||||
We encourage build system vendors and end users to try this out and bring feedback.
|
||||
We encourage build system vendors and end users try this out and bring feedback.
|
||||
|
||||
Before Clang 19, a change in BMI of any (transitive) dependency would cause the
|
||||
outputs of the BMI to change. Starting with Clang 19, changes to non-direct
|
||||
@ -786,7 +786,7 @@ We encourage build systems to add an experimental mode that
|
||||
reuses the cached BMI when **direct** dependencies did not change,
|
||||
even if **transitive** dependencies did change.
|
||||
|
||||
Given that there are potential compiler bugs, we recommend that build systems
|
||||
Given there are potential compiler bugs, we recommend that build systems
|
||||
support this feature as a configurable option so that users
|
||||
can go back to the transitive change mode safely at any time.
|
||||
|
||||
@ -813,7 +813,7 @@ With reduced BMI, non-cascading changes can be more powerful. For example,
|
||||
$ md5sum B.pcm
|
||||
6c2bd452ca32ab418bf35cd141b060b9 B.pcm
|
||||
|
||||
And let's change the implementation for ``A.cppm`` to:
|
||||
And let's change the implementation for ``A.cppm`` into:
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
@ -830,7 +830,7 @@ and recompile the example:
|
||||
$ md5sum B.pcm
|
||||
6c2bd452ca32ab418bf35cd141b060b9 B.pcm
|
||||
|
||||
We should find the contents of ``B.pcm`` remain the same. In this case, the build system is
|
||||
We should find the contents of ``B.pcm`` remains the same. In this case, the build system is
|
||||
allowed to skip recompilations of TUs which solely and directly depend on module ``B``.
|
||||
|
||||
This only happens with a reduced BMI. With reduced BMIs, we won't record the function body
|
||||
@ -845,7 +845,7 @@ Reduce duplications
|
||||
|
||||
While it is valid to have duplicated declarations in the global module fragments
|
||||
of different module units, it is not free for Clang to deal with the duplicated
|
||||
declarations. A translation unit will compile more slowly if there are a lot of
|
||||
declarations. A translation unit will compile more slowly if there is a lot of
|
||||
duplicated declarations between the translation unit and modules it imports.
|
||||
For example:
|
||||
|
||||
@ -937,7 +937,7 @@ possible. However, it may be a breaking change for existing code or libraries
|
||||
to switch to modules. As a result, many existing libraries need to provide
|
||||
both headers and module interfaces for a while to not break existing users.
|
||||
|
||||
This section provides some suggestions on how to ease the transition process
|
||||
This section suggests some suggestions on how to ease the transition process
|
||||
for existing libraries. **Note that this information is only intended as
|
||||
guidance, rather than as requirements to use modules in Clang.** It presumes
|
||||
the project is starting with no module-based dependencies.
|
||||
@ -1140,7 +1140,7 @@ module unit which is internal to the module itself.
|
||||
Providing a header to skip parsing redundant headers
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Many redeclarations shared between translation units cause Clang to have
|
||||
Many redeclarations shared between translation units causes Clang to have
|
||||
slower compile-time performance. Further, there are known issues with
|
||||
`include after import <https://github.com/llvm/llvm-project/issues/61465>`_.
|
||||
Even when that issue is resolved, users may still get slower compilation speed
|
||||
@ -1408,8 +1408,8 @@ P1857R3 is implemented. This is tracked by
|
||||
Until then, it is recommended not to mix macros with module declarations.
|
||||
|
||||
|
||||
Inconsistent filename suffix requirement for importable module units
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
In consistent filename suffix requirement for importable module units
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Currently, Clang requires the file name of an ``importable module unit`` to
|
||||
have ``.cppm`` (or ``.ccm``, ``.cxxm``, ``.c++m``) as the file extension.
|
||||
@ -1484,7 +1484,7 @@ How to build projects using header units
|
||||
.. warning::
|
||||
|
||||
The support for header units, including related command line options, is
|
||||
experimental. There are still many unanswered questions about how tools
|
||||
experimental. There are still many unanswered question about how tools
|
||||
should interact with header units. The details described here may change in
|
||||
the future.
|
||||
|
||||
@ -1881,7 +1881,7 @@ Individual command line options can be specified after ``--``.
|
||||
options. Note that the path to the compiler executable needs to be specified
|
||||
explicitly instead of using ``clang++`` directly.
|
||||
|
||||
Users may want the scanner to get the transitive dependency information for
|
||||
Users may want the scanner to get the transitional dependency information for
|
||||
headers. Otherwise, the project has to be scanned twice, once for headers and
|
||||
once for modules. To address this, ``clang-scan-deps`` will recognize the
|
||||
specified preprocessor options in the given command line and generate the
|
||||
@ -1912,7 +1912,7 @@ Possible Issues: Failed to find system headers
|
||||
|
||||
If encountering an error like ``fatal error: 'stddef.h' file not found``,
|
||||
the specified ``<path-to-compiler-executable>/clang++`` probably refers to a
|
||||
symlink instead of a real binary. There are four potential solutions to the
|
||||
symlink instead a real binary. There are four potential solutions to the
|
||||
problem:
|
||||
|
||||
1. Point the specified compiler executable to the real binary instead of the
|
||||
|
@ -825,6 +825,13 @@ doesn't know that munl.mu == mutex. The SCOPED_CAPABILITY attribute handles
|
||||
aliasing for MutexLocker, but does so only for that particular pattern.
|
||||
|
||||
|
||||
ACQUIRED_BEFORE(...) and ACQUIRED_AFTER(...) support is still experimental.
|
||||
---------------------------------------------------------------------------
|
||||
|
||||
ACQUIRED_BEFORE(...) and ACQUIRED_AFTER(...) are currently being developed under
|
||||
the ``-Wthread-safety-beta`` flag.
|
||||
|
||||
|
||||
.. _mutexheader:
|
||||
|
||||
mutex.h
|
||||
|
@ -230,8 +230,6 @@ class ASTContext : public RefCountedBase<ASTContext> {
|
||||
SubstTemplateTypeParmTypes;
|
||||
mutable llvm::FoldingSet<SubstTemplateTypeParmPackType>
|
||||
SubstTemplateTypeParmPackTypes;
|
||||
mutable llvm::FoldingSet<SubstBuiltinTemplatePackType>
|
||||
SubstBuiltinTemplatePackTypes;
|
||||
mutable llvm::ContextualFoldingSet<TemplateSpecializationType, ASTContext&>
|
||||
TemplateSpecializationTypes;
|
||||
mutable llvm::FoldingSet<ParenType> ParenTypes{GeneralTypesLog2InitSize};
|
||||
@ -1897,7 +1895,6 @@ public:
|
||||
QualType getSubstTemplateTypeParmPackType(Decl *AssociatedDecl,
|
||||
unsigned Index, bool Final,
|
||||
const TemplateArgument &ArgPack);
|
||||
QualType getSubstBuiltinTemplatePack(const TemplateArgument &ArgPack);
|
||||
|
||||
QualType
|
||||
getTemplateTypeParmType(unsigned Depth, unsigned Index,
|
||||
|
@ -359,7 +359,7 @@ class CXXFinalOverriderMap
|
||||
|
||||
/// A set of all the primary bases for a class.
|
||||
class CXXIndirectPrimaryBaseSet
|
||||
: public llvm::SmallPtrSet<const CXXRecordDecl *, 32> {};
|
||||
: public llvm::SmallSet<const CXXRecordDecl*, 32> {};
|
||||
|
||||
inline bool
|
||||
inheritanceModelHasVBPtrOffsetField(MSInheritanceModel Inheritance) {
|
||||
|
@ -3526,7 +3526,7 @@ protected:
|
||||
public:
|
||||
// Low-level accessor. If you just want the type defined by this node,
|
||||
// check out ASTContext::getTypeDeclType or one of
|
||||
// ASTContext::getTypedefType, ASTContext::getTagType, etc. if you
|
||||
// ASTContext::getTypedefType, ASTContext::getRecordType, etc. if you
|
||||
// already know the specific kind of node this is.
|
||||
const Type *getTypeForDecl() const {
|
||||
assert(!isa<TagDecl>(this));
|
||||
|
@ -1796,10 +1796,7 @@ public:
|
||||
}
|
||||
|
||||
BuiltinTemplateKind getBuiltinTemplateKind() const { return BTK; }
|
||||
|
||||
bool isPackProducingBuiltinTemplate() const;
|
||||
};
|
||||
bool isPackProducingBuiltinTemplateName(TemplateName N);
|
||||
|
||||
/// Provides information about an explicit instantiation of a variable or class
|
||||
/// template.
|
||||
|
@ -1250,32 +1250,19 @@ public:
|
||||
SourceLocation EndLoc);
|
||||
};
|
||||
|
||||
// A structure to stand in for the recipe on a reduction. RecipeDecl is the
|
||||
// 'main' declaration used for initializaiton, which is fixed.
|
||||
struct OpenACCReductionRecipe {
|
||||
VarDecl *RecipeDecl;
|
||||
// TODO: OpenACC: this should eventually have the operations here too.
|
||||
};
|
||||
|
||||
class OpenACCReductionClause final
|
||||
: public OpenACCClauseWithVarList,
|
||||
private llvm::TrailingObjects<OpenACCReductionClause, Expr *,
|
||||
OpenACCReductionRecipe> {
|
||||
private llvm::TrailingObjects<OpenACCReductionClause, Expr *> {
|
||||
friend TrailingObjects;
|
||||
OpenACCReductionOperator Op;
|
||||
|
||||
OpenACCReductionClause(SourceLocation BeginLoc, SourceLocation LParenLoc,
|
||||
OpenACCReductionOperator Operator,
|
||||
ArrayRef<Expr *> VarList,
|
||||
ArrayRef<OpenACCReductionRecipe> Recipes,
|
||||
SourceLocation EndLoc)
|
||||
ArrayRef<Expr *> VarList, SourceLocation EndLoc)
|
||||
: OpenACCClauseWithVarList(OpenACCClauseKind::Reduction, BeginLoc,
|
||||
LParenLoc, EndLoc),
|
||||
Op(Operator) {
|
||||
assert(VarList.size() == Recipes.size());
|
||||
setExprs(getTrailingObjects<Expr *>(VarList.size()), VarList);
|
||||
llvm::uninitialized_copy(Recipes, getTrailingObjects<
|
||||
OpenACCReductionRecipe > ());
|
||||
setExprs(getTrailingObjects(VarList.size()), VarList);
|
||||
}
|
||||
|
||||
public:
|
||||
@ -1283,26 +1270,12 @@ public:
|
||||
return C->getClauseKind() == OpenACCClauseKind::Reduction;
|
||||
}
|
||||
|
||||
ArrayRef<OpenACCReductionRecipe> getRecipes() {
|
||||
return ArrayRef<OpenACCReductionRecipe>{
|
||||
getTrailingObjects<OpenACCReductionRecipe>(), getExprs().size()};
|
||||
}
|
||||
|
||||
ArrayRef<OpenACCReductionRecipe> getRecipes() const {
|
||||
return ArrayRef<OpenACCReductionRecipe>{
|
||||
getTrailingObjects<OpenACCReductionRecipe>(), getExprs().size()};
|
||||
}
|
||||
|
||||
static OpenACCReductionClause *
|
||||
Create(const ASTContext &C, SourceLocation BeginLoc, SourceLocation LParenLoc,
|
||||
OpenACCReductionOperator Operator, ArrayRef<Expr *> VarList,
|
||||
ArrayRef<OpenACCReductionRecipe> Recipes, SourceLocation EndLoc);
|
||||
SourceLocation EndLoc);
|
||||
|
||||
OpenACCReductionOperator getReductionOp() const { return Op; }
|
||||
|
||||
size_t numTrailingObjects(OverloadToken<Expr *>) const {
|
||||
return getExprs().size();
|
||||
}
|
||||
};
|
||||
|
||||
class OpenACCLinkClause final
|
||||
|
@ -492,8 +492,6 @@ private:
|
||||
bool TraverseTemplateArgumentLocsHelper(const TemplateArgumentLoc *TAL,
|
||||
unsigned Count);
|
||||
bool TraverseArrayTypeLocHelper(ArrayTypeLoc TL);
|
||||
bool TraverseSubstPackTypeHelper(SubstPackType *T);
|
||||
bool TraverseSubstPackTypeLocHelper(SubstPackTypeLoc TL);
|
||||
bool TraverseRecordHelper(RecordDecl *D);
|
||||
bool TraverseCXXRecordHelper(CXXRecordDecl *D);
|
||||
bool TraverseDeclaratorHelper(DeclaratorDecl *D);
|
||||
@ -1140,10 +1138,9 @@ DEF_TRAVERSE_TYPE(TemplateTypeParmType, {})
|
||||
DEF_TRAVERSE_TYPE(SubstTemplateTypeParmType, {
|
||||
TRY_TO(TraverseType(T->getReplacementType()));
|
||||
})
|
||||
DEF_TRAVERSE_TYPE(SubstTemplateTypeParmPackType,
|
||||
{ TRY_TO(TraverseSubstPackTypeHelper(T)); })
|
||||
DEF_TRAVERSE_TYPE(SubstBuiltinTemplatePackType,
|
||||
{ TRY_TO(TraverseSubstPackTypeHelper(T)); })
|
||||
DEF_TRAVERSE_TYPE(SubstTemplateTypeParmPackType, {
|
||||
TRY_TO(TraverseTemplateArgument(T->getArgumentPack()));
|
||||
})
|
||||
|
||||
DEF_TRAVERSE_TYPE(AttributedType,
|
||||
{ TRY_TO(TraverseType(T->getModifiedType())); })
|
||||
@ -1484,26 +1481,9 @@ DEF_TRAVERSE_TYPELOC(TemplateTypeParmType, {})
|
||||
DEF_TRAVERSE_TYPELOC(SubstTemplateTypeParmType, {
|
||||
TRY_TO(TraverseType(TL.getTypePtr()->getReplacementType()));
|
||||
})
|
||||
|
||||
template <typename Derived>
|
||||
bool RecursiveASTVisitor<Derived>::TraverseSubstPackTypeLocHelper(
|
||||
SubstPackTypeLoc TL) {
|
||||
DEF_TRAVERSE_TYPELOC(SubstTemplateTypeParmPackType, {
|
||||
TRY_TO(TraverseTemplateArgument(TL.getTypePtr()->getArgumentPack()));
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename Derived>
|
||||
bool RecursiveASTVisitor<Derived>::TraverseSubstPackTypeHelper(
|
||||
SubstPackType *T) {
|
||||
TRY_TO(TraverseTemplateArgument(T->getArgumentPack()));
|
||||
return true;
|
||||
}
|
||||
|
||||
DEF_TRAVERSE_TYPELOC(SubstTemplateTypeParmPackType,
|
||||
{ TRY_TO(TraverseSubstPackTypeLocHelper(TL)); })
|
||||
|
||||
DEF_TRAVERSE_TYPELOC(SubstBuiltinTemplatePackType,
|
||||
{ TRY_TO(TraverseSubstPackTypeLocHelper(TL)); })
|
||||
})
|
||||
|
||||
DEF_TRAVERSE_TYPELOC(ParenType, { TRY_TO(TraverseTypeLoc(TL.getInnerLoc())); })
|
||||
|
||||
|
@ -2210,24 +2210,20 @@ protected:
|
||||
unsigned PackIndex : 15;
|
||||
};
|
||||
|
||||
class SubstPackTypeBitfields {
|
||||
friend class SubstPackType;
|
||||
class SubstTemplateTypeParmPackTypeBitfields {
|
||||
friend class SubstTemplateTypeParmPackType;
|
||||
|
||||
LLVM_PREFERRED_TYPE(TypeBitfields)
|
||||
unsigned : NumTypeBits;
|
||||
|
||||
// The index of the template parameter this substitution represents.
|
||||
unsigned Index : 16;
|
||||
|
||||
/// The number of template arguments in \c Arguments, which is
|
||||
/// expected to be able to hold at least 1024 according to [implimits].
|
||||
/// However as this limit is somewhat easy to hit with template
|
||||
/// metaprogramming we'd prefer to keep it as large as possible.
|
||||
unsigned NumArgs : 16;
|
||||
|
||||
// The index of the template parameter this substitution represents.
|
||||
// Only used by SubstTemplateTypeParmPackType. We keep it in the same
|
||||
// class to avoid dealing with complexities of bitfields that go over
|
||||
// the size of `unsigned`.
|
||||
unsigned SubstTemplTypeParmPackIndex : 16;
|
||||
};
|
||||
|
||||
class TemplateSpecializationTypeBitfields {
|
||||
@ -2344,7 +2340,7 @@ protected:
|
||||
VectorTypeBitfields VectorTypeBits;
|
||||
TemplateTypeParmTypeBitfields TemplateTypeParmTypeBits;
|
||||
SubstTemplateTypeParmTypeBitfields SubstTemplateTypeParmTypeBits;
|
||||
SubstPackTypeBitfields SubstPackTypeBits;
|
||||
SubstTemplateTypeParmPackTypeBitfields SubstTemplateTypeParmPackTypeBits;
|
||||
TemplateSpecializationTypeBitfields TemplateSpecializationTypeBits;
|
||||
DependentTemplateSpecializationTypeBitfields
|
||||
DependentTemplateSpecializationTypeBits;
|
||||
@ -6405,9 +6401,6 @@ protected:
|
||||
bool IsInjected, const Type *CanonicalType);
|
||||
|
||||
public:
|
||||
// FIXME: Temporarily renamed from `getDecl` in order to facilitate
|
||||
// rebasing, due to change in behaviour. This should be renamed back
|
||||
// to `getDecl` once the change is settled.
|
||||
TagDecl *getOriginalDecl() const { return decl; }
|
||||
|
||||
NestedNameSpecifier getQualifier() const;
|
||||
@ -6473,9 +6466,6 @@ class RecordType final : public TagType {
|
||||
using TagType::TagType;
|
||||
|
||||
public:
|
||||
// FIXME: Temporarily renamed from `getDecl` in order to facilitate
|
||||
// rebasing, due to change in behaviour. This should be renamed back
|
||||
// to `getDecl` once the change is settled.
|
||||
RecordDecl *getOriginalDecl() const {
|
||||
return reinterpret_cast<RecordDecl *>(TagType::getOriginalDecl());
|
||||
}
|
||||
@ -6493,9 +6483,6 @@ class EnumType final : public TagType {
|
||||
using TagType::TagType;
|
||||
|
||||
public:
|
||||
// FIXME: Temporarily renamed from `getDecl` in order to facilitate
|
||||
// rebasing, due to change in behaviour. This should be renamed back
|
||||
// to `getDecl` once the change is settled.
|
||||
EnumDecl *getOriginalDecl() const {
|
||||
return reinterpret_cast<EnumDecl *>(TagType::getOriginalDecl());
|
||||
}
|
||||
@ -6528,9 +6515,6 @@ class InjectedClassNameType final : public TagType {
|
||||
bool IsInjected, const Type *CanonicalType);
|
||||
|
||||
public:
|
||||
// FIXME: Temporarily renamed from `getDecl` in order to facilitate
|
||||
// rebasing, due to change in behaviour. This should be renamed back
|
||||
// to `getDecl` once the change is settled.
|
||||
CXXRecordDecl *getOriginalDecl() const {
|
||||
return reinterpret_cast<CXXRecordDecl *>(TagType::getOriginalDecl());
|
||||
}
|
||||
@ -6996,56 +6980,6 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
/// Represents the result of substituting a set of types as a template argument
|
||||
/// that needs to be expanded later.
|
||||
///
|
||||
/// These types are always dependent and produced depending on the situations:
|
||||
/// - SubstTemplateTypeParmPack is an expansion that had to be delayed,
|
||||
/// - SubstBuiltinTemplatePackType is an expansion from a builtin.
|
||||
class SubstPackType : public Type, public llvm::FoldingSetNode {
|
||||
friend class ASTContext;
|
||||
|
||||
/// A pointer to the set of template arguments that this
|
||||
/// parameter pack is instantiated with.
|
||||
const TemplateArgument *Arguments;
|
||||
|
||||
protected:
|
||||
SubstPackType(TypeClass Derived, QualType Canon,
|
||||
const TemplateArgument &ArgPack);
|
||||
|
||||
public:
|
||||
unsigned getNumArgs() const { return SubstPackTypeBits.NumArgs; }
|
||||
|
||||
TemplateArgument getArgumentPack() const;
|
||||
|
||||
void Profile(llvm::FoldingSetNodeID &ID);
|
||||
static void Profile(llvm::FoldingSetNodeID &ID,
|
||||
const TemplateArgument &ArgPack);
|
||||
|
||||
static bool classof(const Type *T) {
|
||||
return T->getTypeClass() == SubstTemplateTypeParmPack ||
|
||||
T->getTypeClass() == SubstBuiltinTemplatePack;
|
||||
}
|
||||
};
|
||||
|
||||
/// Represents the result of substituting a builtin template as a pack.
|
||||
class SubstBuiltinTemplatePackType : public SubstPackType {
|
||||
friend class ASTContext;
|
||||
|
||||
SubstBuiltinTemplatePackType(QualType Canon, const TemplateArgument &ArgPack);
|
||||
|
||||
public:
|
||||
bool isSugared() const { return false; }
|
||||
QualType desugar() const { return QualType(this, 0); }
|
||||
|
||||
/// Mark that we reuse the Profile. We do not introduce new fields.
|
||||
using SubstPackType::Profile;
|
||||
|
||||
static bool classof(const Type *T) {
|
||||
return T->getTypeClass() == SubstBuiltinTemplatePack;
|
||||
}
|
||||
};
|
||||
|
||||
/// Represents the result of substituting a set of types for a template
|
||||
/// type parameter pack.
|
||||
///
|
||||
@ -7058,7 +6992,7 @@ public:
|
||||
/// that pack expansion (e.g., when all template parameters have corresponding
|
||||
/// arguments), this type will be replaced with the \c SubstTemplateTypeParmType
|
||||
/// at the current pack substitution index.
|
||||
class SubstTemplateTypeParmPackType : public SubstPackType {
|
||||
class SubstTemplateTypeParmPackType : public Type, public llvm::FoldingSetNode {
|
||||
friend class ASTContext;
|
||||
|
||||
/// A pointer to the set of template arguments that this
|
||||
@ -7084,17 +7018,21 @@ public:
|
||||
|
||||
/// Returns the index of the replaced parameter in the associated declaration.
|
||||
/// This should match the result of `getReplacedParameter()->getIndex()`.
|
||||
unsigned getIndex() const {
|
||||
return SubstPackTypeBits.SubstTemplTypeParmPackIndex;
|
||||
}
|
||||
unsigned getIndex() const { return SubstTemplateTypeParmPackTypeBits.Index; }
|
||||
|
||||
// This substitution will be Final, which means the substitution will be fully
|
||||
// sugared: it doesn't need to be resugared later.
|
||||
bool getFinal() const;
|
||||
|
||||
unsigned getNumArgs() const {
|
||||
return SubstTemplateTypeParmPackTypeBits.NumArgs;
|
||||
}
|
||||
|
||||
bool isSugared() const { return false; }
|
||||
QualType desugar() const { return QualType(this, 0); }
|
||||
|
||||
TemplateArgument getArgumentPack() const;
|
||||
|
||||
void Profile(llvm::FoldingSetNodeID &ID);
|
||||
static void Profile(llvm::FoldingSetNodeID &ID, const Decl *AssociatedDecl,
|
||||
unsigned Index, bool Final,
|
||||
@ -7329,7 +7267,9 @@ public:
|
||||
TemplateSpecializationTypeBits.NumArgs};
|
||||
}
|
||||
|
||||
bool isSugared() const;
|
||||
bool isSugared() const {
|
||||
return !isDependentType() || isCurrentInstantiation() || isTypeAlias();
|
||||
}
|
||||
|
||||
QualType desugar() const {
|
||||
return isTypeAlias() ? getAliasedType() : getCanonicalTypeInternal();
|
||||
|
@ -989,22 +989,12 @@ class SubstTemplateTypeParmTypeLoc :
|
||||
SubstTemplateTypeParmType> {
|
||||
};
|
||||
|
||||
/// Abstract type representing delayed type pack expansions.
|
||||
class SubstPackTypeLoc
|
||||
: public InheritingConcreteTypeLoc<TypeSpecTypeLoc, SubstPackTypeLoc,
|
||||
SubstPackType> {};
|
||||
|
||||
/// Wrapper for substituted template type parameters.
|
||||
class SubstTemplateTypeParmPackTypeLoc
|
||||
: public InheritingConcreteTypeLoc<SubstPackTypeLoc,
|
||||
SubstTemplateTypeParmPackTypeLoc,
|
||||
SubstTemplateTypeParmPackType> {};
|
||||
|
||||
/// Wrapper for substituted template type parameters.
|
||||
class SubstBuiltinTemplatePackTypeLoc
|
||||
: public InheritingConcreteTypeLoc<SubstPackTypeLoc,
|
||||
SubstBuiltinTemplatePackTypeLoc,
|
||||
SubstBuiltinTemplatePackType> {};
|
||||
/// Wrapper for substituted template type parameters.
|
||||
class SubstTemplateTypeParmPackTypeLoc :
|
||||
public InheritingConcreteTypeLoc<TypeSpecTypeLoc,
|
||||
SubstTemplateTypeParmPackTypeLoc,
|
||||
SubstTemplateTypeParmPackType> {
|
||||
};
|
||||
|
||||
struct AttributedLocInfo {
|
||||
const Attr *TypeAttr;
|
||||
|
@ -820,12 +820,6 @@ let Class = PackExpansionType in {
|
||||
}]>;
|
||||
}
|
||||
|
||||
let Class = SubstPackType in {
|
||||
def : Property<"replacementPack", TemplateArgument> {
|
||||
let Read = [{ node->getArgumentPack() }];
|
||||
}
|
||||
}
|
||||
|
||||
let Class = SubstTemplateTypeParmPackType in {
|
||||
def : Property<"associatedDecl", DeclRef> {
|
||||
let Read = [{ node->getAssociatedDecl() }];
|
||||
@ -833,7 +827,12 @@ let Class = SubstTemplateTypeParmPackType in {
|
||||
def : Property<"Index", UInt32> {
|
||||
let Read = [{ node->getIndex() }];
|
||||
}
|
||||
def : Property<"Final", Bool> { let Read = [{ node->getFinal() }]; }
|
||||
def : Property<"Final", Bool> {
|
||||
let Read = [{ node->getFinal() }];
|
||||
}
|
||||
def : Property<"replacementPack", TemplateArgument> {
|
||||
let Read = [{ node->getArgumentPack() }];
|
||||
}
|
||||
|
||||
def : Creator<[{
|
||||
return ctx.getSubstTemplateTypeParmPackType(
|
||||
@ -841,12 +840,6 @@ let Class = SubstTemplateTypeParmPackType in {
|
||||
}]>;
|
||||
}
|
||||
|
||||
let Class = SubstBuiltinTemplatePackType in {
|
||||
def : Creator<[{
|
||||
return ctx.getSubstBuiltinTemplatePack(replacementPack);
|
||||
}]>;
|
||||
}
|
||||
|
||||
let Class = BuiltinType in {
|
||||
def : Property<"kind", BuiltinTypeKind> {
|
||||
let Read = [{ node->getKind() }];
|
||||
|
@ -5661,8 +5661,8 @@ AST_POLYMORPHIC_MATCHER_P(hasInitStatement,
|
||||
return Init != nullptr && InnerMatcher.matches(*Init, Finder, Builder);
|
||||
}
|
||||
|
||||
/// Matches the condition expression of an if statement, for loop, while loop,
|
||||
/// do-while loop, switch statement or conditional operator.
|
||||
/// Matches the condition expression of an if statement, for loop,
|
||||
/// switch statement or conditional operator.
|
||||
///
|
||||
/// Example matches true (matcher = hasCondition(cxxBoolLiteral(equals(true))))
|
||||
/// \code
|
||||
@ -5739,29 +5739,16 @@ AST_POLYMORPHIC_MATCHER_P(equalsBoundNode,
|
||||
return Builder->removeBindings(Predicate);
|
||||
}
|
||||
|
||||
/// Matches a declaration if it declares the same entity as the node previously
|
||||
/// bound to \p ID.
|
||||
AST_MATCHER_P(Decl, declaresSameEntityAsBoundNode, std::string, ID) {
|
||||
return Builder->removeBindings([&](const internal::BoundNodesMap &Nodes) {
|
||||
return !clang::declaresSameEntity(&Node, Nodes.getNodeAs<Decl>(ID));
|
||||
});
|
||||
}
|
||||
|
||||
/// Matches the condition variable statement in an if statement, for loop,
|
||||
/// while loop or switch statement.
|
||||
/// Matches the condition variable statement in an if statement.
|
||||
///
|
||||
/// Given
|
||||
/// \code
|
||||
/// if (A* a = GetAPointer()) {}
|
||||
/// for (; A* a = GetAPointer(); ) {}
|
||||
/// \endcode
|
||||
/// hasConditionVariableStatement(...)
|
||||
/// matches both 'A* a = GetAPointer()'.
|
||||
AST_POLYMORPHIC_MATCHER_P(hasConditionVariableStatement,
|
||||
AST_POLYMORPHIC_SUPPORTED_TYPES(IfStmt, ForStmt,
|
||||
WhileStmt,
|
||||
SwitchStmt),
|
||||
internal::Matcher<DeclStmt>, InnerMatcher) {
|
||||
/// matches 'A* a = GetAPointer()'.
|
||||
AST_MATCHER_P(IfStmt, hasConditionVariableStatement,
|
||||
internal::Matcher<DeclStmt>, InnerMatcher) {
|
||||
const DeclStmt* const DeclarationStatement =
|
||||
Node.getConditionVariableDeclStmt();
|
||||
return DeclarationStatement != nullptr &&
|
||||
|
@ -17,7 +17,6 @@
|
||||
#include "clang/AST/Decl.h"
|
||||
#include "clang/AST/Type.h"
|
||||
#include "llvm/ADT/DenseMap.h"
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
#include "llvm/Support/Debug.h"
|
||||
#include <cassert>
|
||||
|
||||
@ -153,11 +152,6 @@ public:
|
||||
return {SyntheticFields.begin(), SyntheticFields.end()};
|
||||
}
|
||||
|
||||
/// Add a synthetic field, if none by that name is already present.
|
||||
void addSyntheticField(llvm::StringRef Name, StorageLocation &Loc) {
|
||||
SyntheticFields.insert({Name, &Loc});
|
||||
}
|
||||
|
||||
/// Changes the child storage location for a field `D` of reference type.
|
||||
/// All other fields cannot change their storage location and always retain
|
||||
/// the storage location passed to the `RecordStorageLocation` constructor.
|
||||
@ -170,11 +164,6 @@ public:
|
||||
Children[&D] = Loc;
|
||||
}
|
||||
|
||||
/// Add a child storage location for a field `D`, if not already present.
|
||||
void addChild(const ValueDecl &D, StorageLocation *Loc) {
|
||||
Children.insert({&D, Loc});
|
||||
}
|
||||
|
||||
llvm::iterator_range<FieldToLoc::const_iterator> children() const {
|
||||
return {Children.begin(), Children.end()};
|
||||
}
|
||||
|
@ -23,20 +23,20 @@
|
||||
#include "clang/Basic/DiagnosticInstallAPI.h"
|
||||
#include "clang/Basic/DiagnosticLex.h"
|
||||
#include "clang/Basic/DiagnosticParse.h"
|
||||
#include "clang/Basic/DiagnosticRefactoring.h"
|
||||
#include "clang/Basic/DiagnosticSema.h"
|
||||
#include "clang/Basic/DiagnosticSerialization.h"
|
||||
#include "clang/Basic/DiagnosticRefactoring.h"
|
||||
|
||||
namespace clang {
|
||||
template <size_t SizeOfStr, typename FieldType> class StringSizerHelper {
|
||||
template <size_t SizeOfStr, typename FieldType>
|
||||
class StringSizerHelper {
|
||||
static_assert(SizeOfStr <= FieldType(~0U), "Field too small!");
|
||||
|
||||
public:
|
||||
enum { Size = SizeOfStr };
|
||||
};
|
||||
} // end namespace clang
|
||||
|
||||
#define STR_SIZE(str, fieldTy) \
|
||||
clang::StringSizerHelper<sizeof(str) - 1, fieldTy>::Size
|
||||
#define STR_SIZE(str, fieldTy) clang::StringSizerHelper<sizeof(str)-1, \
|
||||
fieldTy>::Size
|
||||
|
||||
#endif
|
||||
|
@ -1632,13 +1632,6 @@ def DeviceKernel : DeclOrTypeAttr {
|
||||
}];
|
||||
}
|
||||
|
||||
def SYCLExternal : InheritableAttr {
|
||||
let Spellings = [CXX11<"clang", "sycl_external">];
|
||||
let Subjects = SubjectList<[Function], ErrorDiag>;
|
||||
let LangOpts = [SYCLHost, SYCLDevice];
|
||||
let Documentation = [SYCLExternalDocs];
|
||||
}
|
||||
|
||||
def SYCLKernelEntryPoint : InheritableAttr {
|
||||
let Spellings = [CXX11<"clang", "sycl_kernel_entry_point">];
|
||||
let Args = [
|
||||
|
@ -476,47 +476,6 @@ The SYCL kernel in the previous code sample meets these expectations.
|
||||
}];
|
||||
}
|
||||
|
||||
def SYCLExternalDocs : Documentation {
|
||||
let Category = DocCatFunction;
|
||||
let Heading = "sycl_external";
|
||||
let Content = [{
|
||||
The ``sycl_external`` attribute indicates that a function defined in another
|
||||
translation unit may be called by a device function defined in the current
|
||||
translation unit or, if defined in the current translation unit, the function
|
||||
may be called by device functions defined in other translation units.
|
||||
The attribute is intended for use in the implementation of the ``SYCL_EXTERNAL``
|
||||
macro as specified in section 5.10.1, "SYCL functions and member functions
|
||||
linkage", of the SYCL 2020 specification.
|
||||
|
||||
The attribute only appertains to functions and only those that meet the
|
||||
following requirements:
|
||||
|
||||
* Has external linkage
|
||||
* Is not explicitly defined as deleted (the function may be an explicitly
|
||||
defaulted function that is defined as deleted)
|
||||
|
||||
The attribute shall be present on the first declaration of a function and
|
||||
may optionally be present on subsequent declarations.
|
||||
|
||||
When compiling for a SYCL device target that does not support the generic
|
||||
address space, the function shall not specify a raw pointer or reference type
|
||||
as the return type or as a parameter type.
|
||||
See section 5.10, "SYCL offline linking", of the SYCL 2020 specification.
|
||||
The following examples demonstrate the use of this attribute:
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
[[clang::sycl_external]] void Foo(); // Ok.
|
||||
|
||||
[[clang::sycl_external]] void Bar() { /* ... */ } // Ok.
|
||||
|
||||
[[clang::sycl_external]] extern void Baz(); // Ok.
|
||||
|
||||
[[clang::sycl_external]] static void Quux() { /* ... */ } // error: Quux() has internal linkage.
|
||||
|
||||
}];
|
||||
}
|
||||
|
||||
def SYCLKernelEntryPointDocs : Documentation {
|
||||
let Category = DocCatFunction;
|
||||
let Content = [{
|
||||
|
@ -62,7 +62,3 @@ def __builtin_common_type : CPlusPlusBuiltinTemplate<
|
||||
// typename ...Operands>
|
||||
def __hlsl_spirv_type : HLSLBuiltinTemplate<
|
||||
[Uint32T, Uint32T, Uint32T, Class<"Operands", /*is_variadic=*/1>]>;
|
||||
|
||||
// template <class ...Args>
|
||||
def __builtin_dedup_pack
|
||||
: CPlusPlusBuiltinTemplate<[Class<"Args", /*is_variadic=*/1>]>;
|
||||
|
@ -1232,18 +1232,6 @@ def ConvertVector : Builtin {
|
||||
let Prototype = "void(...)";
|
||||
}
|
||||
|
||||
def MaskedLoad : Builtin {
|
||||
let Spellings = ["__builtin_masked_load"];
|
||||
let Attributes = [NoThrow, CustomTypeChecking];
|
||||
let Prototype = "void(...)";
|
||||
}
|
||||
|
||||
def MaskedStore : Builtin {
|
||||
let Spellings = ["__builtin_masked_store"];
|
||||
let Attributes = [NoThrow, CustomTypeChecking];
|
||||
let Prototype = "void(...)";
|
||||
}
|
||||
|
||||
def AllocaUninitialized : Builtin {
|
||||
let Spellings = ["__builtin_alloca_uninitialized"];
|
||||
let Attributes = [FunctionWithBuiltinPrefix, NoThrow];
|
||||
@ -1510,7 +1498,7 @@ def ElementwiseCopysign : Builtin {
|
||||
|
||||
def ElementwiseFma : Builtin {
|
||||
let Spellings = ["__builtin_elementwise_fma"];
|
||||
let Attributes = [NoThrow, Const, CustomTypeChecking, Constexpr];
|
||||
let Attributes = [NoThrow, Const, CustomTypeChecking];
|
||||
let Prototype = "void(...)";
|
||||
}
|
||||
|
||||
@ -1538,18 +1526,6 @@ def ElementwiseFshr : Builtin {
|
||||
let Prototype = "void(...)";
|
||||
}
|
||||
|
||||
def ElementwiseCtlz : Builtin {
|
||||
let Spellings = ["__builtin_elementwise_ctlz"];
|
||||
let Attributes = [NoThrow, Const, CustomTypeChecking, Constexpr];
|
||||
let Prototype = "void(...)";
|
||||
}
|
||||
|
||||
def ElementwiseCttz : Builtin {
|
||||
let Spellings = ["__builtin_elementwise_cttz"];
|
||||
let Attributes = [NoThrow, Const, CustomTypeChecking, Constexpr];
|
||||
let Prototype = "void(...)";
|
||||
}
|
||||
|
||||
def ReduceMax : Builtin {
|
||||
let Spellings = ["__builtin_reduce_max"];
|
||||
let Attributes = [NoThrow, Const, CustomTypeChecking, Constexpr];
|
||||
|
@ -580,8 +580,6 @@ TARGET_BUILTIN(__builtin_ppc_bcdsub_p, "iiV16UcV16Uc", "",
|
||||
"isa-v207-instructions")
|
||||
|
||||
// P9 Binary-coded decimal (BCD) builtins.
|
||||
TARGET_BUILTIN(__builtin_ppc_bcdcopysign, "V16UcV16UcV16Uc", "", "power9-vector")
|
||||
TARGET_BUILTIN(__builtin_ppc_bcdsetsign, "V16UcV16UcUc", "t", "power9-vector")
|
||||
TARGET_BUILTIN(__builtin_ppc_national2packed, "V16UcV16UcUc", "t", "power9-vector")
|
||||
TARGET_BUILTIN(__builtin_ppc_packed2national, "V16UcV16Uc", "", "power9-vector")
|
||||
TARGET_BUILTIN(__builtin_ppc_packed2zoned, "V16UcV16UcUc", "t", "power9-vector")
|
||||
|
@ -627,23 +627,11 @@ let Features = "avx2", Attributes = [NoThrow, Const, RequiredVectorWidth<256>] i
|
||||
let Features = "avx2", Attributes = [NoThrow, Const, Constexpr, RequiredVectorWidth<256>] in {
|
||||
def pmuldq256 : X86Builtin<"_Vector<4, long long int>(_Vector<8, int>, _Vector<8, int>)">;
|
||||
def pmuludq256 : X86Builtin<"_Vector<4, long long int>(_Vector<8, int>, _Vector<8, int>)">;
|
||||
|
||||
def pmulhuw256 : X86Builtin<"_Vector<16, unsigned short>(_Vector<16, unsigned short>, _Vector<16, unsigned short>)">;
|
||||
def pmulhw256 : X86Builtin<"_Vector<16, short>(_Vector<16, short>, _Vector<16, short>)">;
|
||||
|
||||
def psllv8si : X86Builtin<"_Vector<8, int>(_Vector<8, int>, _Vector<8, int>)">;
|
||||
def psrav8si : X86Builtin<"_Vector<8, int>(_Vector<8, int>, _Vector<8, int>)">;
|
||||
def psrlv8si : X86Builtin<"_Vector<8, int>(_Vector<8, int>, _Vector<8, int>)">;
|
||||
def psllv4di : X86Builtin<"_Vector<4, long long int>(_Vector<4, long long int>, _Vector<4, long long int>)">;
|
||||
def psrlv4di : X86Builtin<"_Vector<4, long long int>(_Vector<4, long long int>, _Vector<4, long long int>)">;
|
||||
}
|
||||
|
||||
let Features = "avx2", Attributes = [NoThrow, Const, Constexpr, RequiredVectorWidth<128>] in {
|
||||
def psllv4si : X86Builtin<"_Vector<4, int>(_Vector<4, int>, _Vector<4, int>)">;
|
||||
def psrav4si : X86Builtin<"_Vector<4, int>(_Vector<4, int>, _Vector<4, int>)">;
|
||||
def psrlv4si : X86Builtin<"_Vector<4, int>(_Vector<4, int>, _Vector<4, int>)">;
|
||||
def psllv2di : X86Builtin<"_Vector<2, long long int>(_Vector<2, long long int>, _Vector<2, long long int>)">;
|
||||
def psrlv2di : X86Builtin<"_Vector<2, long long int>(_Vector<2, long long int>, _Vector<2, long long int>)">;
|
||||
let Features = "avx2", Attributes = [NoThrow, Const, Constexpr, RequiredVectorWidth<256>] in {
|
||||
def pmulhuw256 : X86Builtin<"_Vector<16, unsigned short>(_Vector<16, unsigned short>, _Vector<16, unsigned short>)">;
|
||||
def pmulhw256 : X86Builtin<"_Vector<16, short>(_Vector<16, short>, _Vector<16, short>)">;
|
||||
}
|
||||
|
||||
let Features = "avx2", Attributes = [NoThrow, RequiredVectorWidth<256>] in {
|
||||
@ -666,6 +654,46 @@ let Features = "avx2", Attributes = [NoThrow, RequiredVectorWidth<128>] in {
|
||||
def maskstoreq : X86Builtin<"void(_Vector<2, long long int *>, _Vector<2, long long int>, _Vector<2, long long int>)">;
|
||||
}
|
||||
|
||||
let Features = "avx2", Attributes = [NoThrow, Const, RequiredVectorWidth<256>] in {
|
||||
def psllv8si : X86Builtin<"_Vector<8, int>(_Vector<8, int>, _Vector<8, int>)">;
|
||||
}
|
||||
|
||||
let Features = "avx2", Attributes = [NoThrow, Const, RequiredVectorWidth<128>] in {
|
||||
def psllv4si : X86Builtin<"_Vector<4, int>(_Vector<4, int>, _Vector<4, int>)">;
|
||||
}
|
||||
|
||||
let Features = "avx2", Attributes = [NoThrow, Const, RequiredVectorWidth<256>] in {
|
||||
def psllv4di : X86Builtin<"_Vector<4, long long int>(_Vector<4, long long int>, _Vector<4, long long int>)">;
|
||||
}
|
||||
|
||||
let Features = "avx2", Attributes = [NoThrow, Const, RequiredVectorWidth<128>] in {
|
||||
def psllv2di : X86Builtin<"_Vector<2, long long int>(_Vector<2, long long int>, _Vector<2, long long int>)">;
|
||||
}
|
||||
|
||||
let Features = "avx2", Attributes = [NoThrow, Const, RequiredVectorWidth<256>] in {
|
||||
def psrav8si : X86Builtin<"_Vector<8, int>(_Vector<8, int>, _Vector<8, int>)">;
|
||||
}
|
||||
|
||||
let Features = "avx2", Attributes = [NoThrow, Const, RequiredVectorWidth<128>] in {
|
||||
def psrav4si : X86Builtin<"_Vector<4, int>(_Vector<4, int>, _Vector<4, int>)">;
|
||||
}
|
||||
|
||||
let Features = "avx2", Attributes = [NoThrow, Const, RequiredVectorWidth<256>] in {
|
||||
def psrlv8si : X86Builtin<"_Vector<8, int>(_Vector<8, int>, _Vector<8, int>)">;
|
||||
}
|
||||
|
||||
let Features = "avx2", Attributes = [NoThrow, Const, RequiredVectorWidth<128>] in {
|
||||
def psrlv4si : X86Builtin<"_Vector<4, int>(_Vector<4, int>, _Vector<4, int>)">;
|
||||
}
|
||||
|
||||
let Features = "avx2", Attributes = [NoThrow, Const, RequiredVectorWidth<256>] in {
|
||||
def psrlv4di : X86Builtin<"_Vector<4, long long int>(_Vector<4, long long int>, _Vector<4, long long int>)">;
|
||||
}
|
||||
|
||||
let Features = "avx2", Attributes = [NoThrow, Const, RequiredVectorWidth<128>] in {
|
||||
def psrlv2di : X86Builtin<"_Vector<2, long long int>(_Vector<2, long long int>, _Vector<2, long long int>)">;
|
||||
}
|
||||
|
||||
let Features = "avx2", Attributes = [NoThrow, RequiredVectorWidth<128>] in {
|
||||
def gatherd_pd : X86Builtin<"_Vector<2, double>(_Vector<2, double>, double const *, _Vector<4, int>, _Vector<2, double>, _Constant char)">;
|
||||
}
|
||||
|
@ -400,9 +400,6 @@ def note_constexpr_non_const_vectorelements : Note<
|
||||
"cannot determine number of elements for sizeless vectors in a constant expression">;
|
||||
def note_constexpr_assumption_failed : Note<
|
||||
"assumption evaluated to false">;
|
||||
def note_constexpr_countzeroes_zero : Note<
|
||||
"evaluation of %select{__builtin_elementwise_ctlz|__builtin_elementwise_cttz}0 "
|
||||
"with a zero value is undefined">;
|
||||
def err_experimental_clang_interp_failed : Error<
|
||||
"the experimental clang interpreter failed to evaluate an expression">;
|
||||
|
||||
|
@ -581,13 +581,6 @@ def err_drv_reduced_module_output_overrided : Warning<
|
||||
"please consider use '-fmodule-output=' to specify the output file for reduced BMI explicitly">,
|
||||
InGroup<DiagGroup<"reduced-bmi-output-overrided">>;
|
||||
|
||||
def remark_found_cxx20_module_usage : Remark<
|
||||
"found C++20 module usage in file '%0'">,
|
||||
InGroup<ModulesDriver>;
|
||||
def remark_performing_driver_managed_module_build : Remark<
|
||||
"performing driver managed module build">,
|
||||
InGroup<ModulesDriver>;
|
||||
|
||||
def warn_drv_delayed_template_parsing_after_cxx20 : Warning<
|
||||
"-fdelayed-template-parsing is deprecated after C++20">,
|
||||
InGroup<DiagGroup<"delayed-template-parsing-in-cxx20">>;
|
||||
@ -885,9 +878,4 @@ def warn_drv_openacc_without_cir
|
||||
: Warning<"OpenACC directives will result in no runtime behavior; use "
|
||||
"-fclangir to enable runtime effect">,
|
||||
InGroup<SourceUsesOpenACC>;
|
||||
|
||||
def warn_drv_gcc_install_dir_libstdcxx : Warning<
|
||||
"future releases of the clang compiler will prefer GCC installations "
|
||||
"containing libstdc++ include directories; '%0' would be chosen over '%1'">,
|
||||
InGroup<DiagGroup<"gcc-install-dir-libstdcxx">>;
|
||||
}
|
||||
|
@ -635,7 +635,6 @@ def ModuleConflict : DiagGroup<"module-conflict">;
|
||||
def ModuleFileExtension : DiagGroup<"module-file-extension">;
|
||||
def ModuleIncludeDirectiveTranslation : DiagGroup<"module-include-translation">;
|
||||
def ModuleMap : DiagGroup<"module-map">;
|
||||
def ModulesDriver : DiagGroup<"modules-driver">;
|
||||
def RoundTripCC1Args : DiagGroup<"round-trip-cc1-args">;
|
||||
def NewlineEOF : DiagGroup<"newline-eof">;
|
||||
def Nullability : DiagGroup<"nullability">;
|
||||
@ -652,7 +651,6 @@ def NonNull : DiagGroup<"nonnull">;
|
||||
def NonPODVarargs : DiagGroup<"non-pod-varargs">;
|
||||
def ClassVarargs : DiagGroup<"class-varargs", [NonPODVarargs]>;
|
||||
def : DiagGroup<"nonportable-cfstrings">;
|
||||
def NonPortableSYCL : DiagGroup<"nonportable-sycl">;
|
||||
def NonVirtualDtor : DiagGroup<"non-virtual-dtor">;
|
||||
def GNUNullPointerArithmetic : DiagGroup<"gnu-null-pointer-arithmetic">;
|
||||
def NullPointerArithmetic
|
||||
|
@ -23,78 +23,76 @@
|
||||
#include <vector>
|
||||
|
||||
namespace clang {
|
||||
class DiagnosticsEngine;
|
||||
class DiagnosticBuilder;
|
||||
class LangOptions;
|
||||
class SourceLocation;
|
||||
class DiagnosticsEngine;
|
||||
class DiagnosticBuilder;
|
||||
class LangOptions;
|
||||
class SourceLocation;
|
||||
|
||||
// Import the diagnostic enums themselves.
|
||||
namespace diag {
|
||||
enum class Group;
|
||||
// Import the diagnostic enums themselves.
|
||||
namespace diag {
|
||||
enum class Group;
|
||||
|
||||
// Size of each of the diagnostic categories.
|
||||
enum {
|
||||
DIAG_SIZE_COMMON = 300,
|
||||
DIAG_SIZE_DRIVER = 400,
|
||||
DIAG_SIZE_FRONTEND = 200,
|
||||
DIAG_SIZE_SERIALIZATION = 120,
|
||||
DIAG_SIZE_LEX = 500,
|
||||
DIAG_SIZE_PARSE = 800,
|
||||
DIAG_SIZE_AST = 300,
|
||||
DIAG_SIZE_COMMENT = 100,
|
||||
DIAG_SIZE_CROSSTU = 100,
|
||||
DIAG_SIZE_SEMA = 5000,
|
||||
DIAG_SIZE_ANALYSIS = 100,
|
||||
DIAG_SIZE_REFACTORING = 1000,
|
||||
DIAG_SIZE_INSTALLAPI = 100,
|
||||
};
|
||||
// Start position for diagnostics.
|
||||
// clang-format off
|
||||
enum {
|
||||
DIAG_START_COMMON = 0,
|
||||
DIAG_START_DRIVER = DIAG_START_COMMON + static_cast<int>(DIAG_SIZE_COMMON),
|
||||
DIAG_START_FRONTEND = DIAG_START_DRIVER + static_cast<int>(DIAG_SIZE_DRIVER),
|
||||
DIAG_START_SERIALIZATION = DIAG_START_FRONTEND + static_cast<int>(DIAG_SIZE_FRONTEND),
|
||||
DIAG_START_LEX = DIAG_START_SERIALIZATION + static_cast<int>(DIAG_SIZE_SERIALIZATION),
|
||||
DIAG_START_PARSE = DIAG_START_LEX + static_cast<int>(DIAG_SIZE_LEX),
|
||||
DIAG_START_AST = DIAG_START_PARSE + static_cast<int>(DIAG_SIZE_PARSE),
|
||||
DIAG_START_COMMENT = DIAG_START_AST + static_cast<int>(DIAG_SIZE_AST),
|
||||
DIAG_START_CROSSTU = DIAG_START_COMMENT + static_cast<int>(DIAG_SIZE_COMMENT),
|
||||
DIAG_START_SEMA = DIAG_START_CROSSTU + static_cast<int>(DIAG_SIZE_CROSSTU),
|
||||
DIAG_START_ANALYSIS = DIAG_START_SEMA + static_cast<int>(DIAG_SIZE_SEMA),
|
||||
DIAG_START_REFACTORING = DIAG_START_ANALYSIS + static_cast<int>(DIAG_SIZE_ANALYSIS),
|
||||
DIAG_START_INSTALLAPI = DIAG_START_REFACTORING + static_cast<int>(DIAG_SIZE_REFACTORING),
|
||||
DIAG_UPPER_LIMIT = DIAG_START_INSTALLAPI + static_cast<int>(DIAG_SIZE_INSTALLAPI)
|
||||
};
|
||||
// clang-format on
|
||||
// Size of each of the diagnostic categories.
|
||||
enum {
|
||||
DIAG_SIZE_COMMON = 300,
|
||||
DIAG_SIZE_DRIVER = 400,
|
||||
DIAG_SIZE_FRONTEND = 200,
|
||||
DIAG_SIZE_SERIALIZATION = 120,
|
||||
DIAG_SIZE_LEX = 500,
|
||||
DIAG_SIZE_PARSE = 800,
|
||||
DIAG_SIZE_AST = 300,
|
||||
DIAG_SIZE_COMMENT = 100,
|
||||
DIAG_SIZE_CROSSTU = 100,
|
||||
DIAG_SIZE_SEMA = 5000,
|
||||
DIAG_SIZE_ANALYSIS = 100,
|
||||
DIAG_SIZE_REFACTORING = 1000,
|
||||
DIAG_SIZE_INSTALLAPI = 100,
|
||||
};
|
||||
// Start position for diagnostics.
|
||||
enum {
|
||||
DIAG_START_COMMON = 0,
|
||||
DIAG_START_DRIVER = DIAG_START_COMMON + static_cast<int>(DIAG_SIZE_COMMON),
|
||||
DIAG_START_FRONTEND = DIAG_START_DRIVER + static_cast<int>(DIAG_SIZE_DRIVER),
|
||||
DIAG_START_SERIALIZATION = DIAG_START_FRONTEND + static_cast<int>(DIAG_SIZE_FRONTEND),
|
||||
DIAG_START_LEX = DIAG_START_SERIALIZATION + static_cast<int>(DIAG_SIZE_SERIALIZATION),
|
||||
DIAG_START_PARSE = DIAG_START_LEX + static_cast<int>(DIAG_SIZE_LEX),
|
||||
DIAG_START_AST = DIAG_START_PARSE + static_cast<int>(DIAG_SIZE_PARSE),
|
||||
DIAG_START_COMMENT = DIAG_START_AST + static_cast<int>(DIAG_SIZE_AST),
|
||||
DIAG_START_CROSSTU = DIAG_START_COMMENT + static_cast<int>(DIAG_SIZE_COMMENT),
|
||||
DIAG_START_SEMA = DIAG_START_CROSSTU + static_cast<int>(DIAG_SIZE_CROSSTU),
|
||||
DIAG_START_ANALYSIS = DIAG_START_SEMA + static_cast<int>(DIAG_SIZE_SEMA),
|
||||
DIAG_START_REFACTORING = DIAG_START_ANALYSIS + static_cast<int>(DIAG_SIZE_ANALYSIS),
|
||||
DIAG_START_INSTALLAPI = DIAG_START_REFACTORING + static_cast<int>(DIAG_SIZE_REFACTORING),
|
||||
DIAG_UPPER_LIMIT = DIAG_START_INSTALLAPI + static_cast<int>(DIAG_SIZE_INSTALLAPI)
|
||||
};
|
||||
|
||||
class CustomDiagInfo;
|
||||
class CustomDiagInfo;
|
||||
|
||||
/// All of the diagnostics that can be emitted by the frontend.
|
||||
typedef unsigned kind;
|
||||
/// All of the diagnostics that can be emitted by the frontend.
|
||||
typedef unsigned kind;
|
||||
|
||||
/// Enum values that allow the client to map NOTEs, WARNINGs, and EXTENSIONs
|
||||
/// to either Ignore (nothing), Remark (emit a remark), Warning
|
||||
/// (emit a warning) or Error (emit as an error). It allows clients to
|
||||
/// map ERRORs to Error or Fatal (stop emitting diagnostics after this one).
|
||||
enum class Severity : uint8_t {
|
||||
// NOTE: 0 means "uncomputed".
|
||||
Ignored = 1, ///< Do not present this diagnostic, ignore it.
|
||||
Remark = 2, ///< Present this diagnostic as a remark.
|
||||
Warning = 3, ///< Present this diagnostic as a warning.
|
||||
Error = 4, ///< Present this diagnostic as an error.
|
||||
Fatal = 5 ///< Present this diagnostic as a fatal error.
|
||||
};
|
||||
/// Enum values that allow the client to map NOTEs, WARNINGs, and EXTENSIONs
|
||||
/// to either Ignore (nothing), Remark (emit a remark), Warning
|
||||
/// (emit a warning) or Error (emit as an error). It allows clients to
|
||||
/// map ERRORs to Error or Fatal (stop emitting diagnostics after this one).
|
||||
enum class Severity : uint8_t {
|
||||
// NOTE: 0 means "uncomputed".
|
||||
Ignored = 1, ///< Do not present this diagnostic, ignore it.
|
||||
Remark = 2, ///< Present this diagnostic as a remark.
|
||||
Warning = 3, ///< Present this diagnostic as a warning.
|
||||
Error = 4, ///< Present this diagnostic as an error.
|
||||
Fatal = 5 ///< Present this diagnostic as a fatal error.
|
||||
};
|
||||
|
||||
/// Flavors of diagnostics we can emit. Used to filter for a particular
|
||||
/// kind of diagnostic (for instance, for -W/-R flags).
|
||||
enum class Flavor {
|
||||
WarningOrError, ///< A diagnostic that indicates a problem or potential
|
||||
///< problem. Can be made fatal by -Werror.
|
||||
Remark ///< A diagnostic that indicates normal progress through
|
||||
///< compilation.
|
||||
};
|
||||
} // end namespace diag
|
||||
/// Flavors of diagnostics we can emit. Used to filter for a particular
|
||||
/// kind of diagnostic (for instance, for -W/-R flags).
|
||||
enum class Flavor {
|
||||
WarningOrError, ///< A diagnostic that indicates a problem or potential
|
||||
///< problem. Can be made fatal by -Werror.
|
||||
Remark ///< A diagnostic that indicates normal progress through
|
||||
///< compilation.
|
||||
};
|
||||
} // end namespace diag
|
||||
} // end namespace clang
|
||||
|
||||
// This has to be included *after* the DIAG_START_ enums above are defined.
|
||||
@ -175,8 +173,7 @@ public:
|
||||
|
||||
/// Used for handling and querying diagnostic IDs.
|
||||
///
|
||||
/// Can be used and shared by multiple Diagnostics for multiple translation
|
||||
/// units.
|
||||
/// Can be used and shared by multiple Diagnostics for multiple translation units.
|
||||
class DiagnosticIDs : public RefCountedBase<DiagnosticIDs> {
|
||||
public:
|
||||
/// The level of the diagnostic, after it has been through mapping.
|
||||
@ -501,6 +498,6 @@ private:
|
||||
friend class DiagnosticsEngine;
|
||||
};
|
||||
|
||||
} // end namespace clang
|
||||
} // end namespace clang
|
||||
|
||||
#endif
|
||||
|
@ -6074,13 +6074,6 @@ def warn_cxx23_pack_indexing : Warning<
|
||||
def err_pack_outside_template : Error<
|
||||
"pack declaration outside of template">;
|
||||
|
||||
def err_builtin_pack_outside_template
|
||||
: Error<"%0 cannot be used outside of template">;
|
||||
|
||||
def err_unsupported_builtin_template_pack_expansion
|
||||
: Error<"expansions of %0 are not supported here. Only expansions in "
|
||||
"template arguments and class bases are supported">;
|
||||
|
||||
def err_fold_expression_packs_both_sides : Error<
|
||||
"binary fold expression has unexpanded parameter packs in both operands">;
|
||||
def err_fold_expression_empty : Error<
|
||||
@ -11001,15 +10994,10 @@ def err_block_on_vm : Error<
|
||||
def err_sizeless_nonlocal : Error<
|
||||
"non-local variable with sizeless type %0">;
|
||||
|
||||
def err_vec_masked_load_store_ptr : Error<
|
||||
"%ordinal0 argument must be a %1">;
|
||||
def err_vec_masked_load_store_size : Error<
|
||||
"all arguments to %0 must have the same number of elements (was %1 and %2)">;
|
||||
|
||||
def err_vec_builtin_non_vector : Error<
|
||||
"%select{first two|all}1 arguments to %0 must be vectors">;
|
||||
def err_vec_builtin_incompatible_vector : Error<
|
||||
"%select{first two|all|last two}1 arguments to %0 must have the same type">;
|
||||
"%select{first two|all}1 arguments to %0 must have the same type">;
|
||||
def err_vsx_builtin_nonconstant_argument : Error<
|
||||
"argument %0 to %1 must be a 2-bit unsigned literal (i.e. 0, 1, 2 or 3)">;
|
||||
|
||||
@ -12871,7 +12859,7 @@ def err_builtin_invalid_arg_type: Error<
|
||||
"%plural{0:|: }1"
|
||||
// Second component: integer-like types
|
||||
"%select{|integer|signed integer|unsigned integer|'int'|"
|
||||
"pointer to a valid matrix element|boolean}2"
|
||||
"pointer to a valid matrix element}2"
|
||||
// A space after a non-empty second component
|
||||
"%plural{0:|: }2"
|
||||
// An 'or' if non-empty second and third components are combined
|
||||
@ -12963,17 +12951,6 @@ def err_sycl_special_type_num_init_method : Error<
|
||||
"types with 'sycl_special_class' attribute must have one and only one '__init' "
|
||||
"method defined">;
|
||||
|
||||
// SYCL external attribute diagnostics
|
||||
def err_sycl_external_invalid_linkage : Error<
|
||||
"%0 can only be applied to functions with external linkage">;
|
||||
def err_sycl_external_invalid_main : Error<
|
||||
"%0 cannot be applied to the 'main' function">;
|
||||
def err_sycl_external_invalid_deleted_function : Error<
|
||||
"%0 cannot be applied to an explicitly deleted function">;
|
||||
def warn_sycl_external_missing_on_first_decl : Warning<
|
||||
"%0 attribute does not appear on the first declaration">,
|
||||
InGroup<NonPortableSYCL>;
|
||||
|
||||
// SYCL kernel entry point diagnostics
|
||||
def err_sycl_entry_point_invalid : Error<
|
||||
"the %0 attribute cannot be applied to a"
|
||||
@ -12988,7 +12965,7 @@ def err_sycl_kernel_name_conflict : Error<
|
||||
"the %0 kernel name argument conflicts with a previous declaration">;
|
||||
def warn_sycl_kernel_name_not_a_class_type : Warning<
|
||||
"%0 is not a valid SYCL kernel name type; a non-union class type is required">,
|
||||
InGroup<NonPortableSYCL>, DefaultError;
|
||||
InGroup<DiagGroup<"nonportable-sycl">>, DefaultError;
|
||||
def warn_sycl_entry_point_redundant_declaration : Warning<
|
||||
"redundant %0 attribute">, InGroup<RedundantAttribute>;
|
||||
def err_sycl_entry_point_after_definition : Error<
|
||||
@ -13391,23 +13368,16 @@ def err_acc_reduction_num_gangs_conflict
|
||||
"appear on a '%2' construct "
|
||||
"with a '%3' clause%select{ with more than 1 argument|}0">;
|
||||
def err_acc_reduction_type
|
||||
: Error<"invalid type %0 used in OpenACC 'reduction' variable reference; "
|
||||
"type is %enum_select<OACCReductionTy>{%NotScalar{not a scalar "
|
||||
"value, or array of scalars, or composite of "
|
||||
"scalars}|%MemberNotScalar{not a scalar value}|%NotAgg{not an "
|
||||
"aggregate}|%NotComplete{not a complete type}|%NotClassStruct{not "
|
||||
"a class or struct}}1">;
|
||||
def note_acc_reduction_array
|
||||
: Note<"used as element type of "
|
||||
"%enum_select<OACCReductionArray>{%Section{sub-array"
|
||||
"}|%Subscript{array}|%ArrayTy{array}}0 type %1">;
|
||||
def note_acc_reduction_member_of_composite
|
||||
: Note<"used as field '%0' of composite '%1'">;
|
||||
def note_acc_reduction_type_summary
|
||||
: Note<"OpenACC 'reduction' variable reference must be a scalar variable "
|
||||
"or a "
|
||||
"composite of scalars, or an array, sub-array, or element of scalar "
|
||||
"types">;
|
||||
: Error<"OpenACC 'reduction' variable must be of scalar type, aggregate, "
|
||||
"sub-array, or a composite of scalar types;%select{| sub-array "
|
||||
"base}1 type is %0">;
|
||||
def err_acc_reduction_composite_type
|
||||
: Error<"OpenACC 'reduction' variable must be a composite of scalar types; "
|
||||
"%1 %select{is not a class or struct|is incomplete|is not an "
|
||||
"aggregate}0">;
|
||||
def err_acc_reduction_composite_member_type :Error<
|
||||
"OpenACC 'reduction' composite variable must not have non-scalar field">;
|
||||
def note_acc_reduction_composite_member_loc : Note<"invalid field is here">;
|
||||
def err_acc_loop_not_for_loop
|
||||
: Error<"OpenACC '%0' construct can only be applied to a 'for' loop">;
|
||||
def note_acc_construct_here : Note<"'%0' construct is here">;
|
||||
|
@ -128,7 +128,6 @@ FEATURE(attribute_overloadable, true)
|
||||
FEATURE(attribute_unavailable_with_message, true)
|
||||
FEATURE(attribute_unused_on_fields, true)
|
||||
FEATURE(attribute_diagnose_if_objc, true)
|
||||
FEATURE(ext_vector_type_boolean, true)
|
||||
FEATURE(blocks, LangOpts.Blocks)
|
||||
FEATURE(c_thread_safety_attributes, true)
|
||||
FEATURE(cxx_exceptions, LangOpts.CXXExceptions)
|
||||
@ -148,10 +147,8 @@ FEATURE(type_sanitizer, LangOpts.Sanitize.has(SanitizerKind::Type))
|
||||
FEATURE(thread_sanitizer, LangOpts.Sanitize.has(SanitizerKind::Thread))
|
||||
FEATURE(dataflow_sanitizer, LangOpts.Sanitize.has(SanitizerKind::DataFlow))
|
||||
FEATURE(scudo, LangOpts.Sanitize.hasOneOf(SanitizerKind::Scudo))
|
||||
FEATURE(ptrauth_intrinsics, LangOpts.PointerAuthIntrinsics &&
|
||||
PP.getTargetInfo().getTriple().isOSDarwin())
|
||||
FEATURE(ptrauth_qualifier, LangOpts.PointerAuthIntrinsics &&
|
||||
PP.getTargetInfo().getTriple().isOSDarwin())
|
||||
FEATURE(ptrauth_intrinsics, LangOpts.PointerAuthIntrinsics)
|
||||
EXTENSION(ptrauth_qualifier, LangOpts.PointerAuthIntrinsics)
|
||||
FEATURE(ptrauth_calls, LangOpts.PointerAuthCalls)
|
||||
FEATURE(ptrauth_returns, LangOpts.PointerAuthReturns)
|
||||
FEATURE(ptrauth_vtable_pointer_address_discrimination, LangOpts.PointerAuthVTPtrAddressDiscrimination)
|
||||
@ -167,7 +164,7 @@ FEATURE(ptrauth_elf_got, LangOpts.PointerAuthELFGOT)
|
||||
|
||||
FEATURE(ptrauth_objc_isa, LangOpts.PointerAuthObjcIsa)
|
||||
FEATURE(ptrauth_objc_interface_sel, LangOpts.PointerAuthObjcInterfaceSel)
|
||||
FEATURE(ptrauth_objc_signable_class, LangOpts.PointerAuthIntrinsics)
|
||||
FEATURE(ptrauth_objc_signable_class, true)
|
||||
FEATURE(ptrauth_objc_method_list_pointer, LangOpts.PointerAuthCalls)
|
||||
|
||||
EXTENSION(swiftcc,
|
||||
|
@ -97,9 +97,7 @@ def HLSLAttributedResourceType : TypeNode<Type>;
|
||||
def HLSLInlineSpirvType : TypeNode<Type>;
|
||||
def TemplateTypeParmType : TypeNode<Type>, AlwaysDependent, LeafType;
|
||||
def SubstTemplateTypeParmType : TypeNode<Type>, NeverCanonical;
|
||||
def SubstPackType : TypeNode<Type, 1>;
|
||||
def SubstTemplateTypeParmPackType : TypeNode<SubstPackType>, AlwaysDependent;
|
||||
def SubstBuiltinTemplatePackType : TypeNode<SubstPackType>, AlwaysDependent;
|
||||
def SubstTemplateTypeParmPackType : TypeNode<Type>, AlwaysDependent;
|
||||
def TemplateSpecializationType : TypeNode<Type>, NeverCanonicalUnlessDependent;
|
||||
def DeducedType : TypeNode<Type, 1>;
|
||||
def AutoType : TypeNode<DeducedType>;
|
||||
|
@ -721,6 +721,8 @@ multiclass RVVUnitStridedSegLoadTuple<string op> {
|
||||
NF = nf,
|
||||
ManualCodegen = [{
|
||||
{
|
||||
SmallVector<llvm::Value*, 6> Operands;
|
||||
|
||||
bool NoPassthru =
|
||||
(IsMasked && (PolicyAttrs & RVV_VTA) && (PolicyAttrs & RVV_VMA)) |
|
||||
(!IsMasked && (PolicyAttrs & RVV_VTA));
|
||||
@ -731,18 +733,24 @@ multiclass RVVUnitStridedSegLoadTuple<string op> {
|
||||
else
|
||||
IntrinsicTypes = {ResultType, Ops[Offset]->getType(), Ops.back()->getType()};
|
||||
|
||||
if (IsMasked)
|
||||
std::rotate(Ops.begin(), Ops.begin() + 1, Ops.end() - 1);
|
||||
if (NoPassthru)
|
||||
Ops.insert(Ops.begin(), llvm::PoisonValue::get(ResultType));
|
||||
if (NoPassthru) { // Push poison into passthru
|
||||
Operands.push_back(llvm::PoisonValue::get(ResultType));
|
||||
} else { // Push intrinsics operands into passthru
|
||||
llvm::Value *PassthruOperand = IsMasked ? Ops[1] : Ops[0];
|
||||
Operands.push_back(PassthruOperand);
|
||||
}
|
||||
|
||||
Operands.push_back(Ops[Offset]); // Ptr
|
||||
if (IsMasked)
|
||||
Ops.push_back(ConstantInt::get(Ops.back()->getType(), PolicyAttrs));
|
||||
Ops.push_back(ConstantInt::get(Ops.back()->getType(), SegInstSEW));
|
||||
Operands.push_back(Ops[0]);
|
||||
Operands.push_back(Ops[Offset + 1]); // VL
|
||||
if (IsMasked)
|
||||
Operands.push_back(ConstantInt::get(Ops.back()->getType(), PolicyAttrs));
|
||||
Operands.push_back(ConstantInt::get(Ops.back()->getType(), SegInstSEW));
|
||||
|
||||
llvm::Function *F = CGM.getIntrinsic(ID, IntrinsicTypes);
|
||||
|
||||
llvm::Value *LoadValue = Builder.CreateCall(F, Ops, "");
|
||||
llvm::Value *LoadValue = Builder.CreateCall(F, Operands, "");
|
||||
if (ReturnValue.isNull())
|
||||
return LoadValue;
|
||||
else
|
||||
@ -779,24 +787,26 @@ multiclass RVVUnitStridedSegStoreTuple<string op> {
|
||||
{
|
||||
// Masked
|
||||
// Builtin: (mask, ptr, v_tuple, vl)
|
||||
// Intrinsic: (tuple, ptr, mask, vl, SegInstSEW)
|
||||
// Intrinsic: (tuple, ptr, mask, vl)
|
||||
// Unmasked
|
||||
// Builtin: (ptr, v_tuple, vl)
|
||||
// Intrinsic: (tuple, ptr, vl, SegInstSEW)
|
||||
// Intrinsic: (tuple, ptr, vl)
|
||||
unsigned Offset = IsMasked ? 1 : 0;
|
||||
|
||||
SmallVector<llvm::Value*, 5> Operands;
|
||||
Operands.push_back(Ops[Offset + 1]); // tuple
|
||||
Operands.push_back(Ops[Offset]); // Ptr
|
||||
if (IsMasked)
|
||||
Operands.push_back(Ops[0]);
|
||||
Operands.push_back(Ops[Offset + 2]); // VL
|
||||
Operands.push_back(ConstantInt::get(Ops.back()->getType(), SegInstSEW));
|
||||
|
||||
if (IsMasked)
|
||||
std::swap(Ops[0], Ops[2]);
|
||||
IntrinsicTypes = {Operands[0]->getType(), Ops[Offset]->getType(), Ops[0]->getType(), Operands.back()->getType()};
|
||||
else
|
||||
std::swap(Ops[0], Ops[1]);
|
||||
|
||||
Ops.push_back(ConstantInt::get(Ops.back()->getType(), SegInstSEW));
|
||||
|
||||
if (IsMasked)
|
||||
IntrinsicTypes = {Ops[0]->getType(), Ops[1]->getType(), Ops[2]->getType(), Ops[3]->getType()};
|
||||
else
|
||||
IntrinsicTypes = {Ops[0]->getType(), Ops[1]->getType(), Ops[2]->getType()};
|
||||
|
||||
break;
|
||||
IntrinsicTypes = {Operands[0]->getType(), Ops[Offset]->getType(), Operands.back()->getType()};
|
||||
llvm::Function *F = CGM.getIntrinsic(ID, IntrinsicTypes);
|
||||
return Builder.CreateCall(F, Operands, "");
|
||||
}
|
||||
}] in {
|
||||
defvar T = "(Tuple:" # nf # ")";
|
||||
@ -826,6 +836,8 @@ multiclass RVVUnitStridedSegLoadFFTuple<string op> {
|
||||
NF = nf,
|
||||
ManualCodegen = [{
|
||||
{
|
||||
SmallVector<llvm::Value*, 6> Operands;
|
||||
|
||||
bool NoPassthru =
|
||||
(IsMasked && (PolicyAttrs & RVV_VTA) && (PolicyAttrs & RVV_VMA)) |
|
||||
(!IsMasked && (PolicyAttrs & RVV_VTA));
|
||||
@ -836,21 +848,24 @@ multiclass RVVUnitStridedSegLoadFFTuple<string op> {
|
||||
else
|
||||
IntrinsicTypes = {ResultType, Ops.back()->getType(), Ops[Offset]->getType()};
|
||||
|
||||
if (IsMasked)
|
||||
std::rotate(Ops.begin(), Ops.begin() + 1, Ops.end() - 1);
|
||||
if (NoPassthru)
|
||||
Ops.insert(Ops.begin(), llvm::PoisonValue::get(ResultType));
|
||||
if (NoPassthru) { // Push poison into passthru
|
||||
Operands.push_back(llvm::PoisonValue::get(ResultType));
|
||||
} else { // Push intrinsics operands into passthru
|
||||
llvm::Value *PassthruOperand = IsMasked ? Ops[1] : Ops[0];
|
||||
Operands.push_back(PassthruOperand);
|
||||
}
|
||||
|
||||
Operands.push_back(Ops[Offset]); // Ptr
|
||||
if (IsMasked)
|
||||
Ops.push_back(ConstantInt::get(Ops.back()->getType(), PolicyAttrs));
|
||||
Ops.push_back(ConstantInt::get(Ops.back()->getType(), SegInstSEW));
|
||||
|
||||
Value *NewVL = Ops[2];
|
||||
Ops.erase(Ops.begin() + 2);
|
||||
Operands.push_back(Ops[0]);
|
||||
Operands.push_back(Ops[Offset + 2]); // vl
|
||||
if (IsMasked)
|
||||
Operands.push_back(ConstantInt::get(Ops.back()->getType(), PolicyAttrs));
|
||||
Operands.push_back(ConstantInt::get(Ops.back()->getType(), SegInstSEW));
|
||||
|
||||
llvm::Function *F = CGM.getIntrinsic(ID, IntrinsicTypes);
|
||||
|
||||
llvm::Value *LoadValue = Builder.CreateCall(F, Ops, "");
|
||||
llvm::Value *LoadValue = Builder.CreateCall(F, Operands, "");
|
||||
// Get alignment from the new vl operand
|
||||
clang::CharUnits Align =
|
||||
CGM.getNaturalPointeeTypeAlignment(E->getArg(Offset + 1)->getType());
|
||||
@ -859,7 +874,7 @@ multiclass RVVUnitStridedSegLoadFFTuple<string op> {
|
||||
|
||||
// Store new_vl
|
||||
llvm::Value *V = Builder.CreateExtractValue(LoadValue, 1);
|
||||
Builder.CreateStore(V, Address(NewVL, V->getType(), Align));
|
||||
Builder.CreateStore(V, Address(Ops[Offset + 1], V->getType(), Align));
|
||||
|
||||
if (ReturnValue.isNull())
|
||||
return ReturnTuple;
|
||||
@ -894,6 +909,8 @@ multiclass RVVStridedSegLoadTuple<string op> {
|
||||
NF = nf,
|
||||
ManualCodegen = [{
|
||||
{
|
||||
SmallVector<llvm::Value*, 7> Operands;
|
||||
|
||||
bool NoPassthru =
|
||||
(IsMasked && (PolicyAttrs & RVV_VTA) && (PolicyAttrs & RVV_VMA)) |
|
||||
(!IsMasked && (PolicyAttrs & RVV_VTA));
|
||||
@ -904,17 +921,24 @@ multiclass RVVStridedSegLoadTuple<string op> {
|
||||
else
|
||||
IntrinsicTypes = {ResultType, Ops[Offset]->getType(), Ops.back()->getType()};
|
||||
|
||||
if (IsMasked)
|
||||
std::rotate(Ops.begin(), Ops.begin() + 1, Ops.end() - 1);
|
||||
if (NoPassthru)
|
||||
Ops.insert(Ops.begin(), llvm::PoisonValue::get(ResultType));
|
||||
if (NoPassthru) { // Push poison into passthru
|
||||
Operands.push_back(llvm::PoisonValue::get(ResultType));
|
||||
} else { // Push intrinsics operands into passthru
|
||||
llvm::Value *PassthruOperand = IsMasked ? Ops[1] : Ops[0];
|
||||
Operands.push_back(PassthruOperand);
|
||||
}
|
||||
|
||||
Operands.push_back(Ops[Offset]); // Ptr
|
||||
Operands.push_back(Ops[Offset + 1]); // Stride
|
||||
if (IsMasked)
|
||||
Ops.push_back(ConstantInt::get(Ops.back()->getType(), PolicyAttrs));
|
||||
Ops.push_back(ConstantInt::get(Ops.back()->getType(), SegInstSEW));
|
||||
Operands.push_back(Ops[0]);
|
||||
Operands.push_back(Ops[Offset + 2]); // VL
|
||||
if (IsMasked)
|
||||
Operands.push_back(ConstantInt::get(Ops.back()->getType(), PolicyAttrs));
|
||||
Operands.push_back(ConstantInt::get(Ops.back()->getType(), SegInstSEW));
|
||||
|
||||
llvm::Function *F = CGM.getIntrinsic(ID, IntrinsicTypes);
|
||||
llvm::Value *LoadValue = Builder.CreateCall(F, Ops, "");
|
||||
llvm::Value *LoadValue = Builder.CreateCall(F, Operands, "");
|
||||
|
||||
if (ReturnValue.isNull())
|
||||
return LoadValue;
|
||||
@ -953,23 +977,27 @@ multiclass RVVStridedSegStoreTuple<string op> {
|
||||
{
|
||||
// Masked
|
||||
// Builtin: (mask, ptr, stride, v_tuple, vl)
|
||||
// Intrinsic: (tuple, ptr, stride, mask, vl, SegInstSEW)
|
||||
// Intrinsic: (tuple, ptr, stride, mask, vl)
|
||||
// Unmasked
|
||||
// Builtin: (ptr, stride, v_tuple, vl)
|
||||
// Intrinsic: (tuple, ptr, stride, vl, SegInstSEW)
|
||||
// Intrinsic: (tuple, ptr, stride, vl)
|
||||
unsigned Offset = IsMasked ? 1 : 0;
|
||||
|
||||
SmallVector<llvm::Value*, 6> Operands;
|
||||
Operands.push_back(Ops[Offset + 2]); // tuple
|
||||
Operands.push_back(Ops[Offset]); // Ptr
|
||||
Operands.push_back(Ops[Offset + 1]); // Stride
|
||||
if (IsMasked)
|
||||
Operands.push_back(Ops[0]);
|
||||
Operands.push_back(Ops[Offset + 3]); // VL
|
||||
Operands.push_back(ConstantInt::get(Ops.back()->getType(), SegInstSEW));
|
||||
|
||||
if (IsMasked)
|
||||
std::swap(Ops[0], Ops[3]);
|
||||
IntrinsicTypes = {Operands[0]->getType(), Operands[1]->getType(), Operands.back()->getType(), Ops[0]->getType()};
|
||||
else
|
||||
std::rotate(Ops.begin(), Ops.begin() + 2, Ops.begin() + 3);
|
||||
|
||||
Ops.push_back(ConstantInt::get(Ops.back()->getType(), SegInstSEW));
|
||||
|
||||
if (IsMasked)
|
||||
IntrinsicTypes = {Ops[0]->getType(), Ops[1]->getType(), Ops[4]->getType(), Ops[3]->getType()};
|
||||
else
|
||||
IntrinsicTypes = {Ops[0]->getType(), Ops[1]->getType(), Ops[3]->getType()};
|
||||
break;
|
||||
IntrinsicTypes = {Operands[0]->getType(), Operands[1]->getType(), Operands.back()->getType()};
|
||||
llvm::Function *F = CGM.getIntrinsic(ID, IntrinsicTypes);
|
||||
return Builder.CreateCall(F, Operands, "");
|
||||
}
|
||||
}] in {
|
||||
defvar T = "(Tuple:" # nf # ")";
|
||||
@ -994,30 +1022,40 @@ multiclass RVVIndexedSegLoadTuple<string op> {
|
||||
NF = nf,
|
||||
ManualCodegen = [{
|
||||
{
|
||||
SmallVector<llvm::Value*, 7> Operands;
|
||||
|
||||
bool NoPassthru =
|
||||
(IsMasked && (PolicyAttrs & RVV_VTA) && (PolicyAttrs & RVV_VMA)) |
|
||||
(!IsMasked && (PolicyAttrs & RVV_VTA));
|
||||
unsigned Offset = IsMasked ? NoPassthru ? 1 : 2 : NoPassthru ? 0 : 1;
|
||||
|
||||
if (NoPassthru) { // Push poison into passthru
|
||||
Operands.push_back(llvm::PoisonValue::get(ResultType));
|
||||
} else { // Push intrinsics operands into passthru
|
||||
llvm::Value *PassthruOperand = IsMasked ? Ops[1] : Ops[0];
|
||||
Operands.push_back(PassthruOperand);
|
||||
}
|
||||
|
||||
Operands.push_back(Ops[Offset]); // Ptr
|
||||
Operands.push_back(Ops[Offset + 1]); // Idx
|
||||
if (IsMasked)
|
||||
Operands.push_back(Ops[0]);
|
||||
Operands.push_back(Ops[Offset + 2]); // VL
|
||||
if (IsMasked)
|
||||
Operands.push_back(ConstantInt::get(Ops.back()->getType(), PolicyAttrs));
|
||||
Operands.push_back(ConstantInt::get(Ops.back()->getType(), SegInstSEW));
|
||||
|
||||
if (IsMasked)
|
||||
std::rotate(Ops.begin(), Ops.begin() + 1, Ops.end() - 1);
|
||||
if (NoPassthru)
|
||||
Ops.insert(Ops.begin(), llvm::PoisonValue::get(ResultType));
|
||||
|
||||
if (IsMasked)
|
||||
Ops.push_back(ConstantInt::get(Ops.back()->getType(), PolicyAttrs));
|
||||
Ops.push_back(ConstantInt::get(Ops.back()->getType(), SegInstSEW));
|
||||
|
||||
if (IsMasked)
|
||||
IntrinsicTypes = {ResultType, Ops[1]->getType(),
|
||||
Ops[2]->getType(),
|
||||
Ops[3]->getType(),
|
||||
Ops[4]->getType()};
|
||||
IntrinsicTypes = {ResultType, Ops[Offset]->getType(),
|
||||
Ops[Offset + 1]->getType(),
|
||||
Ops[0]->getType(),
|
||||
Ops.back()->getType()};
|
||||
else
|
||||
IntrinsicTypes = {ResultType, Ops[1]->getType(),
|
||||
Ops[2]->getType(),
|
||||
Ops[3]->getType()};
|
||||
IntrinsicTypes = {ResultType, Ops[Offset]->getType(),
|
||||
Ops[Offset + 1]->getType(),
|
||||
Ops.back()->getType()};
|
||||
llvm::Function *F = CGM.getIntrinsic(ID, IntrinsicTypes);
|
||||
llvm::Value *LoadValue = Builder.CreateCall(F, Ops, "");
|
||||
llvm::Value *LoadValue = Builder.CreateCall(F, Operands, "");
|
||||
|
||||
if (ReturnValue.isNull())
|
||||
return LoadValue;
|
||||
@ -1052,25 +1090,30 @@ multiclass RVVIndexedSegStoreTuple<string op> {
|
||||
{
|
||||
// Masked
|
||||
// Builtin: (mask, ptr, index, v_tuple, vl)
|
||||
// Intrinsic: (tuple, ptr, index, mask, vl, SegInstSEW)
|
||||
// Intrinsic: (tuple, ptr, index, mask, vl)
|
||||
// Unmasked
|
||||
// Builtin: (ptr, index, v_tuple, vl)
|
||||
// Intrinsic: (tuple, ptr, index, vl, SegInstSEW)
|
||||
// Intrinsic: (tuple, ptr, index, vl)
|
||||
unsigned Offset = IsMasked ? 1 : 0;
|
||||
|
||||
SmallVector<llvm::Value*, 6> Operands;
|
||||
Operands.push_back(Ops[Offset + 2]); // tuple
|
||||
Operands.push_back(Ops[Offset]); // Ptr
|
||||
Operands.push_back(Ops[Offset + 1]); // Idx
|
||||
if (IsMasked)
|
||||
Operands.push_back(Ops[0]);
|
||||
Operands.push_back(Ops[Offset + 3]); // VL
|
||||
Operands.push_back(ConstantInt::get(Ops.back()->getType(), SegInstSEW));
|
||||
|
||||
if (IsMasked)
|
||||
std::swap(Ops[0], Ops[3]);
|
||||
IntrinsicTypes = {Operands[0]->getType(), Ops[Offset]->getType(), Ops[Offset + 1]->getType(),
|
||||
Ops[0]->getType(),
|
||||
Operands.back()->getType()};
|
||||
else
|
||||
std::rotate(Ops.begin(), Ops.begin() + 2, Ops.begin() + 3);
|
||||
|
||||
Ops.push_back(ConstantInt::get(Ops.back()->getType(), SegInstSEW));
|
||||
|
||||
if (IsMasked)
|
||||
IntrinsicTypes = {Ops[0]->getType(), Ops[1]->getType(), Ops[2]->getType(),
|
||||
Ops[3]->getType(), Ops[4]->getType()};
|
||||
else
|
||||
IntrinsicTypes = {Ops[0]->getType(), Ops[1]->getType(), Ops[2]->getType(),
|
||||
Ops[3]->getType()};
|
||||
break;
|
||||
IntrinsicTypes = {Operands[0]->getType(), Ops[Offset]->getType(), Ops[Offset + 1]->getType(),
|
||||
Operands.back()->getType()};
|
||||
llvm::Function *F = CGM.getIntrinsic(ID, IntrinsicTypes);
|
||||
return Builder.CreateCall(F, Operands, "");
|
||||
}
|
||||
}] in {
|
||||
defvar T = "(Tuple:" # nf # ")";
|
||||
@ -1316,21 +1359,33 @@ let ManualCodegen = [{
|
||||
// Unmasked: (passthru, op0, op1, round_mode, vl)
|
||||
// Masked: (passthru, vector_in, vector_in/scalar_in, mask, vxrm, vl, policy)
|
||||
|
||||
SmallVector<llvm::Value*, 7> Operands;
|
||||
bool HasMaskedOff = !(
|
||||
(IsMasked && (PolicyAttrs & RVV_VTA) && (PolicyAttrs & RVV_VMA)) ||
|
||||
(!IsMasked && PolicyAttrs & RVV_VTA));
|
||||
|
||||
if (IsMasked)
|
||||
std::rotate(Ops.begin(), Ops.begin() + 1, Ops.end() - 2);
|
||||
unsigned Offset = IsMasked ?
|
||||
(HasMaskedOff ? 2 : 1) : (HasMaskedOff ? 1 : 0);
|
||||
|
||||
if (!HasMaskedOff)
|
||||
Ops.insert(Ops.begin(), llvm::PoisonValue::get(ResultType));
|
||||
Operands.push_back(llvm::PoisonValue::get(ResultType));
|
||||
else
|
||||
Operands.push_back(Ops[IsMasked ? 1 : 0]);
|
||||
|
||||
Operands.push_back(Ops[Offset]); // op0
|
||||
Operands.push_back(Ops[Offset + 1]); // op1
|
||||
|
||||
if (IsMasked)
|
||||
Ops.push_back(ConstantInt::get(Ops.back()->getType(), PolicyAttrs));
|
||||
Operands.push_back(Ops[0]); // mask
|
||||
|
||||
IntrinsicTypes = {ResultType, Ops[2]->getType(), Ops.back()->getType()};
|
||||
break;
|
||||
Operands.push_back(Ops[Offset + 2]); // vxrm
|
||||
Operands.push_back(Ops[Offset + 3]); // vl
|
||||
|
||||
if (IsMasked)
|
||||
Operands.push_back(ConstantInt::get(Ops.back()->getType(), PolicyAttrs));
|
||||
|
||||
IntrinsicTypes = {ResultType, Ops[Offset + 1]->getType(), Ops.back()->getType()};
|
||||
llvm::Function *F = CGM.getIntrinsic(ID, IntrinsicTypes);
|
||||
return Builder.CreateCall(F, Operands, "");
|
||||
}
|
||||
}] in {
|
||||
// 12.2. Vector Single-Width Averaging Add and Subtract
|
||||
@ -1353,22 +1408,34 @@ let ManualCodegen = [{
|
||||
// Unmasked: (passthru, op0, op1, round_mode, vl)
|
||||
// Masked: (passthru, vector_in, vector_in/scalar_in, mask, vxrm, vl, policy)
|
||||
|
||||
SmallVector<llvm::Value*, 7> Operands;
|
||||
bool HasMaskedOff = !(
|
||||
(IsMasked && (PolicyAttrs & RVV_VTA) && (PolicyAttrs & RVV_VMA)) ||
|
||||
(!IsMasked && PolicyAttrs & RVV_VTA));
|
||||
|
||||
if (IsMasked)
|
||||
std::rotate(Ops.begin(), Ops.begin() + 1, Ops.end() - 2);
|
||||
unsigned Offset = IsMasked ?
|
||||
(HasMaskedOff ? 2 : 1) : (HasMaskedOff ? 1 : 0);
|
||||
|
||||
if (!HasMaskedOff)
|
||||
Ops.insert(Ops.begin(), llvm::PoisonValue::get(ResultType));
|
||||
Operands.push_back(llvm::PoisonValue::get(ResultType));
|
||||
else
|
||||
Operands.push_back(Ops[IsMasked ? 1 : 0]);
|
||||
|
||||
Operands.push_back(Ops[Offset]); // op0
|
||||
Operands.push_back(Ops[Offset + 1]); // op1
|
||||
|
||||
if (IsMasked)
|
||||
Ops.push_back(ConstantInt::get(Ops.back()->getType(), PolicyAttrs));
|
||||
Operands.push_back(Ops[0]); // mask
|
||||
|
||||
IntrinsicTypes = {ResultType, Ops[1]->getType(), Ops[2]->getType(),
|
||||
Operands.push_back(Ops[Offset + 2]); // vxrm
|
||||
Operands.push_back(Ops[Offset + 3]); // vl
|
||||
|
||||
if (IsMasked)
|
||||
Operands.push_back(ConstantInt::get(Ops.back()->getType(), PolicyAttrs));
|
||||
|
||||
IntrinsicTypes = {ResultType, Ops[Offset]->getType(), Ops[Offset + 1]->getType(),
|
||||
Ops.back()->getType()};
|
||||
break;
|
||||
llvm::Function *F = CGM.getIntrinsic(ID, IntrinsicTypes);
|
||||
return Builder.CreateCall(F, Operands, "");
|
||||
}
|
||||
}] in {
|
||||
// 12.5. Vector Narrowing Fixed-Point Clip Instructions
|
||||
@ -1396,6 +1463,7 @@ let ManualCodegen = [{
|
||||
// Unmasked: (passthru, op0, op1, round_mode, vl)
|
||||
// Masked: (passthru, vector_in, vector_in/scalar_in, mask, frm, vl, policy)
|
||||
|
||||
SmallVector<llvm::Value*, 7> Operands;
|
||||
bool HasMaskedOff = !(
|
||||
(IsMasked && (PolicyAttrs & RVV_VTA) && (PolicyAttrs & RVV_VMA)) ||
|
||||
(!IsMasked && PolicyAttrs & RVV_VTA));
|
||||
@ -1403,20 +1471,35 @@ let ManualCodegen = [{
|
||||
(HasMaskedOff ? Ops.size() == 6 : Ops.size() == 5) :
|
||||
(HasMaskedOff ? Ops.size() == 5 : Ops.size() == 4);
|
||||
|
||||
if (!HasRoundModeOp)
|
||||
Ops.insert(Ops.end() - 1, ConstantInt::get(Ops.back()->getType(), 7)); // frm
|
||||
|
||||
if (IsMasked)
|
||||
std::rotate(Ops.begin(), Ops.begin() + 1, Ops.end() - 2);
|
||||
unsigned Offset = IsMasked ?
|
||||
(HasMaskedOff ? 2 : 1) : (HasMaskedOff ? 1 : 0);
|
||||
|
||||
if (!HasMaskedOff)
|
||||
Ops.insert(Ops.begin(), llvm::PoisonValue::get(ResultType));
|
||||
Operands.push_back(llvm::PoisonValue::get(ResultType));
|
||||
else
|
||||
Operands.push_back(Ops[IsMasked ? 1 : 0]);
|
||||
|
||||
Operands.push_back(Ops[Offset]); // op0
|
||||
Operands.push_back(Ops[Offset + 1]); // op1
|
||||
|
||||
if (IsMasked)
|
||||
Ops.push_back(ConstantInt::get(Ops.back()->getType(), PolicyAttrs));
|
||||
Operands.push_back(Ops[0]); // mask
|
||||
|
||||
IntrinsicTypes = {ResultType, Ops[2]->getType(), Ops.back()->getType()};
|
||||
break;
|
||||
if (HasRoundModeOp) {
|
||||
Operands.push_back(Ops[Offset + 2]); // frm
|
||||
Operands.push_back(Ops[Offset + 3]); // vl
|
||||
} else {
|
||||
Operands.push_back(ConstantInt::get(Ops[Offset + 2]->getType(), 7)); // frm
|
||||
Operands.push_back(Ops[Offset + 2]); // vl
|
||||
}
|
||||
|
||||
if (IsMasked)
|
||||
Operands.push_back(ConstantInt::get(Ops.back()->getType(), PolicyAttrs));
|
||||
|
||||
IntrinsicTypes = {ResultType, Ops[Offset + 1]->getType(),
|
||||
Operands.back()->getType()};
|
||||
llvm::Function *F = CGM.getIntrinsic(ID, IntrinsicTypes);
|
||||
return Builder.CreateCall(F, Operands, "");
|
||||
}
|
||||
}] in {
|
||||
let HasFRMRoundModeOp = true in {
|
||||
@ -1457,6 +1540,7 @@ let ManualCodegen = [{
|
||||
// Unmasked: (passthru, op0, op1, round_mode, vl)
|
||||
// Masked: (passthru, vector_in, vector_in/scalar_in, mask, frm, vl, policy)
|
||||
|
||||
SmallVector<llvm::Value*, 7> Operands;
|
||||
bool HasMaskedOff = !(
|
||||
(IsMasked && (PolicyAttrs & RVV_VTA) && (PolicyAttrs & RVV_VMA)) ||
|
||||
(!IsMasked && PolicyAttrs & RVV_VTA));
|
||||
@ -1464,21 +1548,35 @@ let ManualCodegen = [{
|
||||
(HasMaskedOff ? Ops.size() == 6 : Ops.size() == 5) :
|
||||
(HasMaskedOff ? Ops.size() == 5 : Ops.size() == 4);
|
||||
|
||||
if (!HasRoundModeOp)
|
||||
Ops.insert(Ops.end() - 1, ConstantInt::get(Ops.back()->getType(), 7)); // frm
|
||||
|
||||
if (IsMasked)
|
||||
std::rotate(Ops.begin(), Ops.begin() + 1, Ops.end() - 2);
|
||||
unsigned Offset = IsMasked ?
|
||||
(HasMaskedOff ? 2 : 1) : (HasMaskedOff ? 1 : 0);
|
||||
|
||||
if (!HasMaskedOff)
|
||||
Ops.insert(Ops.begin(), llvm::PoisonValue::get(ResultType));
|
||||
Operands.push_back(llvm::PoisonValue::get(ResultType));
|
||||
else
|
||||
Operands.push_back(Ops[IsMasked ? 1 : 0]);
|
||||
|
||||
Operands.push_back(Ops[Offset]); // op0
|
||||
Operands.push_back(Ops[Offset + 1]); // op1
|
||||
|
||||
if (IsMasked)
|
||||
Ops.push_back(ConstantInt::get(Ops.back()->getType(), PolicyAttrs));
|
||||
Operands.push_back(Ops[0]); // mask
|
||||
|
||||
IntrinsicTypes = {ResultType, Ops[1]->getType(), Ops[2]->getType(),
|
||||
if (HasRoundModeOp) {
|
||||
Operands.push_back(Ops[Offset + 2]); // frm
|
||||
Operands.push_back(Ops[Offset + 3]); // vl
|
||||
} else {
|
||||
Operands.push_back(ConstantInt::get(Ops[Offset + 2]->getType(), 7)); // frm
|
||||
Operands.push_back(Ops[Offset + 2]); // vl
|
||||
}
|
||||
|
||||
if (IsMasked)
|
||||
Operands.push_back(ConstantInt::get(Ops.back()->getType(), PolicyAttrs));
|
||||
|
||||
IntrinsicTypes = {ResultType, Ops[Offset]->getType(), Ops[Offset + 1]->getType(),
|
||||
Ops.back()->getType()};
|
||||
break;
|
||||
llvm::Function *F = CGM.getIntrinsic(ID, IntrinsicTypes);
|
||||
return Builder.CreateCall(F, Operands, "");
|
||||
}
|
||||
}] in {
|
||||
let HasFRMRoundModeOp = true in {
|
||||
@ -1521,23 +1619,38 @@ let UnMaskedPolicyScheme = HasPolicyOperand in {
|
||||
let ManualCodegen = [{
|
||||
{
|
||||
// LLVM intrinsic
|
||||
// Unmasked: (vector_in, vector_in/scalar_in, vector_in, round_mode, vl, policy)
|
||||
// Masked: (vector_in, vector_in/scalar_in, vector_in, mask, frm, vl, policy)
|
||||
// Unmasked: (passthru, op0, op1, round_mode, vl)
|
||||
// Masked: (passthru, vector_in, vector_in/scalar_in, mask, frm, vl, policy)
|
||||
|
||||
SmallVector<llvm::Value*, 7> Operands;
|
||||
bool HasRoundModeOp = IsMasked ? Ops.size() == 6 : Ops.size() == 5;
|
||||
|
||||
if (!HasRoundModeOp)
|
||||
Ops.insert(Ops.end() - 1, ConstantInt::get(Ops.back()->getType(), 7)); // frm
|
||||
unsigned Offset = IsMasked ? 2 : 1;
|
||||
|
||||
Operands.push_back(Ops[IsMasked ? 1 : 0]); // passthrough
|
||||
|
||||
Operands.push_back(Ops[Offset]); // op0
|
||||
Operands.push_back(Ops[Offset + 1]); // op1
|
||||
|
||||
if (IsMasked)
|
||||
std::rotate(Ops.begin(), Ops.begin() + 1, Ops.end() - 2);
|
||||
Operands.push_back(Ops[0]); // mask
|
||||
|
||||
Ops.push_back(ConstantInt::get(Ops.back()->getType(), PolicyAttrs));
|
||||
if (HasRoundModeOp) {
|
||||
Operands.push_back(Ops[Offset + 2]); // frm
|
||||
Operands.push_back(Ops[Offset + 3]); // vl
|
||||
} else {
|
||||
Operands.push_back(ConstantInt::get(Ops[Offset + 2]->getType(), 7)); // frm
|
||||
Operands.push_back(Ops[Offset + 2]); // vl
|
||||
}
|
||||
|
||||
IntrinsicTypes = {ResultType, Ops[1]->getType(),
|
||||
Ops.back()->getType()};
|
||||
Operands.push_back(ConstantInt::get(Ops.back()->getType(), PolicyAttrs));
|
||||
|
||||
break;
|
||||
IntrinsicTypes = {ResultType, Ops[Offset]->getType(),
|
||||
Operands.back()->getType()};
|
||||
|
||||
llvm::Function *F = CGM.getIntrinsic(ID, IntrinsicTypes);
|
||||
|
||||
return Builder.CreateCall(F, Operands, "");
|
||||
}
|
||||
}] in {
|
||||
let HasFRMRoundModeOp = 1 in {
|
||||
@ -1565,23 +1678,38 @@ let ManualCodegen = [{
|
||||
let ManualCodegen = [{
|
||||
{
|
||||
// LLVM intrinsic
|
||||
// Unmasked: (vector_in, vector_in/scalar_in, vector_in, round_mode, vl, policy)
|
||||
// Masked: (vector_in, vector_in/scalar_in, vector_in, mask, frm, vl, policy)
|
||||
// Unmasked: (passthru, op0, op1, round_mode, vl)
|
||||
// Masked: (passthru, vector_in, vector_in/scalar_in, mask, frm, vl, policy)
|
||||
|
||||
SmallVector<llvm::Value*, 7> Operands;
|
||||
bool HasRoundModeOp = IsMasked ? Ops.size() == 6 : Ops.size() == 5;
|
||||
|
||||
if (!HasRoundModeOp)
|
||||
Ops.insert(Ops.end() - 1, ConstantInt::get(Ops.back()->getType(), 7)); // frm
|
||||
unsigned Offset = IsMasked ? 2 : 1;
|
||||
|
||||
Operands.push_back(Ops[IsMasked ? 1 : 0]); // passthrough
|
||||
|
||||
Operands.push_back(Ops[Offset]); // op0
|
||||
Operands.push_back(Ops[Offset + 1]); // op1
|
||||
|
||||
if (IsMasked)
|
||||
std::rotate(Ops.begin(), Ops.begin() + 1, Ops.begin() + 4);
|
||||
Operands.push_back(Ops[0]); // mask
|
||||
|
||||
Ops.push_back(ConstantInt::get(Ops.back()->getType(), PolicyAttrs));
|
||||
if (HasRoundModeOp) {
|
||||
Operands.push_back(Ops[Offset + 2]); // frm
|
||||
Operands.push_back(Ops[Offset + 3]); // vl
|
||||
} else {
|
||||
Operands.push_back(ConstantInt::get(Ops[Offset + 2]->getType(), 7)); // frm
|
||||
Operands.push_back(Ops[Offset + 2]); // vl
|
||||
}
|
||||
|
||||
IntrinsicTypes = {ResultType, Ops[1]->getType(), Ops[2]->getType(),
|
||||
Ops.back()->getType()};
|
||||
Operands.push_back(ConstantInt::get(Ops.back()->getType(), PolicyAttrs));
|
||||
|
||||
break;
|
||||
IntrinsicTypes = {ResultType, Ops[Offset]->getType(), Ops[Offset + 1]->getType(),
|
||||
Operands.back()->getType()};
|
||||
|
||||
llvm::Function *F = CGM.getIntrinsic(ID, IntrinsicTypes);
|
||||
|
||||
return Builder.CreateCall(F, Operands, "");
|
||||
}
|
||||
}] in {
|
||||
let HasFRMRoundModeOp = 1 in {
|
||||
@ -1623,6 +1751,7 @@ let ManualCodegen = [{
|
||||
// Unmasked: (passthru, op0, round_mode, vl)
|
||||
// Masked: (passthru, op0, mask, frm, vl, policy)
|
||||
|
||||
SmallVector<llvm::Value*, 6> Operands;
|
||||
bool HasMaskedOff = !(
|
||||
(IsMasked && (PolicyAttrs & RVV_VTA) && (PolicyAttrs & RVV_VMA)) ||
|
||||
(!IsMasked && PolicyAttrs & RVV_VTA));
|
||||
@ -1630,20 +1759,33 @@ let ManualCodegen = [{
|
||||
(HasMaskedOff ? Ops.size() == 5 : Ops.size() == 4) :
|
||||
(HasMaskedOff ? Ops.size() == 4 : Ops.size() == 3);
|
||||
|
||||
if (!HasRoundModeOp)
|
||||
Ops.insert(Ops.end() - 1, ConstantInt::get(Ops.back()->getType(), 7)); // frm
|
||||
|
||||
if (IsMasked)
|
||||
std::rotate(Ops.begin(), Ops.begin() + 1, Ops.end() - 2);
|
||||
unsigned Offset = IsMasked ?
|
||||
(HasMaskedOff ? 2 : 1) : (HasMaskedOff ? 1 : 0);
|
||||
|
||||
if (!HasMaskedOff)
|
||||
Ops.insert(Ops.begin(), llvm::PoisonValue::get(ResultType));
|
||||
Operands.push_back(llvm::PoisonValue::get(ResultType));
|
||||
else
|
||||
Operands.push_back(Ops[IsMasked ? 1 : 0]);
|
||||
|
||||
Operands.push_back(Ops[Offset]); // op0
|
||||
|
||||
if (IsMasked)
|
||||
Ops.push_back(ConstantInt::get(Ops.back()->getType(), PolicyAttrs));
|
||||
Operands.push_back(Ops[0]); // mask
|
||||
|
||||
IntrinsicTypes = {ResultType, Ops.back()->getType()};
|
||||
break;
|
||||
if (HasRoundModeOp) {
|
||||
Operands.push_back(Ops[Offset + 1]); // frm
|
||||
Operands.push_back(Ops[Offset + 2]); // vl
|
||||
} else {
|
||||
Operands.push_back(ConstantInt::get(Ops[Offset + 1]->getType(), 7)); // frm
|
||||
Operands.push_back(Ops[Offset + 1]); // vl
|
||||
}
|
||||
|
||||
if (IsMasked)
|
||||
Operands.push_back(ConstantInt::get(Ops.back()->getType(), PolicyAttrs));
|
||||
|
||||
IntrinsicTypes = {ResultType, Operands.back()->getType()};
|
||||
llvm::Function *F = CGM.getIntrinsic(ID, IntrinsicTypes);
|
||||
return Builder.CreateCall(F, Operands, "");
|
||||
}
|
||||
}] in {
|
||||
let HasFRMRoundModeOp = 1 in {
|
||||
@ -1808,6 +1950,7 @@ let ManualCodegen = [{
|
||||
// LLVM intrinsic
|
||||
// Unmasked: (passthru, op0, frm, vl)
|
||||
// Masked: (passthru, op0, mask, frm, vl, policy)
|
||||
SmallVector<llvm::Value*, 6> Operands;
|
||||
bool HasMaskedOff = !(
|
||||
(IsMasked && (PolicyAttrs & RVV_VTA) && (PolicyAttrs & RVV_VMA)) ||
|
||||
(!IsMasked && PolicyAttrs & RVV_VTA));
|
||||
@ -1815,20 +1958,34 @@ let ManualCodegen = [{
|
||||
(HasMaskedOff ? Ops.size() == 5 : Ops.size() == 4) :
|
||||
(HasMaskedOff ? Ops.size() == 4 : Ops.size() == 3);
|
||||
|
||||
if (!HasRoundModeOp)
|
||||
Ops.insert(Ops.end() - 1, ConstantInt::get(Ops.back()->getType(), 7)); // frm
|
||||
|
||||
if (IsMasked)
|
||||
std::rotate(Ops.begin(), Ops.begin() + 1, Ops.end() - 2);
|
||||
unsigned Offset = IsMasked ?
|
||||
(HasMaskedOff ? 2 : 1) : (HasMaskedOff ? 1 : 0);
|
||||
|
||||
if (!HasMaskedOff)
|
||||
Ops.insert(Ops.begin(), llvm::PoisonValue::get(ResultType));
|
||||
Operands.push_back(llvm::PoisonValue::get(ResultType));
|
||||
else
|
||||
Operands.push_back(Ops[IsMasked ? 1 : 0]);
|
||||
|
||||
Operands.push_back(Ops[Offset]); // op0
|
||||
|
||||
if (IsMasked)
|
||||
Ops.push_back(ConstantInt::get(Ops.back()->getType(), PolicyAttrs));
|
||||
Operands.push_back(Ops[0]); // mask
|
||||
|
||||
IntrinsicTypes = {ResultType, Ops[1]->getType(), Ops.back()->getType()};
|
||||
break;
|
||||
if (HasRoundModeOp) {
|
||||
Operands.push_back(Ops[Offset + 1]); // frm
|
||||
Operands.push_back(Ops[Offset + 2]); // vl
|
||||
} else {
|
||||
Operands.push_back(ConstantInt::get(Ops[Offset + 1]->getType(), 7)); // frm
|
||||
Operands.push_back(Ops[Offset + 1]); // vl
|
||||
}
|
||||
|
||||
if (IsMasked)
|
||||
Operands.push_back(ConstantInt::get(Ops.back()->getType(), PolicyAttrs));
|
||||
|
||||
IntrinsicTypes = {ResultType, Ops[Offset]->getType(),
|
||||
Operands.back()->getType()};
|
||||
llvm::Function *F = CGM.getIntrinsic(ID, IntrinsicTypes);
|
||||
return Builder.CreateCall(F, Operands, "");
|
||||
}
|
||||
}] in {
|
||||
let HasFRMRoundModeOp = 1 in {
|
||||
@ -1998,6 +2155,7 @@ let ManualCodegen = [{
|
||||
// Unmasked: (passthru, op0, op1, round_mode, vl)
|
||||
// Masked: (passthru, vector_in, vector_in/scalar_in, mask, frm, vl, policy)
|
||||
|
||||
SmallVector<llvm::Value*, 6> Operands;
|
||||
bool HasMaskedOff = !(
|
||||
(IsMasked && (PolicyAttrs & RVV_VTA) && (PolicyAttrs & RVV_VMA)) ||
|
||||
(!IsMasked && PolicyAttrs & RVV_VTA));
|
||||
@ -2005,17 +2163,32 @@ let ManualCodegen = [{
|
||||
(HasMaskedOff ? Ops.size() == 6 : Ops.size() == 5) :
|
||||
(HasMaskedOff ? Ops.size() == 5 : Ops.size() == 4);
|
||||
|
||||
if (!HasRoundModeOp)
|
||||
Ops.insert(Ops.end() - 1, ConstantInt::get(Ops.back()->getType(), 7)); // frm
|
||||
|
||||
if (IsMasked)
|
||||
std::rotate(Ops.begin(), Ops.begin() + 1, Ops.end() - 2);
|
||||
unsigned Offset = IsMasked ?
|
||||
(HasMaskedOff ? 2 : 1) : (HasMaskedOff ? 1 : 0);
|
||||
|
||||
if (!HasMaskedOff)
|
||||
Ops.insert(Ops.begin(), llvm::PoisonValue::get(ResultType));
|
||||
Operands.push_back(llvm::PoisonValue::get(ResultType));
|
||||
else
|
||||
Operands.push_back(Ops[IsMasked ? 1 : 0]);
|
||||
|
||||
IntrinsicTypes = {ResultType, Ops[1]->getType(), Ops.back()->getType()};
|
||||
break;
|
||||
Operands.push_back(Ops[Offset]); // op0
|
||||
Operands.push_back(Ops[Offset + 1]); // op1
|
||||
|
||||
if (IsMasked)
|
||||
Operands.push_back(Ops[0]); // mask
|
||||
|
||||
if (HasRoundModeOp) {
|
||||
Operands.push_back(Ops[Offset + 2]); // frm
|
||||
Operands.push_back(Ops[Offset + 3]); // vl
|
||||
} else {
|
||||
Operands.push_back(ConstantInt::get(Ops[Offset + 2]->getType(), 7)); // frm
|
||||
Operands.push_back(Ops[Offset + 2]); // vl
|
||||
}
|
||||
|
||||
IntrinsicTypes = {ResultType, Ops[Offset]->getType(),
|
||||
Ops.back()->getType()};
|
||||
llvm::Function *F = CGM.getIntrinsic(ID, IntrinsicTypes);
|
||||
return Builder.CreateCall(F, Operands, "");
|
||||
}
|
||||
}] in {
|
||||
let HasFRMRoundModeOp = 1 in {
|
||||
|
@ -63,11 +63,11 @@ public:
|
||||
|
||||
mlir::Value getConstAPInt(mlir::Location loc, mlir::Type typ,
|
||||
const llvm::APInt &val) {
|
||||
return cir::ConstantOp::create(*this, loc, cir::IntAttr::get(typ, val));
|
||||
return create<cir::ConstantOp>(loc, cir::IntAttr::get(typ, val));
|
||||
}
|
||||
|
||||
cir::ConstantOp getConstant(mlir::Location loc, mlir::TypedAttr attr) {
|
||||
return cir::ConstantOp::create(*this, loc, attr);
|
||||
return create<cir::ConstantOp>(loc, attr);
|
||||
}
|
||||
|
||||
cir::ConstantOp getConstantInt(mlir::Location loc, mlir::Type ty,
|
||||
@ -119,7 +119,7 @@ public:
|
||||
}
|
||||
|
||||
cir::ConstantOp getBool(bool state, mlir::Location loc) {
|
||||
return cir::ConstantOp::create(*this, loc, getCIRBoolAttr(state));
|
||||
return create<cir::ConstantOp>(loc, getCIRBoolAttr(state));
|
||||
}
|
||||
cir::ConstantOp getFalse(mlir::Location loc) { return getBool(false, loc); }
|
||||
cir::ConstantOp getTrue(mlir::Location loc) { return getBool(true, loc); }
|
||||
@ -144,37 +144,21 @@ public:
|
||||
mlir::Value createComplexCreate(mlir::Location loc, mlir::Value real,
|
||||
mlir::Value imag) {
|
||||
auto resultComplexTy = cir::ComplexType::get(real.getType());
|
||||
return cir::ComplexCreateOp::create(*this, loc, resultComplexTy, real,
|
||||
imag);
|
||||
return create<cir::ComplexCreateOp>(loc, resultComplexTy, real, imag);
|
||||
}
|
||||
|
||||
mlir::Value createComplexReal(mlir::Location loc, mlir::Value operand) {
|
||||
auto operandTy = mlir::cast<cir::ComplexType>(operand.getType());
|
||||
return cir::ComplexRealOp::create(*this, loc, operandTy.getElementType(),
|
||||
operand);
|
||||
return create<cir::ComplexRealOp>(loc, operandTy.getElementType(), operand);
|
||||
}
|
||||
|
||||
mlir::Value createComplexImag(mlir::Location loc, mlir::Value operand) {
|
||||
auto operandTy = mlir::cast<cir::ComplexType>(operand.getType());
|
||||
return cir::ComplexImagOp::create(*this, loc, operandTy.getElementType(),
|
||||
operand);
|
||||
}
|
||||
|
||||
cir::LoadOp createLoad(mlir::Location loc, mlir::Value ptr,
|
||||
uint64_t alignment = 0) {
|
||||
mlir::IntegerAttr alignmentAttr = getAlignmentAttr(alignment);
|
||||
assert(!cir::MissingFeatures::opLoadStoreVolatile());
|
||||
return cir::LoadOp::create(*this, loc, ptr, /*isDeref=*/false,
|
||||
alignmentAttr, cir::MemOrderAttr{});
|
||||
}
|
||||
|
||||
mlir::Value createAlignedLoad(mlir::Location loc, mlir::Value ptr,
|
||||
uint64_t alignment) {
|
||||
return createLoad(loc, ptr, alignment);
|
||||
return create<cir::ComplexImagOp>(loc, operandTy.getElementType(), operand);
|
||||
}
|
||||
|
||||
mlir::Value createNot(mlir::Value value) {
|
||||
return cir::UnaryOp::create(*this, value.getLoc(), value.getType(),
|
||||
return create<cir::UnaryOp>(value.getLoc(), value.getType(),
|
||||
cir::UnaryOpKind::Not, value);
|
||||
}
|
||||
|
||||
@ -183,7 +167,7 @@ public:
|
||||
mlir::Location loc,
|
||||
llvm::function_ref<void(mlir::OpBuilder &, mlir::Location)> condBuilder,
|
||||
llvm::function_ref<void(mlir::OpBuilder &, mlir::Location)> bodyBuilder) {
|
||||
return cir::DoWhileOp::create(*this, loc, condBuilder, bodyBuilder);
|
||||
return create<cir::DoWhileOp>(loc, condBuilder, bodyBuilder);
|
||||
}
|
||||
|
||||
/// Create a while operation.
|
||||
@ -191,7 +175,7 @@ public:
|
||||
mlir::Location loc,
|
||||
llvm::function_ref<void(mlir::OpBuilder &, mlir::Location)> condBuilder,
|
||||
llvm::function_ref<void(mlir::OpBuilder &, mlir::Location)> bodyBuilder) {
|
||||
return cir::WhileOp::create(*this, loc, condBuilder, bodyBuilder);
|
||||
return create<cir::WhileOp>(loc, condBuilder, bodyBuilder);
|
||||
}
|
||||
|
||||
/// Create a for operation.
|
||||
@ -200,23 +184,22 @@ public:
|
||||
llvm::function_ref<void(mlir::OpBuilder &, mlir::Location)> condBuilder,
|
||||
llvm::function_ref<void(mlir::OpBuilder &, mlir::Location)> bodyBuilder,
|
||||
llvm::function_ref<void(mlir::OpBuilder &, mlir::Location)> stepBuilder) {
|
||||
return cir::ForOp::create(*this, loc, condBuilder, bodyBuilder,
|
||||
stepBuilder);
|
||||
return create<cir::ForOp>(loc, condBuilder, bodyBuilder, stepBuilder);
|
||||
}
|
||||
|
||||
/// Create a break operation.
|
||||
cir::BreakOp createBreak(mlir::Location loc) {
|
||||
return cir::BreakOp::create(*this, loc);
|
||||
return create<cir::BreakOp>(loc);
|
||||
}
|
||||
|
||||
/// Create a continue operation.
|
||||
cir::ContinueOp createContinue(mlir::Location loc) {
|
||||
return cir::ContinueOp::create(*this, loc);
|
||||
return create<cir::ContinueOp>(loc);
|
||||
}
|
||||
|
||||
mlir::Value createUnaryOp(mlir::Location loc, cir::UnaryOpKind kind,
|
||||
mlir::Value operand) {
|
||||
return cir::UnaryOp::create(*this, loc, kind, operand);
|
||||
return create<cir::UnaryOp>(loc, kind, operand);
|
||||
}
|
||||
|
||||
mlir::TypedAttr getConstPtrAttr(mlir::Type type, int64_t value) {
|
||||
@ -226,7 +209,7 @@ public:
|
||||
mlir::Value createAlloca(mlir::Location loc, cir::PointerType addrType,
|
||||
mlir::Type type, llvm::StringRef name,
|
||||
mlir::IntegerAttr alignment) {
|
||||
return cir::AllocaOp::create(*this, loc, addrType, type, name, alignment);
|
||||
return create<cir::AllocaOp>(loc, addrType, type, name, alignment);
|
||||
}
|
||||
|
||||
/// Get constant address of a global variable as an MLIR attribute.
|
||||
@ -239,8 +222,8 @@ public:
|
||||
|
||||
mlir::Value createGetGlobal(mlir::Location loc, cir::GlobalOp global) {
|
||||
assert(!cir::MissingFeatures::addressSpace());
|
||||
return cir::GetGlobalOp::create(
|
||||
*this, loc, getPointerTo(global.getSymType()), global.getSymName());
|
||||
return create<cir::GetGlobalOp>(loc, getPointerTo(global.getSymType()),
|
||||
global.getSymName());
|
||||
}
|
||||
|
||||
mlir::Value createGetGlobal(cir::GlobalOp global) {
|
||||
@ -248,39 +231,36 @@ public:
|
||||
}
|
||||
|
||||
cir::StoreOp createStore(mlir::Location loc, mlir::Value val, mlir::Value dst,
|
||||
bool isVolatile = false,
|
||||
mlir::IntegerAttr align = {},
|
||||
cir::MemOrderAttr order = {}) {
|
||||
return cir::StoreOp::create(*this, loc, val, dst, align, order);
|
||||
mlir::IntegerAttr align = {}) {
|
||||
return create<cir::StoreOp>(loc, val, dst, align);
|
||||
}
|
||||
|
||||
[[nodiscard]] cir::GlobalOp createGlobal(mlir::ModuleOp mlirModule,
|
||||
mlir::Location loc,
|
||||
mlir::StringRef name,
|
||||
mlir::Type type, bool isConstant,
|
||||
mlir::Type type,
|
||||
cir::GlobalLinkageKind linkage) {
|
||||
mlir::OpBuilder::InsertionGuard guard(*this);
|
||||
setInsertionPointToStart(mlirModule.getBody());
|
||||
return cir::GlobalOp::create(*this, loc, name, type, isConstant, linkage);
|
||||
return create<cir::GlobalOp>(loc, name, type, linkage);
|
||||
}
|
||||
|
||||
cir::GetMemberOp createGetMember(mlir::Location loc, mlir::Type resultTy,
|
||||
mlir::Value base, llvm::StringRef name,
|
||||
unsigned index) {
|
||||
return cir::GetMemberOp::create(*this, loc, resultTy, base, name, index);
|
||||
return create<cir::GetMemberOp>(loc, resultTy, base, name, index);
|
||||
}
|
||||
|
||||
mlir::Value createDummyValue(mlir::Location loc, mlir::Type type,
|
||||
clang::CharUnits alignment) {
|
||||
mlir::IntegerAttr alignmentAttr = getAlignmentAttr(alignment);
|
||||
auto addr = createAlloca(loc, getPointerTo(type), type, {}, alignmentAttr);
|
||||
return cir::LoadOp::create(*this, loc, addr, /*isDeref=*/false,
|
||||
alignmentAttr, /*mem_order=*/{});
|
||||
return create<cir::LoadOp>(loc, addr, /*isDeref=*/false, alignmentAttr);
|
||||
}
|
||||
|
||||
cir::PtrStrideOp createPtrStride(mlir::Location loc, mlir::Value base,
|
||||
mlir::Value stride) {
|
||||
return cir::PtrStrideOp::create(*this, loc, base.getType(), base, stride);
|
||||
return create<cir::PtrStrideOp>(loc, base.getType(), base, stride);
|
||||
}
|
||||
|
||||
//===--------------------------------------------------------------------===//
|
||||
@ -290,7 +270,7 @@ public:
|
||||
cir::CallOp createCallOp(mlir::Location loc, mlir::SymbolRefAttr callee,
|
||||
mlir::Type returnType, mlir::ValueRange operands,
|
||||
llvm::ArrayRef<mlir::NamedAttribute> attrs = {}) {
|
||||
auto op = cir::CallOp::create(*this, loc, callee, returnType, operands);
|
||||
auto op = create<cir::CallOp>(loc, callee, returnType, operands);
|
||||
op->setAttrs(attrs);
|
||||
return op;
|
||||
}
|
||||
@ -321,7 +301,7 @@ public:
|
||||
mlir::Value src, mlir::Type newTy) {
|
||||
if (newTy == src.getType())
|
||||
return src;
|
||||
return cir::CastOp::create(*this, loc, newTy, kind, src);
|
||||
return create<cir::CastOp>(loc, newTy, kind, src);
|
||||
}
|
||||
|
||||
mlir::Value createCast(cir::CastKind kind, mlir::Value src,
|
||||
@ -371,7 +351,7 @@ public:
|
||||
|
||||
mlir::Value createBinop(mlir::Location loc, mlir::Value lhs,
|
||||
cir::BinOpKind kind, mlir::Value rhs) {
|
||||
return cir::BinOp::create(*this, loc, lhs.getType(), kind, lhs, rhs);
|
||||
return create<cir::BinOp>(loc, lhs.getType(), kind, lhs, rhs);
|
||||
}
|
||||
|
||||
mlir::Value createLowBitsSet(mlir::Location loc, unsigned size,
|
||||
@ -393,8 +373,8 @@ public:
|
||||
mlir::Value trueValue, mlir::Value falseValue) {
|
||||
assert(trueValue.getType() == falseValue.getType() &&
|
||||
"trueValue and falseValue should have the same type");
|
||||
return cir::SelectOp::create(*this, loc, trueValue.getType(), condition,
|
||||
trueValue, falseValue);
|
||||
return create<cir::SelectOp>(loc, trueValue.getType(), condition, trueValue,
|
||||
falseValue);
|
||||
}
|
||||
|
||||
mlir::Value createLogicalAnd(mlir::Location loc, mlir::Value lhs,
|
||||
@ -409,8 +389,8 @@ public:
|
||||
|
||||
mlir::Value createMul(mlir::Location loc, mlir::Value lhs, mlir::Value rhs,
|
||||
OverflowBehavior ob = OverflowBehavior::None) {
|
||||
auto op = cir::BinOp::create(*this, loc, lhs.getType(), cir::BinOpKind::Mul,
|
||||
lhs, rhs);
|
||||
auto op =
|
||||
create<cir::BinOp>(loc, lhs.getType(), cir::BinOpKind::Mul, lhs, rhs);
|
||||
op.setNoUnsignedWrap(
|
||||
llvm::to_underlying(ob & OverflowBehavior::NoUnsignedWrap));
|
||||
op.setNoSignedWrap(
|
||||
@ -428,8 +408,8 @@ public:
|
||||
|
||||
mlir::Value createSub(mlir::Location loc, mlir::Value lhs, mlir::Value rhs,
|
||||
OverflowBehavior ob = OverflowBehavior::Saturated) {
|
||||
auto op = cir::BinOp::create(*this, loc, lhs.getType(), cir::BinOpKind::Sub,
|
||||
lhs, rhs);
|
||||
auto op =
|
||||
create<cir::BinOp>(loc, lhs.getType(), cir::BinOpKind::Sub, lhs, rhs);
|
||||
op.setNoUnsignedWrap(
|
||||
llvm::to_underlying(ob & OverflowBehavior::NoUnsignedWrap));
|
||||
op.setNoSignedWrap(
|
||||
@ -450,8 +430,8 @@ public:
|
||||
|
||||
mlir::Value createAdd(mlir::Location loc, mlir::Value lhs, mlir::Value rhs,
|
||||
OverflowBehavior ob = OverflowBehavior::None) {
|
||||
auto op = cir::BinOp::create(*this, loc, lhs.getType(), cir::BinOpKind::Add,
|
||||
lhs, rhs);
|
||||
auto op =
|
||||
create<cir::BinOp>(loc, lhs.getType(), cir::BinOpKind::Add, lhs, rhs);
|
||||
op.setNoUnsignedWrap(
|
||||
llvm::to_underlying(ob & OverflowBehavior::NoUnsignedWrap));
|
||||
op.setNoSignedWrap(
|
||||
@ -472,7 +452,7 @@ public:
|
||||
|
||||
cir::CmpOp createCompare(mlir::Location loc, cir::CmpOpKind kind,
|
||||
mlir::Value lhs, mlir::Value rhs) {
|
||||
return cir::CmpOp::create(*this, loc, getBoolTy(), kind, lhs, rhs);
|
||||
return create<cir::CmpOp>(loc, getBoolTy(), kind, lhs, rhs);
|
||||
}
|
||||
|
||||
mlir::Value createIsNaN(mlir::Location loc, mlir::Value operand) {
|
||||
@ -481,8 +461,7 @@ public:
|
||||
|
||||
mlir::Value createShift(mlir::Location loc, mlir::Value lhs, mlir::Value rhs,
|
||||
bool isShiftLeft) {
|
||||
return cir::ShiftOp::create(*this, loc, lhs.getType(), lhs, rhs,
|
||||
isShiftLeft);
|
||||
return create<cir::ShiftOp>(loc, lhs.getType(), lhs, rhs, isShiftLeft);
|
||||
}
|
||||
|
||||
mlir::Value createShift(mlir::Location loc, mlir::Value lhs,
|
||||
@ -560,12 +539,12 @@ public:
|
||||
|
||||
/// Create a loop condition.
|
||||
cir::ConditionOp createCondition(mlir::Value condition) {
|
||||
return cir::ConditionOp::create(*this, condition.getLoc(), condition);
|
||||
return create<cir::ConditionOp>(condition.getLoc(), condition);
|
||||
}
|
||||
|
||||
/// Create a yield operation.
|
||||
cir::YieldOp createYield(mlir::Location loc, mlir::ValueRange value = {}) {
|
||||
return cir::YieldOp::create(*this, loc, value);
|
||||
return create<cir::YieldOp>(loc, value);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -50,45 +50,6 @@ class CIR_UnitAttr<string name, string attrMnemonic, list<Trait> traits = []>
|
||||
let isOptional = 1;
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// SourceLanguageAttr
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// TODO: Add cases for other languages that Clang supports.
|
||||
|
||||
def CIR_SourceLanguage : CIR_I32EnumAttr<"SourceLanguage", "source language", [
|
||||
I32EnumAttrCase<"C", 1, "c">,
|
||||
I32EnumAttrCase<"CXX", 2, "cxx">
|
||||
]> {
|
||||
// The enum attr class is defined in `CIR_SourceLanguageAttr` below,
|
||||
// so that it can define extra class methods.
|
||||
let genSpecializedAttr = 0;
|
||||
}
|
||||
|
||||
def CIR_SourceLanguageAttr : CIR_EnumAttr<CIR_SourceLanguage, "lang"> {
|
||||
|
||||
let summary = "Module source language";
|
||||
let description = [{
|
||||
Represents the source language used to generate the module.
|
||||
|
||||
Example:
|
||||
```
|
||||
// Module compiled from C.
|
||||
module attributes {cir.lang = cir.lang<c>} {}
|
||||
// Module compiled from C++.
|
||||
module attributes {cir.lang = cir.lang<cxx>} {}
|
||||
```
|
||||
|
||||
Module source language attribute name is `cir.lang` is defined by
|
||||
`getSourceLanguageAttrName` method in CIRDialect class.
|
||||
}];
|
||||
|
||||
let extraClassDeclaration = [{
|
||||
bool isC() const { return getValue() == SourceLanguage::C; }
|
||||
bool isCXX() const { return getValue() == SourceLanguage::CXX; }
|
||||
}];
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// OptInfoAttr
|
||||
//===----------------------------------------------------------------------===//
|
||||
@ -380,44 +341,6 @@ def CIR_ConstVectorAttr : CIR_Attr<"ConstVector", "const_vector", [
|
||||
let genVerifyDecl = 1;
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// ConstRecordAttr
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
def CIR_ConstRecordAttr : CIR_Attr<"ConstRecord", "const_record", [
|
||||
TypedAttrInterface
|
||||
]> {
|
||||
let summary = "Represents a constant record";
|
||||
let description = [{
|
||||
Effectively supports "struct-like" constants. It's must be built from
|
||||
an `mlir::ArrayAttr` instance where each element is a typed attribute
|
||||
(`mlir::TypedAttribute`).
|
||||
|
||||
Example:
|
||||
```
|
||||
cir.global external @rgb2 = #cir.const_record<{0 : i8,
|
||||
5 : i64, #cir.null : !cir.ptr<i8>
|
||||
}> : !cir.record<"", i8, i64, !cir.ptr<i8>>
|
||||
```
|
||||
}];
|
||||
|
||||
let parameters = (ins AttributeSelfTypeParameter<"">:$type,
|
||||
"mlir::ArrayAttr":$members);
|
||||
|
||||
let builders = [
|
||||
AttrBuilderWithInferredContext<(ins "cir::RecordType":$type,
|
||||
"mlir::ArrayAttr":$members), [{
|
||||
return $_get(type.getContext(), type, members);
|
||||
}]>
|
||||
];
|
||||
|
||||
let assemblyFormat = [{
|
||||
`<` custom<RecordMembers>($members) `>`
|
||||
}];
|
||||
|
||||
let genVerifyDecl = 1;
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// ConstPtrAttr
|
||||
//===----------------------------------------------------------------------===//
|
||||
@ -535,72 +458,6 @@ def CIR_GlobalViewAttr : CIR_Attr<"GlobalView", "global_view", [
|
||||
}];
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// VTableAttr
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
def CIR_VTableAttr : CIR_Attr<"VTable", "vtable", [TypedAttrInterface]> {
|
||||
let summary = "Represents a C++ vtable";
|
||||
let description = [{
|
||||
Wraps a #cir.const_record containing one or more vtable arrays.
|
||||
|
||||
In most cases, the anonymous record type wrapped by this attribute will
|
||||
contain a single array corresponding to the vtable for one class. However,
|
||||
in the case of multiple inheritence, the anonymous structure may contain
|
||||
multiple arrays, each of which is a vtable.
|
||||
|
||||
Example 1 (single vtable):
|
||||
```mlir
|
||||
cir.global linkonce_odr @_ZTV6Mother =
|
||||
#cir.vtable<{
|
||||
#cir.const_array<[
|
||||
#cir.ptr<null> : !cir.ptr<!u8i>,
|
||||
#cir.global_view<@_ZTI6Mother> : !cir.ptr<!u8i>,
|
||||
#cir.global_view<@_ZN6Mother9MotherFooEv> : !cir.ptr<!u8i>,
|
||||
#cir.global_view<@_ZN6Mother10MotherFoo2Ev> : !cir.ptr<!u8i>
|
||||
]> : !cir.array<!cir.ptr<!u8i> x 4>
|
||||
}> : !rec_anon_struct1
|
||||
```
|
||||
|
||||
Example 2 (multiple vtables):
|
||||
```mlir
|
||||
cir.global linkonce_odr @_ZTV5Child =
|
||||
#cir.vtable<{
|
||||
#cir.const_array<[
|
||||
#cir.ptr<null> : !cir.ptr<!u8i>,
|
||||
#cir.global_view<@_ZTI5Child> : !cir.ptr<!u8i>,
|
||||
#cir.global_view<@_ZN5Child9MotherFooEv> : !cir.ptr<!u8i>,
|
||||
#cir.global_view<@_ZN6Mother10MotherFoo2Ev> : !cir.ptr<!u8i>
|
||||
]> : !cir.array<!cir.ptr<!u8i> x 4>,
|
||||
#cir.const_array<[
|
||||
#cir.ptr<-8 : i64> : !cir.ptr<!u8i>,
|
||||
#cir.global_view<@_ZTI5Child> : !cir.ptr<!u8i>,
|
||||
#cir.global_view<@_ZN6Father9FatherFooEv> : !cir.ptr<!u8i>
|
||||
]> : !cir.array<!cir.ptr<!u8i> x 3>
|
||||
}> : !rec_anon_struct2
|
||||
```
|
||||
}];
|
||||
|
||||
// `data` is a const record with one element, containing an array of
|
||||
// vtable information.
|
||||
let parameters = (ins
|
||||
AttributeSelfTypeParameter<"">:$type,
|
||||
"mlir::ArrayAttr":$data
|
||||
);
|
||||
|
||||
let builders = [
|
||||
AttrBuilderWithInferredContext<(ins "mlir::Type":$type,
|
||||
"mlir::ArrayAttr":$data), [{
|
||||
return $_get(type.getContext(), type, data);
|
||||
}]>
|
||||
];
|
||||
|
||||
let genVerifyDecl = 1;
|
||||
let assemblyFormat = [{
|
||||
`<` custom<RecordMembers>($data) `>`
|
||||
}];
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// ConstComplexAttr
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
@ -35,7 +35,6 @@ def CIR_Dialect : Dialect {
|
||||
let hasConstantMaterializer = 1;
|
||||
|
||||
let extraClassDeclaration = [{
|
||||
static llvm::StringRef getSourceLanguageAttrName() { return "cir.lang"; }
|
||||
static llvm::StringRef getTripleAttrName() { return "cir.triple"; }
|
||||
static llvm::StringRef getOptInfoAttrName() { return "cir.opt_info"; }
|
||||
static llvm::StringRef getCalleeAttrName() { return "callee"; }
|
||||
|
@ -299,20 +299,6 @@ def CIR_ConstantOp : CIR_Op<"const", [
|
||||
let hasFolder = 1;
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// C/C++ memory order definitions
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
def CIR_MemOrder : CIR_I32EnumAttr<
|
||||
"MemOrder", "Memory order according to C++11 memory model", [
|
||||
I32EnumAttrCase<"Relaxed", 0, "relaxed">,
|
||||
I32EnumAttrCase<"Consume", 1, "consume">,
|
||||
I32EnumAttrCase<"Acquire", 2, "acquire">,
|
||||
I32EnumAttrCase<"Release", 3, "release">,
|
||||
I32EnumAttrCase<"AcquireRelease", 4, "acq_rel">,
|
||||
I32EnumAttrCase<"SequentiallyConsistent", 5, "seq_cst">
|
||||
]>;
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// AllocaOp
|
||||
//===----------------------------------------------------------------------===//
|
||||
@ -422,14 +408,13 @@ def CIR_LoadOp : CIR_Op<"load", [
|
||||
let arguments = (ins Arg<CIR_PointerType, "the address to load from",
|
||||
[MemRead]>:$addr,
|
||||
UnitAttr:$isDeref,
|
||||
OptionalAttr<I64Attr>:$alignment,
|
||||
OptionalAttr<CIR_MemOrder>:$mem_order);
|
||||
OptionalAttr<I64Attr>:$alignment
|
||||
);
|
||||
let results = (outs CIR_AnyType:$result);
|
||||
|
||||
let assemblyFormat = [{
|
||||
(`deref` $isDeref^)?
|
||||
(`align` `(` $alignment^ `)`)?
|
||||
(`atomic` `(` $mem_order^ `)`)?
|
||||
$addr `:` qualified(type($addr)) `,` type($result) attr-dict
|
||||
}];
|
||||
|
||||
@ -466,12 +451,10 @@ def CIR_StoreOp : CIR_Op<"store", [
|
||||
let arguments = (ins CIR_AnyType:$value,
|
||||
Arg<CIR_PointerType, "the address to store the value",
|
||||
[MemWrite]>:$addr,
|
||||
OptionalAttr<I64Attr>:$alignment,
|
||||
OptionalAttr<CIR_MemOrder>:$mem_order);
|
||||
OptionalAttr<I64Attr>:$alignment);
|
||||
|
||||
let assemblyFormat = [{
|
||||
(`align` `(` $alignment^ `)`)?
|
||||
(`atomic` `(` $mem_order^ `)`)?
|
||||
$value `,` $addr attr-dict `:` type($value) `,` qualified(type($addr))
|
||||
}];
|
||||
|
||||
@ -1719,14 +1702,12 @@ def CIR_GlobalOp : CIR_Op<"global", [
|
||||
CIR_GlobalLinkageKind:$linkage,
|
||||
OptionalAttr<AnyAttr>:$initial_value,
|
||||
UnitAttr:$comdat,
|
||||
UnitAttr:$constant,
|
||||
UnitAttr:$dso_local,
|
||||
OptionalAttr<I64Attr>:$alignment);
|
||||
|
||||
let assemblyFormat = [{
|
||||
($sym_visibility^)?
|
||||
(`` $global_visibility^)?
|
||||
(`constant` $constant^)?
|
||||
$linkage
|
||||
(`comdat` $comdat^)?
|
||||
(`dso_local` $dso_local^)?
|
||||
@ -1745,7 +1726,6 @@ def CIR_GlobalOp : CIR_Op<"global", [
|
||||
let builders = [OpBuilder<(ins
|
||||
"llvm::StringRef":$sym_name,
|
||||
"mlir::Type":$sym_type,
|
||||
CArg<"bool", "false">:$isConstant,
|
||||
// CIR defaults to external linkage.
|
||||
CArg<"cir::GlobalLinkageKind",
|
||||
"cir::GlobalLinkageKind::ExternalLinkage">:$linkage)>];
|
||||
@ -1858,54 +1838,6 @@ def CIR_VTableGetVPtrOp : CIR_Op<"vtable.get_vptr", [Pure]> {
|
||||
}];
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// VTableGetVirtualFnAddrOp
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
def CIR_VTableGetVirtualFnAddrOp : CIR_Op<"vtable.get_virtual_fn_addr", [
|
||||
Pure
|
||||
]> {
|
||||
let summary = "Get a the address of a virtual function pointer";
|
||||
let description = [{
|
||||
The `vtable.get_virtual_fn_addr` operation retrieves the address of a
|
||||
virtual function pointer from an object's vtable (__vptr).
|
||||
This is an abstraction to perform the basic pointer arithmetic to get
|
||||
the address of the virtual function pointer, which can then be loaded and
|
||||
called.
|
||||
|
||||
The `vptr` operand must be a `!cir.ptr<!cir.vptr>` value, which would
|
||||
have been returned by a previous call to `cir.vatble.get_vptr`. The
|
||||
`index` operand is an index of the virtual function in the vtable.
|
||||
|
||||
The return type is a pointer-to-pointer to the function type.
|
||||
|
||||
Example:
|
||||
```mlir
|
||||
%2 = cir.load %0 : !cir.ptr<!cir.ptr<!rec_C>>, !cir.ptr<!rec_C>
|
||||
%3 = cir.vtable.get_vptr %2 : !cir.ptr<!rec_C> -> !cir.ptr<!cir.vptr>
|
||||
%4 = cir.load %3 : !cir.ptr<!cir.vptr>, !cir.vptr
|
||||
%5 = cir.vtable.get_virtual_fn_addr %4[2] : !cir.vptr
|
||||
-> !cir.ptr<!cir.ptr<!cir.func<(!cir.ptr<!rec_C>) -> !s32i>>>
|
||||
%6 = cir.load align(8) %5 : !cir.ptr<!cir.ptr<!cir.func<(!cir.ptr<!rec_C>)
|
||||
-> !s32i>>>,
|
||||
!cir.ptr<!cir.func<(!cir.ptr<!rec_C>) -> !s32i>>
|
||||
%7 = cir.call %6(%2) : (!cir.ptr<!cir.func<(!cir.ptr<!rec_C>) -> !s32i>>,
|
||||
!cir.ptr<!rec_C>) -> !s32i
|
||||
```
|
||||
}];
|
||||
|
||||
let arguments = (ins
|
||||
Arg<CIR_VPtrType, "vptr", [MemRead]>:$vptr,
|
||||
I64Attr:$index);
|
||||
|
||||
let results = (outs CIR_PointerType:$result);
|
||||
|
||||
let assemblyFormat = [{
|
||||
$vptr `[` $index `]` attr-dict
|
||||
`:` qualified(type($vptr)) `->` qualified(type($result))
|
||||
}];
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// SetBitfieldOp
|
||||
//===----------------------------------------------------------------------===//
|
||||
@ -3634,127 +3566,4 @@ def CIR_FAbsOp : CIR_UnaryFPToFPBuiltinOp<"fabs", "FAbsOp"> {
|
||||
}];
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Variadic Operations
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
def CIR_VAStartOp : CIR_Op<"va_start"> {
|
||||
let summary = "Starts a variable argument list";
|
||||
let description = [{
|
||||
The cir.va_start operation models the C/C++ va_start macro by
|
||||
initializing a variable argument list at the given va_list storage
|
||||
location.
|
||||
|
||||
The first operand must be a pointer to the target's `va_list`
|
||||
representation. This operation has no results and produces its effect by
|
||||
mutating the storage referenced by the pointer operand. The second operand
|
||||
must be an integer value that contains the expected number of arguments in
|
||||
that list.
|
||||
|
||||
Each `cir.va_start` must be paired with a corresponding `cir.va_end`
|
||||
on the same logical `va_list` object along all control-flow paths. After
|
||||
`cir.va_end`, the `va_list` must not be accessed unless reinitialized
|
||||
with another `cir.va_start`.
|
||||
|
||||
Lowering maps this to the LLVM intrinsic `llvm.va_start`, passing the
|
||||
appropriately decayed pointer to the underlying `va_list` storage.
|
||||
|
||||
Example:
|
||||
|
||||
```mlir
|
||||
// %args : !cir.ptr<!cir.array<!rec___va_list_tag x 1>>
|
||||
%p = cir.cast(array_to_ptrdecay, %args
|
||||
: !cir.ptr<!cir.array<!rec___va_list_tag x 1>>),
|
||||
!cir.ptr<!rec___va_list_tag>
|
||||
%count = cir.load %0 : !cir.ptr<!s32i>, !s32i
|
||||
cir.va_start %p %count : !cir.ptr<!rec___va_list_tag>, !s32i
|
||||
```
|
||||
}];
|
||||
let arguments = (ins
|
||||
CIR_PointerType:$arg_list,
|
||||
CIR_AnyFundamentalIntType:$count
|
||||
);
|
||||
|
||||
let assemblyFormat = [{
|
||||
$arg_list $count attr-dict `:` type(operands)
|
||||
}];
|
||||
}
|
||||
|
||||
def CIR_VAEndOp : CIR_Op<"va_end"> {
|
||||
let summary = "Ends a variable argument list";
|
||||
let description = [{
|
||||
The `cir.va_end` operation models the C/C++ va_end macro by finalizing
|
||||
and cleaning up a variable argument list previously initialized with
|
||||
`cir.va_start`.
|
||||
|
||||
The operand must be a pointer to the target's `va_list` representation.
|
||||
This operation has no results and produces its effect by mutating the
|
||||
storage referenced by the pointer operand.
|
||||
|
||||
`cir.va_end` must only be called after a matching `cir.va_start` on the
|
||||
same `va_list` along all control-flow paths. After `cir.va_end`, the
|
||||
`va_list` is invalid and must not be accessed unless reinitialized.
|
||||
|
||||
Lowering typically maps this to the LLVM intrinsic `llvm.va_end`,
|
||||
passing the appropriately decayed pointer to the underlying `va_list`
|
||||
storage.
|
||||
|
||||
Example:
|
||||
```mlir
|
||||
// %args : !cir.ptr<!cir.array<!rec___va_list_tag x 1>>
|
||||
%p = cir.cast(array_to_ptrdecay, %args
|
||||
: !cir.ptr<!cir.array<!rec___va_list_tag x 1>>),
|
||||
!cir.ptr<!rec___va_list_tag>
|
||||
cir.va_end %p : !cir.ptr<!rec___va_list_tag>
|
||||
```
|
||||
}];
|
||||
|
||||
let arguments = (ins CIR_PointerType:$arg_list);
|
||||
|
||||
let assemblyFormat = [{
|
||||
$arg_list attr-dict `:` type(operands)
|
||||
}];
|
||||
}
|
||||
|
||||
def CIR_VAArgOp : CIR_Op<"va_arg"> {
|
||||
let summary = "Fetches next variadic element as a given type";
|
||||
let description = [{
|
||||
The `cir.va_arg` operation models the C/C++ `va_arg` macro by reading the
|
||||
next argument from an active variable argument list and producing it as a
|
||||
value of a specified result type.
|
||||
|
||||
The operand must be a pointer to the target's `va_list` representation.
|
||||
The operation advances the `va_list` state as a side effect and returns
|
||||
the fetched value as the result, whose type is chosen by the user of the
|
||||
operation.
|
||||
|
||||
A `cir.va_arg` must only be used on a `va_list` that has been initialized
|
||||
with `cir.va.start` and not yet finalized by `cir.va.end`. The semantics
|
||||
(including alignment and promotion rules) follow the platform ABI; the
|
||||
frontend is responsible for providing a `va_list` pointer that matches the
|
||||
target representation.
|
||||
|
||||
Example:
|
||||
```mlir
|
||||
// %args : !cir.ptr<!cir.array<!rec___va_list_tag x 1>>
|
||||
%p = cir.cast(array_to_ptrdecay, %args
|
||||
: !cir.ptr<!cir.array<!rec___va_list_tag x 1>>),
|
||||
!cir.ptr<!rec___va_list_tag>
|
||||
cir.va.start %p : !cir.ptr<!rec___va_list_tag>
|
||||
|
||||
// Fetch an `int` from the vararg list.
|
||||
%v = cir.va_arg %p : (!cir.ptr<!rec___va_list_tag>) -> !s32i
|
||||
|
||||
cir.va.end %p : !cir.ptr<!rec___va_list_tag>
|
||||
```
|
||||
}];
|
||||
|
||||
let arguments = (ins CIR_PointerType:$arg_list);
|
||||
let results = (outs CIR_AnyType:$result);
|
||||
|
||||
let assemblyFormat = [{
|
||||
$arg_list attr-dict `:` functional-type(operands, $result)
|
||||
}];
|
||||
}
|
||||
|
||||
#endif // CLANG_CIR_DIALECT_IR_CIROPS_TD
|
||||
|
@ -113,18 +113,6 @@ LLVM_ATTRIBUTE_UNUSED static bool isValidLinkage(GlobalLinkageKind gl) {
|
||||
isLinkOnceLinkage(gl);
|
||||
}
|
||||
|
||||
bool operator<(cir::MemOrder, cir::MemOrder) = delete;
|
||||
bool operator>(cir::MemOrder, cir::MemOrder) = delete;
|
||||
bool operator<=(cir::MemOrder, cir::MemOrder) = delete;
|
||||
bool operator>=(cir::MemOrder, cir::MemOrder) = delete;
|
||||
|
||||
// Validate an integral value which isn't known to fit within the enum's range
|
||||
// is a valid AtomicOrderingCABI.
|
||||
template <typename Int> inline bool isValidCIRAtomicOrderingCABI(Int value) {
|
||||
return static_cast<Int>(cir::MemOrder::Relaxed) <= value &&
|
||||
value <= static_cast<Int>(cir::MemOrder::SequentiallyConsistent);
|
||||
}
|
||||
|
||||
} // namespace cir
|
||||
|
||||
#endif // CLANG_CIR_DIALECT_IR_CIROPSENUMS_H
|
||||
|
@ -26,7 +26,6 @@ std::unique_ptr<Pass> createCIRSimplifyPass();
|
||||
std::unique_ptr<Pass> createHoistAllocasPass();
|
||||
std::unique_ptr<Pass> createLoweringPreparePass();
|
||||
std::unique_ptr<Pass> createLoweringPreparePass(clang::ASTContext *astCtx);
|
||||
std::unique_ptr<Pass> createGotoSolverPass();
|
||||
|
||||
void populateCIRPreLoweringPasses(mlir::OpPassManager &pm);
|
||||
|
||||
|
@ -72,16 +72,6 @@ def CIRFlattenCFG : Pass<"cir-flatten-cfg"> {
|
||||
let dependentDialects = ["cir::CIRDialect"];
|
||||
}
|
||||
|
||||
def GotoSolver : Pass<"cir-goto-solver"> {
|
||||
let summary = "Replaces goto operations with branches";
|
||||
let description = [{
|
||||
This pass transforms CIR and replaces goto-s with branch
|
||||
operations to the proper blocks.
|
||||
}];
|
||||
let constructor = "mlir::createGotoSolverPass()";
|
||||
let dependentDialects = ["cir::CIRDialect"];
|
||||
}
|
||||
|
||||
def LoweringPrepare : Pass<"cir-lowering-prepare"> {
|
||||
let summary = "Lower to more fine-grained CIR operations before lowering to "
|
||||
"other dialects";
|
||||
|
@ -49,6 +49,7 @@ struct MissingFeatures {
|
||||
static bool opLoadEmitScalarRangeCheck() { return false; }
|
||||
static bool opLoadBooleanRepresentation() { return false; }
|
||||
static bool opLoadStoreTbaa() { return false; }
|
||||
static bool opLoadStoreMemOrder() { return false; }
|
||||
static bool opLoadStoreVolatile() { return false; }
|
||||
static bool opLoadStoreAtomic() { return false; }
|
||||
static bool opLoadStoreObjC() { return false; }
|
||||
@ -94,6 +95,7 @@ struct MissingFeatures {
|
||||
static bool opCallArgEvaluationOrder() { return false; }
|
||||
static bool opCallCallConv() { return false; }
|
||||
static bool opCallMustTail() { return false; }
|
||||
static bool opCallVirtual() { return false; }
|
||||
static bool opCallInAlloca() { return false; }
|
||||
static bool opCallAttrs() { return false; }
|
||||
static bool opCallSurroundingTry() { return false; }
|
||||
@ -162,8 +164,6 @@ struct MissingFeatures {
|
||||
static bool atomicInfoGetAtomicPointer() { return false; }
|
||||
static bool atomicInfoGetAtomicAddress() { return false; }
|
||||
static bool atomicUseLibCall() { return false; }
|
||||
static bool atomicScope() { return false; }
|
||||
static bool atomicSyncScopeID() { return false; }
|
||||
|
||||
// Misc
|
||||
static bool abiArgInfo() { return false; }
|
||||
@ -176,12 +176,7 @@ struct MissingFeatures {
|
||||
static bool aggValueSlotVolatile() { return false; }
|
||||
static bool alignCXXRecordDecl() { return false; }
|
||||
static bool armComputeVolatileBitfields() { return false; }
|
||||
static bool asmGoto() { return false; }
|
||||
static bool asmInputOperands() { return false; }
|
||||
static bool asmLabelAttr() { return false; }
|
||||
static bool asmMemoryEffects() { return false; }
|
||||
static bool asmOutputOperands() { return false; }
|
||||
static bool asmUnwindClobber() { return false; }
|
||||
static bool assignMemcpyizer() { return false; }
|
||||
static bool astVarDeclInterface() { return false; }
|
||||
static bool attributeBuiltin() { return false; }
|
||||
@ -209,7 +204,6 @@ struct MissingFeatures {
|
||||
static bool dataLayoutTypeAllocSize() { return false; }
|
||||
static bool dataLayoutTypeStoreSize() { return false; }
|
||||
static bool deferredCXXGlobalInit() { return false; }
|
||||
static bool devirtualizeMemberFunction() { return false; }
|
||||
static bool ehCleanupFlags() { return false; }
|
||||
static bool ehCleanupScope() { return false; }
|
||||
static bool ehCleanupScopeRequiresEHCleanup() { return false; }
|
||||
@ -221,7 +215,6 @@ struct MissingFeatures {
|
||||
static bool emitLValueAlignmentAssumption() { return false; }
|
||||
static bool emitNullabilityCheck() { return false; }
|
||||
static bool emitTypeCheck() { return false; }
|
||||
static bool emitTypeMetadataCodeForVCall() { return false; }
|
||||
static bool fastMathFlags() { return false; }
|
||||
static bool fpConstraints() { return false; }
|
||||
static bool generateDebugInfo() { return false; }
|
||||
@ -264,7 +257,6 @@ struct MissingFeatures {
|
||||
static bool setNonGC() { return false; }
|
||||
static bool setObjCGCLValueClass() { return false; }
|
||||
static bool setTargetAttributes() { return false; }
|
||||
static bool sourceLanguageCases() { return false; }
|
||||
static bool stackBase() { return false; }
|
||||
static bool stackSaveOp() { return false; }
|
||||
static bool targetCIRGenInfoArch() { return false; }
|
||||
@ -281,7 +273,6 @@ struct MissingFeatures {
|
||||
static bool vtableInitialization() { return false; }
|
||||
static bool vtableRelativeLayout() { return false; }
|
||||
static bool msvcBuiltins() { return false; }
|
||||
static bool vaArgABILowering() { return false; }
|
||||
static bool vlas() { return false; }
|
||||
|
||||
// Missing types
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user