diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs index 5b70b18d9dad9a633016906a5ddfd9f7504a93e4..0e24d6132b4cb032d66f505941f1057bf47b527f 100644 --- a/.git-blame-ignore-revs +++ b/.git-blame-ignore-revs @@ -64,3 +64,6 @@ f6d557ee34b6bbdb1dc32f29e34b4a4a8ad35e81 # [libc++][NFC] clang-format 2d7eb9c9ea1a146412a83603d5c0c6339a5d8284 + +# [libc++] Rename _LIBCPP_INLINE_VISIBILITY to _LIBCPP_HIDE_FROM_ABI +4c198542226223f6a5c5511a1f89b37d15ee10b9 diff --git a/.github/workflows/libcxx-build-and-test.yaml b/.github/workflows/libcxx-build-and-test.yaml index dccfe54d46467670759bd53b3376979bd6dbb34b..83786052a53910668410cd2087d80a5267193d2b 100644 --- a/.github/workflows/libcxx-build-and-test.yaml +++ b/.github/workflows/libcxx-build-and-test.yaml @@ -22,6 +22,12 @@ on: - 'runtimes/**' - 'cmake/**' - '.github/workflows/libcxx-build-and-test.yaml' + schedule: + # Run nightly at 8 AM UTC (or roughly 3 AM eastern) + - cron: '0 3 * * *' + +permissions: + contents: read # Default everything to read-only concurrency: group: ${{ github.workflow }}-${{ github.event.pull_request.number }} @@ -44,8 +50,7 @@ env: jobs: stage1: if: github.repository_owner == 'llvm' - runs-on: - group: libcxx-runners-8 + runs-on: libcxx-runners-8-set continue-on-error: false strategy: fail-fast: true @@ -83,8 +88,7 @@ jobs: **/crash_diagnostics/* stage2: if: github.repository_owner == 'llvm' - runs-on: - group: libcxx-runners-8 + runs-on: libcxx-runners-8-set needs: [ stage1 ] continue-on-error: false strategy: @@ -157,29 +161,32 @@ jobs: 'generic-no-unicode', 'generic-no-wide-characters', 'generic-static', - 'generic-with_llvm_unwinder' + 'generic-with_llvm_unwinder', + # TODO Find a better place for the benchmark and bootstrapping builds to live. They're either very expensive + # or don't provide much value since the benchmark run results are too noise on the bots. + 'benchmarks', + 'bootstrapping-build' ] - machine: [ 'libcxx-runners-8' ] + machine: [ 'libcxx-runners-8-set' ] std_modules: [ 'OFF' ] include: - config: 'generic-cxx26' - machine: libcxx-runners-8 + machine: libcxx-runners-8-set std_modules: 'ON' - config: 'generic-asan' - machine: libcxx-runners-8 + machine: libcxx-runners-8-set std_modules: 'OFF' - config: 'generic-tsan' - machine: libcxx-runners-8 + machine: libcxx-runners-8-set std_modules: 'OFF' - config: 'generic-ubsan' - machine: libcxx-runners-8 + machine: libcxx-runners-8-set std_modules: 'OFF' # Use a larger machine for MSAN to avoid timeout and memory allocation issues. - config: 'generic-msan' - machine: libcxx-runners-32 + machine: libcxx-runners-32-set std_modules: 'OFF' - runs-on: - group: ${{ matrix.machine }} + runs-on: ${{ matrix.machine }} steps: - uses: actions/checkout@v4 - name: ${{ matrix.config }} diff --git a/.github/workflows/new-prs.yml b/.github/workflows/new-prs.yml index 9ba55d59ff15b4c754a8e45610c2d79e92bea2e2..18caa408df57b605e73a951c51660ca16cfc8631 100644 --- a/.github/workflows/new-prs.yml +++ b/.github/workflows/new-prs.yml @@ -15,16 +15,43 @@ on: - synchronize jobs: - automate-prs-labels: + greeter: + runs-on: ubuntu-latest permissions: pull-requests: write + # Only comment on PRs that have been opened for the first time, by someone + # new to LLVM or to GitHub as a whole. + if: >- + (github.repository == 'llvm/llvm-project') && + (github.event.action == 'opened') && + (github.event.pull_request.author_association == 'FIRST_TIME_CONTRIBUTOR' || + github.event.pull_request.author_association == 'FIRST_TIMER') + steps: + - name: Setup Automation Script + run: | + curl -O -L --fail https://raw.githubusercontent.com/"$GITHUB_REPOSITORY"/main/llvm/utils/git/github-automation.py + curl -O -L --fail https://raw.githubusercontent.com/"$GITHUB_REPOSITORY"/main/llvm/utils/git/requirements.txt + chmod a+x github-automation.py + pip install -r requirements.txt + + - name: Greet Author + run: | + ./github-automation.py \ + --token '${{ secrets.GITHUB_TOKEN }}' \ + pr-greeter \ + --issue-number "${{ github.event.pull_request.number }}" + + automate-prs-labels: + # Greet first so that only the author gets that notification. + needs: greeter runs-on: ubuntu-latest # Ignore PRs with more than 10 commits. Pull requests with a lot of # commits tend to be accidents usually when someone made a mistake while trying # to rebase. We want to ignore these pull requests to avoid excessive # notifications. + # always() means that even if greeter is skipped, this job will run. if: > - github.repository == 'llvm/llvm-project' && + always() && github.repository == 'llvm/llvm-project' && github.event.pull_request.draft == false && github.event.pull_request.commits < 10 steps: diff --git a/.github/workflows/sync-release-repo.yml b/.github/workflows/sync-release-repo.yml deleted file mode 100644 index c328a9133e179de52f7c362fcc97bc89a00610f1..0000000000000000000000000000000000000000 --- a/.github/workflows/sync-release-repo.yml +++ /dev/null @@ -1,17 +0,0 @@ -name: Sync Release Repositories -permissions: - contents: write -on: - workflow_dispatch -env: - RELEASE_BRANCH: release/17.x - GH_TOKEN: ${{ secrets.RELEASE_WORKFLOW_PUSH_SECRET }} -jobs: - sync: - runs-on: ubuntu-latest - steps: - - name: Fetch LLVM sources - uses: actions/checkout@v4 - - name: Run Sync Script - run: | - llvm/utils/git/sync-release-repo.sh diff --git a/.mailmap b/.mailmap index 867af2c55f3bc4e9f2e217b07ba04e76c7180800..f4d7ffe3013818c430331c665a77093718bcb8d0 100644 --- a/.mailmap +++ b/.mailmap @@ -24,6 +24,7 @@ + @@ -42,3 +43,6 @@ Med Ismail Bennani Ramkumar Ramachandra Saleem Abdulrasool Tommy Chiang +Wang Pengcheng +Wang Pengcheng <137158460+wangpc-pp@users.noreply.github.com> +Wang Pengcheng diff --git a/bolt/include/bolt/Core/BinaryContext.h b/bolt/include/bolt/Core/BinaryContext.h index 312678f475347a4f0b936415c9800e5b74fd50f8..c7672285b06c09f8f99123e34e8e35dd8793c21a 100644 --- a/bolt/include/bolt/Core/BinaryContext.h +++ b/bolt/include/bolt/Core/BinaryContext.h @@ -611,6 +611,14 @@ public: /// Indicates if the binary contains split functions. bool HasSplitFunctions{false}; + /// Indicates if the function ordering of the binary is finalized. + bool HasFinalizedFunctionOrder{false}; + + /// Indicates if a separate .text.warm section is needed that contains + /// function fragments with + /// FunctionFragment::getFragmentNum() == FragmentNum::warm() + bool HasWarmSection{false}; + /// Is the binary always loaded at a fixed address. Shared objects and /// position-independent executables (PIEs) are examples of binaries that /// will have HasFixedLoadAddress set to false. @@ -927,6 +935,8 @@ public: const char *getMainCodeSectionName() const { return ".text"; } + const char *getWarmCodeSectionName() const { return ".text.warm"; } + const char *getColdCodeSectionName() const { return ".text.cold"; } const char *getHotTextMoverSectionName() const { return ".text.mover"; } diff --git a/bolt/include/bolt/Core/BinaryFunction.h b/bolt/include/bolt/Core/BinaryFunction.h index 72c360ca0c2db66b98c37fd278ed2c058757082d..182d5ff049c3762d08f95c8c62d69f4ec5ba7784 100644 --- a/bolt/include/bolt/Core/BinaryFunction.h +++ b/bolt/include/bolt/Core/BinaryFunction.h @@ -1236,6 +1236,8 @@ public: return SmallString<32>(CodeSectionName); if (Fragment == FragmentNum::cold()) return SmallString<32>(ColdCodeSectionName); + if (BC.HasWarmSection && Fragment == FragmentNum::warm()) + return SmallString<32>(BC.getWarmCodeSectionName()); return formatv("{0}.{1}", ColdCodeSectionName, Fragment.get() - 1); } diff --git a/bolt/include/bolt/Core/DIEBuilder.h b/bolt/include/bolt/Core/DIEBuilder.h index fb86e59468b835abf4c124b8d4dd9bb292c01d2e..1c5252142d4ebf52c205d50443ca02eb8e356ff6 100644 --- a/bolt/include/bolt/Core/DIEBuilder.h +++ b/bolt/include/bolt/Core/DIEBuilder.h @@ -273,7 +273,8 @@ public: void buildCompileUnits(const std::vector &CUs); /// Preventing implicit conversions. template void buildCompileUnits(T) = delete; - void buildBoth(); + /// Builds DWO Unit. For DWARF5 this includes the type units. + void buildDWOUnit(DWARFUnit &U); /// Returns DWARFUnitInfo for DWARFUnit DWARFUnitInfo &getUnitInfoByDwarfUnit(const DWARFUnit &DwarfUnit) { diff --git a/bolt/include/bolt/Core/FunctionLayout.h b/bolt/include/bolt/Core/FunctionLayout.h index 904da3a4a93aade21fccd4d2b9262d46ce5ed429..2e4c184ba4511ca6836ca9d031014c1f784385f0 100644 --- a/bolt/include/bolt/Core/FunctionLayout.h +++ b/bolt/include/bolt/Core/FunctionLayout.h @@ -63,6 +63,7 @@ public: static constexpr FragmentNum main() { return FragmentNum(0); } static constexpr FragmentNum cold() { return FragmentNum(1); } + static constexpr FragmentNum warm() { return FragmentNum(2); } }; /// A freestanding subset of contiguous blocks of a function. diff --git a/bolt/include/bolt/Passes/SplitFunctions.h b/bolt/include/bolt/Passes/SplitFunctions.h index 4058f3317dfbdbbd74f3f48040ca0b745f5984ca..28e9e79d1b8f87c9442163d12903315a84a6e892 100644 --- a/bolt/include/bolt/Passes/SplitFunctions.h +++ b/bolt/include/bolt/Passes/SplitFunctions.h @@ -23,6 +23,9 @@ enum SplitFunctionsStrategy : char { /// Split each function into a hot and cold fragment using profiling /// information. Profile2 = 0, + /// Split each function into a hot, warm, and cold fragment using + /// profiling information. + CDSplit, /// Split each function into a hot and cold fragment at a randomly chosen /// split point (ignoring any available profiling information). Random2, @@ -40,7 +43,7 @@ public: virtual ~SplitStrategy() = default; virtual bool canSplit(const BinaryFunction &BF) = 0; - virtual bool keepEmpty() = 0; + virtual bool compactFragments() = 0; virtual void fragment(const BlockIt Start, const BlockIt End) = 0; }; diff --git a/bolt/lib/Core/BinaryEmitter.cpp b/bolt/lib/Core/BinaryEmitter.cpp index fb1bf530c1974aa2069487c4d0d34b87548b3fb2..3bff3125a57a8600f13540c54be3b360937c3601 100644 --- a/bolt/lib/Core/BinaryEmitter.cpp +++ b/bolt/lib/Core/BinaryEmitter.cpp @@ -287,7 +287,10 @@ void BinaryEmitter::emitFunctions() { // Mark the end of hot text. if (opts::HotText) { - Streamer.switchSection(BC.getTextSection()); + if (BC.HasWarmSection) + Streamer.switchSection(BC.getCodeSection(BC.getWarmCodeSectionName())); + else + Streamer.switchSection(BC.getTextSection()); Streamer.emitLabel(BC.getHotTextEndSymbol()); } } diff --git a/bolt/lib/Core/BinaryFunction.cpp b/bolt/lib/Core/BinaryFunction.cpp index 3b0a2314dc1d25249c7e3d4e2b05e8270a608116..be033cf07668801fffc5ed6e8c7e609ba3824ec5 100644 --- a/bolt/lib/Core/BinaryFunction.cpp +++ b/bolt/lib/Core/BinaryFunction.cpp @@ -3296,7 +3296,7 @@ void BinaryFunction::fixBranches() { BB->eraseInstruction(BB->findInstruction(UncondBranch)); // Basic block that follows the current one in the final layout. - const BinaryBasicBlock *NextBB = + const BinaryBasicBlock *const NextBB = Layout.getBasicBlockAfter(BB, /*IgnoreSplits=*/false); if (BB->succ_size() == 1) { @@ -3313,39 +3313,54 @@ void BinaryFunction::fixBranches() { assert(CondBranch && "conditional branch expected"); const BinaryBasicBlock *TSuccessor = BB->getConditionalSuccessor(true); const BinaryBasicBlock *FSuccessor = BB->getConditionalSuccessor(false); - // Check whether we support reversing this branch direction - const bool IsSupported = !MIB->isUnsupportedBranch(*CondBranch); - if (NextBB && NextBB == TSuccessor && IsSupported) { + + // Eliminate unnecessary conditional branch. + if (TSuccessor == FSuccessor) { + BB->removeDuplicateConditionalSuccessor(CondBranch); + if (TSuccessor != NextBB) + BB->addBranchInstruction(TSuccessor); + continue; + } + + // Reverse branch condition and swap successors. + auto swapSuccessors = [&]() { + if (MIB->isUnsupportedBranch(*CondBranch)) + return false; std::swap(TSuccessor, FSuccessor); - { - auto L = BC.scopeLock(); - MIB->reverseBranchCondition(*CondBranch, TSuccessor->getLabel(), Ctx); - } BB->swapConditionalSuccessors(); - } else { + auto L = BC.scopeLock(); + MIB->reverseBranchCondition(*CondBranch, TSuccessor->getLabel(), Ctx); + return true; + }; + + // Check whether the next block is a "taken" target and try to swap it + // with a "fall-through" target. + if (TSuccessor == NextBB && swapSuccessors()) + continue; + + // Update conditional branch destination if needed. + if (MIB->getTargetSymbol(*CondBranch) != TSuccessor->getLabel()) { auto L = BC.scopeLock(); MIB->replaceBranchTarget(*CondBranch, TSuccessor->getLabel(), Ctx); } - if (TSuccessor == FSuccessor) - BB->removeDuplicateConditionalSuccessor(CondBranch); - if (!NextBB || - ((NextBB != TSuccessor || !IsSupported) && NextBB != FSuccessor)) { - // If one of the branches is guaranteed to be "long" while the other - // could be "short", then prioritize short for "taken". This will - // generate a sequence 1 byte shorter on x86. - if (IsSupported && BC.isX86() && - TSuccessor->getFragmentNum() != FSuccessor->getFragmentNum() && - BB->getFragmentNum() != TSuccessor->getFragmentNum()) { - std::swap(TSuccessor, FSuccessor); - { - auto L = BC.scopeLock(); - MIB->reverseBranchCondition(*CondBranch, TSuccessor->getLabel(), - Ctx); - } - BB->swapConditionalSuccessors(); - } - BB->addBranchInstruction(FSuccessor); + + // No need for the unconditional branch. + if (FSuccessor == NextBB) + continue; + + if (BC.isX86()) { + // We are going to generate two branches. Check if their targets are in + // the same fragment as this block. If only one target is in the same + // fragment, make it the destination of the conditional branch. There + // is a chance it will be a short branch which takes 4 bytes fewer than + // a long conditional branch. For unconditional branch, the difference + // is 3 bytes. + if (BB->getFragmentNum() != TSuccessor->getFragmentNum() && + BB->getFragmentNum() == FSuccessor->getFragmentNum()) + swapSuccessors(); } + + BB->addBranchInstruction(FSuccessor); } // Cases where the number of successors is 0 (block ends with a // terminator) or more than 2 (switch table) don't require branch diff --git a/bolt/lib/Core/DIEBuilder.cpp b/bolt/lib/Core/DIEBuilder.cpp index 223ae714440d97da660f7561dc98a3646fffad66..b809b2935ee9ebf04c0e631f96f14807febdefa2 100644 --- a/bolt/lib/Core/DIEBuilder.cpp +++ b/bolt/lib/Core/DIEBuilder.cpp @@ -193,12 +193,6 @@ void DIEBuilder::buildTypeUnits(const bool Init) { if (Init) BuilderState.reset(new State()); - unsigned int CUNum = getCUNum(DwarfContext, IsDWO); - getState().CloneUnitCtxMap.resize(CUNum); - DWARFContext::unit_iterator_range CU4TURanges = - IsDWO ? DwarfContext->dwo_types_section_units() - : DwarfContext->types_section_units(); - const DWARFUnitIndex &TUIndex = DwarfContext->getTUIndex(); if (!TUIndex.getRows().empty()) { for (auto &Row : TUIndex.getRows()) { @@ -208,6 +202,11 @@ void DIEBuilder::buildTypeUnits(const bool Init) { true); } } + const unsigned int CUNum = getCUNum(DwarfContext, IsDWO); + getState().CloneUnitCtxMap.resize(CUNum); + DWARFContext::unit_iterator_range CU4TURanges = + IsDWO ? DwarfContext->dwo_types_section_units() + : DwarfContext->types_section_units(); getState().Type = ProcessingType::DWARF4TUs; for (std::unique_ptr &DU : CU4TURanges) @@ -278,11 +277,13 @@ void DIEBuilder::buildCompileUnits(const std::vector &CUs) { constructFromUnit(*DU); } -void DIEBuilder::buildBoth() { +void DIEBuilder::buildDWOUnit(DWARFUnit &U) { BuilderState.release(); BuilderState = std::make_unique(); buildTypeUnits(false); - buildCompileUnits(false); + getState().Type = ProcessingType::CUs; + registerUnit(U, false); + constructFromUnit(U); } DIE *DIEBuilder::constructDIEFast(DWARFDie &DDie, DWARFUnit &U, diff --git a/bolt/lib/Core/FunctionLayout.cpp b/bolt/lib/Core/FunctionLayout.cpp index 37b07bd28f26f6565d2df173f73cb869dce423ee..27e40de94be660f0ffbe9b5f6ddacb5b0393b0ac 100644 --- a/bolt/lib/Core/FunctionLayout.cpp +++ b/bolt/lib/Core/FunctionLayout.cpp @@ -188,10 +188,6 @@ bool FunctionLayout::update(const ArrayRef NewLayout) { for (BinaryBasicBlock *const BB : NewLayout) { FragmentNum Num = BB->getFragmentNum(); - assert(Num >= Fragments.back()->getFragmentNum() && - "Blocks must be arranged such that fragments are monotonically " - "increasing."); - // Add empty fragments if necessary while (Fragments.back()->getFragmentNum() < Num) addFragment(); diff --git a/bolt/lib/Passes/ReorderFunctions.cpp b/bolt/lib/Passes/ReorderFunctions.cpp index 92c50f49d20cf7a454547072751e2ae7d29d13bb..7e672969098130da7d62b95fff1abd11d4b83afd 100644 --- a/bolt/lib/Passes/ReorderFunctions.cpp +++ b/bolt/lib/Passes/ReorderFunctions.cpp @@ -427,6 +427,8 @@ void ReorderFunctions::runOnFunctions(BinaryContext &BC) { reorder(std::move(Clusters), BFs); + BC.HasFinalizedFunctionOrder = true; + std::unique_ptr FuncsFile; if (!opts::GenerateFunctionOrderFile.empty()) { FuncsFile = std::make_unique(opts::GenerateFunctionOrderFile, diff --git a/bolt/lib/Passes/SplitFunctions.cpp b/bolt/lib/Passes/SplitFunctions.cpp index 34973cecdf49161658bb46409ec1e645ccf52db2..d9f5da3e3bcab568b28f34b12143f14971d67033 100644 --- a/bolt/lib/Passes/SplitFunctions.cpp +++ b/bolt/lib/Passes/SplitFunctions.cpp @@ -92,6 +92,9 @@ static cl::opt SplitStrategy( cl::values(clEnumValN(SplitFunctionsStrategy::Profile2, "profile2", "split each function into a hot and cold fragment " "using profiling information")), + cl::values(clEnumValN(SplitFunctionsStrategy::CDSplit, "cdsplit", + "split each function into a hot, warm, and cold " + "fragment using profiling information")), cl::values(clEnumValN( SplitFunctionsStrategy::Random2, "random2", "split each function into a hot and cold fragment at a randomly chosen " @@ -106,6 +109,21 @@ static cl::opt SplitStrategy( "fragment contains exactly a single basic block")), cl::desc("strategy used to partition blocks into fragments"), cl::cat(BoltOptCategory)); + +static cl::opt CallScale( + "call-scale", + cl::desc("Call score scale coefficient (when --split-strategy=cdsplit)"), + cl::init(0.95), cl::ReallyHidden, cl::cat(BoltOptCategory)); + +static cl::opt + CallPower("call-power", + cl::desc("Call score power (when --split-strategy=cdsplit)"), + cl::init(0.05), cl::ReallyHidden, cl::cat(BoltOptCategory)); + +static cl::opt + JumpPower("jump-power", + cl::desc("Jump score power (when --split-strategy=cdsplit)"), + cl::init(0.15), cl::ReallyHidden, cl::cat(BoltOptCategory)); } // namespace opts namespace { @@ -126,7 +144,7 @@ struct SplitProfile2 final : public SplitStrategy { return BF.hasValidProfile() && hasFullProfile(BF) && !allBlocksCold(BF); } - bool keepEmpty() override { return false; } + bool compactFragments() override { return true; } void fragment(const BlockIt Start, const BlockIt End) override { for (BinaryBasicBlock *const BB : llvm::make_range(Start, End)) { @@ -136,6 +154,430 @@ struct SplitProfile2 final : public SplitStrategy { } }; +struct SplitCacheDirected final : public SplitStrategy { + BinaryContext &BC; + using BasicBlockOrder = BinaryFunction::BasicBlockOrderType; + + bool canSplit(const BinaryFunction &BF) override { + return BF.hasValidProfile() && hasFullProfile(BF) && !allBlocksCold(BF); + } + + explicit SplitCacheDirected(BinaryContext &BC) : BC(BC) { + initializeAuxiliaryVariables(); + buildCallGraph(); + } + + // When some functions are hot-warm split and others are hot-warm-cold split, + // we do not want to change the fragment numbers of the blocks in the hot-warm + // split functions. + bool compactFragments() override { return false; } + + void fragment(const BlockIt Start, const BlockIt End) override { + BasicBlockOrder BlockOrder(Start, End); + BinaryFunction &BF = *BlockOrder.front()->getFunction(); + + size_t BestSplitIndex = findSplitIndex(BF, BlockOrder); + + // Assign fragments based on the computed best split index. + // All basic blocks with index up to the best split index become hot. + // All remaining blocks are warm / cold depending on if count is + // greater than zero or not. + for (size_t Index = 0; Index < BlockOrder.size(); Index++) { + BinaryBasicBlock *BB = BlockOrder[Index]; + if (Index <= BestSplitIndex) + BB->setFragmentNum(FragmentNum::main()); + else + BB->setFragmentNum(BB->getKnownExecutionCount() > 0 + ? FragmentNum::warm() + : FragmentNum::cold()); + } + } + +private: + struct CallInfo { + size_t Length; + size_t Count; + }; + + struct SplitScore { + size_t SplitIndex; + size_t HotSizeReduction = 0; + double LocalScore = 0; + double CoverCallScore = 0; + }; + + // Auxiliary variables used by the algorithm. + size_t TotalNumBlocks{0}; + size_t OrigHotSectionSize{0}; + DenseMap GlobalIndices; + DenseMap BBSizes; + DenseMap BBOffsets; + + // Call graph. + std::vector> Callers; + std::vector> Callees; + + bool shouldConsiderForCallGraph(const BinaryFunction &BF) { + // Only a subset of the functions in the binary will be considered + // for initializing auxiliary variables and building call graph. + return BF.hasValidIndex() && BF.hasValidProfile() && !BF.empty(); + } + + void initializeAuxiliaryVariables() { + for (BinaryFunction *BF : BC.getSortedFunctions()) { + if (!shouldConsiderForCallGraph(*BF)) + continue; + + // Calculate the size of each BB after hot-cold splitting. + // This populates BinaryBasicBlock::OutputAddressRange which + // can be used to compute the size of each BB. + BC.calculateEmittedSize(*BF, /*FixBranches=*/true); + + for (BinaryBasicBlock *BB : BF->getLayout().blocks()) { + // Unique global index. + GlobalIndices[BB] = TotalNumBlocks; + TotalNumBlocks++; + + // Block size after hot-cold splitting. + BBSizes[BB] = BB->getOutputSize(); + + // Hot block offset after hot-cold splitting. + BBOffsets[BB] = OrigHotSectionSize; + if (!BB->isSplit()) + OrigHotSectionSize += BBSizes[BB]; + } + } + } + + void buildCallGraph() { + Callers.resize(TotalNumBlocks); + Callees.resize(TotalNumBlocks); + for (const BinaryFunction *SrcFunction : BC.getSortedFunctions()) { + if (!shouldConsiderForCallGraph(*SrcFunction)) + continue; + + for (BinaryBasicBlock &SrcBB : SrcFunction->blocks()) { + // Skip blocks that are not executed + if (SrcBB.getKnownExecutionCount() == 0) + continue; + + // Find call instructions and extract target symbols from each one + for (const MCInst &Inst : SrcBB) { + if (!BC.MIB->isCall(Inst)) + continue; + + // Call info + const MCSymbol *DstSym = BC.MIB->getTargetSymbol(Inst); + // Ignore calls w/o information + if (!DstSym) + continue; + + const BinaryFunction *DstFunction = BC.getFunctionForSymbol(DstSym); + // Ignore calls that do not have a valid target, but do not ignore + // recursive calls, because caller block could be moved to warm. + if (!DstFunction || DstFunction->getLayout().block_empty()) + continue; + + const BinaryBasicBlock *DstBB = &(DstFunction->front()); + + // Record the call only if DstBB is also in functions to consider for + // call graph. + if (GlobalIndices.contains(DstBB)) { + Callers[GlobalIndices[DstBB]].push_back(&SrcBB); + Callees[GlobalIndices[&SrcBB]].push_back(DstBB); + } + } + } + } + } + + /// Populate BinaryBasicBlock::OutputAddressRange with estimated basic block + /// start and end addresses for hot and warm basic blocks, assuming hot-warm + /// splitting happens at \p SplitIndex. Also return estimated end addresses + /// of the hot fragment before and after splitting. + /// The estimations take into account the potential addition of branch + /// instructions due to split fall through branches as well as the need to + /// use longer branch instructions for split (un)conditional branches. + std::pair + estimatePostSplitBBAddress(const BasicBlockOrder &BlockOrder, + const size_t SplitIndex) { + assert(SplitIndex < BlockOrder.size() && "Invalid split index"); + + // Update function layout assuming hot-warm splitting at SplitIndex + for (size_t Index = 0; Index < BlockOrder.size(); Index++) { + BinaryBasicBlock *BB = BlockOrder[Index]; + if (BB->getFragmentNum() == FragmentNum::cold()) + break; + BB->setFragmentNum(Index <= SplitIndex ? FragmentNum::main() + : FragmentNum::warm()); + } + BinaryFunction *BF = BlockOrder[0]->getFunction(); + BF->getLayout().update(BlockOrder); + // Populate BB.OutputAddressRange under the updated layout. + BC.calculateEmittedSize(*BF); + + // Populate BB.OutputAddressRange with estimated new start and end addresses + // and compute the old end address of the hot section and the new end + // address of the hot section. + size_t OldHotEndAddr; + size_t NewHotEndAddr; + size_t CurrentAddr = BBOffsets[BlockOrder[0]]; + for (BinaryBasicBlock *BB : BlockOrder) { + // We only care about new addresses of blocks in hot/warm. + if (BB->getFragmentNum() == FragmentNum::cold()) + break; + const size_t NewSize = BB->getOutputSize(); + BB->setOutputStartAddress(CurrentAddr); + CurrentAddr += NewSize; + BB->setOutputEndAddress(CurrentAddr); + if (BB->getLayoutIndex() == SplitIndex) { + NewHotEndAddr = CurrentAddr; + // Approximate the start address of the warm fragment of the current + // function using the original hot section size. + CurrentAddr = OrigHotSectionSize; + } + OldHotEndAddr = BBOffsets[BB] + BBSizes[BB]; + } + return std::make_pair(OldHotEndAddr, NewHotEndAddr); + } + + /// Get a collection of "shortenable" calls, that is, calls of type X->Y + /// when the function order is [... X ... BF ... Y ...]. + /// If the hot fragment size of BF is reduced, then such calls are guaranteed + /// to get shorter by the reduced hot fragment size. + std::vector extractCoverCalls(const BinaryFunction &BF) { + // Record the length and the count of the calls that can be shortened + std::vector CoverCalls; + if (opts::CallScale == 0) + return CoverCalls; + + const BinaryFunction *ThisBF = &BF; + const BinaryBasicBlock *ThisBB = &(ThisBF->front()); + const size_t ThisGI = GlobalIndices[ThisBB]; + + for (const BinaryFunction *DstBF : BC.getSortedFunctions()) { + if (!shouldConsiderForCallGraph(*DstBF)) + continue; + + const BinaryBasicBlock *DstBB = &(DstBF->front()); + if (DstBB->getKnownExecutionCount() == 0) + continue; + + const size_t DstGI = GlobalIndices[DstBB]; + for (const BinaryBasicBlock *SrcBB : Callers[DstGI]) { + const BinaryFunction *SrcBF = SrcBB->getFunction(); + if (ThisBF == SrcBF) + continue; + + const size_t CallCount = SrcBB->getKnownExecutionCount(); + + const size_t SrcGI = GlobalIndices[SrcBB]; + + const bool IsCoverCall = (SrcGI < ThisGI && ThisGI < DstGI) || + (DstGI <= ThisGI && ThisGI < SrcGI); + if (!IsCoverCall) + continue; + + const size_t SrcBBEndAddr = BBOffsets[SrcBB] + BBSizes[SrcBB]; + const size_t DstBBStartAddr = BBOffsets[DstBB]; + const size_t CallLength = + AbsoluteDifference(SrcBBEndAddr, DstBBStartAddr); + const CallInfo CI{CallLength, CallCount}; + CoverCalls.emplace_back(CI); + } + } + return CoverCalls; + } + + /// Compute the edge score of a call edge. + double computeCallScore(uint64_t CallCount, size_t CallLength) { + // Increase call lengths by 1 to avoid raising 0 to a negative power. + return opts::CallScale * static_cast(CallCount) / + std::pow(static_cast(CallLength + 1), opts::CallPower); + } + + /// Compute the edge score of a jump (branch) edge. + double computeJumpScore(uint64_t JumpCount, size_t JumpLength) { + // Increase jump lengths by 1 to avoid raising 0 to a negative power. + return static_cast(JumpCount) / + std::pow(static_cast(JumpLength + 1), opts::JumpPower); + } + + /// Compute sum of scores over jumps within \p BlockOrder given \p SplitIndex. + /// Increament Score.LocalScore in place by the sum. + void computeJumpScore(const BasicBlockOrder &BlockOrder, + const size_t SplitIndex, SplitScore &Score) { + + for (const BinaryBasicBlock *SrcBB : BlockOrder) { + if (SrcBB->getKnownExecutionCount() == 0) + continue; + + const size_t SrcBBEndAddr = SrcBB->getOutputAddressRange().second; + + for (const auto Pair : zip(SrcBB->successors(), SrcBB->branch_info())) { + const BinaryBasicBlock *DstBB = std::get<0>(Pair); + const BinaryBasicBlock::BinaryBranchInfo &Branch = std::get<1>(Pair); + const size_t JumpCount = Branch.Count; + + if (JumpCount == 0) + continue; + + const size_t DstBBStartAddr = DstBB->getOutputAddressRange().first; + const size_t NewJumpLength = + AbsoluteDifference(SrcBBEndAddr, DstBBStartAddr); + Score.LocalScore += computeJumpScore(JumpCount, NewJumpLength); + } + } + } + + /// Compute sum of scores over calls originated in the current function + /// given \p SplitIndex. Increament Score.LocalScore in place by the sum. + void computeLocalCallScore(const BasicBlockOrder &BlockOrder, + const size_t SplitIndex, SplitScore &Score) { + if (opts::CallScale == 0) + return; + + // Global index of the last block in the current function. + // This is later used to determine whether a call originated in the current + // function is to a function that comes after the current function. + const size_t LastGlobalIndex = GlobalIndices[BlockOrder.back()]; + + // The length of calls originated in the input function can increase / + // decrease depending on the splitting decision. + for (const BinaryBasicBlock *SrcBB : BlockOrder) { + const size_t CallCount = SrcBB->getKnownExecutionCount(); + // If SrcBB does not call any functions, skip it. + if (CallCount == 0) + continue; + + // Obtain an estimate on the end address of the src basic block + // after splitting at SplitIndex. + const size_t SrcBBEndAddr = SrcBB->getOutputAddressRange().second; + + for (const BinaryBasicBlock *DstBB : Callees[GlobalIndices[SrcBB]]) { + // Obtain an estimate on the start address of the dst basic block + // after splitting at SplitIndex. If DstBB is in a function before + // the current function, then its start address remains unchanged. + size_t DstBBStartAddr = BBOffsets[DstBB]; + // If DstBB is in a function after the current function, then its + // start address should be adjusted based on the reduction in hot size. + if (GlobalIndices[DstBB] > LastGlobalIndex) { + assert(DstBBStartAddr >= Score.HotSizeReduction); + DstBBStartAddr -= Score.HotSizeReduction; + } + const size_t NewCallLength = + AbsoluteDifference(SrcBBEndAddr, DstBBStartAddr); + Score.LocalScore += computeCallScore(CallCount, NewCallLength); + } + } + } + + /// Compute sum of splitting scores for cover calls of the input function. + /// Increament Score.CoverCallScore in place by the sum. + void computeCoverCallScore(const BasicBlockOrder &BlockOrder, + const size_t SplitIndex, + const std::vector &CoverCalls, + SplitScore &Score) { + if (opts::CallScale == 0) + return; + + for (const CallInfo CI : CoverCalls) { + assert(CI.Length >= Score.HotSizeReduction && + "Length of cover calls must exceed reduced size of hot fragment."); + // Compute the new length of the call, which is shorter than the original + // one by the size of the splitted fragment minus the total size increase. + const size_t NewCallLength = CI.Length - Score.HotSizeReduction; + Score.CoverCallScore += computeCallScore(CI.Count, NewCallLength); + } + } + + /// Compute the split score of splitting a function at a given index. + /// The split score consists of local score and cover score. Cover call score + /// is expensive to compute. As a result, we pass in a \p ReferenceScore and + /// compute cover score only when the local score exceeds that in the + /// ReferenceScore or that the size reduction of the hot fragment is larger + /// than that achieved by the split index of the ReferenceScore. This function + /// returns \p Score of SplitScore type. It contains the local score and cover + /// score (if computed) of the current splitting index. For easier book + /// keeping and comparison, it also stores the split index and the resulting + /// reduction in hot fragment size. + SplitScore computeSplitScore(const BinaryFunction &BF, + const BasicBlockOrder &BlockOrder, + const size_t SplitIndex, + const std::vector &CoverCalls, + const SplitScore &ReferenceScore) { + // Populate BinaryBasicBlock::OutputAddressRange with estimated + // new start and end addresses after hot-warm splitting at SplitIndex. + size_t OldHotEnd; + size_t NewHotEnd; + std::tie(OldHotEnd, NewHotEnd) = + estimatePostSplitBBAddress(BlockOrder, SplitIndex); + + SplitScore Score; + Score.SplitIndex = SplitIndex; + + // It's not worth splitting if OldHotEnd < NewHotEnd. + if (OldHotEnd < NewHotEnd) + return Score; + + // Hot fragment size reduction due to splitting. + Score.HotSizeReduction = OldHotEnd - NewHotEnd; + + // First part of LocalScore is the sum over call edges originated in the + // input function. These edges can get shorter or longer depending on + // SplitIndex. Score.LocalScore is increamented in place. + computeLocalCallScore(BlockOrder, SplitIndex, Score); + + // Second part of LocalScore is the sum over jump edges with src basic block + // and dst basic block in the current function. Score.LocalScore is + // increamented in place. + computeJumpScore(BlockOrder, SplitIndex, Score); + + // There is no need to compute CoverCallScore if we have already found + // another split index with a bigger LocalScore and bigger HotSizeReduction. + if (Score.LocalScore <= ReferenceScore.LocalScore && + Score.HotSizeReduction <= ReferenceScore.HotSizeReduction) + return Score; + + // Compute CoverCallScore and store in Score in place. + computeCoverCallScore(BlockOrder, SplitIndex, CoverCalls, Score); + return Score; + } + + /// Find the best index for splitting. The returned value is the index of the + /// last hot basic block. Hence, "no splitting" is equivalent to returning the + /// value which is one less than the size of the function. + size_t findSplitIndex(const BinaryFunction &BF, + const BasicBlockOrder &BlockOrder) { + // Find all function calls that can be shortened if we move blocks of the + // current function to warm/cold + const std::vector CoverCalls = extractCoverCalls(BF); + + // Try all possible split indices (blocks with Index <= SplitIndex are in + // hot) and find the one maximizing the splitting score. + SplitScore BestScore; + double BestScoreSum = -1.0; + SplitScore ReferenceScore; + for (size_t Index = 0; Index < BlockOrder.size(); Index++) { + const BinaryBasicBlock *LastHotBB = BlockOrder[Index]; + // No need to keep cold blocks in the hot section. + if (LastHotBB->getFragmentNum() == FragmentNum::cold()) + break; + const SplitScore Score = + computeSplitScore(BF, BlockOrder, Index, CoverCalls, ReferenceScore); + double ScoreSum = Score.LocalScore + Score.CoverCallScore; + if (ScoreSum > BestScoreSum) { + BestScoreSum = ScoreSum; + BestScore = Score; + } + if (Score.LocalScore > ReferenceScore.LocalScore) + ReferenceScore = Score; + } + + return BestScore.SplitIndex; + } +}; + struct SplitRandom2 final : public SplitStrategy { std::minstd_rand0 Gen; @@ -143,7 +585,7 @@ struct SplitRandom2 final : public SplitStrategy { bool canSplit(const BinaryFunction &BF) override { return true; } - bool keepEmpty() override { return false; } + bool compactFragments() override { return true; } void fragment(const BlockIt Start, const BlockIt End) override { using DiffT = typename std::iterator_traits::difference_type; @@ -170,7 +612,7 @@ struct SplitRandomN final : public SplitStrategy { bool canSplit(const BinaryFunction &BF) override { return true; } - bool keepEmpty() override { return false; } + bool compactFragments() override { return true; } void fragment(const BlockIt Start, const BlockIt End) override { using DiffT = typename std::iterator_traits::difference_type; @@ -217,10 +659,10 @@ struct SplitRandomN final : public SplitStrategy { struct SplitAll final : public SplitStrategy { bool canSplit(const BinaryFunction &BF) override { return true; } - bool keepEmpty() override { + bool compactFragments() override { // Keeping empty fragments allows us to test, that empty fragments do not // generate symbols. - return true; + return false; } void fragment(const BlockIt Start, const BlockIt End) override { @@ -246,10 +688,27 @@ void SplitFunctions::runOnFunctions(BinaryContext &BC) { if (!opts::SplitFunctions) return; + // If split strategy is not CDSplit, then a second run of the pass is not + // needed after function reordering. + if (BC.HasFinalizedFunctionOrder && + opts::SplitStrategy != SplitFunctionsStrategy::CDSplit) + return; + std::unique_ptr Strategy; bool ForceSequential = false; switch (opts::SplitStrategy) { + case SplitFunctionsStrategy::CDSplit: + // CDSplit runs two splitting passes: hot-cold splitting (SplitPrfoile2) + // before function reordering and hot-warm-cold splitting + // (SplitCacheDirected) after function reordering. + if (BC.HasFinalizedFunctionOrder) + Strategy = std::make_unique(BC); + else + Strategy = std::make_unique(); + opts::AggressiveSplitting = true; + BC.HasWarmSection = true; + break; case SplitFunctionsStrategy::Profile2: Strategy = std::make_unique(); break; @@ -382,7 +841,7 @@ void SplitFunctions::splitFunction(BinaryFunction &BF, SplitStrategy &S) { CurrentFragment = BB->getFragmentNum(); } - if (!S.keepEmpty()) { + if (S.compactFragments()) { FragmentNum CurrentFragment = FragmentNum::main(); FragmentNum NewFragment = FragmentNum::main(); for (BinaryBasicBlock *const BB : NewLayout) { @@ -394,7 +853,7 @@ void SplitFunctions::splitFunction(BinaryFunction &BF, SplitStrategy &S) { } } - BF.getLayout().update(NewLayout); + const bool LayoutUpdated = BF.getLayout().update(NewLayout); // For shared objects, invoke instructions and corresponding landing pads // have to be placed in the same fragment. When we split them, create @@ -404,7 +863,7 @@ void SplitFunctions::splitFunction(BinaryFunction &BF, SplitStrategy &S) { Trampolines = createEHTrampolines(BF); // Check the new size to see if it's worth splitting the function. - if (BC.isX86() && BF.isSplit()) { + if (BC.isX86() && LayoutUpdated) { std::tie(HotSize, ColdSize) = BC.calculateEmittedSize(BF); LLVM_DEBUG(dbgs() << "Estimated size for function " << BF << " post-split is <0x" << Twine::utohexstr(HotSize) @@ -431,6 +890,11 @@ void SplitFunctions::splitFunction(BinaryFunction &BF, SplitStrategy &S) { SplitBytesCold += ColdSize; } } + + // Fix branches if the splitting decision of the pass after function + // reordering is different from that of the pass before function reordering. + if (LayoutUpdated && BC.HasFinalizedFunctionOrder) + BF.fixBranches(); } SplitFunctions::TrampolineSetType diff --git a/bolt/lib/Rewrite/BinaryPassManager.cpp b/bolt/lib/Rewrite/BinaryPassManager.cpp index 37de3eabc6d235de8edc0f5cce74e42fa26f043c..9946608c96d8ee99b2ca7c977ca7999c3373d98d 100644 --- a/bolt/lib/Rewrite/BinaryPassManager.cpp +++ b/bolt/lib/Rewrite/BinaryPassManager.cpp @@ -430,6 +430,13 @@ void BinaryFunctionPassManager::runAllPasses(BinaryContext &BC) { Manager.registerPass( std::make_unique(PrintReorderedFunctions)); + // This is the second run of the SplitFunctions pass required by certain + // splitting strategies (e.g. cdsplit). Running the SplitFunctions pass again + // after ReorderFunctions allows the finalized function order to be utilized + // to make more sophisticated splitting decisions, like hot-warm-cold + // splitting. + Manager.registerPass(std::make_unique(PrintSplit)); + // Print final dyno stats right while CFG and instruction analysis are intact. Manager.registerPass( std::make_unique( diff --git a/bolt/lib/Rewrite/DWARFRewriter.cpp b/bolt/lib/Rewrite/DWARFRewriter.cpp index 360b82f45bd7754f2ccf1b9c6d7246bbc0c8d4dc..5580d85e3fa7716f6b47ea1ea8cd5f286fc95e0d 100644 --- a/bolt/lib/Rewrite/DWARFRewriter.cpp +++ b/bolt/lib/Rewrite/DWARFRewriter.cpp @@ -481,7 +481,7 @@ emitUnit(DIEBuilder &DIEBldr, DIEStreamer &Streamer, DWARFUnit &Unit) { static void emitDWOBuilder(const std::string &DWOName, DIEBuilder &DWODIEBuilder, DWARFRewriter &Rewriter, - const DWARFUnit &SplitCU, DWARFUnit &CU, + DWARFUnit &SplitCU, DWARFUnit &CU, DWARFRewriter::DWPState &State, DebugLocWriter &LocWriter) { // Populate debug_info and debug_abbrev for current dwo into StringRef. @@ -498,17 +498,15 @@ static void emitDWOBuilder(const std::string &DWOName, DWARFRewriter::UnitMetaVectorType TUMetaVector; DWARFRewriter::UnitMeta CUMI = {0, 0, 0}; if (SplitCU.getContext().getMaxDWOVersion() >= 5) { - // TODO: Handle DWP as input. Right now it will iterate over all of CUs and - // TUs for (std::unique_ptr &CU : SplitCU.getContext().dwo_info_section_units()) { + if (!CU->isTypeUnit()) + continue; DWARFRewriter::UnitMeta MI = emitUnit(DWODIEBuilder, *Streamer, *CU.get()); - if (CU->isTypeUnit()) - TUMetaVector.emplace_back(MI); - else - CUMI = MI; + TUMetaVector.emplace_back(MI); } + CUMI = emitUnit(DWODIEBuilder, *Streamer, SplitCU); } else { for (std::unique_ptr &CU : SplitCU.getContext().dwo_compile_units()) @@ -681,6 +679,8 @@ void DWARFRewriter::updateDebugInfo() { assert(CompDirAttrInfo && "DW_AT_comp_dir is not in Skeleton CU."); if (!opts::DwarfOutputPath.empty()) { + if (!sys::fs::exists(opts::DwarfOutputPath)) + sys::fs::create_directory(opts::DwarfOutputPath); addStringHelper(DIEBldr, UnitDIE, Unit, CompDirAttrInfo, opts::DwarfOutputPath.c_str()); } @@ -707,7 +707,7 @@ void DWARFRewriter::updateDebugInfo() { // Skipping CUs that failed to load. if (SplitCU) { DIEBuilder DWODIEBuilder(&(*SplitCU)->getContext(), true); - DWODIEBuilder.buildBoth(); + DWODIEBuilder.buildDWOUnit(**SplitCU); std::string DWOName = updateDWONameCompDir( *Unit, *DIEBlder, *DIEBlder->getUnitDIEbyUnit(*Unit)); @@ -963,8 +963,7 @@ void DWARFRewriter::updateUnitDebugInfo( std::move(OutputRanges), CachedRanges); OutputRanges.clear(); } else if (OutputRanges.empty()) { - OutputRanges.push_back({RangesOrError.get().front().LowPC, - RangesOrError.get().front().HighPC}); + OutputRanges.push_back({0, RangesOrError.get().front().HighPC}); } } else if (!RangesOrError) { consumeError(RangesOrError.takeError()); diff --git a/bolt/lib/Rewrite/RewriteInstance.cpp b/bolt/lib/Rewrite/RewriteInstance.cpp index 81c9cbff726bb9a1df68c7a00d9651677a6ebb05..8cda0b7fcca9f8438708f9ede6fe5e8b6570d6e5 100644 --- a/bolt/lib/Rewrite/RewriteInstance.cpp +++ b/bolt/lib/Rewrite/RewriteInstance.cpp @@ -3478,11 +3478,21 @@ std::vector RewriteInstance::getCodeSections() { if (B->getName() == BC->getHotTextMoverSectionName()) return false; - // Depending on the option, put main text at the beginning or at the end. - if (opts::HotFunctionsAtEnd) - return B->getName() == BC->getMainCodeSectionName(); - else - return A->getName() == BC->getMainCodeSectionName(); + // Depending on opts::HotFunctionsAtEnd, place main and warm sections in + // order. + if (opts::HotFunctionsAtEnd) { + if (B->getName() == BC->getMainCodeSectionName()) + return true; + if (A->getName() == BC->getMainCodeSectionName()) + return false; + return (B->getName() == BC->getWarmCodeSectionName()); + } else { + if (A->getName() == BC->getMainCodeSectionName()) + return true; + if (B->getName() == BC->getMainCodeSectionName()) + return false; + return (A->getName() == BC->getWarmCodeSectionName()); + } }; // Determine the order of sections. @@ -3524,6 +3534,9 @@ void RewriteInstance::mapCodeSections(BOLTLinker::SectionMapper MapSection) { // Allocate sections starting at a given Address. auto allocateAt = [&](uint64_t Address) { + const char *LastNonColdSectionName = BC->HasWarmSection + ? BC->getWarmCodeSectionName() + : BC->getMainCodeSectionName(); for (BinarySection *Section : CodeSections) { Address = alignTo(Address, Section->getAlignment()); Section->setOutputAddress(Address); @@ -3532,13 +3545,13 @@ void RewriteInstance::mapCodeSections(BOLTLinker::SectionMapper MapSection) { // Hugify: Additional huge page from right side due to // weird ASLR mapping addresses (4KB aligned) if (opts::Hugify && !BC->HasFixedLoadAddress && - Section->getName() == BC->getMainCodeSectionName()) + Section->getName() == LastNonColdSectionName) Address = alignTo(Address, Section->getAlignment()); } // Make sure we allocate enough space for huge pages. ErrorOr TextSection = - BC->getUniqueSectionByName(BC->getMainCodeSectionName()); + BC->getUniqueSectionByName(LastNonColdSectionName); if (opts::HotText && TextSection && TextSection->hasValidSectionID()) { uint64_t HotTextEnd = TextSection->getOutputAddress() + TextSection->getOutputSize(); @@ -4396,6 +4409,21 @@ void RewriteInstance::updateELFSymbolTable( return NewIndex; }; + // Get the extra symbol name of a split fragment; used in addExtraSymbols. + auto getSplitSymbolName = [&](const FunctionFragment &FF, + const ELFSymTy &FunctionSymbol) { + SmallString<256> SymbolName; + if (BC->HasWarmSection) + SymbolName = + formatv("{0}.{1}", cantFail(FunctionSymbol.getName(StringSection)), + FF.getFragmentNum() == FragmentNum::warm() ? "warm" : "cold"); + else + SymbolName = formatv("{0}.cold.{1}", + cantFail(FunctionSymbol.getName(StringSection)), + FF.getFragmentNum().get() - 1); + return SymbolName; + }; + // Add extra symbols for the function. // // Note that addExtraSymbols() could be called multiple times for the same @@ -4423,9 +4451,8 @@ void RewriteInstance::updateELFSymbolTable( Function.getLayout().getSplitFragments()) { if (FF.getAddress()) { ELFSymTy NewColdSym = FunctionSymbol; - const SmallString<256> SymbolName = formatv( - "{0}.cold.{1}", cantFail(FunctionSymbol.getName(StringSection)), - FF.getFragmentNum().get() - 1); + const SmallString<256> SymbolName = + getSplitSymbolName(FF, FunctionSymbol); NewColdSym.st_name = AddToStrTab(SymbolName); NewColdSym.st_shndx = Function.getCodeSection(FF.getFragmentNum())->getIndex(); diff --git a/bolt/test/X86/Inputs/dwarf5-df-types-dup-helper.s b/bolt/test/X86/Inputs/dwarf5-df-types-dup-helper.s new file mode 100644 index 0000000000000000000000000000000000000000..cf0bdad3bbda188300fc60e677f4d0196694676d --- /dev/null +++ b/bolt/test/X86/Inputs/dwarf5-df-types-dup-helper.s @@ -0,0 +1,504 @@ +# clang++ -gsplit-dwarf -g2 -gdwarf-5 -gpubnames -fdebug-types-section -fdebug-compilation-dir='.' +# header.h +# struct Foo2a { +# char *c1; +# char *c2; +# char *c3; +# }; + +# main.cpp +# #include "header.h" +# int fooint; +# struct Foo2Int { +# int *c1; +# int *c2; +# }; +# +# int foo() { +# Foo2Int fint; +# Foo2a f; +# return 0; +# } + + .text + .file "helper.cpp" + .file 0 "." "helper.cpp" md5 0xc33186b2db66a78883b1546aace9855d + .globl _Z3foov # -- Begin function _Z3foov + .p2align 4, 0x90 + .type _Z3foov,@function +_Z3foov: # @_Z3foov +.Lfunc_begin0: + .loc 0 8 0 # helper.cpp:8:0 + .cfi_startproc +# %bb.0: # %entry + pushq %rbp + .cfi_def_cfa_offset 16 + .cfi_offset %rbp, -16 + movq %rsp, %rbp + .cfi_def_cfa_register %rbp +.Ltmp0: + .loc 0 11 3 prologue_end # helper.cpp:11:3 + xorl %eax, %eax + .loc 0 11 3 epilogue_begin is_stmt 0 # helper.cpp:11:3 + popq %rbp + .cfi_def_cfa %rsp, 8 + retq +.Ltmp1: +.Lfunc_end0: + .size _Z3foov, .Lfunc_end0-_Z3foov + .cfi_endproc + # -- End function + .type fooint,@object # @fooint + .bss + .globl fooint + .p2align 2, 0x0 +fooint: + .long 0 # 0x0 + .size fooint, 4 + + .section .debug_info.dwo,"e",@progbits + .long .Ldebug_info_dwo_end0-.Ldebug_info_dwo_start0 # Length of Unit +.Ldebug_info_dwo_start0: + .short 5 # DWARF version number + .byte 6 # DWARF Unit Type + .byte 8 # Address Size (in bytes) + .long 0 # Offset Into Abbrev. Section + .quad -3882554063269480080 # Type Signature + .long 31 # Type DIE Offset + .byte 1 # Abbrev [1] 0x18:0x2a DW_TAG_type_unit + .short 33 # DW_AT_language + .long 0 # DW_AT_stmt_list + .byte 2 # Abbrev [2] 0x1f:0x19 DW_TAG_structure_type + .byte 5 # DW_AT_calling_convention + .byte 7 # DW_AT_name + .byte 16 # DW_AT_byte_size + .byte 0 # DW_AT_decl_file + .byte 3 # DW_AT_decl_line + .byte 3 # Abbrev [3] 0x25:0x9 DW_TAG_member + .byte 5 # DW_AT_name + .long 56 # DW_AT_type + .byte 0 # DW_AT_decl_file + .byte 4 # DW_AT_decl_line + .byte 0 # DW_AT_data_member_location + .byte 3 # Abbrev [3] 0x2e:0x9 DW_TAG_member + .byte 6 # DW_AT_name + .long 56 # DW_AT_type + .byte 0 # DW_AT_decl_file + .byte 5 # DW_AT_decl_line + .byte 8 # DW_AT_data_member_location + .byte 0 # End Of Children Mark + .byte 4 # Abbrev [4] 0x38:0x5 DW_TAG_pointer_type + .long 61 # DW_AT_type + .byte 5 # Abbrev [5] 0x3d:0x4 DW_TAG_base_type + .byte 1 # DW_AT_name + .byte 5 # DW_AT_encoding + .byte 4 # DW_AT_byte_size + .byte 0 # End Of Children Mark +.Ldebug_info_dwo_end0: + .long .Ldebug_info_dwo_end1-.Ldebug_info_dwo_start1 # Length of Unit +.Ldebug_info_dwo_start1: + .short 5 # DWARF version number + .byte 6 # DWARF Unit Type + .byte 8 # Address Size (in bytes) + .long 0 # Offset Into Abbrev. Section + .quad 1175092228111723119 # Type Signature + .long 31 # Type DIE Offset + .byte 1 # Abbrev [1] 0x18:0x33 DW_TAG_type_unit + .short 33 # DW_AT_language + .long 0 # DW_AT_stmt_list + .byte 2 # Abbrev [2] 0x1f:0x22 DW_TAG_structure_type + .byte 5 # DW_AT_calling_convention + .byte 11 # DW_AT_name + .byte 24 # DW_AT_byte_size + .byte 1 # DW_AT_decl_file + .byte 1 # DW_AT_decl_line + .byte 3 # Abbrev [3] 0x25:0x9 DW_TAG_member + .byte 5 # DW_AT_name + .long 65 # DW_AT_type + .byte 1 # DW_AT_decl_file + .byte 2 # DW_AT_decl_line + .byte 0 # DW_AT_data_member_location + .byte 3 # Abbrev [3] 0x2e:0x9 DW_TAG_member + .byte 6 # DW_AT_name + .long 65 # DW_AT_type + .byte 1 # DW_AT_decl_file + .byte 3 # DW_AT_decl_line + .byte 8 # DW_AT_data_member_location + .byte 3 # Abbrev [3] 0x37:0x9 DW_TAG_member + .byte 10 # DW_AT_name + .long 65 # DW_AT_type + .byte 1 # DW_AT_decl_file + .byte 4 # DW_AT_decl_line + .byte 16 # DW_AT_data_member_location + .byte 0 # End Of Children Mark + .byte 4 # Abbrev [4] 0x41:0x5 DW_TAG_pointer_type + .long 70 # DW_AT_type + .byte 5 # Abbrev [5] 0x46:0x4 DW_TAG_base_type + .byte 9 # DW_AT_name + .byte 6 # DW_AT_encoding + .byte 1 # DW_AT_byte_size + .byte 0 # End Of Children Mark +.Ldebug_info_dwo_end1: + .section .debug_abbrev,"",@progbits + .byte 1 # Abbreviation Code + .byte 74 # DW_TAG_skeleton_unit + .byte 0 # DW_CHILDREN_no + .byte 16 # DW_AT_stmt_list + .byte 23 # DW_FORM_sec_offset + .byte 114 # DW_AT_str_offsets_base + .byte 23 # DW_FORM_sec_offset + .byte 27 # DW_AT_comp_dir + .byte 37 # DW_FORM_strx1 + .byte 118 # DW_AT_dwo_name + .byte 37 # DW_FORM_strx1 + .byte 17 # DW_AT_low_pc + .byte 27 # DW_FORM_addrx + .byte 18 # DW_AT_high_pc + .byte 6 # DW_FORM_data4 + .byte 115 # DW_AT_addr_base + .byte 23 # DW_FORM_sec_offset + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 0 # EOM(3) + .section .debug_info,"",@progbits +.Lcu_begin0: + .long .Ldebug_info_end0-.Ldebug_info_start0 # Length of Unit +.Ldebug_info_start0: + .short 5 # DWARF version number + .byte 4 # DWARF Unit Type + .byte 8 # Address Size (in bytes) + .long .debug_abbrev # Offset Into Abbrev. Section + .quad 2142419470755914572 + .byte 1 # Abbrev [1] 0x14:0x14 DW_TAG_skeleton_unit + .long .Lline_table_start0 # DW_AT_stmt_list + .long .Lstr_offsets_base0 # DW_AT_str_offsets_base + .byte 0 # DW_AT_comp_dir + .byte 1 # DW_AT_dwo_name + .byte 1 # DW_AT_low_pc + .long .Lfunc_end0-.Lfunc_begin0 # DW_AT_high_pc + .long .Laddr_table_base0 # DW_AT_addr_base +.Ldebug_info_end0: + .section .debug_str_offsets,"",@progbits + .long 12 # Length of String Offsets Set + .short 5 + .short 0 +.Lstr_offsets_base0: + .section .debug_str,"MS",@progbits,1 +.Lskel_string0: + .asciz "." # string offset=0 +.Lskel_string1: + .asciz "helper.dwo" # string offset=2 + .section .debug_str_offsets,"",@progbits + .long .Lskel_string0 + .long .Lskel_string1 + .section .debug_str_offsets.dwo,"e",@progbits + .long 64 # Length of String Offsets Set + .short 5 + .short 0 + .section .debug_str.dwo,"eMS",@progbits,1 +.Linfo_string0: + .asciz "fooint" # string offset=0 +.Linfo_string1: + .asciz "int" # string offset=7 +.Linfo_string2: + .asciz "_Z3foov" # string offset=11 +.Linfo_string3: + .asciz "foo" # string offset=19 +.Linfo_string4: + .asciz "fint" # string offset=23 +.Linfo_string5: + .asciz "c1" # string offset=28 +.Linfo_string6: + .asciz "c2" # string offset=31 +.Linfo_string7: + .asciz "Foo2Int" # string offset=34 +.Linfo_string8: + .asciz "f" # string offset=42 +.Linfo_string9: + .asciz "char" # string offset=44 +.Linfo_string10: + .asciz "c3" # string offset=49 +.Linfo_string11: + .asciz "Foo2a" # string offset=52 +.Linfo_string12: + .asciz "clang version 18.0.0" # string offset=58 +.Linfo_string13: + .asciz "helper.cpp" # string offset=79 +.Linfo_string14: + .asciz "helper.dwo" # string offset=90 + .section .debug_str_offsets.dwo,"e",@progbits + .long 0 + .long 7 + .long 11 + .long 19 + .long 23 + .long 28 + .long 31 + .long 34 + .long 42 + .long 44 + .long 49 + .long 52 + .long 58 + .long 79 + .long 90 + .section .debug_info.dwo,"e",@progbits + .long .Ldebug_info_dwo_end2-.Ldebug_info_dwo_start2 # Length of Unit +.Ldebug_info_dwo_start2: + .short 5 # DWARF version number + .byte 5 # DWARF Unit Type + .byte 8 # Address Size (in bytes) + .long 0 # Offset Into Abbrev. Section + .quad 2142419470755914572 + .byte 6 # Abbrev [6] 0x14:0x4f DW_TAG_compile_unit + .byte 12 # DW_AT_producer + .short 33 # DW_AT_language + .byte 13 # DW_AT_name + .byte 14 # DW_AT_dwo_name + .byte 7 # Abbrev [7] 0x1a:0xb DW_TAG_variable + .byte 0 # DW_AT_name + .long 37 # DW_AT_type + # DW_AT_external + .byte 0 # DW_AT_decl_file + .byte 2 # DW_AT_decl_line + .byte 2 # DW_AT_location + .byte 161 + .byte 0 + .byte 5 # Abbrev [5] 0x25:0x4 DW_TAG_base_type + .byte 1 # DW_AT_name + .byte 5 # DW_AT_encoding + .byte 4 # DW_AT_byte_size + .byte 8 # Abbrev [8] 0x29:0x27 DW_TAG_subprogram + .byte 1 # DW_AT_low_pc + .long .Lfunc_end0-.Lfunc_begin0 # DW_AT_high_pc + .byte 1 # DW_AT_frame_base + .byte 86 + .byte 2 # DW_AT_linkage_name + .byte 3 # DW_AT_name + .byte 0 # DW_AT_decl_file + .byte 8 # DW_AT_decl_line + .long 37 # DW_AT_type + # DW_AT_external + .byte 9 # Abbrev [9] 0x39:0xb DW_TAG_variable + .byte 2 # DW_AT_location + .byte 145 + .byte 112 + .byte 4 # DW_AT_name + .byte 0 # DW_AT_decl_file + .byte 9 # DW_AT_decl_line + .long 80 # DW_AT_type + .byte 9 # Abbrev [9] 0x44:0xb DW_TAG_variable + .byte 2 # DW_AT_location + .byte 145 + .byte 88 + .byte 8 # DW_AT_name + .byte 0 # DW_AT_decl_file + .byte 10 # DW_AT_decl_line + .long 89 # DW_AT_type + .byte 0 # End Of Children Mark + .byte 10 # Abbrev [10] 0x50:0x9 DW_TAG_structure_type + # DW_AT_declaration + .quad -3882554063269480080 # DW_AT_signature + .byte 10 # Abbrev [10] 0x59:0x9 DW_TAG_structure_type + # DW_AT_declaration + .quad 1175092228111723119 # DW_AT_signature + .byte 0 # End Of Children Mark +.Ldebug_info_dwo_end2: + .section .debug_abbrev.dwo,"e",@progbits + .byte 1 # Abbreviation Code + .byte 65 # DW_TAG_type_unit + .byte 1 # DW_CHILDREN_yes + .byte 19 # DW_AT_language + .byte 5 # DW_FORM_data2 + .byte 16 # DW_AT_stmt_list + .byte 23 # DW_FORM_sec_offset + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 2 # Abbreviation Code + .byte 19 # DW_TAG_structure_type + .byte 1 # DW_CHILDREN_yes + .byte 54 # DW_AT_calling_convention + .byte 11 # DW_FORM_data1 + .byte 3 # DW_AT_name + .byte 37 # DW_FORM_strx1 + .byte 11 # DW_AT_byte_size + .byte 11 # DW_FORM_data1 + .byte 58 # DW_AT_decl_file + .byte 11 # DW_FORM_data1 + .byte 59 # DW_AT_decl_line + .byte 11 # DW_FORM_data1 + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 3 # Abbreviation Code + .byte 13 # DW_TAG_member + .byte 0 # DW_CHILDREN_no + .byte 3 # DW_AT_name + .byte 37 # DW_FORM_strx1 + .byte 73 # DW_AT_type + .byte 19 # DW_FORM_ref4 + .byte 58 # DW_AT_decl_file + .byte 11 # DW_FORM_data1 + .byte 59 # DW_AT_decl_line + .byte 11 # DW_FORM_data1 + .byte 56 # DW_AT_data_member_location + .byte 11 # DW_FORM_data1 + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 4 # Abbreviation Code + .byte 15 # DW_TAG_pointer_type + .byte 0 # DW_CHILDREN_no + .byte 73 # DW_AT_type + .byte 19 # DW_FORM_ref4 + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 5 # Abbreviation Code + .byte 36 # DW_TAG_base_type + .byte 0 # DW_CHILDREN_no + .byte 3 # DW_AT_name + .byte 37 # DW_FORM_strx1 + .byte 62 # DW_AT_encoding + .byte 11 # DW_FORM_data1 + .byte 11 # DW_AT_byte_size + .byte 11 # DW_FORM_data1 + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 6 # Abbreviation Code + .byte 17 # DW_TAG_compile_unit + .byte 1 # DW_CHILDREN_yes + .byte 37 # DW_AT_producer + .byte 37 # DW_FORM_strx1 + .byte 19 # DW_AT_language + .byte 5 # DW_FORM_data2 + .byte 3 # DW_AT_name + .byte 37 # DW_FORM_strx1 + .byte 118 # DW_AT_dwo_name + .byte 37 # DW_FORM_strx1 + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 7 # Abbreviation Code + .byte 52 # DW_TAG_variable + .byte 0 # DW_CHILDREN_no + .byte 3 # DW_AT_name + .byte 37 # DW_FORM_strx1 + .byte 73 # DW_AT_type + .byte 19 # DW_FORM_ref4 + .byte 63 # DW_AT_external + .byte 25 # DW_FORM_flag_present + .byte 58 # DW_AT_decl_file + .byte 11 # DW_FORM_data1 + .byte 59 # DW_AT_decl_line + .byte 11 # DW_FORM_data1 + .byte 2 # DW_AT_location + .byte 24 # DW_FORM_exprloc + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 8 # Abbreviation Code + .byte 46 # DW_TAG_subprogram + .byte 1 # DW_CHILDREN_yes + .byte 17 # DW_AT_low_pc + .byte 27 # DW_FORM_addrx + .byte 18 # DW_AT_high_pc + .byte 6 # DW_FORM_data4 + .byte 64 # DW_AT_frame_base + .byte 24 # DW_FORM_exprloc + .byte 110 # DW_AT_linkage_name + .byte 37 # DW_FORM_strx1 + .byte 3 # DW_AT_name + .byte 37 # DW_FORM_strx1 + .byte 58 # DW_AT_decl_file + .byte 11 # DW_FORM_data1 + .byte 59 # DW_AT_decl_line + .byte 11 # DW_FORM_data1 + .byte 73 # DW_AT_type + .byte 19 # DW_FORM_ref4 + .byte 63 # DW_AT_external + .byte 25 # DW_FORM_flag_present + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 9 # Abbreviation Code + .byte 52 # DW_TAG_variable + .byte 0 # DW_CHILDREN_no + .byte 2 # DW_AT_location + .byte 24 # DW_FORM_exprloc + .byte 3 # DW_AT_name + .byte 37 # DW_FORM_strx1 + .byte 58 # DW_AT_decl_file + .byte 11 # DW_FORM_data1 + .byte 59 # DW_AT_decl_line + .byte 11 # DW_FORM_data1 + .byte 73 # DW_AT_type + .byte 19 # DW_FORM_ref4 + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 10 # Abbreviation Code + .byte 19 # DW_TAG_structure_type + .byte 0 # DW_CHILDREN_no + .byte 60 # DW_AT_declaration + .byte 25 # DW_FORM_flag_present + .byte 105 # DW_AT_signature + .byte 32 # DW_FORM_ref_sig8 + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 0 # EOM(3) + .section .debug_line.dwo,"e",@progbits +.Ltmp2: + .long .Ldebug_line_end0-.Ldebug_line_start0 # unit length +.Ldebug_line_start0: + .short 5 + .byte 8 + .byte 0 + .long .Lprologue_end0-.Lprologue_start0 +.Lprologue_start0: + .byte 1 + .byte 1 + .byte 1 + .byte -5 + .byte 14 + .byte 1 + .byte 1 + .byte 1 + .byte 8 + .byte 2 + .byte 46 + .byte 0 + .byte 46 + .byte 0 + .byte 3 + .byte 1 + .byte 8 + .byte 2 + .byte 15 + .byte 5 + .byte 30 + .byte 2 + .ascii "helper.cpp" + .byte 0 + .byte 0 + .byte 0xc3, 0x31, 0x86, 0xb2 + .byte 0xdb, 0x66, 0xa7, 0x88 + .byte 0x83, 0xb1, 0x54, 0x6a + .byte 0xac, 0xe9, 0x85, 0x5d + .ascii "header.h" + .byte 0 + .byte 1 + .byte 0xfe, 0xa7, 0xbb, 0x1f + .byte 0x22, 0xc4, 0x7f, 0x12 + .byte 0x9e, 0x15, 0x69, 0x5f + .byte 0x71, 0x37, 0xa1, 0xe7 +.Lprologue_end0: +.Ldebug_line_end0: + .section .debug_addr,"",@progbits + .long .Ldebug_addr_end0-.Ldebug_addr_start0 # Length of contribution +.Ldebug_addr_start0: + .short 5 # DWARF version number + .byte 8 # Address size + .byte 0 # Segment selector size +.Laddr_table_base0: + .quad fooint + .quad .Lfunc_begin0 +.Ldebug_addr_end0: + .ident "clang version 18.0.0" + .section ".note.GNU-stack","",@progbits + .addrsig + .section .debug_line,"",@progbits +.Lline_table_start0: diff --git a/bolt/test/X86/Inputs/dwarf5-df-types-dup-main.s b/bolt/test/X86/Inputs/dwarf5-df-types-dup-main.s new file mode 100644 index 0000000000000000000000000000000000000000..d991094cf0a0818061c061c178e8eb6f66c29e58 --- /dev/null +++ b/bolt/test/X86/Inputs/dwarf5-df-types-dup-main.s @@ -0,0 +1,498 @@ +# clang++ -gsplit-dwarf -g2 -gdwarf-5 -gpubnames -fdebug-types-section -fdebug-compilation-dir='.' +# header.h +# struct Foo2a { +# char *c1; +# char *c2; +# char *c3; +# }; + +# main.cpp +# #include "header.h" +# struct Foo2 { +# char *c1; +# }; +# int main(int argc, char *argv[]) { +# Foo2 f2; +# Foo2a f3; +# return 0; +# } + .text + .file "main.cpp" + .globl main # -- Begin function main + .p2align 4, 0x90 + .type main,@function +main: # @main +.Lfunc_begin0: + .file 0 "." "main.cpp" md5 0x9c5cea5bb78d3fc265cd175110bfe903 + .loc 0 5 0 # main.cpp:5:0 + .cfi_startproc +# %bb.0: # %entry + pushq %rbp + .cfi_def_cfa_offset 16 + .cfi_offset %rbp, -16 + movq %rsp, %rbp + .cfi_def_cfa_register %rbp + movl $0, -4(%rbp) + movl %edi, -8(%rbp) + movq %rsi, -16(%rbp) +.Ltmp0: + .loc 0 8 2 prologue_end # main.cpp:8:2 + xorl %eax, %eax + .loc 0 8 2 epilogue_begin is_stmt 0 # main.cpp:8:2 + popq %rbp + .cfi_def_cfa %rsp, 8 + retq +.Ltmp1: +.Lfunc_end0: + .size main, .Lfunc_end0-main + .cfi_endproc + # -- End function + .section .debug_info.dwo,"e",@progbits + .long .Ldebug_info_dwo_end0-.Ldebug_info_dwo_start0 # Length of Unit +.Ldebug_info_dwo_start0: + .short 5 # DWARF version number + .byte 6 # DWARF Unit Type + .byte 8 # Address Size (in bytes) + .long 0 # Offset Into Abbrev. Section + .quad 5322170643381124694 # Type Signature + .long 31 # Type DIE Offset + .byte 1 # Abbrev [1] 0x18:0x21 DW_TAG_type_unit + .short 33 # DW_AT_language + .long 0 # DW_AT_stmt_list + .byte 2 # Abbrev [2] 0x1f:0x10 DW_TAG_structure_type + .byte 5 # DW_AT_calling_convention + .byte 7 # DW_AT_name + .byte 8 # DW_AT_byte_size + .byte 0 # DW_AT_decl_file + .byte 2 # DW_AT_decl_line + .byte 3 # Abbrev [3] 0x25:0x9 DW_TAG_member + .byte 6 # DW_AT_name + .long 47 # DW_AT_type + .byte 0 # DW_AT_decl_file + .byte 3 # DW_AT_decl_line + .byte 0 # DW_AT_data_member_location + .byte 0 # End Of Children Mark + .byte 4 # Abbrev [4] 0x2f:0x5 DW_TAG_pointer_type + .long 52 # DW_AT_type + .byte 5 # Abbrev [5] 0x34:0x4 DW_TAG_base_type + .byte 4 # DW_AT_name + .byte 6 # DW_AT_encoding + .byte 1 # DW_AT_byte_size + .byte 0 # End Of Children Mark +.Ldebug_info_dwo_end0: + .long .Ldebug_info_dwo_end1-.Ldebug_info_dwo_start1 # Length of Unit +.Ldebug_info_dwo_start1: + .short 5 # DWARF version number + .byte 6 # DWARF Unit Type + .byte 8 # Address Size (in bytes) + .long 0 # Offset Into Abbrev. Section + .quad 1175092228111723119 # Type Signature + .long 31 # Type DIE Offset + .byte 1 # Abbrev [1] 0x18:0x33 DW_TAG_type_unit + .short 33 # DW_AT_language + .long 0 # DW_AT_stmt_list + .byte 2 # Abbrev [2] 0x1f:0x22 DW_TAG_structure_type + .byte 5 # DW_AT_calling_convention + .byte 11 # DW_AT_name + .byte 24 # DW_AT_byte_size + .byte 1 # DW_AT_decl_file + .byte 1 # DW_AT_decl_line + .byte 3 # Abbrev [3] 0x25:0x9 DW_TAG_member + .byte 6 # DW_AT_name + .long 65 # DW_AT_type + .byte 1 # DW_AT_decl_file + .byte 2 # DW_AT_decl_line + .byte 0 # DW_AT_data_member_location + .byte 3 # Abbrev [3] 0x2e:0x9 DW_TAG_member + .byte 9 # DW_AT_name + .long 65 # DW_AT_type + .byte 1 # DW_AT_decl_file + .byte 3 # DW_AT_decl_line + .byte 8 # DW_AT_data_member_location + .byte 3 # Abbrev [3] 0x37:0x9 DW_TAG_member + .byte 10 # DW_AT_name + .long 65 # DW_AT_type + .byte 1 # DW_AT_decl_file + .byte 4 # DW_AT_decl_line + .byte 16 # DW_AT_data_member_location + .byte 0 # End Of Children Mark + .byte 4 # Abbrev [4] 0x41:0x5 DW_TAG_pointer_type + .long 70 # DW_AT_type + .byte 5 # Abbrev [5] 0x46:0x4 DW_TAG_base_type + .byte 4 # DW_AT_name + .byte 6 # DW_AT_encoding + .byte 1 # DW_AT_byte_size + .byte 0 # End Of Children Mark +.Ldebug_info_dwo_end1: + .section .debug_abbrev,"",@progbits + .byte 1 # Abbreviation Code + .byte 74 # DW_TAG_skeleton_unit + .byte 0 # DW_CHILDREN_no + .byte 16 # DW_AT_stmt_list + .byte 23 # DW_FORM_sec_offset + .byte 114 # DW_AT_str_offsets_base + .byte 23 # DW_FORM_sec_offset + .byte 27 # DW_AT_comp_dir + .byte 37 # DW_FORM_strx1 + .byte 118 # DW_AT_dwo_name + .byte 37 # DW_FORM_strx1 + .byte 17 # DW_AT_low_pc + .byte 27 # DW_FORM_addrx + .byte 18 # DW_AT_high_pc + .byte 6 # DW_FORM_data4 + .byte 115 # DW_AT_addr_base + .byte 23 # DW_FORM_sec_offset + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 0 # EOM(3) + .section .debug_info,"",@progbits +.Lcu_begin0: + .long .Ldebug_info_end0-.Ldebug_info_start0 # Length of Unit +.Ldebug_info_start0: + .short 5 # DWARF version number + .byte 4 # DWARF Unit Type + .byte 8 # Address Size (in bytes) + .long .debug_abbrev # Offset Into Abbrev. Section + .quad 5962099678818150071 + .byte 1 # Abbrev [1] 0x14:0x14 DW_TAG_skeleton_unit + .long .Lline_table_start0 # DW_AT_stmt_list + .long .Lstr_offsets_base0 # DW_AT_str_offsets_base + .byte 0 # DW_AT_comp_dir + .byte 1 # DW_AT_dwo_name + .byte 0 # DW_AT_low_pc + .long .Lfunc_end0-.Lfunc_begin0 # DW_AT_high_pc + .long .Laddr_table_base0 # DW_AT_addr_base +.Ldebug_info_end0: + .section .debug_str_offsets,"",@progbits + .long 12 # Length of String Offsets Set + .short 5 + .short 0 +.Lstr_offsets_base0: + .section .debug_str,"MS",@progbits,1 +.Lskel_string0: + .asciz "." # string offset=0 +.Lskel_string1: + .asciz "main.dwo" # string offset=2 + .section .debug_str_offsets,"",@progbits + .long .Lskel_string0 + .long .Lskel_string1 + .section .debug_str_offsets.dwo,"e",@progbits + .long 64 # Length of String Offsets Set + .short 5 + .short 0 + .section .debug_str.dwo,"eMS",@progbits,1 +.Linfo_string0: + .asciz "main" # string offset=0 +.Linfo_string1: + .asciz "int" # string offset=5 +.Linfo_string2: + .asciz "argc" # string offset=9 +.Linfo_string3: + .asciz "argv" # string offset=14 +.Linfo_string4: + .asciz "char" # string offset=19 +.Linfo_string5: + .asciz "f2" # string offset=24 +.Linfo_string6: + .asciz "c1" # string offset=27 +.Linfo_string7: + .asciz "Foo2" # string offset=30 +.Linfo_string8: + .asciz "f3" # string offset=35 +.Linfo_string9: + .asciz "c2" # string offset=38 +.Linfo_string10: + .asciz "c3" # string offset=41 +.Linfo_string11: + .asciz "Foo2a" # string offset=44 +.Linfo_string12: + .asciz "clang version 18.0.0" # string offset=50 +.Linfo_string13: + .asciz "main.cpp" # string offset=71 +.Linfo_string14: + .asciz "main.dwo" # string offset=80 + .section .debug_str_offsets.dwo,"e",@progbits + .long 0 + .long 5 + .long 9 + .long 14 + .long 19 + .long 24 + .long 27 + .long 30 + .long 35 + .long 38 + .long 41 + .long 44 + .long 50 + .long 71 + .long 80 + .section .debug_info.dwo,"e",@progbits + .long .Ldebug_info_dwo_end2-.Ldebug_info_dwo_start2 # Length of Unit +.Ldebug_info_dwo_start2: + .short 5 # DWARF version number + .byte 5 # DWARF Unit Type + .byte 8 # Address Size (in bytes) + .long 0 # Offset Into Abbrev. Section + .quad 5962099678818150071 + .byte 6 # Abbrev [6] 0x14:0x67 DW_TAG_compile_unit + .byte 12 # DW_AT_producer + .short 33 # DW_AT_language + .byte 13 # DW_AT_name + .byte 14 # DW_AT_dwo_name + .byte 7 # Abbrev [7] 0x1a:0x3c DW_TAG_subprogram + .byte 0 # DW_AT_low_pc + .long .Lfunc_end0-.Lfunc_begin0 # DW_AT_high_pc + .byte 1 # DW_AT_frame_base + .byte 86 + .byte 0 # DW_AT_name + .byte 0 # DW_AT_decl_file + .byte 5 # DW_AT_decl_line + .long 86 # DW_AT_type + # DW_AT_external + .byte 8 # Abbrev [8] 0x29:0xb DW_TAG_formal_parameter + .byte 2 # DW_AT_location + .byte 145 + .byte 120 + .byte 2 # DW_AT_name + .byte 0 # DW_AT_decl_file + .byte 5 # DW_AT_decl_line + .long 86 # DW_AT_type + .byte 8 # Abbrev [8] 0x34:0xb DW_TAG_formal_parameter + .byte 2 # DW_AT_location + .byte 145 + .byte 112 + .byte 3 # DW_AT_name + .byte 0 # DW_AT_decl_file + .byte 5 # DW_AT_decl_line + .long 90 # DW_AT_type + .byte 9 # Abbrev [9] 0x3f:0xb DW_TAG_variable + .byte 2 # DW_AT_location + .byte 145 + .byte 104 + .byte 5 # DW_AT_name + .byte 0 # DW_AT_decl_file + .byte 6 # DW_AT_decl_line + .long 104 # DW_AT_type + .byte 9 # Abbrev [9] 0x4a:0xb DW_TAG_variable + .byte 2 # DW_AT_location + .byte 145 + .byte 80 + .byte 8 # DW_AT_name + .byte 0 # DW_AT_decl_file + .byte 7 # DW_AT_decl_line + .long 113 # DW_AT_type + .byte 0 # End Of Children Mark + .byte 5 # Abbrev [5] 0x56:0x4 DW_TAG_base_type + .byte 1 # DW_AT_name + .byte 5 # DW_AT_encoding + .byte 4 # DW_AT_byte_size + .byte 4 # Abbrev [4] 0x5a:0x5 DW_TAG_pointer_type + .long 95 # DW_AT_type + .byte 4 # Abbrev [4] 0x5f:0x5 DW_TAG_pointer_type + .long 100 # DW_AT_type + .byte 5 # Abbrev [5] 0x64:0x4 DW_TAG_base_type + .byte 4 # DW_AT_name + .byte 6 # DW_AT_encoding + .byte 1 # DW_AT_byte_size + .byte 10 # Abbrev [10] 0x68:0x9 DW_TAG_structure_type + # DW_AT_declaration + .quad 5322170643381124694 # DW_AT_signature + .byte 10 # Abbrev [10] 0x71:0x9 DW_TAG_structure_type + # DW_AT_declaration + .quad 1175092228111723119 # DW_AT_signature + .byte 0 # End Of Children Mark +.Ldebug_info_dwo_end2: + .section .debug_abbrev.dwo,"e",@progbits + .byte 1 # Abbreviation Code + .byte 65 # DW_TAG_type_unit + .byte 1 # DW_CHILDREN_yes + .byte 19 # DW_AT_language + .byte 5 # DW_FORM_data2 + .byte 16 # DW_AT_stmt_list + .byte 23 # DW_FORM_sec_offset + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 2 # Abbreviation Code + .byte 19 # DW_TAG_structure_type + .byte 1 # DW_CHILDREN_yes + .byte 54 # DW_AT_calling_convention + .byte 11 # DW_FORM_data1 + .byte 3 # DW_AT_name + .byte 37 # DW_FORM_strx1 + .byte 11 # DW_AT_byte_size + .byte 11 # DW_FORM_data1 + .byte 58 # DW_AT_decl_file + .byte 11 # DW_FORM_data1 + .byte 59 # DW_AT_decl_line + .byte 11 # DW_FORM_data1 + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 3 # Abbreviation Code + .byte 13 # DW_TAG_member + .byte 0 # DW_CHILDREN_no + .byte 3 # DW_AT_name + .byte 37 # DW_FORM_strx1 + .byte 73 # DW_AT_type + .byte 19 # DW_FORM_ref4 + .byte 58 # DW_AT_decl_file + .byte 11 # DW_FORM_data1 + .byte 59 # DW_AT_decl_line + .byte 11 # DW_FORM_data1 + .byte 56 # DW_AT_data_member_location + .byte 11 # DW_FORM_data1 + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 4 # Abbreviation Code + .byte 15 # DW_TAG_pointer_type + .byte 0 # DW_CHILDREN_no + .byte 73 # DW_AT_type + .byte 19 # DW_FORM_ref4 + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 5 # Abbreviation Code + .byte 36 # DW_TAG_base_type + .byte 0 # DW_CHILDREN_no + .byte 3 # DW_AT_name + .byte 37 # DW_FORM_strx1 + .byte 62 # DW_AT_encoding + .byte 11 # DW_FORM_data1 + .byte 11 # DW_AT_byte_size + .byte 11 # DW_FORM_data1 + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 6 # Abbreviation Code + .byte 17 # DW_TAG_compile_unit + .byte 1 # DW_CHILDREN_yes + .byte 37 # DW_AT_producer + .byte 37 # DW_FORM_strx1 + .byte 19 # DW_AT_language + .byte 5 # DW_FORM_data2 + .byte 3 # DW_AT_name + .byte 37 # DW_FORM_strx1 + .byte 118 # DW_AT_dwo_name + .byte 37 # DW_FORM_strx1 + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 7 # Abbreviation Code + .byte 46 # DW_TAG_subprogram + .byte 1 # DW_CHILDREN_yes + .byte 17 # DW_AT_low_pc + .byte 27 # DW_FORM_addrx + .byte 18 # DW_AT_high_pc + .byte 6 # DW_FORM_data4 + .byte 64 # DW_AT_frame_base + .byte 24 # DW_FORM_exprloc + .byte 3 # DW_AT_name + .byte 37 # DW_FORM_strx1 + .byte 58 # DW_AT_decl_file + .byte 11 # DW_FORM_data1 + .byte 59 # DW_AT_decl_line + .byte 11 # DW_FORM_data1 + .byte 73 # DW_AT_type + .byte 19 # DW_FORM_ref4 + .byte 63 # DW_AT_external + .byte 25 # DW_FORM_flag_present + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 8 # Abbreviation Code + .byte 5 # DW_TAG_formal_parameter + .byte 0 # DW_CHILDREN_no + .byte 2 # DW_AT_location + .byte 24 # DW_FORM_exprloc + .byte 3 # DW_AT_name + .byte 37 # DW_FORM_strx1 + .byte 58 # DW_AT_decl_file + .byte 11 # DW_FORM_data1 + .byte 59 # DW_AT_decl_line + .byte 11 # DW_FORM_data1 + .byte 73 # DW_AT_type + .byte 19 # DW_FORM_ref4 + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 9 # Abbreviation Code + .byte 52 # DW_TAG_variable + .byte 0 # DW_CHILDREN_no + .byte 2 # DW_AT_location + .byte 24 # DW_FORM_exprloc + .byte 3 # DW_AT_name + .byte 37 # DW_FORM_strx1 + .byte 58 # DW_AT_decl_file + .byte 11 # DW_FORM_data1 + .byte 59 # DW_AT_decl_line + .byte 11 # DW_FORM_data1 + .byte 73 # DW_AT_type + .byte 19 # DW_FORM_ref4 + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 10 # Abbreviation Code + .byte 19 # DW_TAG_structure_type + .byte 0 # DW_CHILDREN_no + .byte 60 # DW_AT_declaration + .byte 25 # DW_FORM_flag_present + .byte 105 # DW_AT_signature + .byte 32 # DW_FORM_ref_sig8 + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 0 # EOM(3) + .section .debug_line.dwo,"e",@progbits +.Ltmp2: + .long .Ldebug_line_end0-.Ldebug_line_start0 # unit length +.Ldebug_line_start0: + .short 5 + .byte 8 + .byte 0 + .long .Lprologue_end0-.Lprologue_start0 +.Lprologue_start0: + .byte 1 + .byte 1 + .byte 1 + .byte -5 + .byte 14 + .byte 1 + .byte 1 + .byte 1 + .byte 8 + .byte 2 + .byte 46 + .byte 0 + .byte 46 + .byte 0 + .byte 3 + .byte 1 + .byte 8 + .byte 2 + .byte 15 + .byte 5 + .byte 30 + .byte 2 + .ascii "main.cpp" + .byte 0 + .byte 0 + .byte 0x9c, 0x5c, 0xea, 0x5b + .byte 0xb7, 0x8d, 0x3f, 0xc2 + .byte 0x65, 0xcd, 0x17, 0x51 + .byte 0x10, 0xbf, 0xe9, 0x03 + .ascii "header.h" + .byte 0 + .byte 1 + .byte 0xfe, 0xa7, 0xbb, 0x1f + .byte 0x22, 0xc4, 0x7f, 0x12 + .byte 0x9e, 0x15, 0x69, 0x5f + .byte 0x71, 0x37, 0xa1, 0xe7 +.Lprologue_end0: +.Ldebug_line_end0: + .section .debug_addr,"",@progbits + .long .Ldebug_addr_end0-.Ldebug_addr_start0 # Length of contribution +.Ldebug_addr_start0: + .short 5 # DWARF version number + .byte 8 # Address size + .byte 0 # Segment selector size +.Laddr_table_base0: + .quad .Lfunc_begin0 +.Ldebug_addr_end0: + .ident "clang version 18.0.0" + .section ".note.GNU-stack","",@progbits + .addrsig + .section .debug_line,"",@progbits +.Lline_table_start0: diff --git a/bolt/test/X86/cdsplit-call-scale.s b/bolt/test/X86/cdsplit-call-scale.s new file mode 100644 index 0000000000000000000000000000000000000000..5b4f92832624cd5eb9e2d1f1ffdd601c9893cbf8 --- /dev/null +++ b/bolt/test/X86/cdsplit-call-scale.s @@ -0,0 +1,137 @@ +# Test the control of aggressiveness of 3-way splitting by -call-scale. +# When -call-scale=0.0, the tested function is 2-way splitted. +# When -call-scale=1.0, the tested function is 3-way splitted with 5 blocks +# in warm because of the increased benefit of shortening the call edges. +# When -call-scale=1000.0, the tested function is 3-way splitted with 7 blocks +# in warm because of the strong benefit of shortening the call edges. + +# RUN: llvm-mc --filetype=obj --triple x86_64-unknown-unknown %s -o %t.o +# RUN: link_fdata %s %t.o %t.fdata +# RUN: llvm-strip --strip-unneeded %t.o +# RUN: %clang %cflags %t.o -o %t.exe -Wl,-q +# RUN: llvm-bolt %t.exe -o %t.bolt --split-functions --split-strategy=cdsplit \ +# RUN: --call-scale=0.0 --print-split --print-only=chain \ +# RUN: --data=%t.fdata --reorder-blocks=ext-tsp \ +# RUN: 2>&1 | FileCheck --check-prefix=LOWINCENTIVE %s +# RUN: llvm-bolt %t.exe -o %t.bolt --split-functions --split-strategy=cdsplit \ +# RUN: --call-scale=1.0 --print-split --print-only=chain \ +# RUN: --data=%t.fdata --reorder-blocks=ext-tsp \ +# RUN: 2>&1 | FileCheck --check-prefix=MEDINCENTIVE %s +# RUN: llvm-bolt %t.exe -o %t.bolt --split-functions --split-strategy=cdsplit \ +# RUN: --call-scale=1000.0 --print-split --print-only=chain \ +# RUN: --data=%t.fdata --reorder-blocks=ext-tsp \ +# RUN: 2>&1 | FileCheck --check-prefix=HIGHINCENTIVE %s + +# LOWINCENTIVE: Binary Function "chain" after split-functions +# LOWINCENTIVE: {{^\.Ltmp5}} +# LOWINCENTIVE: ------- HOT-COLD SPLIT POINT ------- +# LOWINCENTIVE: {{^\.LFT1}} + +# MEDINCENTIVE: Binary Function "chain" after split-functions +# MEDINCENTIVE: {{^\.Ltmp1}} +# MEDINCENTIVE: ------- HOT-COLD SPLIT POINT ------- +# MEDINCENTIVE: {{^\.LFT1}} +# MEDINCENTIVE: ------- HOT-COLD SPLIT POINT ------- +# MEDINCENTIVE: {{^\.Ltmp0}} +# MEDINCENTIVE: {{^\.Ltmp2}} +# MEDINCENTIVE: {{^\.Ltmp3}} +# MEDINCENTIVE: {{^\.Ltmp4}} +# MEDINCENTIVE: {{^\.Ltmp5}} + +# HIGHINCENTIVE: Binary Function "chain" after split-functions +# HIGHINCENTIVE: {{^\.LBB00}} +# HIGHINCENTIVE: ------- HOT-COLD SPLIT POINT ------- +# HIGHINCENTIVE: {{^\.LFT1}} +# HIGHINCENTIVE: ------- HOT-COLD SPLIT POINT ------- +# HIGHINCENTIVE: {{^\.LFT0}} +# HIGHINCENTIVE: {{^\.Ltmp1}} +# HIGHINCENTIVE: {{^\.Ltmp0}} +# HIGHINCENTIVE: {{^\.Ltmp2}} +# HIGHINCENTIVE: {{^\.Ltmp3}} +# HIGHINCENTIVE: {{^\.Ltmp4}} +# HIGHINCENTIVE: {{^\.Ltmp5}} + + + + .text + .globl chain + .type chain, @function +chain: + pushq %rbp + movq %rsp, %rbp + cmpl $2, %edi +LLentry_LLchain_start: + jge LLchain_start +# FDATA: 1 chain #LLentry_LLchain_start# 1 chain #LLchain_start# 0 10 +# FDATA: 1 chain #LLentry_LLchain_start# 1 chain #LLfast# 0 500 +LLfast: + movl $5, %eax +LLfast_LLexit: + jmp LLexit +# FDATA: 1 chain #LLfast_LLexit# 1 chain #LLexit# 0 500 +LLchain_start: + movl $10, %eax +LLchain_start_LLchain1: + jge LLchain1 +# FDATA: 1 chain #LLchain_start_LLchain1# 1 chain #LLchain1# 0 10 +# FDATA: 1 chain #LLchain_start_LLchain1# 1 chain #LLcold# 0 0 +LLcold: + addl $1, %eax + addl $1, %eax + addl $1, %eax + addl $1, %eax + addl $1, %eax + addl $1, %eax +LLchain1: + addl $1, %eax +LLchain1_LLchain2: + jmp LLchain2 +# FDATA: 1 chain #LLchain1_LLchain2# 1 chain #LLchain2# 0 10 +LLchain2: + addl $1, %eax +LLchain2_LLchain3: + jmp LLchain3 +# FDATA: 1 chain #LLchain2_LLchain3# 1 chain #LLchain3# 0 10 +LLchain3: + addl $1, %eax + addl $1, %eax + addl $1, %eax + addl $1, %eax + addl $1, %eax +LLchain3_LLchain4: + jmp LLchain4 +# FDATA: 1 chain #LLchain3_LLchain4# 1 chain #LLchain4# 0 10 +LLchain4: + addl $1, %eax + addl $1, %eax + addl $1, %eax + addl $1, %eax + addl $1, %eax +LLchain4_LLexit: + jmp LLexit +# FDATA: 1 chain #LLchain4_LLexit# 1 chain #LLexit# 0 10 +LLexit: + popq %rbp + ret +LLchain_end: + .size chain, LLchain_end-chain + + + .globl main + .type main, @function +main: + pushq %rbp + movq %rsp, %rbp + movl $1, %edi +LLmain_chain1: + call chain +# FDATA: 1 main #LLmain_chain1# 1 chain 0 0 500 + movl $4, %edi +LLmain_chain2: + call chain +# FDATA: 1 main #LLmain_chain2# 1 chain 0 0 10 + xorl %eax, %eax + popq %rbp + retq +.Lmain_end: + .size main, .Lmain_end-main diff --git a/bolt/test/X86/cdsplit-symbol-names.s b/bolt/test/X86/cdsplit-symbol-names.s new file mode 100644 index 0000000000000000000000000000000000000000..e2259276e2554c406223f9d209a52863fa184ab0 --- /dev/null +++ b/bolt/test/X86/cdsplit-symbol-names.s @@ -0,0 +1,147 @@ +# Test the correctness of section names and function symbol names post cdsplit. +# Warm section should have name .text.warm and warm function fragments should +# have symbol names ending in warm. + +# RUN: llvm-mc --filetype=obj --triple x86_64-unknown-unknown %s -o %t.o +# RUN: link_fdata %s %t.o %t.fdata +# RUN: llvm-strip --strip-unneeded %t.o +# RUN: %clang %cflags %t.o -o %t.exe -Wl,-q +# RUN: llvm-bolt %t.exe -o %t.bolt --split-functions --split-strategy=cdsplit \ +# RUN: --call-scale=2 --data=%t.fdata --reorder-blocks=ext-tsp +# RUN: llvm-objdump --syms %t.bolt | FileCheck %s --check-prefix=CHECK-SYMS-WARM + +# CHECK-SYMS-WARM: .text.warm +# CHECK-SYMS-WARM-SAME: chain.warm +# CHECK-SYMS-WARM: .text.cold +# CHECK-SYMS-WARM-SAME: dummy.cold + + .text + .globl chain + .type chain, @function +chain: + pushq %rbp + movq %rsp, %rbp + cmpl $2, %edi +LLentry_LLchain_start: + jge LLchain_start +# FDATA: 1 chain #LLentry_LLchain_start# 1 chain #LLchain_start# 0 100 +# FDATA: 1 chain #LLentry_LLchain_start# 1 chain #LLfast# 0 500 +LLfast: + movl $5, %eax +LLfast_LLexit: + jmp LLexit +# FDATA: 1 chain #LLfast_LLexit# 1 chain #LLexit# 0 500 +LLchain_start: + movl $10, %eax +LLchain_start_LLchain1: + jge LLchain1 +# FDATA: 1 chain #LLchain_start_LLchain1# 1 chain #LLchain1# 0 99 +# FDATA: 1 chain #LLchain_start_LLchain1# 1 chain #LLloop_entry# 0 1 +LLloop_entry: + addl $1, %eax + addl $1, %eax + addl $1, %eax + addl $1, %eax + xorl %eax, %eax // Initialize result + movl $1000000, %ecx // Set loop counter to a large value +LLloop_entry_LLloop_start: + jmp LLloop_start +# FDATA: 1 chain #LLloop_entry_LLloop_start# 1 chain #LLloop_start# 0 1 +LLloop_start: + addl $1, %eax // Increment result + subl $1, %ecx // Decrement loop counter +LLloop_start_LLloop_start: + jg LLloop_start // Jump if loop counter is greater than 0 +# FDATA: 1 chain #LLloop_start_LLloop_start# 1 chain #LLloop_start# 0 1000000 +# FDATA: 1 chain #LLloop_start_LLloop_start# 1 chain #LLchain1# 0 1 +LLchain1: + addl $1, %eax +LLchain1_LLchain2: + jmp LLchain2 +# FDATA: 1 chain #LLchain1_LLchain2# 1 chain #LLchain2# 0 100 +LLchain2: + addl $1, %eax +LLchain2_LLchain3: + jmp LLchain3 +# FDATA: 1 chain #LLchain2_LLchain3# 1 chain #LLchain3# 0 100 +LLchain3: + addl $1, %eax + addl $1, %eax + addl $1, %eax + addl $1, %eax + addl $1, %eax +LLchain3_LLchain4: + jmp LLchain4 +# FDATA: 1 chain #LLchain3_LLchain4# 1 chain #LLchain4# 0 100 +LLchain4: + addl $1, %eax + addl $1, %eax + addl $1, %eax + addl $1, %eax + addl $1, %eax +LLchain4_LLexit: + jmp LLexit +# FDATA: 1 chain #LLchain4_LLexit# 1 chain #LLexit# 0 100 +LLexit: + popq %rbp + ret +LLchain_end: + .size chain, LLchain_end-chain + + .text + .globl dummy + .type dummy, @function +dummy: + pushq %rbp + movq %rsp, %rbp + cmpl $2, %edi +dummy_dummy_block1: + jg dummy_block1 +# FDATA: 1 dummy #dummy_dummy_block1# 1 dummy #dummy_block1# 0 0 +# FDATA: 1 dummy #dummy_dummy_block1# 1 dummy #dummy_next# 0 100 +dummy_next: + addl $1, %eax + addl $1, %eax +dummy_next_dummy_exit: + jmp dummy_exit +# FDATA: 1 dummy #dummy_next_dummy_exit# 1 dummy #dummy_exit# 0 100 +dummy_block1: + addl $1, %eax + addl $1, %eax + addl $1, %eax + addl $1, %eax + addl $1, %eax +dummy_block1_dummy_block2: + jmp dummy_block2 +# FDATA: 1 dummy #dummy_block1_dummy_block2# 1 dummy #dummy_block2# 0 0 +dummy_block2: + addl $1, %eax + addl $1, %eax + addl $1, %eax + addl $1, %eax + addl $1, %eax +dummy_block2_dummy_exit: + jmp dummy_exit +# FDATA: 1 dummy #dummy_block2_dummy_exit# 1 dummy #dummy_exit# 0 0 +dummy_exit: + popq %rbp + ret + + .globl main + .type main, @function +main: + pushq %rbp + movq %rsp, %rbp + movl $1, %edi +LLmain_chain1: + call chain +# FDATA: 1 main #LLmain_chain1# 1 chain 0 0 600 + movl $4, %edi +LLmain_dummy: + call dummy +# FDATA: 1 main #LLmain_dummy# 1 dummy 0 0 100 + xorl %eax, %eax + popq %rbp + retq +.Lmain_end: + .size main, .Lmain_end-main diff --git a/bolt/test/X86/dwarf4-deleted-range.s b/bolt/test/X86/dwarf4-deleted-range.s new file mode 100644 index 0000000000000000000000000000000000000000..7938bc001c0079033d9eeb39e16fba1342b3bb2f --- /dev/null +++ b/bolt/test/X86/dwarf4-deleted-range.s @@ -0,0 +1,432 @@ +# REQUIRES: system-linux + +# RUN: llvm-mc -dwarf-version=4 -filetype=obj -triple x86_64-unknown-linux %s -o %tmain.o +# RUN: %clang %cflags -no-pie %s -o %t.exe -Wl,-q +# RUN: llvm-bolt %t.exe -o %t.bolt --update-debug-sections +# RUN: llvm-dwarfdump --show-children --name=main --debug-info %t.bolt \ +# RUN: | FileCheck %s + +# CHECK: DW_TAG_inlined_subroutine +# CHECK: DW_AT_low_pc +# CHECK-SAME: 0x0000000000000000 + +# CHECK: DW_TAG_GNU_call_site +# CHECK: DW_AT_low_pc +# CHECK-SAME: 0x0000000000000000 + +## Test that llvm-bolt correctly updates DIEs corresponding to deleted code. + +# Test case built from the following source using: +# +# clang -O2 -g ... +# +# Assembly modified with "je" -> "jmp" to introduce unreachable block. +# +# extern void puts(const char *); +# +# static void foo() { +# puts("hi"); +# } +# +# int main(int argc, char **argv) { +# if (argc) +# foo(); +# return 0; +# } + + .text + .file "unreachable.c" + .file 1 "." "unreachable.c" + .globl main # -- Begin function main + .p2align 4, 0x90 + .type main,@function +main: # @main +.Lfunc_begin0: + .loc 1 7 0 # unreachable.c:7:0 + .cfi_startproc +# %bb.0: + #DEBUG_VALUE: main:argc <- $edi + #DEBUG_VALUE: main:argv <- $rsi + .loc 1 8 7 prologue_end # unreachable.c:8:7 + testl %edi, %edi +.Ltmp0: + .loc 1 8 7 is_stmt 0 # unreachable.c:8:7 + jmp .LBB0_2 +.Ltmp1: +# %bb.1: + #DEBUG_VALUE: main:argc <- $edi + #DEBUG_VALUE: main:argv <- $rsi + pushq %rax + .cfi_def_cfa_offset 16 +.Ltmp2: + .loc 1 4 3 is_stmt 1 # unreachable.c:4:3 + movl $.L.str, %edi +.Ltmp3: + #DEBUG_VALUE: main:argc <- [DW_OP_LLVM_entry_value 1] $edi + callq puts +.Ltmp4: + #DEBUG_VALUE: main:argv <- [DW_OP_LLVM_entry_value 1] $rsi + .loc 1 0 3 is_stmt 0 # unreachable.c:0:3 + addq $8, %rsp +.Ltmp5: + .cfi_def_cfa_offset 8 +.LBB0_2: + #DEBUG_VALUE: main:argc <- [DW_OP_LLVM_entry_value 1] $edi + #DEBUG_VALUE: main:argv <- [DW_OP_LLVM_entry_value 1] $rsi + .loc 1 10 3 is_stmt 1 # unreachable.c:10:3 + xorl %eax, %eax + retq +.Ltmp6: +.Lfunc_end0: + .size main, .Lfunc_end0-main + .cfi_endproc + # -- End function + .type .L.str,@object # @.str + .section .rodata.str1.1,"aMS",@progbits,1 +.L.str: + .asciz "hi" + .size .L.str, 3 + + .section .debug_loc,"",@progbits +.Ldebug_loc0: + .quad .Lfunc_begin0-.Lfunc_begin0 + .quad .Ltmp3-.Lfunc_begin0 + .short 1 # Loc expr size + .byte 85 # super-register DW_OP_reg5 + .quad .Ltmp3-.Lfunc_begin0 + .quad .Lfunc_end0-.Lfunc_begin0 + .short 4 # Loc expr size + .byte 243 # DW_OP_GNU_entry_value + .byte 1 # 1 + .byte 85 # super-register DW_OP_reg5 + .byte 159 # DW_OP_stack_value + .quad 0 + .quad 0 +.Ldebug_loc1: + .quad .Lfunc_begin0-.Lfunc_begin0 + .quad .Ltmp4-.Lfunc_begin0 + .short 1 # Loc expr size + .byte 84 # DW_OP_reg4 + .quad .Ltmp4-.Lfunc_begin0 + .quad .Lfunc_end0-.Lfunc_begin0 + .short 4 # Loc expr size + .byte 243 # DW_OP_GNU_entry_value + .byte 1 # 1 + .byte 84 # DW_OP_reg4 + .byte 159 # DW_OP_stack_value + .quad 0 + .quad 0 + .section .debug_abbrev,"",@progbits + .byte 1 # Abbreviation Code + .byte 17 # DW_TAG_compile_unit + .byte 1 # DW_CHILDREN_yes + .byte 37 # DW_AT_producer + .byte 14 # DW_FORM_strp + .byte 19 # DW_AT_language + .byte 5 # DW_FORM_data2 + .byte 3 # DW_AT_name + .byte 14 # DW_FORM_strp + .byte 16 # DW_AT_stmt_list + .byte 23 # DW_FORM_sec_offset + .byte 27 # DW_AT_comp_dir + .byte 14 # DW_FORM_strp + .byte 17 # DW_AT_low_pc + .byte 1 # DW_FORM_addr + .byte 18 # DW_AT_high_pc + .byte 6 # DW_FORM_data4 + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 2 # Abbreviation Code + .byte 52 # DW_TAG_variable + .byte 0 # DW_CHILDREN_no + .byte 73 # DW_AT_type + .byte 19 # DW_FORM_ref4 + .byte 58 # DW_AT_decl_file + .byte 11 # DW_FORM_data1 + .byte 59 # DW_AT_decl_line + .byte 11 # DW_FORM_data1 + .byte 2 # DW_AT_location + .byte 24 # DW_FORM_exprloc + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 3 # Abbreviation Code + .byte 1 # DW_TAG_array_type + .byte 1 # DW_CHILDREN_yes + .byte 73 # DW_AT_type + .byte 19 # DW_FORM_ref4 + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 4 # Abbreviation Code + .byte 33 # DW_TAG_subrange_type + .byte 0 # DW_CHILDREN_no + .byte 73 # DW_AT_type + .byte 19 # DW_FORM_ref4 + .byte 55 # DW_AT_count + .byte 11 # DW_FORM_data1 + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 5 # Abbreviation Code + .byte 36 # DW_TAG_base_type + .byte 0 # DW_CHILDREN_no + .byte 3 # DW_AT_name + .byte 14 # DW_FORM_strp + .byte 62 # DW_AT_encoding + .byte 11 # DW_FORM_data1 + .byte 11 # DW_AT_byte_size + .byte 11 # DW_FORM_data1 + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 6 # Abbreviation Code + .byte 36 # DW_TAG_base_type + .byte 0 # DW_CHILDREN_no + .byte 3 # DW_AT_name + .byte 14 # DW_FORM_strp + .byte 11 # DW_AT_byte_size + .byte 11 # DW_FORM_data1 + .byte 62 # DW_AT_encoding + .byte 11 # DW_FORM_data1 + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 7 # Abbreviation Code + .byte 46 # DW_TAG_subprogram + .byte 0 # DW_CHILDREN_no + .byte 3 # DW_AT_name + .byte 14 # DW_FORM_strp + .byte 58 # DW_AT_decl_file + .byte 11 # DW_FORM_data1 + .byte 59 # DW_AT_decl_line + .byte 11 # DW_FORM_data1 + .byte 32 # DW_AT_inline + .byte 11 # DW_FORM_data1 + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 8 # Abbreviation Code + .byte 46 # DW_TAG_subprogram + .byte 1 # DW_CHILDREN_yes + .byte 17 # DW_AT_low_pc + .byte 1 # DW_FORM_addr + .byte 18 # DW_AT_high_pc + .byte 6 # DW_FORM_data4 + .byte 64 # DW_AT_frame_base + .byte 24 # DW_FORM_exprloc + .ascii "\227B" # DW_AT_GNU_all_call_sites + .byte 25 # DW_FORM_flag_present + .byte 3 # DW_AT_name + .byte 14 # DW_FORM_strp + .byte 58 # DW_AT_decl_file + .byte 11 # DW_FORM_data1 + .byte 59 # DW_AT_decl_line + .byte 11 # DW_FORM_data1 + .byte 39 # DW_AT_prototyped + .byte 25 # DW_FORM_flag_present + .byte 73 # DW_AT_type + .byte 19 # DW_FORM_ref4 + .byte 63 # DW_AT_external + .byte 25 # DW_FORM_flag_present + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 9 # Abbreviation Code + .byte 5 # DW_TAG_formal_parameter + .byte 0 # DW_CHILDREN_no + .byte 2 # DW_AT_location + .byte 23 # DW_FORM_sec_offset + .byte 3 # DW_AT_name + .byte 14 # DW_FORM_strp + .byte 58 # DW_AT_decl_file + .byte 11 # DW_FORM_data1 + .byte 59 # DW_AT_decl_line + .byte 11 # DW_FORM_data1 + .byte 73 # DW_AT_type + .byte 19 # DW_FORM_ref4 + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 10 # Abbreviation Code + .byte 29 # DW_TAG_inlined_subroutine + .byte 0 # DW_CHILDREN_no + .byte 49 # DW_AT_abstract_origin + .byte 19 # DW_FORM_ref4 + .byte 17 # DW_AT_low_pc + .byte 1 # DW_FORM_addr + .byte 18 # DW_AT_high_pc + .byte 6 # DW_FORM_data4 + .byte 88 # DW_AT_call_file + .byte 11 # DW_FORM_data1 + .byte 89 # DW_AT_call_line + .byte 11 # DW_FORM_data1 + .byte 87 # DW_AT_call_column + .byte 11 # DW_FORM_data1 + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 11 # Abbreviation Code + .ascii "\211\202\001" # DW_TAG_GNU_call_site + .byte 0 # DW_CHILDREN_no + .byte 49 # DW_AT_abstract_origin + .byte 19 # DW_FORM_ref4 + .byte 17 # DW_AT_low_pc + .byte 1 # DW_FORM_addr + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 12 # Abbreviation Code + .byte 46 # DW_TAG_subprogram + .byte 1 # DW_CHILDREN_yes + .byte 3 # DW_AT_name + .byte 14 # DW_FORM_strp + .byte 58 # DW_AT_decl_file + .byte 11 # DW_FORM_data1 + .byte 59 # DW_AT_decl_line + .byte 11 # DW_FORM_data1 + .byte 39 # DW_AT_prototyped + .byte 25 # DW_FORM_flag_present + .byte 60 # DW_AT_declaration + .byte 25 # DW_FORM_flag_present + .byte 63 # DW_AT_external + .byte 25 # DW_FORM_flag_present + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 13 # Abbreviation Code + .byte 5 # DW_TAG_formal_parameter + .byte 0 # DW_CHILDREN_no + .byte 73 # DW_AT_type + .byte 19 # DW_FORM_ref4 + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 14 # Abbreviation Code + .byte 15 # DW_TAG_pointer_type + .byte 0 # DW_CHILDREN_no + .byte 73 # DW_AT_type + .byte 19 # DW_FORM_ref4 + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 15 # Abbreviation Code + .byte 38 # DW_TAG_const_type + .byte 0 # DW_CHILDREN_no + .byte 73 # DW_AT_type + .byte 19 # DW_FORM_ref4 + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 0 # EOM(3) + .section .debug_info,"",@progbits +.Lcu_begin0: + .long .Ldebug_info_end0-.Ldebug_info_start0 # Length of Unit +.Ldebug_info_start0: + .short 4 # DWARF version number + .long .debug_abbrev # Offset Into Abbrev. Section + .byte 8 # Address Size (in bytes) + .byte 1 # Abbrev [1] 0xb:0xd4 DW_TAG_compile_unit + .long .Linfo_string0 # DW_AT_producer + .short 12 # DW_AT_language + .long .Linfo_string1 # DW_AT_name + .long .Lline_table_start0 # DW_AT_stmt_list + .long .Linfo_string2 # DW_AT_comp_dir + .quad .Lfunc_begin0 # DW_AT_low_pc + .long .Lfunc_end0-.Lfunc_begin0 # DW_AT_high_pc + .byte 2 # Abbrev [2] 0x2a:0x11 DW_TAG_variable + .long 59 # DW_AT_type + .byte 1 # DW_AT_decl_file + .byte 4 # DW_AT_decl_line + .byte 9 # DW_AT_location + .byte 3 + .quad .L.str + .byte 3 # Abbrev [3] 0x3b:0xc DW_TAG_array_type + .long 71 # DW_AT_type + .byte 4 # Abbrev [4] 0x40:0x6 DW_TAG_subrange_type + .long 78 # DW_AT_type + .byte 3 # DW_AT_count + .byte 0 # End Of Children Mark + .byte 5 # Abbrev [5] 0x47:0x7 DW_TAG_base_type + .long .Linfo_string3 # DW_AT_name + .byte 6 # DW_AT_encoding + .byte 1 # DW_AT_byte_size + .byte 6 # Abbrev [6] 0x4e:0x7 DW_TAG_base_type + .long .Linfo_string4 # DW_AT_name + .byte 8 # DW_AT_byte_size + .byte 7 # DW_AT_encoding + .byte 7 # Abbrev [7] 0x55:0x8 DW_TAG_subprogram + .long .Linfo_string5 # DW_AT_name + .byte 1 # DW_AT_decl_file + .byte 3 # DW_AT_decl_line + .byte 1 # DW_AT_inline + .byte 8 # Abbrev [8] 0x5d:0x59 DW_TAG_subprogram + .quad .Lfunc_begin0 # DW_AT_low_pc + .long .Lfunc_end0-.Lfunc_begin0 # DW_AT_high_pc + .byte 1 # DW_AT_frame_base + .byte 87 + # DW_AT_GNU_all_call_sites + .long .Linfo_string7 # DW_AT_name + .byte 1 # DW_AT_decl_file + .byte 7 # DW_AT_decl_line + # DW_AT_prototyped + .long 205 # DW_AT_type + # DW_AT_external + .byte 9 # Abbrev [9] 0x76:0xf DW_TAG_formal_parameter + .long .Ldebug_loc0 # DW_AT_location + .long .Linfo_string9 # DW_AT_name + .byte 1 # DW_AT_decl_file + .byte 7 # DW_AT_decl_line + .long 205 # DW_AT_type + .byte 9 # Abbrev [9] 0x85:0xf DW_TAG_formal_parameter + .long .Ldebug_loc1 # DW_AT_location + .long .Linfo_string10 # DW_AT_name + .byte 1 # DW_AT_decl_file + .byte 7 # DW_AT_decl_line + .long 212 # DW_AT_type + .byte 10 # Abbrev [10] 0x94:0x14 DW_TAG_inlined_subroutine + .long 85 # DW_AT_abstract_origin + .quad .Ltmp2 # DW_AT_low_pc + .long .Ltmp5-.Ltmp2 # DW_AT_high_pc + .byte 1 # DW_AT_call_file + .byte 9 # DW_AT_call_line + .byte 5 # DW_AT_call_column + .byte 11 # Abbrev [11] 0xa8:0xd DW_TAG_GNU_call_site + .long 182 # DW_AT_abstract_origin + .quad .Ltmp4 # DW_AT_low_pc + .byte 0 # End Of Children Mark + .byte 12 # Abbrev [12] 0xb6:0xd DW_TAG_subprogram + .long .Linfo_string6 # DW_AT_name + .byte 1 # DW_AT_decl_file + .byte 1 # DW_AT_decl_line + # DW_AT_prototyped + # DW_AT_declaration + # DW_AT_external + .byte 13 # Abbrev [13] 0xbd:0x5 DW_TAG_formal_parameter + .long 195 # DW_AT_type + .byte 0 # End Of Children Mark + .byte 14 # Abbrev [14] 0xc3:0x5 DW_TAG_pointer_type + .long 200 # DW_AT_type + .byte 15 # Abbrev [15] 0xc8:0x5 DW_TAG_const_type + .long 71 # DW_AT_type + .byte 5 # Abbrev [5] 0xcd:0x7 DW_TAG_base_type + .long .Linfo_string8 # DW_AT_name + .byte 5 # DW_AT_encoding + .byte 4 # DW_AT_byte_size + .byte 14 # Abbrev [14] 0xd4:0x5 DW_TAG_pointer_type + .long 217 # DW_AT_type + .byte 14 # Abbrev [14] 0xd9:0x5 DW_TAG_pointer_type + .long 71 # DW_AT_type + .byte 0 # End Of Children Mark +.Ldebug_info_end0: + .section .debug_str,"MS",@progbits,1 +.Linfo_string0: + .asciz "clang version 15.0.7 " # string offset=0 +.Linfo_string1: + .asciz "unreachable.c" # string offset=69 +.Linfo_string2: + .asciz "." # string offset=83 +.Linfo_string3: + .asciz "char" # string offset=85 +.Linfo_string4: + .asciz "__ARRAY_SIZE_TYPE__" # string offset=90 +.Linfo_string5: + .asciz "foo" # string offset=110 +.Linfo_string6: + .asciz "puts" # string offset=114 +.Linfo_string7: + .asciz "main" # string offset=119 +.Linfo_string8: + .asciz "int" # string offset=124 +.Linfo_string9: + .asciz "argc" # string offset=128 +.Linfo_string10: + .asciz "argv" # string offset=133 + .section .debug_line,"",@progbits +.Lline_table_start0: diff --git a/bolt/test/X86/dwarf5-deleted-range.s b/bolt/test/X86/dwarf5-deleted-range.s new file mode 100644 index 0000000000000000000000000000000000000000..241c694ce89b3c6df58872f1fe8915ea867ff2c3 --- /dev/null +++ b/bolt/test/X86/dwarf5-deleted-range.s @@ -0,0 +1,484 @@ +# REQUIRES: system-linux + +# RUN: llvm-mc -dwarf-version=5 -filetype=obj -triple x86_64-unknown-linux %s -o %tmain.o +# RUN: %clang %cflags -no-pie %s -o %t.exe -Wl,-q +# RUN: llvm-bolt %t.exe -o %t.bolt --update-debug-sections +# RUN: llvm-dwarfdump --show-children --name=main --debug-info %t.bolt \ +# RUN: | FileCheck %s + +# CHECK: DW_TAG_inlined_subroutine +# CHECK: DW_AT_low_pc +# CHECK-SAME: 0x0000000000000000 + +# CHECK: DW_TAG_call_site +# CHECK: DW_AT_call_return_pc +# CHECK-SAME: 0x0000000000000000 + +## Test that llvm-bolt correctly updates DIEs corresponding to deleted code. + +# Test case built from the following source using: +# +# clang -O2 -g -gdwarf-5 ... +# +# Assembly modified with "je" -> "jmp" to introduce unreachable block. +# +# extern void puts(const char *); +# +# static void foo() { +# puts("hi"); +# } +# +# int main(int argc, char **argv) { +# if (argc) +# foo(); +# return 0; +# } + + .text + .file "unreachable.c" + .file 0 "." "unreachable.c" md5 0xd268d8460ea246544a440d4d42235947 + .globl main # -- Begin function main + .p2align 4, 0x90 + .type main,@function +main: # @main +.Lfunc_begin0: + .loc 0 7 0 # unreachable.c:7:0 + .cfi_startproc +# %bb.0: + #DEBUG_VALUE: main:argc <- $edi + #DEBUG_VALUE: main:argv <- $rsi + .loc 0 8 7 prologue_end # unreachable.c:8:7 + testl %edi, %edi +.Ltmp0: + .loc 0 8 7 is_stmt 0 # unreachable.c:8:7 + jmp .LBB0_2 +.Ltmp1: +# %bb.1: + #DEBUG_VALUE: main:argc <- $edi + #DEBUG_VALUE: main:argv <- $rsi + pushq %rax + .cfi_def_cfa_offset 16 +.Ltmp2: + .loc 0 4 3 is_stmt 1 # unreachable.c:4:3 + movl $.L.str, %edi +.Ltmp3: + #DEBUG_VALUE: main:argc <- [DW_OP_LLVM_entry_value 1] $edi + callq puts +.Ltmp4: + #DEBUG_VALUE: main:argv <- [DW_OP_LLVM_entry_value 1] $rsi + .loc 0 0 3 is_stmt 0 # unreachable.c:0:3 + addq $8, %rsp +.Ltmp5: + .cfi_def_cfa_offset 8 +.LBB0_2: + #DEBUG_VALUE: main:argc <- [DW_OP_LLVM_entry_value 1] $edi + #DEBUG_VALUE: main:argv <- [DW_OP_LLVM_entry_value 1] $rsi + .loc 0 10 3 is_stmt 1 # unreachable.c:10:3 + xorl %eax, %eax + retq +.Ltmp6: +.Lfunc_end0: + .size main, .Lfunc_end0-main + .cfi_endproc + # -- End function + .type .L.str,@object # @.str + .section .rodata.str1.1,"aMS",@progbits,1 +.L.str: + .asciz "hi" + .size .L.str, 3 + + .section .debug_loclists,"",@progbits + .long .Ldebug_list_header_end0-.Ldebug_list_header_start0 # Length +.Ldebug_list_header_start0: + .short 5 # Version + .byte 8 # Address size + .byte 0 # Segment selector size + .long 2 # Offset entry count +.Lloclists_table_base0: + .long .Ldebug_loc0-.Lloclists_table_base0 + .long .Ldebug_loc1-.Lloclists_table_base0 +.Ldebug_loc0: + .byte 4 # DW_LLE_offset_pair + .uleb128 .Lfunc_begin0-.Lfunc_begin0 # starting offset + .uleb128 .Ltmp3-.Lfunc_begin0 # ending offset + .byte 1 # Loc expr size + .byte 85 # super-register DW_OP_reg5 + .byte 4 # DW_LLE_offset_pair + .uleb128 .Ltmp3-.Lfunc_begin0 # starting offset + .uleb128 .Lfunc_end0-.Lfunc_begin0 # ending offset + .byte 4 # Loc expr size + .byte 163 # DW_OP_entry_value + .byte 1 # 1 + .byte 85 # super-register DW_OP_reg5 + .byte 159 # DW_OP_stack_value + .byte 0 # DW_LLE_end_of_list +.Ldebug_loc1: + .byte 4 # DW_LLE_offset_pair + .uleb128 .Lfunc_begin0-.Lfunc_begin0 # starting offset + .uleb128 .Ltmp4-.Lfunc_begin0 # ending offset + .byte 1 # Loc expr size + .byte 84 # DW_OP_reg4 + .byte 4 # DW_LLE_offset_pair + .uleb128 .Ltmp4-.Lfunc_begin0 # starting offset + .uleb128 .Lfunc_end0-.Lfunc_begin0 # ending offset + .byte 4 # Loc expr size + .byte 163 # DW_OP_entry_value + .byte 1 # 1 + .byte 84 # DW_OP_reg4 + .byte 159 # DW_OP_stack_value + .byte 0 # DW_LLE_end_of_list +.Ldebug_list_header_end0: + .section .debug_abbrev,"",@progbits + .byte 1 # Abbreviation Code + .byte 17 # DW_TAG_compile_unit + .byte 1 # DW_CHILDREN_yes + .byte 37 # DW_AT_producer + .byte 37 # DW_FORM_strx1 + .byte 19 # DW_AT_language + .byte 5 # DW_FORM_data2 + .byte 3 # DW_AT_name + .byte 37 # DW_FORM_strx1 + .byte 114 # DW_AT_str_offsets_base + .byte 23 # DW_FORM_sec_offset + .byte 16 # DW_AT_stmt_list + .byte 23 # DW_FORM_sec_offset + .byte 27 # DW_AT_comp_dir + .byte 37 # DW_FORM_strx1 + .byte 17 # DW_AT_low_pc + .byte 27 # DW_FORM_addrx + .byte 18 # DW_AT_high_pc + .byte 6 # DW_FORM_data4 + .byte 115 # DW_AT_addr_base + .byte 23 # DW_FORM_sec_offset + .ascii "\214\001" # DW_AT_loclists_base + .byte 23 # DW_FORM_sec_offset + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 2 # Abbreviation Code + .byte 52 # DW_TAG_variable + .byte 0 # DW_CHILDREN_no + .byte 73 # DW_AT_type + .byte 19 # DW_FORM_ref4 + .byte 58 # DW_AT_decl_file + .byte 11 # DW_FORM_data1 + .byte 59 # DW_AT_decl_line + .byte 11 # DW_FORM_data1 + .byte 2 # DW_AT_location + .byte 24 # DW_FORM_exprloc + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 3 # Abbreviation Code + .byte 1 # DW_TAG_array_type + .byte 1 # DW_CHILDREN_yes + .byte 73 # DW_AT_type + .byte 19 # DW_FORM_ref4 + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 4 # Abbreviation Code + .byte 33 # DW_TAG_subrange_type + .byte 0 # DW_CHILDREN_no + .byte 73 # DW_AT_type + .byte 19 # DW_FORM_ref4 + .byte 55 # DW_AT_count + .byte 11 # DW_FORM_data1 + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 5 # Abbreviation Code + .byte 36 # DW_TAG_base_type + .byte 0 # DW_CHILDREN_no + .byte 3 # DW_AT_name + .byte 37 # DW_FORM_strx1 + .byte 62 # DW_AT_encoding + .byte 11 # DW_FORM_data1 + .byte 11 # DW_AT_byte_size + .byte 11 # DW_FORM_data1 + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 6 # Abbreviation Code + .byte 36 # DW_TAG_base_type + .byte 0 # DW_CHILDREN_no + .byte 3 # DW_AT_name + .byte 37 # DW_FORM_strx1 + .byte 11 # DW_AT_byte_size + .byte 11 # DW_FORM_data1 + .byte 62 # DW_AT_encoding + .byte 11 # DW_FORM_data1 + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 7 # Abbreviation Code + .byte 46 # DW_TAG_subprogram + .byte 0 # DW_CHILDREN_no + .byte 3 # DW_AT_name + .byte 37 # DW_FORM_strx1 + .byte 58 # DW_AT_decl_file + .byte 11 # DW_FORM_data1 + .byte 59 # DW_AT_decl_line + .byte 11 # DW_FORM_data1 + .byte 32 # DW_AT_inline + .byte 33 # DW_FORM_implicit_const + .byte 1 + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 8 # Abbreviation Code + .byte 46 # DW_TAG_subprogram + .byte 1 # DW_CHILDREN_yes + .byte 17 # DW_AT_low_pc + .byte 27 # DW_FORM_addrx + .byte 18 # DW_AT_high_pc + .byte 6 # DW_FORM_data4 + .byte 64 # DW_AT_frame_base + .byte 24 # DW_FORM_exprloc + .byte 122 # DW_AT_call_all_calls + .byte 25 # DW_FORM_flag_present + .byte 3 # DW_AT_name + .byte 37 # DW_FORM_strx1 + .byte 58 # DW_AT_decl_file + .byte 11 # DW_FORM_data1 + .byte 59 # DW_AT_decl_line + .byte 11 # DW_FORM_data1 + .byte 39 # DW_AT_prototyped + .byte 25 # DW_FORM_flag_present + .byte 73 # DW_AT_type + .byte 19 # DW_FORM_ref4 + .byte 63 # DW_AT_external + .byte 25 # DW_FORM_flag_present + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 9 # Abbreviation Code + .byte 5 # DW_TAG_formal_parameter + .byte 0 # DW_CHILDREN_no + .byte 2 # DW_AT_location + .byte 34 # DW_FORM_loclistx + .byte 3 # DW_AT_name + .byte 37 # DW_FORM_strx1 + .byte 58 # DW_AT_decl_file + .byte 11 # DW_FORM_data1 + .byte 59 # DW_AT_decl_line + .byte 11 # DW_FORM_data1 + .byte 73 # DW_AT_type + .byte 19 # DW_FORM_ref4 + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 10 # Abbreviation Code + .byte 29 # DW_TAG_inlined_subroutine + .byte 0 # DW_CHILDREN_no + .byte 49 # DW_AT_abstract_origin + .byte 19 # DW_FORM_ref4 + .byte 17 # DW_AT_low_pc + .byte 27 # DW_FORM_addrx + .byte 18 # DW_AT_high_pc + .byte 6 # DW_FORM_data4 + .byte 88 # DW_AT_call_file + .byte 11 # DW_FORM_data1 + .byte 89 # DW_AT_call_line + .byte 11 # DW_FORM_data1 + .byte 87 # DW_AT_call_column + .byte 11 # DW_FORM_data1 + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 11 # Abbreviation Code + .byte 72 # DW_TAG_call_site + .byte 0 # DW_CHILDREN_no + .byte 127 # DW_AT_call_origin + .byte 19 # DW_FORM_ref4 + .byte 125 # DW_AT_call_return_pc + .byte 27 # DW_FORM_addrx + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 12 # Abbreviation Code + .byte 46 # DW_TAG_subprogram + .byte 1 # DW_CHILDREN_yes + .byte 3 # DW_AT_name + .byte 37 # DW_FORM_strx1 + .byte 58 # DW_AT_decl_file + .byte 11 # DW_FORM_data1 + .byte 59 # DW_AT_decl_line + .byte 11 # DW_FORM_data1 + .byte 39 # DW_AT_prototyped + .byte 25 # DW_FORM_flag_present + .byte 60 # DW_AT_declaration + .byte 25 # DW_FORM_flag_present + .byte 63 # DW_AT_external + .byte 25 # DW_FORM_flag_present + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 13 # Abbreviation Code + .byte 5 # DW_TAG_formal_parameter + .byte 0 # DW_CHILDREN_no + .byte 73 # DW_AT_type + .byte 19 # DW_FORM_ref4 + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 14 # Abbreviation Code + .byte 15 # DW_TAG_pointer_type + .byte 0 # DW_CHILDREN_no + .byte 73 # DW_AT_type + .byte 19 # DW_FORM_ref4 + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 15 # Abbreviation Code + .byte 38 # DW_TAG_const_type + .byte 0 # DW_CHILDREN_no + .byte 73 # DW_AT_type + .byte 19 # DW_FORM_ref4 + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 0 # EOM(3) + .section .debug_info,"",@progbits +.Lcu_begin0: + .long .Ldebug_info_end0-.Ldebug_info_start0 # Length of Unit +.Ldebug_info_start0: + .short 5 # DWARF version number + .byte 1 # DWARF Unit Type + .byte 8 # Address Size (in bytes) + .long .debug_abbrev # Offset Into Abbrev. Section + .byte 1 # Abbrev [1] 0xc:0x95 DW_TAG_compile_unit + .byte 0 # DW_AT_producer + .short 12 # DW_AT_language + .byte 1 # DW_AT_name + .long .Lstr_offsets_base0 # DW_AT_str_offsets_base + .long .Lline_table_start0 # DW_AT_stmt_list + .byte 2 # DW_AT_comp_dir + .byte 1 # DW_AT_low_pc + .long .Lfunc_end0-.Lfunc_begin0 # DW_AT_high_pc + .long .Laddr_table_base0 # DW_AT_addr_base + .long .Lloclists_table_base0 # DW_AT_loclists_base + .byte 2 # Abbrev [2] 0x27:0xa DW_TAG_variable + .long 49 # DW_AT_type + .byte 0 # DW_AT_decl_file + .byte 4 # DW_AT_decl_line + .byte 2 # DW_AT_location + .byte 161 + .byte 0 + .byte 3 # Abbrev [3] 0x31:0xc DW_TAG_array_type + .long 61 # DW_AT_type + .byte 4 # Abbrev [4] 0x36:0x6 DW_TAG_subrange_type + .long 65 # DW_AT_type + .byte 3 # DW_AT_count + .byte 0 # End Of Children Mark + .byte 5 # Abbrev [5] 0x3d:0x4 DW_TAG_base_type + .byte 3 # DW_AT_name + .byte 6 # DW_AT_encoding + .byte 1 # DW_AT_byte_size + .byte 6 # Abbrev [6] 0x41:0x4 DW_TAG_base_type + .byte 4 # DW_AT_name + .byte 8 # DW_AT_byte_size + .byte 7 # DW_AT_encoding + .byte 7 # Abbrev [7] 0x45:0x4 DW_TAG_subprogram + .byte 5 # DW_AT_name + .byte 0 # DW_AT_decl_file + .byte 3 # DW_AT_decl_line + # DW_AT_inline + .byte 8 # Abbrev [8] 0x49:0x35 DW_TAG_subprogram + .byte 1 # DW_AT_low_pc + .long .Lfunc_end0-.Lfunc_begin0 # DW_AT_high_pc + .byte 1 # DW_AT_frame_base + .byte 87 + # DW_AT_call_all_calls + .byte 7 # DW_AT_name + .byte 0 # DW_AT_decl_file + .byte 7 # DW_AT_decl_line + # DW_AT_prototyped + .long 146 # DW_AT_type + # DW_AT_external + .byte 9 # Abbrev [9] 0x58:0x9 DW_TAG_formal_parameter + .byte 0 # DW_AT_location + .byte 9 # DW_AT_name + .byte 0 # DW_AT_decl_file + .byte 7 # DW_AT_decl_line + .long 146 # DW_AT_type + .byte 9 # Abbrev [9] 0x61:0x9 DW_TAG_formal_parameter + .byte 1 # DW_AT_location + .byte 10 # DW_AT_name + .byte 0 # DW_AT_decl_file + .byte 7 # DW_AT_decl_line + .long 150 # DW_AT_type + .byte 10 # Abbrev [10] 0x6a:0xd DW_TAG_inlined_subroutine + .long 69 # DW_AT_abstract_origin + .byte 2 # DW_AT_low_pc + .long .Ltmp5-.Ltmp2 # DW_AT_high_pc + .byte 0 # DW_AT_call_file + .byte 9 # DW_AT_call_line + .byte 5 # DW_AT_call_column + .byte 11 # Abbrev [11] 0x77:0x6 DW_TAG_call_site + .long 126 # DW_AT_call_origin + .byte 3 # DW_AT_call_return_pc + .byte 0 # End Of Children Mark + .byte 12 # Abbrev [12] 0x7e:0xa DW_TAG_subprogram + .byte 6 # DW_AT_name + .byte 0 # DW_AT_decl_file + .byte 1 # DW_AT_decl_line + # DW_AT_prototyped + # DW_AT_declaration + # DW_AT_external + .byte 13 # Abbrev [13] 0x82:0x5 DW_TAG_formal_parameter + .long 136 # DW_AT_type + .byte 0 # End Of Children Mark + .byte 14 # Abbrev [14] 0x88:0x5 DW_TAG_pointer_type + .long 141 # DW_AT_type + .byte 15 # Abbrev [15] 0x8d:0x5 DW_TAG_const_type + .long 61 # DW_AT_type + .byte 5 # Abbrev [5] 0x92:0x4 DW_TAG_base_type + .byte 8 # DW_AT_name + .byte 5 # DW_AT_encoding + .byte 4 # DW_AT_byte_size + .byte 14 # Abbrev [14] 0x96:0x5 DW_TAG_pointer_type + .long 155 # DW_AT_type + .byte 14 # Abbrev [14] 0x9b:0x5 DW_TAG_pointer_type + .long 61 # DW_AT_type + .byte 0 # End Of Children Mark +.Ldebug_info_end0: + .section .debug_str_offsets,"",@progbits + .long 48 # Length of String Offsets Set + .short 5 + .short 0 +.Lstr_offsets_base0: + .section .debug_str,"MS",@progbits,1 +.Linfo_string0: + .asciz "clang version 15.0.7 " # string offset=0 +.Linfo_string1: + .asciz "unreachable.c" # string offset=69 +.Linfo_string2: + .asciz "." # string offset=83 +.Linfo_string3: + .asciz "char" # string offset=85 +.Linfo_string4: + .asciz "__ARRAY_SIZE_TYPE__" # string offset=90 +.Linfo_string5: + .asciz "foo" # string offset=110 +.Linfo_string6: + .asciz "puts" # string offset=114 +.Linfo_string7: + .asciz "main" # string offset=119 +.Linfo_string8: + .asciz "int" # string offset=124 +.Linfo_string9: + .asciz "argc" # string offset=128 +.Linfo_string10: + .asciz "argv" # string offset=133 + .section .debug_str_offsets,"",@progbits + .long .Linfo_string0 + .long .Linfo_string1 + .long .Linfo_string2 + .long .Linfo_string3 + .long .Linfo_string4 + .long .Linfo_string5 + .long .Linfo_string6 + .long .Linfo_string7 + .long .Linfo_string8 + .long .Linfo_string9 + .long .Linfo_string10 + .section .debug_addr,"",@progbits + .long .Ldebug_addr_end0-.Ldebug_addr_start0 # Length of contribution +.Ldebug_addr_start0: + .short 5 # DWARF version number + .byte 8 # Address size + .byte 0 # Segment selector size +.Laddr_table_base0: + .quad .L.str + .quad .Lfunc_begin0 + .quad .Ltmp2 + .quad .Ltmp4 +.Ldebug_addr_end0: + .section .debug_line,"",@progbits +.Lline_table_start0: diff --git a/bolt/test/X86/dwarf5-df-output-dir-same-name.test b/bolt/test/X86/dwarf5-df-output-dir-same-name.test index 4fd42e287a11b71fd0f54cd13f31d0ed94c4e524..1f78da2022b8ced39b8be57fcb458f11b1ffea24 100644 --- a/bolt/test/X86/dwarf5-df-output-dir-same-name.test +++ b/bolt/test/X86/dwarf5-df-output-dir-same-name.test @@ -21,3 +21,19 @@ ; BOLT: split.dwo1.dwo ; BOLT: DW_AT_dwo_name ("split.dwo0.dwo") ; BOLT: DW_AT_dwo_name ("split.dwo1.dwo") + +; Tests that when --dwarf-output-path is specified, but path do not exist BOLT creates it. + +; RUN: rm -rf dwo +; RUN: llvm-bolt main.exe -o main.exe.bolt --update-debug-sections --dwarf-output-path=%t/dwo +; RUN: ls -l %t/dwo > log +; RUN: llvm-dwarfdump --debug-info main.exe.bolt >> log +; RUN: cat log | FileCheck -check-prefix=BOLT1 %s + +; Tests that BOLT handles correctly writing out .dwo files to the same directory when input has input where part of path +; is in DW_AT_dwo_name and the .dwo file names are the same. + +; BOLT1: split.dwo0.dwo +; BOLT1: split.dwo1.dwo +; BOLT1: DW_AT_dwo_name ("split.dwo0.dwo") +; BOLT1: DW_AT_dwo_name ("split.dwo1.dwo") diff --git a/bolt/test/X86/dwarf5-df-types-dup-dwp-input.test b/bolt/test/X86/dwarf5-df-types-dup-dwp-input.test new file mode 100644 index 0000000000000000000000000000000000000000..036d4c9168ee5aa3f5941abbaa565be6c139e826 --- /dev/null +++ b/bolt/test/X86/dwarf5-df-types-dup-dwp-input.test @@ -0,0 +1,29 @@ +; RUN: rm -rf %t +; RUN: mkdir %t +; RUN: cd %t +; RUN: llvm-mc -dwarf-version=5 -filetype=obj -triple x86_64-unknown-linux %p/Inputs/dwarf5-df-types-dup-main.s \ +; RUN: -split-dwarf-file=main.dwo -o main.o +; RUN: llvm-mc -dwarf-version=5 -filetype=obj -triple x86_64-unknown-linux %p/Inputs/dwarf5-df-types-dup-helper.s \ +; RUN: -split-dwarf-file=helper.dwo -o helper.o +; RUN: %clang %cflags -gdwarf-5 -gsplit-dwarf=split main.o helper.o -o main.exe +; RUN: llvm-dwp -e main.exe -o main.exe.dwp +; RUN: llvm-bolt main.exe -o main.exe.bolt --update-debug-sections +; RUN: llvm-dwarfdump --debug-info -r 0 main.dwo.dwo | FileCheck -check-prefix=BOLT-DWO-DWO-MAIN %s +; RUN: llvm-dwarfdump --debug-info -r 0 helper.dwo.dwo | FileCheck -check-prefix=BOLT-DWO-DWO-HELPER %s + +; Tests that BOLT correctly handles DWARF5 DWP file as input. Output has correct CU, and all the type units are written out. + +; BOLT-DWO-DWO-MAIN: debug_info.dwo +; BOLT-DWO-DWO-MAIN-NEXT: type_signature = 0x49dc260088be7e56 +; BOLT-DWO-DWO-MAIN: type_signature = 0x104ec427d2ebea6f +; BOLT-DWO-DWO-MAIN: type_signature = 0xca1e65a66d92b970 +; BOLT-DWO-DWO-MAIN: Compile Unit +; BOLT-DWO-DWO-MAIN-SAME: DWO_id = 0x52bda211bf6d26b7 +; BOLT-DWO-DWO-MAIN-NOT: Compile Unit +; BOLT-DWO-DWO-HELPER: debug_info.dwo +; BOLT-DWO-DWO-HELPER-NEXT: type_signature = 0x49dc260088be7e56 +; BOLT-DWO-DWO-HELPER: type_signature = 0x104ec427d2ebea6f +; BOLT-DWO-DWO-HELPER: type_signature = 0xca1e65a66d92b970 +; BOLT-DWO-DWO-HELPER: Compile Unit +; BOLT-DWO-DWO-HELPER-SAME: DWO_id = 0x1dbb67285a49634c +; BOLT-DWO-DWO-HELPER-NOT: Compile Unit diff --git a/bolt/utils/llvm-bolt-wrapper.py b/bolt/utils/llvm-bolt-wrapper.py index cd48204026fb77cedb3afa400d90fceaeb1d21ae..b9d6fad825e7864137b277230065b275c64e4e37 100755 --- a/bolt/utils/llvm-bolt-wrapper.py +++ b/bolt/utils/llvm-bolt-wrapper.py @@ -112,7 +112,6 @@ def run_cmd(cmd, out_f, cfg): def run_bolt(bolt_path, bolt_args, out_f, cfg): p2b = os.path.basename(sys.argv[0]) == "perf2bolt" # perf2bolt mode bd = os.path.basename(sys.argv[0]) == "llvm-boltdiff" # boltdiff mode - hm = sys.argv[1] == "heatmap" # heatmap mode cmd = ["/usr/bin/time", "-f", "%e %M", bolt_path] + bolt_args if p2b: # -ignore-build-id can occur at most once, hence remove it from cmd @@ -121,7 +120,7 @@ def run_bolt(bolt_path, bolt_args, out_f, cfg): cmd += PERF2BOLT_MODE elif bd: cmd += BOLTDIFF_MODE - elif not cfg.NO_MINIMIZE and not hm: + elif not cfg.NO_MINIMIZE: cmd += MINIMIZE_DIFFS return run_cmd(cmd, out_f, cfg) diff --git a/bolt/utils/nfc-stat-parser.py b/bolt/utils/nfc-stat-parser.py index 72a21e0e24a1a7ddfd4be700e33b5a8c1c8d32e8..4a0d677ec962b3046e848b1a6408df208d6841dc 100755 --- a/bolt/utils/nfc-stat-parser.py +++ b/bolt/utils/nfc-stat-parser.py @@ -19,7 +19,7 @@ def main(): ) parser.add_argument( "--check_longer_than", - default=1, + default=2, type=float, help="Only warn on tests longer than X seconds for at least one side", ) diff --git a/clang-tools-extra/clang-tidy/abseil/StringFindStartswithCheck.h b/clang-tools-extra/clang-tidy/abseil/StringFindStartswithCheck.h index 923b5caece5439b1a85bb4ba3627a565b27046a5..de3bd4d4222000998bd2ad0624056e245184efd6 100644 --- a/clang-tools-extra/clang-tidy/abseil/StringFindStartswithCheck.h +++ b/clang-tools-extra/clang-tidy/abseil/StringFindStartswithCheck.h @@ -21,7 +21,6 @@ namespace clang::tidy::abseil { // Find string.find(...) == 0 comparisons and suggest replacing with StartsWith. // FIXME(niko): Add similar check for EndsWith -// FIXME(niko): Add equivalent modernize checks for C++20's std::starts_With class StringFindStartswithCheck : public ClangTidyCheck { public: using ClangTidyCheck::ClangTidyCheck; @@ -31,6 +30,10 @@ public: void registerMatchers(ast_matchers::MatchFinder *Finder) override; void check(const ast_matchers::MatchFinder::MatchResult &Result) override; void storeOptions(ClangTidyOptions::OptionMap &Opts) override; + bool isLanguageVersionSupported(const LangOptions &LangOpts) const override { + // Prefer modernize-use-starts-ends-with when C++20 is available. + return LangOpts.CPlusPlus && !LangOpts.CPlusPlus20; + } private: const std::vector StringLikeClasses; diff --git a/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt b/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt index 717c400c479033002a1dd8347a24f5f7283f9b4c..c40065358d2dc3d70a71eb8e21110a100990bc9e 100644 --- a/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt +++ b/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt @@ -38,6 +38,7 @@ add_clang_library(clangTidyModernizeModule UseNoexceptCheck.cpp UseNullptrCheck.cpp UseOverrideCheck.cpp + UseStartsEndsWithCheck.cpp UseStdPrintCheck.cpp UseTrailingReturnTypeCheck.cpp UseTransparentFunctorsCheck.cpp diff --git a/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp b/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp index 73751cf2705068d92c538b7f63d73a16a47549ed..e994ffd2a75c857b895f1c765128aa44ae748edc 100644 --- a/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp +++ b/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp @@ -39,6 +39,7 @@ #include "UseNoexceptCheck.h" #include "UseNullptrCheck.h" #include "UseOverrideCheck.h" +#include "UseStartsEndsWithCheck.h" #include "UseStdPrintCheck.h" #include "UseTrailingReturnTypeCheck.h" #include "UseTransparentFunctorsCheck.h" @@ -66,6 +67,8 @@ public: CheckFactories.registerCheck("modernize-make-shared"); CheckFactories.registerCheck("modernize-make-unique"); CheckFactories.registerCheck("modernize-pass-by-value"); + CheckFactories.registerCheck( + "modernize-use-starts-ends-with"); CheckFactories.registerCheck("modernize-use-std-print"); CheckFactories.registerCheck( "modernize-raw-string-literal"); diff --git a/clang-tools-extra/clang-tidy/modernize/UseStartsEndsWithCheck.cpp b/clang-tools-extra/clang-tidy/modernize/UseStartsEndsWithCheck.cpp new file mode 100644 index 0000000000000000000000000000000000000000..062f6e9911dbed35d3848a8cf7e1202ae1ec85ba --- /dev/null +++ b/clang-tools-extra/clang-tidy/modernize/UseStartsEndsWithCheck.cpp @@ -0,0 +1,109 @@ +//===--- UseStartsEndsWithCheck.cpp - clang-tidy --------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "UseStartsEndsWithCheck.h" + +#include "../utils/OptionsUtils.h" +#include "clang/Lex/Lexer.h" + +#include + +using namespace clang::ast_matchers; + +namespace clang::tidy::modernize { + +UseStartsEndsWithCheck::UseStartsEndsWithCheck(StringRef Name, + ClangTidyContext *Context) + : ClangTidyCheck(Name, Context) {} + +void UseStartsEndsWithCheck::registerMatchers(MatchFinder *Finder) { + const auto ZeroLiteral = integerLiteral(equals(0)); + const auto HasStartsWithMethodWithName = [](const std::string &Name) { + return hasMethod( + cxxMethodDecl(hasName(Name), isConst(), parameterCountIs(1)) + .bind("starts_with_fun")); + }; + const auto HasStartsWithMethod = + anyOf(HasStartsWithMethodWithName("starts_with"), + HasStartsWithMethodWithName("startsWith"), + HasStartsWithMethodWithName("startswith")); + const auto ClassWithStartsWithFunction = cxxRecordDecl(anyOf( + HasStartsWithMethod, hasAnyBase(hasType(hasCanonicalType(hasDeclaration( + cxxRecordDecl(HasStartsWithMethod))))))); + + const auto FindExpr = cxxMemberCallExpr( + // A method call with no second argument or the second argument is zero... + anyOf(argumentCountIs(1), hasArgument(1, ZeroLiteral)), + // ... named find... + callee(cxxMethodDecl(hasName("find")).bind("find_fun")), + // ... on a class with a starts_with function. + on(hasType( + hasCanonicalType(hasDeclaration(ClassWithStartsWithFunction))))); + + const auto RFindExpr = cxxMemberCallExpr( + // A method call with a second argument of zero... + hasArgument(1, ZeroLiteral), + // ... named rfind... + callee(cxxMethodDecl(hasName("rfind")).bind("find_fun")), + // ... on a class with a starts_with function. + on(hasType( + hasCanonicalType(hasDeclaration(ClassWithStartsWithFunction))))); + + const auto FindOrRFindExpr = + cxxMemberCallExpr(anyOf(FindExpr, RFindExpr)).bind("find_expr"); + + Finder->addMatcher( + // Match [=!]= with a zero on one side and a string.(r?)find on the other. + binaryOperator(hasAnyOperatorName("==", "!="), + hasOperands(FindOrRFindExpr, ZeroLiteral)) + .bind("expr"), + this); +} + +void UseStartsEndsWithCheck::check(const MatchFinder::MatchResult &Result) { + const auto *ComparisonExpr = Result.Nodes.getNodeAs("expr"); + const auto *FindExpr = Result.Nodes.getNodeAs("find_expr"); + const auto *FindFun = Result.Nodes.getNodeAs("find_fun"); + const auto *StartsWithFunction = + Result.Nodes.getNodeAs("starts_with_fun"); + + if (ComparisonExpr->getBeginLoc().isMacroID()) { + return; + } + + const bool Neg = ComparisonExpr->getOpcode() == BO_NE; + + auto Diagnostic = + diag(FindExpr->getBeginLoc(), "use %0 instead of %1() %select{==|!=}2 0") + << StartsWithFunction->getName() << FindFun->getName() << Neg; + + // Remove possible zero second argument and ' [!=]= 0' suffix. + Diagnostic << FixItHint::CreateReplacement( + CharSourceRange::getTokenRange( + Lexer::getLocForEndOfToken(FindExpr->getArg(0)->getEndLoc(), 0, + *Result.SourceManager, getLangOpts()), + ComparisonExpr->getEndLoc()), + ")"); + + // Remove possible '0 [!=]= ' prefix. + Diagnostic << FixItHint::CreateRemoval(CharSourceRange::getCharRange( + ComparisonExpr->getBeginLoc(), FindExpr->getBeginLoc())); + + // Replace '(r?)find' with 'starts_with'. + Diagnostic << FixItHint::CreateReplacement( + CharSourceRange::getTokenRange(FindExpr->getExprLoc(), + FindExpr->getExprLoc()), + StartsWithFunction->getName()); + + // Add possible negation '!'. + if (Neg) { + Diagnostic << FixItHint::CreateInsertion(FindExpr->getBeginLoc(), "!"); + } +} + +} // namespace clang::tidy::modernize diff --git a/clang-tools-extra/clang-tidy/modernize/UseStartsEndsWithCheck.h b/clang-tools-extra/clang-tidy/modernize/UseStartsEndsWithCheck.h new file mode 100644 index 0000000000000000000000000000000000000000..34e97177682575c97849f542f5679a3366ba0ec6 --- /dev/null +++ b/clang-tools-extra/clang-tidy/modernize/UseStartsEndsWithCheck.h @@ -0,0 +1,37 @@ +//===--- UseStartsEndsWithCheck.h - clang-tidy ------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USESTARTSENDSWITHCHECK_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USESTARTSENDSWITHCHECK_H + +#include "../ClangTidyCheck.h" + +namespace clang::tidy::modernize { + +/// Checks whether a ``find`` or ``rfind`` result is compared with 0 and +/// suggests replacing with ``starts_with`` when the method exists in the class. +/// Notably, this will work with ``std::string`` and ``std::string_view``. +/// +/// For the user-facing documentation see: +/// http://clang.llvm.org/extra/clang-tidy/checks/modernize/use-starts-ends-with.html +class UseStartsEndsWithCheck : public ClangTidyCheck { +public: + UseStartsEndsWithCheck(StringRef Name, ClangTidyContext *Context); + void registerMatchers(ast_matchers::MatchFinder *Finder) override; + void check(const ast_matchers::MatchFinder::MatchResult &Result) override; + bool isLanguageVersionSupported(const LangOptions &LangOpts) const override { + return LangOpts.CPlusPlus; + } + std::optional getCheckTraversalKind() const override { + return TK_IgnoreUnlessSpelledInSource; + } +}; + +} // namespace clang::tidy::modernize + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USESTARTSENDSWITHCHECK_H diff --git a/clang-tools-extra/clang-tidy/readability/ContainerContainsCheck.cpp b/clang-tools-extra/clang-tidy/readability/ContainerContainsCheck.cpp index 970ed8b83e0a76478bb6e7d498a9f1a0581c3a53..dbb50a060e596011bcfe6c9cdf4d8bc732be2fcb 100644 --- a/clang-tools-extra/clang-tidy/readability/ContainerContainsCheck.cpp +++ b/clang-tools-extra/clang-tidy/readability/ContainerContainsCheck.cpp @@ -131,8 +131,8 @@ void ContainerContainsCheck::check(const MatchFinder::MatchResult &Result) { Diag << FixItHint::CreateReplacement( CharSourceRange::getCharRange(ComparisonBegin, CallBegin), Negated ? "!" : ""); - Diag << FixItHint::CreateRemoval(CharSourceRange::getCharRange( - CallEnd.getLocWithOffset(1), ComparisonEnd.getLocWithOffset(1))); + Diag << FixItHint::CreateRemoval(CharSourceRange::getTokenRange( + CallEnd.getLocWithOffset(1), ComparisonEnd)); } } // namespace clang::tidy::readability diff --git a/clang-tools-extra/clangd/InlayHints.cpp b/clang-tools-extra/clangd/InlayHints.cpp index 3da047f9542138565fe204103967f9dd978a5e00..b540c273cbd59672b07c8eede970e2824f17ca53 100644 --- a/clang-tools-extra/clangd/InlayHints.cpp +++ b/clang-tools-extra/clangd/InlayHints.cpp @@ -589,6 +589,31 @@ public: return true; } + // Carefully recurse into PseudoObjectExprs, which typically incorporate + // a syntactic expression and several semantic expressions. + bool TraversePseudoObjectExpr(PseudoObjectExpr *E) { + Expr *SyntacticExpr = E->getSyntacticForm(); + if (isa(SyntacticExpr)) + // Since the counterpart semantics usually get the identical source + // locations as the syntactic one, visiting those would end up presenting + // confusing hints e.g., __builtin_dump_struct. + // Thus, only traverse the syntactic forms if this is written as a + // CallExpr. This leaves the door open in case the arguments in the + // syntactic form could possibly get parameter names. + return RecursiveASTVisitor::TraverseStmt(SyntacticExpr); + // We don't want the hints for some of the MS property extensions. + // e.g. + // struct S { + // __declspec(property(get=GetX, put=PutX)) int x[]; + // void PutX(int y); + // void Work(int y) { x = y; } // Bad: `x = y: y`. + // }; + if (isa(SyntacticExpr)) + return true; + // FIXME: Handle other forms of a pseudo object expression. + return RecursiveASTVisitor::TraversePseudoObjectExpr(E); + } + bool VisitCallExpr(CallExpr *E) { if (!Cfg.InlayHints.Parameters) return true; diff --git a/clang-tools-extra/clangd/unittests/InlayHintTests.cpp b/clang-tools-extra/clangd/unittests/InlayHintTests.cpp index 20c1cdd985dbc0179644ec5a462e87835ab67002..6e91053632e00bfac1800f2771c2cbee097c53ae 100644 --- a/clang-tools-extra/clangd/unittests/InlayHintTests.cpp +++ b/clang-tools-extra/clangd/unittests/InlayHintTests.cpp @@ -1709,7 +1709,8 @@ TEST(DesignatorHints, NoCrash) { void test() { Foo f{A(), $b[[1]]}; } - )cpp", ExpectedHint{".b=", "b"}); + )cpp", + ExpectedHint{".b=", "b"}); } TEST(InlayHints, RestrictRange) { @@ -1724,6 +1725,38 @@ TEST(InlayHints, RestrictRange) { ElementsAre(labelIs(": int"), labelIs(": char"))); } +TEST(ParameterHints, PseudoObjectExpr) { + Annotations Code(R"cpp( + struct S { + __declspec(property(get=GetX, put=PutX)) int x[]; + int GetX(int y, int z) { return 42 + y; } + void PutX(int) { } + + // This is a PseudoObjectExpression whose syntactic form is a binary + // operator. + void Work(int y) { x = y; } // Not `x = y: y`. + }; + + int printf(const char *Format, ...); + + int main() { + S s; + __builtin_dump_struct(&s, printf); // Not `Format: __builtin_dump_struct()` + printf($Param[["Hello, %d"]], 42); // Normal calls are not affected. + // This builds a PseudoObjectExpr, but here it's useful for showing the + // arguments from the semantic form. + return s.x[ $one[[1]] ][ $two[[2]] ]; // `x[y: 1][z: 2]` + } + )cpp"); + auto TU = TestTU::withCode(Code.code()); + TU.ExtraArgs.push_back("-fms-extensions"); + auto AST = TU.build(); + EXPECT_THAT(inlayHints(AST, std::nullopt), + ElementsAre(HintMatcher(ExpectedHint{"Format: ", "Param"}, Code), + HintMatcher(ExpectedHint{"y: ", "one"}, Code), + HintMatcher(ExpectedHint{"z: ", "two"}, Code))); +} + TEST(ParameterHints, ArgPacksAndConstructors) { assertParameterHints( R"cpp( diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst index 6d5f49dc06254512128ba9e0099d2b32e1806b8d..4ff4494cef5624153b84a63c63bbacffcb19a640 100644 --- a/clang-tools-extra/docs/ReleaseNotes.rst +++ b/clang-tools-extra/docs/ReleaseNotes.rst @@ -186,6 +186,13 @@ New checks Replace ``enable_if`` with C++20 requires clauses. +- New :doc:`modernize-use-starts-ends-with + ` check. + + Checks whether a ``find`` or ``rfind`` result is compared with 0 and suggests + replacing with ``starts_with`` when the method exists in the class. Notably, + this will work with ``std::string`` and ``std::string_view``. + - New :doc:`performance-enum-size ` check. @@ -399,6 +406,10 @@ Changes in existing checks ` diagnositics to highlight the const location +- Improved :doc:`readability-container-contains + ` to correctly handle + interger literals with suffixes in fix-its. + - Improved :doc:`readability-container-size-empty ` check to detect comparison between string and empty string literals and support diff --git a/clang-tools-extra/docs/clang-tidy/checks/abseil/string-find-startswith.rst b/clang-tools-extra/docs/clang-tidy/checks/abseil/string-find-startswith.rst index c82c38772a5c9a8ace23952586dcf686c75f046d..41a7ab500d7ce0410e9d17bdfcf814a4862bbd10 100644 --- a/clang-tools-extra/docs/clang-tidy/checks/abseil/string-find-startswith.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/abseil/string-find-startswith.rst @@ -8,6 +8,10 @@ corresponding ``std::string_view`` methods) result is compared with 0, and suggests replacing with ``absl::StartsWith()``. This is both a readability and performance issue. +``starts_with`` was added as a built-in function on those types in C++20. If +available, prefer enabling :doc:`modernize-use-starts-ends-with +<../modernize/use-starts-ends-with>` instead of this check. + .. code-block:: c++ string s = "..."; diff --git a/clang-tools-extra/docs/clang-tidy/checks/list.rst b/clang-tools-extra/docs/clang-tidy/checks/list.rst index 6f987ba1672e3f25c77f84275f9f4d44871ade97..df2d5d15238d63c6ecb74f1c1e83eea2df030b5f 100644 --- a/clang-tools-extra/docs/clang-tidy/checks/list.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/list.rst @@ -292,6 +292,7 @@ Clang-Tidy Checks :doc:`modernize-use-noexcept `, "Yes" :doc:`modernize-use-nullptr `, "Yes" :doc:`modernize-use-override `, "Yes" + :doc:`modernize-use-starts-ends-with `, "Yes" :doc:`modernize-use-std-print `, "Yes" :doc:`modernize-use-trailing-return-type `, "Yes" :doc:`modernize-use-transparent-functors `, "Yes" diff --git a/clang-tools-extra/docs/clang-tidy/checks/modernize/use-starts-ends-with.rst b/clang-tools-extra/docs/clang-tidy/checks/modernize/use-starts-ends-with.rst new file mode 100644 index 0000000000000000000000000000000000000000..7f8a262d2ab3aac9f4e13064062f584ea5d1460e --- /dev/null +++ b/clang-tools-extra/docs/clang-tidy/checks/modernize/use-starts-ends-with.rst @@ -0,0 +1,22 @@ +.. title:: clang-tidy - modernize-use-starts-ends-with + +modernize-use-starts-ends-with +============================== + +Checks whether a ``find`` or ``rfind`` result is compared with 0 and suggests +replacing with ``starts_with`` when the method exists in the class. Notably, +this will work with ``std::string`` and ``std::string_view``. + +.. code-block:: c++ + + std::string s = "..."; + if (s.find("prefix") == 0) { /* do something */ } + if (s.rfind("prefix", 0) == 0) { /* do something */ } + +becomes + +.. code-block:: c++ + + std::string s = "..."; + if (s.starts_with("prefix")) { /* do something */ } + if (s.starts_with("prefix")) { /* do something */ } diff --git a/clang-tools-extra/docs/clang-tidy/checks/readability/named-parameter.rst b/clang-tools-extra/docs/clang-tidy/checks/readability/named-parameter.rst index 8d28c0aa02169a7cdb98cec4a87c0a2c72756b11..73677a48605f4957ec60779f9c9dc5c64152ee8b 100644 --- a/clang-tools-extra/docs/clang-tidy/checks/readability/named-parameter.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/readability/named-parameter.rst @@ -10,7 +10,16 @@ Guide: https://google.github.io/styleguide/cppguide.html#Function_Declarations_and_Definitions -All parameters should be named, with identical names in the declaration and -implementation. +All parameters should have the same name in both the function declaration and definition. +If a parameter is not utilized, its name can be commented out in a function definition. + +.. code-block:: c++ + + int doingSomething(int a, int b, int c); + + int doingSomething(int a, int b, int /*c*/) { + // Ok: the third param is not used + return a + b; + } Corresponding cpplint.py check name: `readability/function`. diff --git a/clang-tools-extra/test/clang-tidy/checkers/Inputs/Headers/stddef.h b/clang-tools-extra/test/clang-tidy/checkers/Inputs/Headers/stddef.h index 24b8ef012d4a00fc88e54cd909c94b6b930e06d9..11feddf0f0676a5389ac361df5192b686d5c5421 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/Inputs/Headers/stddef.h +++ b/clang-tools-extra/test/clang-tidy/checkers/Inputs/Headers/stddef.h @@ -12,4 +12,4 @@ typedef __PTRDIFF_TYPE__ ptrdiff_t; typedef __SIZE_TYPE__ size_t; -#endif _STDDEF_H_ +#endif // _STDDEF_H_ diff --git a/clang-tools-extra/test/clang-tidy/checkers/Inputs/Headers/string b/clang-tools-extra/test/clang-tidy/checkers/Inputs/Headers/string index d0aac7b78ec939b71e7196a4450d25c2879e800b..f2e4159a224513f073890f1c86c9964327336a14 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/Inputs/Headers/string +++ b/clang-tools-extra/test/clang-tidy/checkers/Inputs/Headers/string @@ -10,8 +10,13 @@ typedef unsigned __INT32_TYPE__ char32; namespace std { template class allocator {}; + template class char_traits {}; + +template > +struct basic_string_view; + template , typename A = allocator> struct basic_string { typedef size_t size_type; @@ -52,6 +57,10 @@ struct basic_string { _Type& insert(size_type pos, const C* s); _Type& insert(size_type pos, const C* s, size_type n); + constexpr bool starts_with(std::basic_string_view sv) const noexcept; + constexpr bool starts_with(C ch) const noexcept; + constexpr bool starts_with(const C* s) const; + _Type& operator[](size_type); const _Type& operator[](size_type) const; @@ -68,7 +77,7 @@ typedef basic_string wstring; typedef basic_string u16string; typedef basic_string u32string; -template > +template struct basic_string_view { typedef size_t size_type; typedef basic_string_view _Type; @@ -86,8 +95,13 @@ struct basic_string_view { size_type rfind(const C* s, size_type pos, size_type count) const; size_type rfind(const C* s, size_type pos = npos) const; + constexpr bool starts_with(basic_string_view sv) const noexcept; + constexpr bool starts_with(C ch) const noexcept; + constexpr bool starts_with(const C* s) const; + static constexpr size_t npos = -1; }; + typedef basic_string_view string_view; typedef basic_string_view wstring_view; typedef basic_string_view u16string_view; diff --git a/clang-tools-extra/test/clang-tidy/checkers/abseil/string-find-startswith.cpp b/clang-tools-extra/test/clang-tidy/checkers/abseil/string-find-startswith.cpp index 417598790bc007f8eeee6ae0bc8a8029f6203f2d..aabb30fe34f782cdcf0a8e609ca20b2a4fe72da2 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/abseil/string-find-startswith.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/abseil/string-find-startswith.cpp @@ -1,4 +1,4 @@ -// RUN: %check_clang_tidy %s abseil-string-find-startswith %t -- \ +// RUN: %check_clang_tidy -std=c++17 %s abseil-string-find-startswith %t -- \ // RUN: -config="{CheckOptions: \ // RUN: {abseil-string-find-startswith.StringLikeClasses: \ // RUN: '::std::basic_string;::std::basic_string_view;::basic_string'}}" \ diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone/suspicious-enum-usage-strict.cpp b/clang-tools-extra/test/clang-tidy/checkers/bugprone/suspicious-enum-usage-strict.cpp index 405dec22eea77c8c1a13b4d27bb80da45e979f51..ec214945539ebf937e17321be0d3a59e57300c49 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/bugprone/suspicious-enum-usage-strict.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/suspicious-enum-usage-strict.cpp @@ -1,4 +1,4 @@ -// RUN: %check_clang_tidy %s bugprone-suspicious-enum-usage %t -- -config="{CheckOptions: {bugprone-suspicious-enum-usage.StrictMode: true}}" -- +// RUN: %check_clang_tidy -std=c++17 %s bugprone-suspicious-enum-usage %t -- -config="{CheckOptions: {bugprone-suspicious-enum-usage.StrictMode: true}}" -- enum A { A = 1, @@ -71,7 +71,7 @@ int trigger() { unsigned p = R; PP pp = Q; p |= pp; - + enum X x = Z; p = x | Z; return 0; diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone/suspicious-enum-usage.cpp b/clang-tools-extra/test/clang-tidy/checkers/bugprone/suspicious-enum-usage.cpp index 257c82e88f9a3ddebe95aca6c9eb84388132aa10..de7eb989bd2d4ef90a69e81e73e9088be3b8437f 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/bugprone/suspicious-enum-usage.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/suspicious-enum-usage.cpp @@ -1,4 +1,4 @@ -// RUN: %check_clang_tidy %s bugprone-suspicious-enum-usage %t -- -config="{CheckOptions: {bugprone-suspicious-enum-usage.StrictMode: false}}" -- +// RUN: %check_clang_tidy -std=c++17 %s bugprone-suspicious-enum-usage %t -- -config="{CheckOptions: {bugprone-suspicious-enum-usage.StrictMode: false}}" enum Empty { }; @@ -79,7 +79,7 @@ int dont_trigger() { int d = c | H, e = b * a; a = B | C; b = X | Z; - + if (Tuesday != Monday + 1 || Friday - Thursday != 1 || Sunday + Wednesday == (Sunday | Wednesday)) diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-starts-ends-with.cpp b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-starts-ends-with.cpp new file mode 100644 index 0000000000000000000000000000000000000000..65ed9ed895bc4731e8c3fa8ab13ff0db11445ab8 --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-starts-ends-with.cpp @@ -0,0 +1,167 @@ +// RUN: %check_clang_tidy -std=c++20 %s modernize-use-starts-ends-with %t -- \ +// RUN: -- -isystem %clang_tidy_headers + +#include + +std::string foo(std::string); +std::string bar(); + +class sub_string : public std::string {}; +class sub_sub_string : public sub_string {}; + +struct string_like { + bool starts_with(const char *s) const; + size_t find(const char *s, size_t pos = 0) const; +}; + +struct string_like_camel { + bool startsWith(const char *s) const; + size_t find(const char *s, size_t pos = 0) const; +}; + +struct prefer_underscore_version { + bool starts_with(const char *s) const; + bool startsWith(const char *s) const; + size_t find(const char *s, size_t pos = 0) const; +}; + +struct prefer_underscore_version_flip { + bool startsWith(const char *s) const; + bool starts_with(const char *s) const; + size_t find(const char *s, size_t pos = 0) const; +}; + +struct prefer_underscore_version_inherit : public string_like { + bool startsWith(const char *s) const; +}; + +void test(std::string s, std::string_view sv, sub_string ss, sub_sub_string sss, + string_like sl, string_like_camel slc, prefer_underscore_version puv, + prefer_underscore_version_flip puvf, + prefer_underscore_version_inherit puvi) { + s.find("a") == 0; + // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with instead of find() == 0 + // CHECK-FIXES: s.starts_with("a"); + + (((((s)).find("a")))) == ((0)); + // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with + // CHECK-FIXES: ((s)).starts_with("a"); + + (s + "a").find("a") == ((0)); + // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with + // CHECK-FIXES: (s + "a").starts_with("a"); + + s.find(s) == 0; + // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with + // CHECK-FIXES: s.starts_with(s); + + s.find("aaa") != 0; + // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with + // CHECK-FIXES: !s.starts_with("aaa"); + + s.find(foo(foo(bar()))) != 0; + // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with + // CHECK-FIXES: !s.starts_with(foo(foo(bar()))); + + if (s.find("....") == 0) { /* do something */ } + // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with + // CHECK-FIXES: if (s.starts_with("....")) + + 0 != s.find("a"); + // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with + // CHECK-FIXES: !s.starts_with("a"); + + s.rfind("a", 0) == 0; + // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with instead of rfind() == 0 + // CHECK-FIXES: s.starts_with("a"); + + s.rfind(s, 0) == 0; + // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with + // CHECK-FIXES: s.starts_with(s); + + s.rfind("aaa", 0) != 0; + // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with + // CHECK-FIXES: !s.starts_with("aaa"); + + s.rfind(foo(foo(bar())), 0) != 0; + // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with + // CHECK-FIXES: !s.starts_with(foo(foo(bar()))); + + if (s.rfind("....", 0) == 0) { /* do something */ } + // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with + // CHECK-FIXES: if (s.starts_with("....")) + + 0 != s.rfind("a", 0); + // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with + // CHECK-FIXES: !s.starts_with("a"); + + #define STR(x) std::string(x) + 0 == STR(s).find("a"); + // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with + // CHECK-FIXES: STR(s).starts_with("a"); + + #define STRING s + if (0 == STRING.find("ala")) { /* do something */} + // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with + // CHECK-FIXES: if (STRING.starts_with("ala")) + + #define FIND find + s.FIND("a") == 0; + // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with + // CHECK-FIXES: s.starts_with("a") + + #define PREFIX "a" + s.find(PREFIX) == 0; + // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with + // CHECK-FIXES: s.starts_with(PREFIX) + + #define ZERO 0 + s.find("a") == ZERO; + // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with + // CHECK-FIXES: s.starts_with("a") + + sv.find("a") == 0; + // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with + // CHECK-FIXES: sv.starts_with("a"); + + sv.rfind("a", 0) != 0; + // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with + // CHECK-FIXES: !sv.starts_with("a"); + + ss.find("a") == 0; + // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with + // CHECK-FIXES: ss.starts_with("a"); + + sss.find("a") == 0; + // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with + // CHECK-FIXES: ss.starts_with("a"); + + sl.find("a") == 0; + // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with + // CHECK-FIXES: sl.starts_with("a"); + + slc.find("a") == 0; + // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use startsWith + // CHECK-FIXES: slc.startsWith("a"); + + puv.find("a") == 0; + // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with + // CHECK-FIXES: puv.starts_with("a"); + + puvf.find("a") == 0; + // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with + // CHECK-FIXES: puvf.starts_with("a"); + + // Here, the subclass has startsWith, the superclass has starts_with. + // We prefer the version from the subclass. + puvi.find("a") == 0; + // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use startsWith + // CHECK-FIXES: puvi.startsWith("a"); + + // Expressions that don't trigger the check are here. + #define EQ(x, y) ((x) == (y)) + EQ(s.find("a"), 0); + + #define DOTFIND(x, y) (x).find(y) + DOTFIND(s, "a") == 0; +} diff --git a/clang-tools-extra/test/clang-tidy/checkers/readability/container-contains.cpp b/clang-tools-extra/test/clang-tidy/checkers/readability/container-contains.cpp index 05a4ebc9c8a17777805cd427b671547e94c7050e..0ecb61b2e7df06d710ca54580238fc7e05684ce8 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/readability/container-contains.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/readability/container-contains.cpp @@ -175,6 +175,37 @@ int nonRewrittenCount(std::multimap &MyMap) { return C1 + C2 + C3 + C4; } +// Check different integer literal suffixes +int testDifferentIntegerLiteralSuffixes(std::map &MyMap) { + + auto C1 = MyMap.count(2) != 0U; + // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: use 'contains' to check for membership [readability-container-contains] + // CHECK-FIXES: auto C1 = MyMap.contains(2); + auto C2 = MyMap.count(2) != 0UL; + // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: use 'contains' to check for membership [readability-container-contains] + // CHECK-FIXES: auto C2 = MyMap.contains(2); + auto C3 = 0U != MyMap.count(2); + // CHECK-MESSAGES: :[[@LINE-1]]:25: warning: use 'contains' to check for membership [readability-container-contains] + // CHECK-FIXES: auto C3 = MyMap.contains(2); + auto C4 = 0UL != MyMap.count(2); + // CHECK-MESSAGES: :[[@LINE-1]]:26: warning: use 'contains' to check for membership [readability-container-contains] + // CHECK-FIXES: auto C4 = MyMap.contains(2); + auto C5 = MyMap.count(2) < 1U; + // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: use 'contains' to check for membership [readability-container-contains] + // CHECK-FIXES: auto C5 = !MyMap.contains(2); + auto C6 = MyMap.count(2) < 1UL; + // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: use 'contains' to check for membership [readability-container-contains] + // CHECK-FIXES: auto C6 = !MyMap.contains(2); + auto C7 = 1U > MyMap.count(2); + // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: use 'contains' to check for membership [readability-container-contains] + // CHECK-FIXES: auto C7 = !MyMap.contains(2); + auto C8 = 1UL > MyMap.count(2); + // CHECK-MESSAGES: :[[@LINE-1]]:25: warning: use 'contains' to check for membership [readability-container-contains] + // CHECK-FIXES: auto C8 = !MyMap.contains(2); + + return C1 + C2 + C3 + C4 + C5 + C6 + C7 + C8; +} + // We don't want to rewrite if the `contains` call is from a macro expansion int testMacroExpansion(std::unordered_set &MySet) { #define COUNT_ONES(SET) SET.count(1) diff --git a/clang-tools-extra/test/clang-tidy/checkers/readability/container-size-empty.cpp b/clang-tools-extra/test/clang-tidy/checkers/readability/container-size-empty.cpp index 29ac86cf1b369c2228ed888b268f70d291bf6fe1..3b9e06084183054138790da63a843fb6bcb38da5 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/readability/container-size-empty.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/readability/container-size-empty.cpp @@ -528,12 +528,12 @@ template void f() { if (s.size()) ; // CHECK-MESSAGES: :[[@LINE-2]]:7: warning: the 'empty' method should be used to check for emptiness instead of 'size' [readability-container-size-empty] - // CHECK-MESSAGES: string:28:8: note: method 'basic_string'::empty() defined here + // CHECK-MESSAGES: string:{{[0-9]+}}:8: note: method 'basic_string'::empty() defined here // CHECK-FIXES: {{^ }}if (!s.empty()){{$}} if (s.length()) ; // CHECK-MESSAGES: :[[@LINE-2]]:7: warning: the 'empty' method should be used to check for emptiness instead of 'length' [readability-container-size-empty] - // CHECK-MESSAGES: string:28:8: note: method 'basic_string'::empty() defined here + // CHECK-MESSAGES: string:{{[0-9]+}}:8: note: method 'basic_string'::empty() defined here // CHECK-FIXES: {{^ }}if (!s.empty()){{$}} } diff --git a/clang/CMakeLists.txt b/clang/CMakeLists.txt index 9b52c58be41e7f7489b66754a18a2e26e8316967..2ca6db02e58791d5f4ea9f6b8e57dccbb425da89 100644 --- a/clang/CMakeLists.txt +++ b/clang/CMakeLists.txt @@ -290,8 +290,10 @@ endif() if(NOT DEFINED CLANG_VERSION_PATCHLEVEL) set(CLANG_VERSION_PATCHLEVEL ${LLVM_VERSION_PATCH}) endif() -# Unlike PACKAGE_VERSION, CLANG_VERSION does not include LLVM_VERSION_SUFFIX. -set(CLANG_VERSION "${CLANG_VERSION_MAJOR}.${CLANG_VERSION_MINOR}.${CLANG_VERSION_PATCHLEVEL}") +if(NOT DEFINED CLANG_VERSION_SUFFIX) + set(CLANG_VERSION_SUFFIX ${LLVM_VERSION_SUFFIX}) +endif() +set(CLANG_VERSION "${CLANG_VERSION_MAJOR}.${CLANG_VERSION_MINOR}.${CLANG_VERSION_PATCHLEVEL}${CLANG_VERSION_SUFFIX}") message(STATUS "Clang version: ${CLANG_VERSION}") # Configure the Version.inc file. @@ -650,6 +652,7 @@ if (CLANG_ENABLE_BOOTSTRAP) CLANG_VERSION_MAJOR CLANG_VERSION_MINOR CLANG_VERSION_PATCHLEVEL + CLANG_VERSION_SUFFIX CLANG_VENDOR LLVM_VERSION_SUFFIX LLVM_BINUTILS_INCDIR diff --git a/clang/docs/ClangFormatStyleOptions.rst b/clang/docs/ClangFormatStyleOptions.rst index 37d76d5c2dd58c05a47af49a0b4f9ce50314f26e..3d42571e82d8a07b37f084b2d6c66aeb33761a90 100644 --- a/clang/docs/ClangFormatStyleOptions.rst +++ b/clang/docs/ClangFormatStyleOptions.rst @@ -4211,6 +4211,32 @@ the configuration (without a prefix: ``Auto``). }] } +.. _ObjCPropertyAttributeOrder: + +**ObjCPropertyAttributeOrder** (``List of Strings``) :versionbadge:`clang-format 18` :ref:`¶ ` + The order in which ObjC property attributes should appear. + + Attributes in code will be sorted in the order specified. Any attributes + encountered that are not mentioned in this array will be sorted last, in + stable order. Comments between attributes will leave the attributes + untouched. + + .. warning:: + + Using this option could lead to incorrect code formatting due to + clang-format's lack of complete semantic information. As such, extra + care should be taken to review code changes made by this option. + + .. code-block:: yaml + + ObjCPropertyAttributeOrder: [ + class, direct, + atomic, nonatomic, + assign, retain, strong, copy, weak, unsafe_unretained, + readonly, readwrite, getter, setter, + nullable, nonnull, null_resettable, null_unspecified + ] + .. _ObjCSpaceAfterProperty: **ObjCSpaceAfterProperty** (``Boolean``) :versionbadge:`clang-format 3.7` :ref:`¶ ` diff --git a/clang/docs/ClangOffloadBundler.rst b/clang/docs/ClangOffloadBundler.rst index 1e21d3e7264d5c396fc18770d3f2393690210ecb..1f8c85a08f8c79c46b9dc375d9ec167d80a78346 100644 --- a/clang/docs/ClangOffloadBundler.rst +++ b/clang/docs/ClangOffloadBundler.rst @@ -30,58 +30,139 @@ includes an ``init`` function that will use the runtime corresponding to the offload kind (see :ref:`clang-offload-kind-table`) to load the offload code objects appropriate to the devices present when the host program is executed. +:program:`clang-offload-bundler` is located in +`clang/tools/clang-offload-bundler`. + +.. code-block:: console + + $ clang-offload-bundler -help + OVERVIEW: A tool to bundle several input files of the specified type + referring to the same source file but different targets into a single + one. The resulting file can also be unbundled into different files by + this tool if -unbundle is provided. + + USAGE: clang-offload-bundler [options] + + OPTIONS: + + Generic Options: + + --help - Display available options (--help-hidden for more) + --help-list - Display list of available options (--help-list-hidden for more) + --version - Display the version of this program + + clang-offload-bundler options: + + --### - Print any external commands that are to be executed instead of actually executing them - for testing purposes. + --allow-missing-bundles - Create empty files if bundles are missing when unbundling. + --bundle-align= - Alignment of bundle for binary files + --check-input-archive - Check if input heterogeneous archive is valid in terms of TargetID rules. + --inputs= - [,...] + --list - List bundle IDs in the bundled file. + --outputs= - [,...] + --targets= - [-,...] + --type= - Type of the files to be bundled/unbundled. + Current supported types are: + i - cpp-output + ii - c++-cpp-output + cui - cuda/hip-output + d - dependency + ll - llvm + bc - llvm-bc + s - assembler + o - object + a - archive of bundled files + gch - precompiled-header + ast - clang AST file + --unbundle - Unbundle bundled file into several output files. + +Usage +===== + +This tool can be used as follows for bundling: + +:: + + clang-offload-bundler -targets=triple1,triple2 -type=ii -inputs=a.triple1.ii,a.triple2.ii -outputs=a.ii + +or, it can be used as follows for unbundling: + +:: + + clang-offload-bundler -targets=triple1,triple2 -type=ii -outputs=a.triple1.ii,a.triple2.ii -inputs=a.ii -unbundle + + Supported File Formats ====================== -Several text and binary file formats are supported for bundling/unbundling. See -:ref:`supported-file-formats-table` for a list of currently supported formats. + +Multiple text and binary file formats are supported for bundling/unbundling. See +:ref:`supported-file-formats-table` for a list of currently supported input +formats. Use the ``File Type`` column to determine the value to pass to the +``--type`` option based on the type of input files while bundling/unbundling. .. table:: Supported File Formats :name: supported-file-formats-table - +--------------------+----------------+-------------+ - | File Format | File Extension | Text/Binary | - +====================+================+=============+ - | CPP output | i | Text | - +--------------------+----------------+-------------+ - | C++ CPP output | ii | Text | - +--------------------+----------------+-------------+ - | CUDA/HIP output | cui | Text | - +--------------------+----------------+-------------+ - | Dependency | d | Text | - +--------------------+----------------+-------------+ - | LLVM | ll | Text | - +--------------------+----------------+-------------+ - | LLVM Bitcode | bc | Binary | - +--------------------+----------------+-------------+ - | Assembler | s | Text | - +--------------------+----------------+-------------+ - | Object | o | Binary | - +--------------------+----------------+-------------+ - | Archive of objects | a | Binary | - +--------------------+----------------+-------------+ - | Precompiled header | gch | Binary | - +--------------------+----------------+-------------+ - | Clang AST file | ast | Binary | - +--------------------+----------------+-------------+ + +--------------------------+----------------+-------------+ + | File Format | File Type | Text/Binary | + +==========================+================+=============+ + | CPP output | i | Text | + +--------------------------+----------------+-------------+ + | C++ CPP output | ii | Text | + +--------------------------+----------------+-------------+ + | CUDA/HIP output | cui | Text | + +--------------------------+----------------+-------------+ + | Dependency | d | Text | + +--------------------------+----------------+-------------+ + | LLVM | ll | Text | + +--------------------------+----------------+-------------+ + | LLVM Bitcode | bc | Binary | + +--------------------------+----------------+-------------+ + | Assembler | s | Text | + +--------------------------+----------------+-------------+ + | Object | o | Binary | + +--------------------------+----------------+-------------+ + | Archive of bundled files | a | Binary | + +--------------------------+----------------+-------------+ + | Precompiled header | gch | Binary | + +--------------------------+----------------+-------------+ + | Clang AST file | ast | Binary | + +--------------------------+----------------+-------------+ .. _clang-bundled-code-object-layout-text: Bundled Text File Layout ======================== -The format of the bundled files is currently very simple: text formats are -concatenated with comments that have a magic string and bundle entry ID in -between. +The text file formats are concatenated with comments that have a magic string +and bundle entry ID in between. The BNF syntax to represent a code object +bundle file is: :: - "Comment OFFLOAD_BUNDLER_MAGIC_STR__START__ 1st Bundle Entry ID" - Bundle 1 - "Comment OFFLOAD_BUNDLER_MAGIC_STR__END__ 1st Bundle Entry ID" - ... - "Comment OFFLOAD_BUNDLER_MAGIC_STR__START__ Nth Bundle Entry ID" - Bundle N - "Comment OFFLOAD_BUNDLER_MAGIC_STR__END__ 1st Bundle Entry ID" + ::== | + ::== + end + ::== OFFLOAD_BUNDLER_MAGIC_STR__START__ + ::== OFFLOAD_BUNDLER_MAGIC_STR__END__ + +**comment** + The symbol used for starting single-line comment in the file type of + constituting bundles. E.g. it is ";" for ll ``File Type`` and "#" for "s" + ``File Type``. + +**bundle_id** + The :ref:`clang-bundle-entry-id` for the enclosing bundle. + +**eol** + The end of line character. + +**bundle** + The code object stored in one of the supported text file formats. + +**OFFLOAD_BUNDLER_MAGIC_STR__** + Magic string that marks the existence of offloading data i.e. + "__CLANG_OFFLOAD_BUNDLE__". .. _clang-bundled-code-object-layout: @@ -126,8 +207,8 @@ The layout of a bundled code object is defined by the following table: Bundle Entry ID =============== -Each entry in a bundled code object (see -:ref:`clang-bundled-code-object-layout`) has a bundle entry ID that indicates +Each entry in a bundled code object (see :ref:`clang-bundled-code-object-layout-text` +and :ref:`clang-bundled-code-object-layout`) has a bundle entry ID that indicates the kind of the entry's code object and the runtime that manages it. Bundle entry ID syntax is defined by the following BNF syntax: @@ -193,11 +274,30 @@ Where: The canonical target ID of the code object. Present only if the target supports a target ID. See :ref:`clang-target-id`. -Each entry of a bundled code object must have a different bundle entry ID. There -can be multiple entries for the same processor provided they differ in target -feature settings. If there is an entry with a target feature specified as *Any*, -then all entries must specify that target feature as *Any* for the same -processor. There may be additional target specific restrictions. +.. _code-object-composition: + +Bundled Code Object Composition +------------------------------- + + * Each entry of a bundled code object must have a different bundle entry ID. + * There can be multiple entries for the same processor provided they differ + in target feature settings. + * If there is an entry with a target feature specified as *Any*, then all + entries must specify that target feature as *Any* for the same processor. + +There may be additional target specific restrictions. + +.. _compatibility-bundle-entry-id: + +Compatibility Rules for Bundle Entry ID +--------------------------------------- + + A code object, specified using its Bundle Entry ID, can be loaded and + executed on a target processor, if: + + * Their offload kinds are the same. + * Their target triples are compatible. + * Their Target IDs are compatible as defined in :ref:`compatibility-target-id`. .. _clang-target-id: @@ -247,6 +347,17 @@ Where: object compiled with a target ID specifying a target feature off can only be loaded on a processor configured with the target feature off. +.. _compatibility-target-id: + +Compatibility Rules for Target ID +--------------------------------- + + A code object compiled for a Target ID is considered compatible for a + target, if: + + * Their processor is same. + * Their feature set is compatible as defined above. + There are two forms of target ID: *Non-Canonical Form* @@ -279,14 +390,14 @@ Most other targets do not support target IDs. Archive Unbundling ================== -Unbundling of heterogeneous device archive is done to create device specific -archives. Heterogeneous Device Archive is in a format compatible with GNU ar -utility and contains a collection of bundled device binaries where each bundle -file will contain device binaries for a host and one or more targets. The -output device specific archive is in a format compatible with GNU ar utility -and contains a collection of device binaries for a specific target. +Unbundling of a heterogeneous device archive (HDA) is done to create device specific +archives. HDA is in a format compatible with GNU ``ar`` utility and contains a +collection of bundled device binaries where each bundle file will contain +device binaries for a host and one or more targets. The output device-specific +archive is in a format compatible with GNU ``ar`` utility and contains a +collection of device binaries for a specific target. -.. code:: +:: Heterogeneous Device Archive, HDA = {F1.X, F2.X, ..., FN.Y} where, Fi = Bundle{Host-DeviceBinary, T1-DeviceBinary, T2-DeviceBinary, ..., @@ -299,16 +410,100 @@ and contains a collection of device binaries for a specific target. where, Fi-Tj-DeviceBinary.X represents device binary of i-th bundled device binary file for target Tj. -clang-offload-bundler extracts compatible device binaries for a given target +The clang-offload-bundler extracts compatible device binaries for a given target from the bundled device binaries in a heterogeneous device archive and creates -a target specific device archive without bundling. +a target-specific device archive without bundling. + +The clang-offload-bundler determines whether a device binary is compatible +with a target by comparing bundle IDs. Two bundle IDs are considered +compatible if: + + * Their offload kinds are the same + * Their target triples are the same + * Their Target IDs are the same + +Creating a Heterogeneous Device Archive +--------------------------------------- + +1. Compile source file(s) to generate object file(s) + + :: + + clang -O2 -fopenmp -fopenmp-targets=amdgcn-amd-amdhsa,amdgcn-amd-amdhsa,\ + nvptx64-nvidia-cuda, nvptx64-nvidia-cuda \ + -Xopenmp-target=amdgcn-amd-amdhsa -march=gfx906:sramecc-:xnack+ \ + -Xopenmp-target=amdgcn-amd-amdhsa -march=gfx906:sramecc+:xnack+ \ + -Xopenmp-target=nvptx64-nvidia-cuda -march=sm_70 \ + -Xopenmp-target=nvptx64-nvidia-cuda -march=sm_80 \ + -c func_1.c -o func_1.o + + clang -O2 -fopenmp -fopenmp-targets=amdgcn-amd-amdhsa,amdgcn-amd-amdhsa, + nvptx64-nvidia-cuda, nvptx64-nvidia-cuda \ + -Xopenmp-target=amdgcn-amd-amdhsa -march=gfx906:sramecc-:xnack+ \ + -Xopenmp-target=amdgcn-amd-amdhsa -march=gfx906:sramecc+:xnack+ \ + -Xopenmp-target=nvptx64-nvidia-cuda -march=sm_70 \ + -Xopenmp-target=nvptx64-nvidia-cuda -march=sm_80 \ + -c func_2.c -o func_2.o + +2. Create a heterogeneous device archive by combining all the object file(s) + + :: + + llvm-ar cr libFatArchive.a func_1.o func_2.o + +Extracting a Device Specific Archive +------------------------------------ + +UnbundleArchive takes a heterogeneous device archive file (".a") as input +containing bundled device binary files, and a list of offload targets (not +host), and extracts the device binaries into a new archive file for each +offload target. Each resulting archive file contains all device binaries +compatible with that particular offload target. Compatibility between a +device binary in HDA and a target is based on the compatibility between their +bundle entry IDs as defined in :ref:`compatibility-bundle-entry-id`. + +Following cases may arise during compatibility testing: + +* A binary is compatible with one or more targets: Insert the binary into the + device-specific archive of each compatible target. +* A binary is not compatible with any target: Skip the binary. +* One or more binaries are compatible with a target: Insert all binaries into + the device-specific archive of the target. The insertion need not be ordered. +* No binary is compatible with a target: If ``allow-missing-bundles`` option is + present then create an empty archive for the target. Otherwise, produce an + error without creating an archive. + +The created archive file does not contain an index of the symbols and device +binary files are named as <->, +with ':' replaced with '_'. + +Usage +----- + +:: + + clang-offload-bundler --unbundle --inputs=libFatArchive.a -type=a \ + -targets=openmp-amdgcn-amdhsa-gfx906:sramecc+:xnack+, \ + openmp-amdgcn-amdhsa-gfx908:sramecc-:xnack+ \ + -outputs=devicelib-gfx906.a,deviceLib-gfx908.a + +.. _additional-options-archive-unbundling: + +Additional Options while Archive Unbundling +------------------------------------------- + +**-allow-missing-bundles** + Create an empty archive file if no compatible device binary is found. -clang-offload-bundler determines whether a device binary is compatible with a -target by comparing bundle ID's. Two bundle ID's are considered compatible if: +**-check-input-archive** + Check if input heterogeneous device archive follows rules for composition + as defined in :ref:`code-object-composition` before creating device-specific + archive(s). - * Their offload kind are the same - * Their target triple are the same - * Their GPUArch are the same +**-debug-only=CodeObjectCompatibility** + Verbose printing of matched/unmatched comparisons between bundle entry id of + a device binary from HDA and bundle entry ID of a given target processor + (see :ref:`compatibility-bundle-entry-id`). Compression and Decompression ============================= diff --git a/clang/docs/InternalsManual.rst b/clang/docs/InternalsManual.rst index b7d88d3d67d0a0c7805887365d6214b3b1baee68..f8e3da5f9736829ab7f409a2f1372ba8222afd64 100644 --- a/clang/docs/InternalsManual.rst +++ b/clang/docs/InternalsManual.rst @@ -3309,6 +3309,173 @@ are similar. as syntax highlighting, cross-referencing, and so on. The ``c-index-test`` helper program can be used to test these features. +Testing +------- +All functional changes to Clang should come with test coverage demonstrating +the change in behavior. + +Verifying Diagnostics +^^^^^^^^^^^^^^^^^^^^^ +Clang ``-cc1`` supports the ``-verify`` command line option as a way to +validate diagnostic behavior. This option will use special comments within the +test file to verify that expected diagnostics appear in the correct source +locations. If all of the expected diagnostics match the actual output of Clang, +then the invocation will return normally. If there are discrepancies between +the expected and actual output, Clang will emit detailed information about +which expected diagnostics were not seen or which unexpected diagnostics were +seen, etc. A complete example is: + +.. code-block: c++ + + // RUN: %clang_cc1 -verify %s + int A = B; // expected-error {{use of undeclared identifier 'B'}} + +If the test is run and the expected error is emitted on the expected line, the +diagnostic verifier will pass. However, if the expected error does not appear +or appears in a different location than expected, or if additional diagnostics +appear, the diagnostic verifier will fail and emit information as to why. + +The ``-verify`` command optionally accepts a comma-delimited list of one or +more verification prefixes that can be used to craft those special comments. +Each prefix must start with a letter and contain only alphanumeric characters, +hyphens, and underscores. ``-verify`` by itself is equivalent to +``-verify=expected``, meaning that special comments will start with +``expected``. Using different prefixes makes it easier to have separate +``RUN:`` lines in the same test file which result in differing diagnostic +behavior. For example: + +.. code-block:: c++ + + // RUN: %clang_cc1 -verify=foo,bar %s + + int A = B; // foo-error {{use of undeclared identifier 'B'}} + int C = D; // bar-error {{use of undeclared identifier 'D'}} + int E = F; // expected-error {{use of undeclared identifier 'F'}} + +The verifier will recognize ``foo-error`` and ``bar-error`` as special comments +but will not recognize ``expected-error`` as one because the ``-verify`` line +does not contain that as a prefix. Thus, this test would fail verification +because an unexpected diagnostic would appear on the declaration of ``E``. + +Multiple occurrences accumulate prefixes. For example, +``-verify -verify=foo,bar -verify=baz`` is equivalent to +``-verify=expected,foo,bar,baz``. + +Specifying Diagnostics +^^^^^^^^^^^^^^^^^^^^^^ +Indicating that a line expects an error or a warning is simple. Put a comment +on the line that has the diagnostic, use +``expected-{error,warning,remark,note}`` to tag if it's an expected error, +warning, remark, or note (respectively), and place the expected text between +``{{`` and ``}}`` markers. The full text doesn't have to be included, only +enough to ensure that the correct diagnostic was emitted. (Note: full text +should be included in test cases unless there is a compelling reason to use +truncated text instead.) + +Here's an example of the most commonly used way to specify expected +diagnostics: + +.. code-block: c++ + + int A = B; // expected-error {{use of undeclared identifier 'B'}} + +You can place as many diagnostics on one line as you wish. To make the code +more readable, you can use slash-newline to separate out the diagnostics. + +Alternatively, it is possible to specify the line on which the diagnostic +should appear by appending ``@`` to ``expected-``, for example: + +.. code-block: c++ + + #warning some text + // expected-warning@10 {{some text}} + +The line number may be absolute (as above), or relative to the current line by +prefixing the number with either ``+`` or ``-``. + +If the diagnostic is generated in a separate file, for example in a shared +header file, it may be beneficial to be able to declare the file in which the +diagnostic will appear, rather than placing the ``expected-*`` directive in the +actual file itself. This can be done using the following syntax: + +.. code-block: c++ + + // expected-error@path/include.h:15 {{error message}} + +The path can be absolute or relative and the same search paths will be used as +for ``#include`` directives. The line number in an external file may be +substituted with ``*`` meaning that any line number will match (useful where +the included file is, for example, a system header where the actual line number +may change and is not critical). + +As an alternative to specifying a fixed line number, the location of a +diagnostic can instead be indicated by a marker of the form ``#``. +Markers are specified by including them in a comment, and then referenced by +appending the marker to the diagnostic with ``@#``, as with: + +.. code-block: c++ + + #warning some text // #1 + // ... other code ... + // expected-warning@#1 {{some text}} + +The name of a marker used in a directive must be unique within the compilation. + +The simple syntax above allows each specification to match exactly one +diagnostic. You can use the extended syntax to customize this. The extended +syntax is ``expected- {{diag text}}``, where ```` is one of +``error``, ``warning``, ``remark``, or ``note``, and ```` is a positive +integer. This allows the diagnostic to appear as many times as specified. For +example: + +.. code-block: c++ + + void f(); // expected-note 2 {{previous declaration is here}} + +Where the diagnostic is expected to occur a minimum number of times, this can +be specified by appending a ``+`` to the number. For example: + +.. code-block: c++ + + void f(); // expected-note 0+ {{previous declaration is here}} + void g(); // expected-note 1+ {{previous declaration is here}} + +In the first example, the diagnostic becomes optional, i.e. it will be +swallowed if it occurs, but will not generate an error if it does not occur. In +the second example, the diagnostic must occur at least once. As a short-hand, +"one or more" can be specified simply by ``+``. For example: + +.. code-block: c++ + + void g(); // expected-note + {{previous declaration is here}} + +A range can also be specified by ``-``. For example: + +.. code-block: c++ + + void f(); // expected-note 0-1 {{previous declaration is here}} + +In this example, the diagnostic may appear only once, if at all. + +Regex matching mode may be selected by appending ``-re`` to the diagnostic type +and including regexes wrapped in double curly braces in the directive, such as: + +.. code-block: c++ + + expected-error-re {{format specifies type 'wchar_t **' (aka '{{.+}}')}} + +Examples matching error: "variable has incomplete type 'struct s'" + +.. code-block: c++ + + // expected-error {{variable has incomplete type 'struct s'}} + // expected-error {{variable has incomplete type}} + + // expected-error-re {{variable has type 'struct {{.}}'}} + // expected-error-re {{variable has type 'struct {{.*}}'}} + // expected-error-re {{variable has type 'struct {{(.*)}}'}} + // expected-error-re {{variable has type 'struct{{[[:space:]](.*)}}'}} + Feature Test Macros =================== Clang implements several ways to test whether a feature is supported or not. diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst index 8e01ef6cbb3997e9d24a9e89b2542bd573a1b0c1..286405850900aab37177b7af98986d64f8576ce6 100644 --- a/clang/docs/LanguageExtensions.rst +++ b/clang/docs/LanguageExtensions.rst @@ -2821,7 +2821,7 @@ Example output: The ``__builtin_dump_struct`` function is used to print the fields of a simple structure and their values for debugging purposes. The first argument of the -builtin should be a pointer to the struct to dump. The second argument ``f`` +builtin should be a pointer to a complete record type to dump. The second argument ``f`` should be some callable expression, and can be a function object or an overload set. The builtin calls ``f``, passing any further arguments ``args...`` followed by a ``printf``-compatible format string and the corresponding diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index e2e8ee8d76d2e108f52c73a31c0acb7bc36eef32..828dd10e3d6db97ea17448693554f1d8047275bf 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -182,6 +182,11 @@ C++2c Feature Support This is applied to both C++ standard attributes, and other attributes supported by Clang. This completes the implementation of `P2361R6 Unevaluated Strings `_ +- Implemented `P2864R2 Remove Deprecated Arithmetic Conversion on Enumerations From C++26 `_. + +- Implemented `P2361R6 Template parameter initialization `_. + This change is applied as a DR in all language modes. + Resolutions to C++ Defect Reports ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -637,9 +642,19 @@ Bug Fixes in This Version - Fix crash during code generation of C++ coroutine initial suspend when the return type of await_resume is not trivially destructible. Fixes (`#63803 `_) +- ``__is_trivially_relocatable`` no longer returns true for non-object types + such as references and functions. + Fixes (`#67498 `_) - Fix crash when the object used as a ``static_assert`` message has ``size`` or ``data`` members which are not member functions. - Support UDLs in ``static_assert`` message. +- Fixed false positive error emitted by clang when performing qualified name + lookup and the current class instantiation has dependent bases. + Fixes (`#13826 `_) +- Fixes compile error that double colon operator cannot resolve macro with parentheses. + Fixes (`#64467 `_) +- Clang's ``-Wchar-subscripts`` no longer warns on chars whose values are known non-negative constants. + Fixes (`#18763 `_) Bug Fixes to Compiler Builtins ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -781,10 +796,19 @@ Bug Fixes to C++ Support Fixes: (`#68769 `_) +- Clang now rejects incomplete types for ``__builtin_dump_struct``. Fixes: + (`#63506 `_) + +- Fixed a crash for C++98/03 while checking an ill-formed ``_Static_assert`` expression. + Fixes: (`#72025 `_) + - Clang now defers the instantiation of explicit specifier until constraint checking completes (except deduction guides). Fixes: (`#59827 `_) +- Fix crash when parsing nested requirement. Fixes: + (`#73112 `_) + Bug Fixes to AST Handling ^^^^^^^^^^^^^^^^^^^^^^^^^ - Fixed an import failure of recursive friend class template. @@ -962,6 +986,8 @@ clang-format - Add ``AllowShortCompoundRequirementOnASingleLine`` option. - Change ``BreakAfterAttributes`` from ``Never`` to ``Leave`` in LLVM style. - Add ``BreakAdjacentStringLiterals`` option. +- Add ``ObjCPropertyAttributeOrder`` which can be used to sort ObjC property + attributes (like ``nonatomic, strong, nullable``). libclang -------- diff --git a/clang/docs/UsersManual.rst b/clang/docs/UsersManual.rst index 2e658557b0e310cd6041af964435c7d63bb49860..9d64195ee338e2c4657a2db5c43db2b6e6b2c4d2 100644 --- a/clang/docs/UsersManual.rst +++ b/clang/docs/UsersManual.rst @@ -2348,9 +2348,10 @@ differences between the two: 1. Profile data generated with one cannot be used by the other, and there is no conversion tool that can convert one to the other. So, a profile generated - via ``-fprofile-instr-generate`` must be used with ``-fprofile-instr-use``. - Similarly, sampling profiles generated by external profilers must be - converted and used with ``-fprofile-sample-use``. + via ``-fprofile-generate`` or ``-fprofile-instr-generate`` must be used with + ``-fprofile-use`` or ``-fprofile-instr-use``. Similarly, sampling profiles + generated by external profilers must be converted and used with ``-fprofile-sample-use`` + or ``-fauto-profile``. 2. Instrumentation profile data can be used for code coverage analysis and optimization. @@ -2598,6 +2599,8 @@ Of those, 31,977 were spent inside the body of ``bar``. The last line of the profile (``2: 0``) corresponds to line 2 inside ``main``. No samples were collected there. +.. _prof_instr: + Profiling with Instrumentation ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -2607,11 +2610,25 @@ overhead during the profiling, but it provides more detailed results than a sampling profiler. It also provides reproducible results, at least to the extent that the code behaves consistently across runs. +Clang supports two types of instrumentation: frontend-based and IR-based. +Frontend-based instrumentation can be enabled with the option ``-fprofile-instr-generate``, +and IR-based instrumentation can be enabled with the option ``-fprofile-generate``. +For best performance with PGO, IR-based instrumentation should be used. It has +the benefits of lower instrumentation overhead, smaller raw profile size, and +better runtime performance. Frontend-based instrumentation, on the other hand, +has better source correlation, so it should be used with source line-based +coverage testing. + +The flag ``-fcs-profile-generate`` also instruments programs using the same +instrumentation method as ``-fprofile-generate``. However, it performs a +post-inline late instrumentation and can produce context-sensitive profiles. + + Here are the steps for using profile guided optimization with instrumentation: 1. Build an instrumented version of the code by compiling and linking with the - ``-fprofile-instr-generate`` option. + ``-fprofile-generate`` or ``-fprofile-instr-generate`` option. .. code-block:: console @@ -2674,8 +2691,8 @@ instrumentation: Note that this step is necessary even when there is only one "raw" profile, since the merge operation also changes the file format. -4. Build the code again using the ``-fprofile-instr-use`` option to specify the - collected profile data. +4. Build the code again using the ``-fprofile-use`` or ``-fprofile-instr-use`` + option to specify the collected profile data. .. code-block:: console @@ -2685,13 +2702,10 @@ instrumentation: profile. As you make changes to your code, clang may no longer be able to use the profile data. It will warn you when this happens. -Profile generation using an alternative instrumentation method can be -controlled by the GCC-compatible flags ``-fprofile-generate`` and -``-fprofile-use``. Although these flags are semantically equivalent to -their GCC counterparts, they *do not* handle GCC-compatible profiles. -They are only meant to implement GCC's semantics with respect to -profile creation and use. Flag ``-fcs-profile-generate`` also instruments -programs using the same instrumentation method as ``-fprofile-generate``. +Note that ``-fprofile-use`` option is semantically equivalent to +its GCC counterpart, it *does not* handle profile formats produced by GCC. +Both ``-fprofile-use`` and ``-fprofile-instr-use`` accept profiles in the +indexed format, regardeless whether it is produced by frontend or the IR pass. .. option:: -fprofile-generate[=] @@ -4401,13 +4415,21 @@ Execute ``clang-cl /?`` to see a list of supported options: Instrument only functions from files where names don't match all the regexes separated by a semi-colon -fprofile-filter-files= Instrument only functions from files where names match any regex separated by a semi-colon - -fprofile-instr-generate= - Generate instrumented code to collect execution counts into + -fprofile-generate= + Generate instrumented code to collect execution counts into a raw profile file in the directory specified by the argument. The filename uses default_%m.profraw pattern + (overridden by LLVM_PROFILE_FILE env var) + -fprofile-generate + Generate instrumented code to collect execution counts into default_%m.profraw file + (overridden by '=' form of option or LLVM_PROFILE_FILE env var) + -fprofile-instr-generate= + Generate instrumented code to collect execution counts into the file whose name pattern is specified as the argument (overridden by LLVM_PROFILE_FILE env var) -fprofile-instr-generate Generate instrumented code to collect execution counts into default.profraw file (overridden by '=' form of option or LLVM_PROFILE_FILE env var) -fprofile-instr-use= + Use instrumentation data for coverage testing or profile-guided optimization + -fprofile-use= Use instrumentation data for profile-guided optimization -fprofile-remapping-file= Use the remappings described in to match the profile data against names in the program @@ -4569,7 +4591,7 @@ clang-cl supports several features that require runtime library support: - Address Sanitizer (ASan): ``-fsanitize=address`` - Undefined Behavior Sanitizer (UBSan): ``-fsanitize=undefined`` - Code coverage: ``-fprofile-instr-generate -fcoverage-mapping`` -- Profile Guided Optimization (PGO): ``-fprofile-instr-generate`` +- Profile Guided Optimization (PGO): ``-fprofile-generate`` - Certain math operations (int128 division) require the builtins library In order to use these features, the user must link the right runtime libraries diff --git a/clang/include/clang/APINotes/APINotesManager.h b/clang/include/clang/APINotes/APINotesManager.h index c19082fdb597d84639d6850660eaf1345a723adf..18375c9e51a173b7e21de4a4851adca9de79112e 100644 --- a/clang/include/clang/APINotes/APINotesManager.h +++ b/clang/include/clang/APINotes/APINotesManager.h @@ -159,7 +159,7 @@ public: ArrayRef getCurrentModuleReaders() const { bool HasPublic = CurrentModuleReaders[ReaderKind::Public]; bool HasPrivate = CurrentModuleReaders[ReaderKind::Private]; - assert(!HasPrivate || (HasPublic && "private module requires public module")); + assert((!HasPrivate || HasPublic) && "private module requires public module"); if (!HasPrivate && !HasPublic) return {}; return ArrayRef(CurrentModuleReaders).slice(0, HasPrivate ? 2 : 1); diff --git a/clang/include/clang/Analysis/FlowSensitive/DataflowAnalysis.h b/clang/include/clang/Analysis/FlowSensitive/DataflowAnalysis.h index c5f14cfcd4f7bee6c93f3e6ceabb75dda64021c8..1dffbe8a09c3609e0af40bb83fff89c9a37e4fea 100644 --- a/clang/include/clang/Analysis/FlowSensitive/DataflowAnalysis.h +++ b/clang/include/clang/Analysis/FlowSensitive/DataflowAnalysis.h @@ -234,6 +234,22 @@ runDataflowAnalysis( return std::move(BlockStates); } +// Create an analysis class that is derived from `DataflowAnalysis`. This is an +// SFINAE adapter that allows us to call two different variants of constructor +// (either with or without the optional `Environment` parameter). +// FIXME: Make all classes derived from `DataflowAnalysis` take an `Environment` +// parameter in their constructor so that we can get rid of this abomination. +template +auto createAnalysis(ASTContext &ASTCtx, Environment &Env) + -> decltype(AnalysisT(ASTCtx, Env)) { + return AnalysisT(ASTCtx, Env); +} +template +auto createAnalysis(ASTContext &ASTCtx, Environment &Env) + -> decltype(AnalysisT(ASTCtx)) { + return AnalysisT(ASTCtx); +} + /// Runs a dataflow analysis over the given function and then runs `Diagnoser` /// over the results. Returns a list of diagnostics for `FuncDecl` or an /// error. Currently, errors can occur (at least) because the analysis requires @@ -262,7 +278,7 @@ llvm::Expected> diagnoseFunction( const WatchedLiteralsSolver *Solver = OwnedSolver.get(); DataflowAnalysisContext AnalysisContext(std::move(OwnedSolver)); Environment Env(AnalysisContext, FuncDecl); - AnalysisT Analysis(ASTCtx); + AnalysisT Analysis = createAnalysis(ASTCtx, Env); llvm::SmallVector Diagnostics; if (llvm::Error Err = runTypeErasedDataflowAnalysis( diff --git a/clang/include/clang/Analysis/FlowSensitive/DataflowAnalysisContext.h b/clang/include/clang/Analysis/FlowSensitive/DataflowAnalysisContext.h index c1aa8484817a9d9cc6ffc1c48e03b429fd872cbe..20e45cc27b01fa8e9eb34f4f93aae70ce0c3d98d 100644 --- a/clang/include/clang/Analysis/FlowSensitive/DataflowAnalysisContext.h +++ b/clang/include/clang/Analysis/FlowSensitive/DataflowAnalysisContext.h @@ -58,6 +58,10 @@ using FieldSet = llvm::SmallSetVector; /// Returns the set of all fields in the type. FieldSet getObjectFields(QualType Type); +/// Returns whether `Fields` and `FieldLocs` contain the same fields. +bool containsSameFields(const FieldSet &Fields, + const RecordStorageLocation::FieldToLoc &FieldLocs); + struct ContextSensitiveOptions { /// The maximum depth to analyze. A value of zero is equivalent to disabling /// context-sensitive analysis entirely. @@ -92,11 +96,39 @@ public: /*Logger=*/nullptr}); ~DataflowAnalysisContext(); + /// Sets a callback that returns the names and types of the synthetic fields + /// to add to a `RecordStorageLocation` of a given type. + /// Typically, this is called from the constructor of a `DataflowAnalysis` + /// + /// To maintain the invariant that all `RecordStorageLocation`s of a given + /// type have the same fields: + /// * The callback must always return the same result for a given type + /// * `setSyntheticFieldCallback()` must be called before any + // `RecordStorageLocation`s are created. + void setSyntheticFieldCallback( + std::function(QualType)> CB) { + assert(!RecordStorageLocationCreated); + SyntheticFieldCallback = CB; + } + /// Returns a new storage location appropriate for `Type`. /// /// A null `Type` is interpreted as the pointee type of `std::nullptr_t`. StorageLocation &createStorageLocation(QualType Type); + /// Creates a `RecordStorageLocation` for the given type and with the given + /// fields. + /// + /// Requirements: + /// + /// `FieldLocs` must contain exactly the fields returned by + /// `getModeledFields(Type)`. + /// `SyntheticFields` must contain exactly the fields returned by + /// `getSyntheticFields(Type)`. + RecordStorageLocation &createRecordStorageLocation( + QualType Type, RecordStorageLocation::FieldToLoc FieldLocs, + RecordStorageLocation::SyntheticFieldMap SyntheticFields); + /// Returns a stable storage location for `D`. StorageLocation &getStableStorageLocation(const ValueDecl &D); @@ -169,6 +201,15 @@ public: /// context. FieldSet getModeledFields(QualType Type); + /// Returns the names and types of the synthetic fields for the given record + /// type. + llvm::StringMap getSyntheticFields(QualType Type) { + assert(Type->isRecordType()); + if (SyntheticFieldCallback) + return SyntheticFieldCallback(Type); + return {}; + } + private: friend class Environment; @@ -250,6 +291,11 @@ private: FieldSet ModeledFields; std::unique_ptr LogOwner; // If created via flags. + + std::function(QualType)> SyntheticFieldCallback; + + /// Has any `RecordStorageLocation` been created yet? + bool RecordStorageLocationCreated = false; }; } // namespace dataflow diff --git a/clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h b/clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h index 7c1f5491096326b48f77f0e43d639f68f35ad993..d7e39ab2616fd90514327ca57ecc0a226cc58590 100644 --- a/clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h +++ b/clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h @@ -166,6 +166,15 @@ public: /// with a symbolic representation of the `this` pointee. Environment(DataflowAnalysisContext &DACtx, const DeclContext &DeclCtx); + /// Assigns storage locations and values to all parameters, captures, global + /// variables, fields and functions referenced in the function currently being + /// analyzed. + /// + /// Requirements: + /// + /// The function must have a body. + void initialize(); + /// Returns a new environment that is a copy of this one. /// /// The state of the program is initially the same, but can be mutated without @@ -283,7 +292,15 @@ public: /// Returns the storage location assigned to the `this` pointee in the /// environment or null if the `this` pointee has no assigned storage location /// in the environment. - RecordStorageLocation *getThisPointeeStorageLocation() const; + RecordStorageLocation *getThisPointeeStorageLocation() const { + return ThisPointeeLoc; + } + + /// Sets the storage location assigned to the `this` pointee in the + /// environment. + void setThisPointeeStorageLocation(RecordStorageLocation &Loc) { + ThisPointeeLoc = &Loc; + } /// Returns the location of the result object for a record-type prvalue. /// @@ -367,7 +384,8 @@ public: /// storage locations and values for indirections until it finds a /// non-pointer/non-reference type. /// - /// If `Type` is a class, struct, or union type, calls `setValue()` to + /// If `Type` is a class, struct, or union type, creates values for all + /// modeled fields (including synthetic fields) and calls `setValue()` to /// associate the `RecordValue` with its storage location /// (`RecordValue::getLoc()`). /// @@ -570,6 +588,9 @@ public: return dyn_cast(getDeclCtx()); } + /// Returns the size of the call stack. + size_t callStackSize() const { return CallStack.size(); } + /// Returns whether this `Environment` can be extended to analyze the given /// `Callee` (i.e. if `pushCall` can be used), with recursion disallowed and a /// given `MaxDepth`. diff --git a/clang/include/clang/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.h b/clang/include/clang/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.h index aeaf75b2154222cad737b2f92c5db06da146ab1b..09eb8b93822612f4e885c6416a5e8fc960d096f0 100644 --- a/clang/include/clang/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.h +++ b/clang/include/clang/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.h @@ -45,7 +45,7 @@ struct UncheckedOptionalAccessModelOptions { class UncheckedOptionalAccessModel : public DataflowAnalysis { public: - UncheckedOptionalAccessModel(ASTContext &Ctx); + UncheckedOptionalAccessModel(ASTContext &Ctx, dataflow::Environment &Env); /// Returns a matcher for the optional classes covered by this model. static ast_matchers::DeclarationMatcher optionalClassDecl(); @@ -54,17 +54,6 @@ public: void transfer(const CFGElement &Elt, NoopLattice &L, Environment &Env); - ComparisonResult compare(QualType Type, const Value &Val1, - const Environment &Env1, const Value &Val2, - const Environment &Env2) override; - - bool merge(QualType Type, const Value &Val1, const Environment &Env1, - const Value &Val2, const Environment &Env2, Value &MergedVal, - Environment &MergedEnv) override; - - Value *widen(QualType Type, Value &Prev, const Environment &PrevEnv, - Value &Current, Environment &CurrentEnv) override; - private: CFGMatchSwitch> TransferMatchSwitch; }; diff --git a/clang/include/clang/Analysis/FlowSensitive/RecordOps.h b/clang/include/clang/Analysis/FlowSensitive/RecordOps.h index f007a947a82f1328a1d2cd196fd8b6e146cfa90e..7b87840d626b4b608960bfd76edbaf833dff4d6a 100644 --- a/clang/include/clang/Analysis/FlowSensitive/RecordOps.h +++ b/clang/include/clang/Analysis/FlowSensitive/RecordOps.h @@ -21,10 +21,10 @@ namespace dataflow { /// Copies a record (struct, class, or union) from `Src` to `Dst`. /// -/// This performs a deep copy, i.e. it copies every field and recurses on -/// fields of record type. It also copies properties from the `RecordValue` -/// associated with `Src` to the `RecordValue` associated with `Dst` (if these -/// `RecordValue`s exist). +/// This performs a deep copy, i.e. it copies every field (including synthetic +/// fields) and recurses on fields of record type. It also copies properties +/// from the `RecordValue` associated with `Src` to the `RecordValue` associated +/// with `Dst` (if these `RecordValue`s exist). /// /// If there is a `RecordValue` associated with `Dst` in the environment, this /// function creates a new `RecordValue` and associates it with `Dst`; clients @@ -47,10 +47,11 @@ void copyRecord(RecordStorageLocation &Src, RecordStorageLocation &Dst, /// retrieved from `Env2`. A convenience overload retrieves values for `Loc1` /// and `Loc2` from the same environment. /// -/// This performs a deep comparison, i.e. it compares every field and recurses -/// on fields of record type. Fields of reference type compare equal if they -/// refer to the same storage location. If `RecordValue`s are associated with -/// `Loc1` and `Loc2`, it also compares the properties on those `RecordValue`s. +/// This performs a deep comparison, i.e. it compares every field (including +/// synthetic fields) and recurses on fields of record type. Fields of reference +/// type compare equal if they refer to the same storage location. If +/// `RecordValue`s are associated with `Loc1` and Loc2`, it also compares the +/// properties on those `RecordValue`s. /// /// Note on how to interpret the result: /// - If this returns true, the records are guaranteed to be equal at runtime. diff --git a/clang/include/clang/Analysis/FlowSensitive/StorageLocation.h b/clang/include/clang/Analysis/FlowSensitive/StorageLocation.h index 89d2bbfbb69f9fbbf1bd38440fffa2b6f57af511..8fcc6a44027a02065d150fb8948ca6cad547f2f3 100644 --- a/clang/include/clang/Analysis/FlowSensitive/StorageLocation.h +++ b/clang/include/clang/Analysis/FlowSensitive/StorageLocation.h @@ -73,7 +73,13 @@ public: /// /// Contains storage locations for all modeled fields of the record (also /// referred to as "children"). The child map is flat, so accessible members of -/// the base class are directly accesible as children of this location. +/// the base class are directly accessible as children of this location. +/// +/// Record storage locations may also contain so-called synthetic fields. These +/// are typically used to model the internal state of a class (e.g. the value +/// stored in a `std::optional`) without having to depend on that class's +/// implementation details. All `RecordStorageLocation`s of a given type should +/// have the same synthetic fields. /// /// The storage location for a field of reference type may be null. This /// typically occurs in one of two situations: @@ -88,12 +94,12 @@ public: class RecordStorageLocation final : public StorageLocation { public: using FieldToLoc = llvm::DenseMap; + using SyntheticFieldMap = llvm::StringMap; - explicit RecordStorageLocation(QualType Type) - : RecordStorageLocation(Type, FieldToLoc()) {} - - RecordStorageLocation(QualType Type, FieldToLoc TheChildren) - : StorageLocation(Kind::Record, Type), Children(std::move(TheChildren)) { + RecordStorageLocation(QualType Type, FieldToLoc TheChildren, + SyntheticFieldMap TheSyntheticFields) + : StorageLocation(Kind::Record, Type), Children(std::move(TheChildren)), + SyntheticFields(std::move(TheSyntheticFields)) { assert(!Type.isNull()); assert(Type->isRecordType()); assert([this] { @@ -133,6 +139,19 @@ public: return It->second; } + /// Returns the storage location for the synthetic field `Name`. + /// The synthetic field must exist. + StorageLocation &getSyntheticField(llvm::StringRef Name) const { + StorageLocation *Loc = SyntheticFields.lookup(Name); + assert(Loc != nullptr); + return *Loc; + } + + llvm::iterator_range + synthetic_fields() const { + return {SyntheticFields.begin(), SyntheticFields.end()}; + } + /// 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. @@ -151,6 +170,7 @@ public: private: FieldToLoc Children; + SyntheticFieldMap SyntheticFields; }; } // namespace dataflow diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td index 03ed6accf700c4e623059ad8474b1cc02c84281b..121ed203829cec44dcf1ec1eb9fc149ff0712e4e 100644 --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -2024,6 +2024,14 @@ def BPFPreserveAccessIndex : InheritableAttr, let LangOpts = [COnly]; } +def BPFPreserveStaticOffset : InheritableAttr, + TargetSpecificAttr { + let Spellings = [Clang<"preserve_static_offset">]; + let Subjects = SubjectList<[Record], ErrorDiag>; + let Documentation = [BPFPreserveStaticOffsetDocs]; + let LangOpts = [COnly]; +} + def BTFDeclTag : InheritableAttr { let Spellings = [Clang<"btf_decl_tag">]; let Args = [StringArgument<"BTFDeclTag">]; @@ -4253,6 +4261,18 @@ def HLSLGroupSharedAddressSpace : TypeAttr { let Documentation = [HLSLGroupSharedAddressSpaceDocs]; } +def HLSLParamModifier : TypeAttr { + let Spellings = [CustomKeyword<"in">, CustomKeyword<"inout">, CustomKeyword<"out">]; + let Accessors = [Accessor<"isIn", [CustomKeyword<"in">]>, + Accessor<"isInOut", [CustomKeyword<"inout">]>, + Accessor<"isOut", [CustomKeyword<"out">]>, + Accessor<"isAnyOut", [CustomKeyword<"out">, CustomKeyword<"inout">]>, + Accessor<"isAnyIn", [CustomKeyword<"in">, CustomKeyword<"inout">]>]; + let Subjects = SubjectList<[ParmVar]>; + let Documentation = [HLSLParamQualifierDocs]; + let Args = [DefaultBoolArgument<"MergedSpelling", /*default*/0, /*fake*/1>]; +} + def RandomizeLayout : InheritableAttr { let Spellings = [GCC<"randomize_layout">]; let Subjects = SubjectList<[Record]>; diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td index be74535e28d8a60d18a25f82070bb8fa6c9fdbbe..53c3dff19fc9b7b09608fafa7df9c6bd1b128dda 100644 --- a/clang/include/clang/Basic/AttrDocs.td +++ b/clang/include/clang/Basic/AttrDocs.td @@ -2199,6 +2199,43 @@ preserving struct or union member access debuginfo indices of this struct or union, similar to clang ``__builtin_preserve_access_index()``. }]; } + +def BPFPreserveStaticOffsetDocs : Documentation { + let Category = DocCatFunction; + let Content = [{ +Clang supports the ``__attribute__((preserve_static_offset))`` +attribute for the BPF target. This attribute may be attached to a +struct or union declaration. Reading or writing fields of types having +such annotation is guaranteed to generate LDX/ST/STX instruction with +offset corresponding to the field. + +For example: + +.. code-block:: c + + struct foo { + int a; + int b; + }; + + struct bar { + int a; + struct foo b; + } __attribute__((preserve_static_offset)); + + void buz(struct bar *g) { + g->b.a = 42; + } + +The assignment to ``g``'s field would produce an ST instruction with +offset 8: ``*(u32)(r1 + 8) = 42;``. + +Without this attribute generated instructions might be different, +depending on optimizations behavior. E.g. the example above could be +rewritten as ``r1 += 8; *(u32)(r1 + 0) = 42;``. + }]; +} + def BTFDeclTagDocs : Documentation { let Category = DocCatFunction; let Content = [{ @@ -7061,6 +7098,26 @@ The full documentation is available here: https://learn.microsoft.com/en-us/wind }]; } +def HLSLParamQualifierDocs : Documentation { + let Category = DocCatVariable; + let Heading = "HLSL Parameter Modifiers"; + let Content = [{ +HLSL function parameters are passed by value. Parameter declarations support +three qualifiers to denote parameter passing behavior. The three qualifiers are +`in`, `out` and `inout`. + +Parameters annotated with `in` or with no annotation are passed by value from +the caller to the callee. + +Parameters annotated with `out` are written to the argument after the callee +returns (Note: arguments values passed into `out` parameters *are not* copied +into the callee). + +Parameters annotated with `inout` are copied into the callee via a temporary, +and copied back to the argument after the callee returns. + }]; +} + def AnnotateTypeDocs : Documentation { let Category = DocCatType; let Heading = "annotate_type"; @@ -7295,7 +7352,7 @@ this usage is debugger-dependent. } def CleanupDocs : Documentation { - let Category = DocCatType; + let Category = DocCatVariable; let Content = [{ This attribute allows a function to be run when a local variable goes out of scope. The attribute takes the identifier of a function with a parameter type @@ -7527,7 +7584,7 @@ For example, Task foo() { return increment(1); } // Error. foo is not a coroutine. // Fine for a coroutine wrapper to return a CRT. - Task [[clang::coro_wrapper]] foo() { return increment(1); } + [[clang::coro_wrapper]] Task foo() { return increment(1); } void bar() { // Invalid. This intantiates a function which returns a CRT but is not marked as diff --git a/clang/include/clang/Basic/AttributeCommonInfo.h b/clang/include/clang/Basic/AttributeCommonInfo.h index 3140d1a838afcecdbfed3e000d8e1d89edc67557..018b92fdc11f55988d98f26c5f3e9dd0a4df3940 100644 --- a/clang/include/clang/Basic/AttributeCommonInfo.h +++ b/clang/include/clang/Basic/AttributeCommonInfo.h @@ -177,6 +177,7 @@ public: IsRegularKeywordAttribute); } const IdentifierInfo *getAttrName() const { return AttrName; } + void setAttrName(const IdentifierInfo *AttrNameII) { AttrName = AttrNameII; } SourceLocation getLoc() const { return AttrRange.getBegin(); } SourceRange getRange() const { return AttrRange; } void setRange(SourceRange R) { AttrRange = R; } diff --git a/clang/include/clang/Basic/BuiltinsAMDGPU.def b/clang/include/clang/Basic/BuiltinsAMDGPU.def index a19c8bd5f219ec6a2fb1f9b624b1fdb94d64db94..8b59b3790d7bc661361cf6f2b8f154877425658a 100644 --- a/clang/include/clang/Basic/BuiltinsAMDGPU.def +++ b/clang/include/clang/Basic/BuiltinsAMDGPU.def @@ -150,8 +150,8 @@ BUILTIN(__builtin_amdgcn_mqsad_u32_u8, "V4UiWUiUiV4Ui", "nc") // Ballot builtins. //===----------------------------------------------------------------------===// -TARGET_BUILTIN(__builtin_amdgcn_ballot_w32, "Uib", "nc", "wavefrontsize32") -TARGET_BUILTIN(__builtin_amdgcn_ballot_w64, "LUib", "nc", "wavefrontsize64") +TARGET_BUILTIN(__builtin_amdgcn_ballot_w32, "ZUib", "nc", "wavefrontsize32") +TARGET_BUILTIN(__builtin_amdgcn_ballot_w64, "WUib", "nc", "wavefrontsize64") // Deprecated intrinsics in favor of __builtin_amdgn_ballot_{w32|w64} BUILTIN(__builtin_amdgcn_uicmp, "WUiUiUiIi", "nc") diff --git a/clang/include/clang/Basic/BuiltinsSystemZ.def b/clang/include/clang/Basic/BuiltinsSystemZ.def index 079e4113648859ee1647cbf96e0a66840f6a0242..4cfc52ae421686d1750a4143eb674ec4b7fea2d7 100644 --- a/clang/include/clang/Basic/BuiltinsSystemZ.def +++ b/clang/include/clang/Basic/BuiltinsSystemZ.def @@ -80,10 +80,10 @@ TARGET_BUILTIN(__builtin_s390_vavglb, "V16UcV16UcV16Uc", "nc", "vector") TARGET_BUILTIN(__builtin_s390_vavglh, "V8UsV8UsV8Us", "nc", "vector") TARGET_BUILTIN(__builtin_s390_vavglf, "V4UiV4UiV4Ui", "nc", "vector") TARGET_BUILTIN(__builtin_s390_vavglg, "V2ULLiV2ULLiV2ULLi", "nc", "vector") -TARGET_BUILTIN(__builtin_s390_vceqbs, "V16ScV16ScV16Sci*", "nc", "vector") -TARGET_BUILTIN(__builtin_s390_vceqhs, "V8SsV8SsV8Ssi*", "nc", "vector") -TARGET_BUILTIN(__builtin_s390_vceqfs, "V4SiV4SiV4Sii*", "nc", "vector") -TARGET_BUILTIN(__builtin_s390_vceqgs, "V2SLLiV2SLLiV2SLLii*", "nc", "vector") +TARGET_BUILTIN(__builtin_s390_vceqbs, "V16ScV16UcV16Uci*", "nc", "vector") +TARGET_BUILTIN(__builtin_s390_vceqhs, "V8SsV8UsV8Usi*", "nc", "vector") +TARGET_BUILTIN(__builtin_s390_vceqfs, "V4SiV4UiV4Uii*", "nc", "vector") +TARGET_BUILTIN(__builtin_s390_vceqgs, "V2SLLiV2ULLiV2ULLii*", "nc", "vector") TARGET_BUILTIN(__builtin_s390_vchbs, "V16ScV16ScV16Sci*", "nc", "vector") TARGET_BUILTIN(__builtin_s390_vchhs, "V8SsV8SsV8Ssi*", "nc", "vector") TARGET_BUILTIN(__builtin_s390_vchfs, "V4SiV4SiV4Sii*", "nc", "vector") @@ -105,10 +105,10 @@ TARGET_BUILTIN(__builtin_s390_verimb, "V16UcV16UcV16UcV16UcIi", "nc", "vector") TARGET_BUILTIN(__builtin_s390_verimh, "V8UsV8UsV8UsV8UsIi", "nc", "vector") TARGET_BUILTIN(__builtin_s390_verimf, "V4UiV4UiV4UiV4UiIi", "nc", "vector") TARGET_BUILTIN(__builtin_s390_verimg, "V2ULLiV2ULLiV2ULLiV2ULLiIi", "nc", "vector") -TARGET_BUILTIN(__builtin_s390_verllb, "V16UcV16UcUi", "nc", "vector") -TARGET_BUILTIN(__builtin_s390_verllh, "V8UsV8UsUi", "nc", "vector") -TARGET_BUILTIN(__builtin_s390_verllf, "V4UiV4UiUi", "nc", "vector") -TARGET_BUILTIN(__builtin_s390_verllg, "V2ULLiV2ULLiUi", "nc", "vector") +TARGET_BUILTIN(__builtin_s390_verllb, "V16UcV16UcUc", "nc", "vector") +TARGET_BUILTIN(__builtin_s390_verllh, "V8UsV8UsUc", "nc", "vector") +TARGET_BUILTIN(__builtin_s390_verllf, "V4UiV4UiUc", "nc", "vector") +TARGET_BUILTIN(__builtin_s390_verllg, "V2ULLiV2ULLiUc", "nc", "vector") TARGET_BUILTIN(__builtin_s390_verllvb, "V16UcV16UcV16Uc", "nc", "vector") TARGET_BUILTIN(__builtin_s390_verllvh, "V8UsV8UsV8Us", "nc", "vector") TARGET_BUILTIN(__builtin_s390_verllvf, "V4UiV4UiV4Ui", "nc", "vector") @@ -253,8 +253,8 @@ TARGET_BUILTIN(__builtin_s390_vfsqdb, "V2dV2d", "nc", "vector") TARGET_BUILTIN(__builtin_s390_vftcidb, "V2SLLiV2dIii*", "nc", "vector") // Vector-enhancements facility 1 intrinsics. -TARGET_BUILTIN(__builtin_s390_vlrl, "V16ScUivC*", "", "vector-enhancements-1") -TARGET_BUILTIN(__builtin_s390_vstrl, "vV16ScUiv*", "", "vector-enhancements-1") +TARGET_BUILTIN(__builtin_s390_vlrlr, "V16ScUivC*", "", "vector-enhancements-1") +TARGET_BUILTIN(__builtin_s390_vstrlr, "vV16ScUiv*", "", "vector-enhancements-1") TARGET_BUILTIN(__builtin_s390_vbperm, "V2ULLiV16UcV16Uc", "nc", "vector-enhancements-1") TARGET_BUILTIN(__builtin_s390_vmslg, "V16UcV2ULLiV2ULLiV16UcIi", "nc", "vector-enhancements-1") TARGET_BUILTIN(__builtin_s390_vfmaxdb, "V2dV2dV2dIi", "nc", "vector-enhancements-1") diff --git a/clang/include/clang/Basic/CodeGenOptions.def b/clang/include/clang/Basic/CodeGenOptions.def index 675645cd534ed76358500294191fe97b0e3d2fb2..0acb5ae134ea245ed60fe91dbe18ff727fc13e71 100644 --- a/clang/include/clang/Basic/CodeGenOptions.def +++ b/clang/include/clang/Basic/CodeGenOptions.def @@ -28,12 +28,8 @@ CODEGENOPT(Name, Bits, Default) #endif CODEGENOPT(DisableIntegratedAS, 1, 0) ///< -no-integrated-as -ENUM_CODEGENOPT(CompressDebugSections, llvm::DebugCompressionType, 2, - llvm::DebugCompressionType::None) CODEGENOPT(RelaxELFRelocations, 1, 1) ///< -Wa,-mrelax-relocations={yes,no} CODEGENOPT(AsmVerbose , 1, 0) ///< -dA, -fverbose-asm. -CODEGENOPT(Dwarf64 , 1, 0) ///< -gdwarf64. -CODEGENOPT(Dwarf32 , 1, 1) ///< -gdwarf32. CODEGENOPT(PreserveAsmComments, 1, 1) ///< -dA, -fno-preserve-as-comments. CODEGENOPT(AssumeSaneOperatorNew , 1, 1) ///< implicit __attribute__((malloc)) operator new CODEGENOPT(AssumeUniqueVTables , 1, 1) ///< Assume a class has only one vtable. @@ -73,10 +69,6 @@ CODEGENOPT(DebugPassManager, 1, 0) ///< Prints debug information for the new CODEGENOPT(DisableRedZone , 1, 0) ///< Set when -mno-red-zone is enabled. CODEGENOPT(EmitCallSiteInfo, 1, 0) ///< Emit call site info only in the case of ///< '-g' + 'O>0' level. -CODEGENOPT(EnableDIPreservationVerify, 1, 0) ///< Enable di preservation verify - ///< each (it means check - ///< the original debug info - ///< metadata preservation). CODEGENOPT(IndirectTlsSegRefs, 1, 0) ///< Set when -mno-tls-direct-seg-refs ///< is specified. CODEGENOPT(DisableTailCalls , 1, 0) ///< Do not emit tail calls. @@ -113,16 +105,10 @@ CODEGENOPT(IndirectBranchCSPrefix, 1, 0) ///< if -mindirect-branch-cs-prefix CODEGENOPT(XRayInstrumentFunctions , 1, 0) ///< Set when -fxray-instrument is ///< enabled. CODEGENOPT(StackSizeSection , 1, 0) ///< Set when -fstack-size-section is enabled. -CODEGENOPT(ForceDwarfFrameSection , 1, 0) ///< Set when -fforce-dwarf-frame is - ///< enabled. ///< Set when -femit-compact-unwind-non-canonical is enabled. CODEGENOPT(EmitCompactUnwindNonCanonical, 1, 0) -///< Set when -femit-dwarf-unwind is passed. -ENUM_CODEGENOPT(EmitDwarfUnwind, llvm::EmitDwarfUnwindType, 2, - llvm::EmitDwarfUnwindType::Default) - ///< Set when -fxray-always-emit-customevents is enabled. CODEGENOPT(XRayAlwaysEmitCustomEvents , 1, 0) @@ -178,8 +164,6 @@ CODEGENOPT(IncrementalLinkerCompatible, 1, 0) ///< Emit an object file which can CODEGENOPT(MergeAllConstants , 1, 1) ///< Merge identical constants. CODEGENOPT(MergeFunctions , 1, 0) ///< Set when -fmerge-functions is enabled. CODEGENOPT(NoCommon , 1, 0) ///< Set when -fno-common or C++ is enabled. -CODEGENOPT(NoDwarfDirectoryAsm , 1, 0) ///< Set when -fno-dwarf-directory-asm is - ///< enabled. CODEGENOPT(NoExecStack , 1, 0) ///< Set when -Wa,--noexecstack is enabled. CODEGENOPT(FatalWarnings , 1, 0) ///< Set when -Wa,--fatal-warnings is ///< enabled. @@ -187,8 +171,6 @@ CODEGENOPT(NoWarn , 1, 0) ///< Set when -Wa,--no-warn is enabled. CODEGENOPT(NoTypeCheck , 1, 0) ///< Set when -Wa,--no-type-check is enabled. CODEGENOPT(MisExpect , 1, 0) ///< Set when -Wmisexpect is enabled CODEGENOPT(EnableSegmentedStacks , 1, 0) ///< Set when -fsplit-stack is enabled. -CODEGENOPT(NoInlineLineTables, 1, 0) ///< Whether debug info should contain - ///< inline line tables. CODEGENOPT(StackClashProtector, 1, 0) ///< Set when -fstack-clash-protection is enabled. CODEGENOPT(NoImplicitFloat , 1, 0) ///< Set when -mno-implicit-float is enabled. CODEGENOPT(NullPointerIsValid , 1, 0) ///< Assume Null pointer deference is defined. @@ -341,37 +323,6 @@ VALUE_CODEGENOPT(StackProbeSize , 32, 4096) ///< Overrides default stack ///< probe size, even if 0. VALUE_CODEGENOPT(WarnStackSize , 32, UINT_MAX) ///< Set via -fwarn-stack-size. CODEGENOPT(NoStackArgProbe, 1, 0) ///< Set when -mno-stack-arg-probe is used -CODEGENOPT(DebugStrictDwarf, 1, 1) ///< Whether or not to use strict DWARF info. - -/// Control the Assignment Tracking debug info feature. -ENUM_CODEGENOPT(AssignmentTrackingMode, AssignmentTrackingOpts, 2, AssignmentTrackingOpts::Disabled) - -CODEGENOPT(DebugColumnInfo, 1, 0) ///< Whether or not to use column information - ///< in debug info. - -CODEGENOPT(DebugTypeExtRefs, 1, 0) ///< Whether or not debug info should contain - ///< external references to a PCH or module. - -CODEGENOPT(DebugExplicitImport, 1, 0) ///< Whether or not debug info should - ///< contain explicit imports for - ///< anonymous namespaces - -/// Set debug info source file hashing algorithm. -ENUM_CODEGENOPT(DebugSrcHash, DebugSrcHashKind, 2, DSH_MD5) - -CODEGENOPT(SplitDwarfInlining, 1, 1) ///< Whether to include inlining info in the - ///< skeleton CU to allow for symbolication - ///< of inline stack frames without .dwo files. -CODEGENOPT(DebugFwdTemplateParams, 1, 0) ///< Whether to emit complete - ///< template parameter descriptions in - ///< forward declarations (versus just - ///< including them in the name). -ENUM_CODEGENOPT(DebugSimpleTemplateNames, llvm::codegenoptions::DebugTemplateNamesKind, 2, llvm::codegenoptions::DebugTemplateNamesKind::Full) ///< Whether to emit template parameters - ///< in the textual names of template - ///< specializations. - ///< Implies DebugFwdTemplateNames to - ///< allow decorated names to be - ///< reconstructed when needed. CODEGENOPT(EmitLLVMUseLists, 1, 0) ///< Control whether to serialize use-lists. CODEGENOPT(WholeProgramVTables, 1, 0) ///< Whether to apply whole-program @@ -395,30 +346,6 @@ VALUE_CODEGENOPT(SmallDataLimit, 32, 0) /// The lower bound for a buffer to be considered for stack protection. VALUE_CODEGENOPT(SSPBufferSize, 32, 0) -/// The kind of generated debug info. -ENUM_CODEGENOPT(DebugInfo, llvm::codegenoptions::DebugInfoKind, 4, llvm::codegenoptions::NoDebugInfo) - -/// Whether to generate macro debug info. -CODEGENOPT(MacroDebugInfo, 1, 0) - -/// Tune the debug info for this debugger. -ENUM_CODEGENOPT(DebuggerTuning, llvm::DebuggerKind, 3, - llvm::DebuggerKind::Default) - -/// Dwarf version. Version zero indicates to LLVM that no DWARF should be -/// emitted. -VALUE_CODEGENOPT(DwarfVersion, 3, 0) - -/// Whether we should emit CodeView debug information. It's possible to emit -/// CodeView and DWARF into the same object. -CODEGENOPT(EmitCodeView, 1, 0) - -/// Whether to emit the .debug$H section containing hashes of CodeView types. -CODEGENOPT(CodeViewGHash, 1, 0) - -/// Whether to emit the compiler path and command line into the CodeView debug information. -CODEGENOPT(CodeViewCommandLine, 1, 0) - /// The kind of inlining to perform. ENUM_CODEGENOPT(Inlining, InliningMethod, 2, NormalInlining) @@ -451,26 +378,14 @@ CODEGENOPT(DirectAccessExternalData, 1, 0) /// paths that reach the end of a function without executing a required return. CODEGENOPT(StrictReturn, 1, 1) -/// Whether emit extra debug info for sample pgo profile collection. -CODEGENOPT(DebugInfoForProfiling, 1, 0) - /// Whether emit pseudo probes for sample pgo profile collection. CODEGENOPT(PseudoProbeForProfiling, 1, 0) /// Whether 3-component vector type is preserved. CODEGENOPT(PreserveVec3Type, 1, 0) -/// Whether to emit .debug_gnu_pubnames section instead of .debug_pubnames. -CODEGENOPT(DebugNameTable, 2, 0) - -/// Whether to use DWARF base address specifiers in .debug_ranges. -CODEGENOPT(DebugRangesBaseAddress, 1, 0) - CODEGENOPT(NoPLT, 1, 0) -/// Whether to embed source in DWARF debug line section. -CODEGENOPT(EmbedSource, 1, 0) - /// Whether to emit all vtables CODEGENOPT(ForceEmitVTables, 1, 0) @@ -515,6 +430,9 @@ ENUM_CODEGENOPT(ZeroCallUsedRegs, llvm::ZeroCallUsedRegs::ZeroCallUsedRegsKind, /// non-deleting destructors. (No effect on Microsoft ABI.) CODEGENOPT(CtorDtorReturnThis, 1, 0) +/// FIXME: Make DebugOptions its own top-level .def file. +#include "DebugOptions.def" + #undef CODEGENOPT #undef ENUM_CODEGENOPT #undef VALUE_CODEGENOPT diff --git a/clang/include/clang/Basic/CodeGenOptions.h b/clang/include/clang/Basic/CodeGenOptions.h index b202d01af0ed6ce13dbaa09b245456b255fdee05..6952b48e898a819bef0f3b38d7d1cca65549c158 100644 --- a/clang/include/clang/Basic/CodeGenOptions.h +++ b/clang/include/clang/Basic/CodeGenOptions.h @@ -530,6 +530,10 @@ public: return SanitizeBinaryMetadataCovered || SanitizeBinaryMetadataAtomics || SanitizeBinaryMetadataUAR; } + + /// Reset all of the options that are not considered when building a + /// module. + void resetNonModularOptions(StringRef ModuleFormat); }; } // end namespace clang diff --git a/clang/include/clang/Basic/DebugOptions.def b/clang/include/clang/Basic/DebugOptions.def new file mode 100644 index 0000000000000000000000000000000000000000..7cd3edf08a17eadb3ae0965e8747654f4e66a593 --- /dev/null +++ b/clang/include/clang/Basic/DebugOptions.def @@ -0,0 +1,146 @@ +//===--- DebugOptions.def - Debug option database ----------------- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file defines debug-specific codegen options. Users of this file +// must define the CODEGENOPT macro to make use of this information. +// Optionally, the user may also define DEBUGOPT (for flags), ENUM_DEBUGOPT (for +// options that have enumeration type), and VALUE_DEBUGOPT (is a debug option +// that describes a value rather than a flag). +// +// BENIGN_ variants of the macros are used to describe options that do not +// affect the generated PCM. +// +//===----------------------------------------------------------------------===// +#ifndef DEBUGOPT +#define DEBUGOPT(Name, Bits, Default) \ +CODEGENOPT(Name, Bits, Default) +#endif + +#ifndef VALUE_DEBUGOPT +# define VALUE_DEBUGOPT(Name, Bits, Default) \ +VALUE_CODEGENOPT(Name, Bits, Default) +#endif + +#ifndef ENUM_DEBUGOPT +# define ENUM_DEBUGOPT(Name, Type, Bits, Default) \ +ENUM_CODEGENOPT(Name, Type, Bits, Default) +#endif + +#ifndef BENIGN_DEBUGOPT +#define BENIGN_DEBUGOPT(Name, Bits, Default) \ +DEBUGOPT(Name, Bits, Default) +#endif + +#ifndef BENIGN_VALUE_DEBUGOPT +# define BENIGN_VALUE_DEBUGOPT(Name, Bits, Default) \ +VALUE_DEBUGOPT(Name, Bits, Default) +#endif + +#ifndef BENIGN_ENUM_DEBUGOPT +# define BENIGN_ENUM_DEBUGOPT(Name, Type, Bits, Default) \ +ENUM_DEBUGOPT(Name, Type, Bits, Default) +#endif + +BENIGN_ENUM_DEBUGOPT(CompressDebugSections, llvm::DebugCompressionType, 2, + llvm::DebugCompressionType::None) +DEBUGOPT(Dwarf64, 1, 0) ///< -gdwarf64. +BENIGN_DEBUGOPT(EnableDIPreservationVerify, 1, 0) ///< Enable di preservation + ///< verify each (it means + ///< check the original debug + ///< info metadata + ///< preservation). +BENIGN_DEBUGOPT(ForceDwarfFrameSection , 1, 0) ///< Set when -fforce-dwarf-frame + ///< is enabled. + +///< Set when -femit-dwarf-unwind is passed. +BENIGN_ENUM_DEBUGOPT(EmitDwarfUnwind, llvm::EmitDwarfUnwindType, 2, + llvm::EmitDwarfUnwindType::Default) + +BENIGN_DEBUGOPT(NoDwarfDirectoryAsm , 1, 0) ///< Set when -fno-dwarf-directory-asm + ///< is enabled. + +BENIGN_DEBUGOPT(NoInlineLineTables, 1, 0) ///< Whether debug info should contain + ///< inline line tables. + +DEBUGOPT(DebugStrictDwarf, 1, 1) ///< Whether or not to use strict DWARF info. + +/// Control the Assignment Tracking debug info feature. +BENIGN_ENUM_DEBUGOPT(AssignmentTrackingMode, AssignmentTrackingOpts, 2, + AssignmentTrackingOpts::Disabled) + +DEBUGOPT(DebugColumnInfo, 1, 0) ///< Whether or not to use column information + ///< in debug info. + +DEBUGOPT(DebugTypeExtRefs, 1, 0) ///< Whether or not debug info should contain + ///< external references to a PCH or module. + +DEBUGOPT(DebugExplicitImport, 1, 0) ///< Whether or not debug info should + ///< contain explicit imports for + ///< anonymous namespaces + +/// Set debug info source file hashing algorithm. +ENUM_DEBUGOPT(DebugSrcHash, DebugSrcHashKind, 2, DSH_MD5) + +DEBUGOPT(SplitDwarfInlining, 1, 1) ///< Whether to include inlining info in the + ///< skeleton CU to allow for symbolication + ///< of inline stack frames without .dwo files. +DEBUGOPT(DebugFwdTemplateParams, 1, 0) ///< Whether to emit complete + ///< template parameter descriptions in + ///< forward declarations (versus just + ///< including them in the name). +ENUM_DEBUGOPT(DebugSimpleTemplateNames, + llvm::codegenoptions::DebugTemplateNamesKind, 2, + llvm::codegenoptions::DebugTemplateNamesKind::Full) + ///< Whether to emit template parameters in the textual names of + ///< template specializations. + ///< Implies DebugFwdTemplateNames to allow decorated names to be + ///< reconstructed when needed. + +/// The kind of generated debug info. +ENUM_DEBUGOPT(DebugInfo, llvm::codegenoptions::DebugInfoKind, 4, + llvm::codegenoptions::NoDebugInfo) + +/// Whether to generate macro debug info. +DEBUGOPT(MacroDebugInfo, 1, 0) + +/// Tune the debug info for this debugger. +ENUM_DEBUGOPT(DebuggerTuning, llvm::DebuggerKind, 3, + llvm::DebuggerKind::Default) + +/// Dwarf version. Version zero indicates to LLVM that no DWARF should be +/// emitted. +VALUE_DEBUGOPT(DwarfVersion, 3, 0) + +/// Whether we should emit CodeView debug information. It's possible to emit +/// CodeView and DWARF into the same object. +DEBUGOPT(EmitCodeView, 1, 0) + +/// Whether to emit the .debug$H section containing hashes of CodeView types. +DEBUGOPT(CodeViewGHash, 1, 0) + +/// Whether to emit the compiler path and command line into the CodeView debug information. +DEBUGOPT(CodeViewCommandLine, 1, 0) + +/// Whether emit extra debug info for sample pgo profile collection. +DEBUGOPT(DebugInfoForProfiling, 1, 0) + +/// Whether to emit .debug_gnu_pubnames section instead of .debug_pubnames. +DEBUGOPT(DebugNameTable, 2, 0) + +/// Whether to use DWARF base address specifiers in .debug_ranges. +DEBUGOPT(DebugRangesBaseAddress, 1, 0) + +/// Whether to embed source in DWARF debug line section. +DEBUGOPT(EmbedSource, 1, 0) + +#undef DEBUGOPT +#undef ENUM_DEBUGOPT +#undef VALUE_DEBUGOPT +#undef BENIGN_DEBUGOPT +#undef BENIGN_ENUM_DEBUGOPT +#undef BENIGN_VALUE_DEBUGOPT diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 020ff6387c280639c30843d65a23840f7107be53..6dfb2d7195203a3d900674331f036dd8306ab224 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -3148,6 +3148,9 @@ def err_attribute_bad_sve_vector_size : Error< def err_attribute_arm_feature_sve_bits_unsupported : Error< "%0 is only supported when '-msve-vector-bits=' is specified with a " "value of 128, 256, 512, 1024 or 2048.">; +def warn_attribute_arm_sm_incompat_builtin : Warning< + "builtin call has undefined behaviour when called from a %0 function">, + InGroup>; def err_sve_vector_in_non_sve_target : Error< "SVE vector type %0 cannot be used in a target without sve">; def err_attribute_riscv_rvv_bits_unsupported : Error< @@ -7220,6 +7223,11 @@ def warn_arith_conv_enum_float_cxx20 : Warning< "%plural{2:with|4:from|:and}0 " "%select{enumeration|floating-point}1 type %3 is deprecated">, InGroup; +def err_arith_conv_enum_float_cxx26 : Error< + "invalid %sub{select_arith_conv_kind}0 " + "%select{floating-point|enumeration}1 type %2 " + "%plural{2:with|4:from|:and}0 " + "%select{enumeration|floating-point}1 type %3">; def warn_arith_conv_mixed_enum_types : Warning< "%sub{select_arith_conv_kind}0 " "different enumeration types%diff{ ($ and $)|}1,2">, @@ -7228,6 +7236,10 @@ def warn_arith_conv_mixed_enum_types_cxx20 : Warning< "%sub{select_arith_conv_kind}0 " "different enumeration types%diff{ ($ and $)|}1,2 is deprecated">, InGroup; +def err_conv_mixed_enum_types_cxx26 : Error< + "invalid %sub{select_arith_conv_kind}0 " + "different enumeration types%diff{ ($ and $)|}1,2">; + def warn_arith_conv_mixed_anon_enum_types : Warning< warn_arith_conv_mixed_enum_types.Summary>, InGroup, DefaultIgnore; @@ -12004,6 +12016,7 @@ def err_hlsl_numthreads_argument_oor : Error<"argument '%select{X|Y|Z}0' to numt def err_hlsl_numthreads_invalid : Error<"total number of threads cannot exceed %0">; def err_hlsl_missing_numthreads : Error<"missing numthreads attribute for %0 shader entry">; def err_hlsl_attribute_param_mismatch : Error<"%0 attribute parameters do not match the previous declaration">; +def err_hlsl_duplicate_parameter_modifier : Error<"duplicate parameter modifier %0">; def err_hlsl_missing_semantic_annotation : Error< "semantic annotations must be present for all parameters of an entry " "function or patch constant function">; @@ -12019,6 +12032,9 @@ def err_hlsl_pointers_unsupported : Error< def err_hlsl_operator_unsupported : Error< "the '%select{&|*|->}0' operator is unsupported in HLSL">; +def err_hlsl_param_qualifier_mismatch : + Error<"conflicting parameter qualifier %0 on parameter %1">; + // Layout randomization diagnostics. def err_non_designated_init_used : Error< "a randomized struct can only be initialized with a designated initializer">; diff --git a/clang/include/clang/Basic/TargetOptions.h b/clang/include/clang/Basic/TargetOptions.h index ba3acd0295871606b6d9bdc6e4a386750f06412f..2049f03b28893fde01bce0ba3cc8d8330356be72 100644 --- a/clang/include/clang/Basic/TargetOptions.h +++ b/clang/include/clang/Basic/TargetOptions.h @@ -78,17 +78,9 @@ public: /// \brief If enabled, allow AMDGPU unsafe floating point atomics. bool AllowAMDGPUUnsafeFPAtomics = false; - /// \brief Enumeration value for AMDGPU code object version, which is the - /// code object version times 100. - enum CodeObjectVersionKind { - COV_None, - COV_2 = 200, // Unsupported. - COV_3 = 300, // Unsupported. - COV_4 = 400, - COV_5 = 500, - }; /// \brief Code object version for AMDGPU. - CodeObjectVersionKind CodeObjectVersion = CodeObjectVersionKind::COV_None; + llvm::CodeObjectVersionKind CodeObjectVersion = + llvm::CodeObjectVersionKind::COV_None; /// \brief Enumeration values for AMDGPU printf lowering scheme enum class AMDGPUPrintfKind { diff --git a/clang/include/clang/Basic/TokenKinds.def b/clang/include/clang/Basic/TokenKinds.def index 3ab420821d82bfeb17bb7175aa4be53bb26be443..5f9915d210221c4f8619f581c030368908077a6b 100644 --- a/clang/include/clang/Basic/TokenKinds.def +++ b/clang/include/clang/Basic/TokenKinds.def @@ -628,6 +628,9 @@ KEYWORD(__noinline__ , KEYCUDA) KEYWORD(cbuffer , KEYHLSL) KEYWORD(tbuffer , KEYHLSL) KEYWORD(groupshared , KEYHLSL) +KEYWORD(in , KEYHLSL) +KEYWORD(inout , KEYHLSL) +KEYWORD(out , KEYHLSL) // OpenMP Type Traits UNARY_EXPR_OR_TYPE_TRAIT(__builtin_omp_required_simd_align, OpenMPRequiredSimdAlign, KEYALL) diff --git a/clang/include/clang/Basic/arm_sme.td b/clang/include/clang/Basic/arm_sme.td index d55deeaa40bbcd54615682c5e00aa7d70146cf07..7cdb531b5aae43f919cdab19d25350848fc151dc 100644 --- a/clang/include/clang/Basic/arm_sme.td +++ b/clang/include/clang/Basic/arm_sme.td @@ -314,3 +314,16 @@ let TargetGuard = "sme2" in { def SVBMOPS : Inst<"svbmops_za32[_{d}]_m", "viPPdd", "iUi", MergeNone, "aarch64_sme_bmops_za32", [IsSharedZA, IsStreaming], [ImmCheck<0, ImmCheck0_3>]>; } + +// +// Spill and fill of ZT0 +// +let TargetGuard = "sme2" in { + def SVLDR_ZT : Inst<"svldr_zt", "viQ", "", MergeNone, "aarch64_sme_ldr_zt", [IsOverloadNone, IsStreamingCompatible, IsSharedZA], [ImmCheck<0, ImmCheck0_0>]>; + def SVSTR_ZT : Inst<"svstr_zt", "vi%", "", MergeNone, "aarch64_sme_str_zt", [IsOverloadNone, IsStreamingCompatible, IsSharedZA, IsPreservesZA], [ImmCheck<0, ImmCheck0_0>]>; + +// +// Zero ZT0 +// + def SVZERO_ZT : Inst<"svzero_zt", "vi", "", MergeNone, "aarch64_sme_zero_zt", [IsOverloadNone, IsStreamingCompatible, IsSharedZA], [ImmCheck<0, ImmCheck0_0>]>; +} diff --git a/clang/include/clang/Basic/arm_sve.td b/clang/include/clang/Basic/arm_sve.td index cd4c09a3ad7a81c1d2b721e9faac7689f7cc5831..3f69a3df9e616ab7f41c81d7b0a3a38c86501eff 100644 --- a/clang/include/clang/Basic/arm_sve.td +++ b/clang/include/clang/Basic/arm_sve.td @@ -2101,10 +2101,35 @@ let TargetGuard = "sme2" in { defm SVMAXNM : SInstMinMaxByVector<"max">; } +let TargetGuard = "sme2" in { + def SVSCLAMP_X2 : SInst<"svclamp[_single_{d}_x2]", "22dd", "csil", MergeNone, "aarch64_sve_sclamp_single_x2", [IsStreaming], []>; + def SVUCLAMP_X2 : SInst<"svclamp[_single_{d}_x2]", "22dd", "UcUsUiUl", MergeNone, "aarch64_sve_uclamp_single_x2", [IsStreaming], []>; + def SVFCLAMP_X2 : SInst<"svclamp[_single_{d}_x2]", "22dd", "hfd", MergeNone, "aarch64_sve_fclamp_single_x2", [IsStreaming], []>; + + def SVSCLAMP_X4 : SInst<"svclamp[_single_{d}_x4]", "44dd", "csil", MergeNone, "aarch64_sve_sclamp_single_x4", [IsStreaming], []>; + def SVUCLAMP_X4 : SInst<"svclamp[_single_{d}_x4]", "44dd", "UcUsUiUl", MergeNone, "aarch64_sve_uclamp_single_x4", [IsStreaming], []>; + def SVFCLAMP_X4 : SInst<"svclamp[_single_{d}_x4]", "44dd", "hfd", MergeNone, "aarch64_sve_fclamp_single_x4", [IsStreaming], []>; +} + let TargetGuard = "sme2" in { // == ADD (vectors) == def SVADD_SINGLE_X2 : SInst<"svadd[_single_{d}_x2]", "22d", "cUcsUsiUilUl", MergeNone, "aarch64_sve_add_single_x2", [IsStreaming], []>; def SVADD_SINGLE_X4 : SInst<"svadd[_single_{d}_x4]", "44d", "cUcsUsiUilUl", MergeNone, "aarch64_sve_add_single_x4", [IsStreaming], []>; + + // 2-way and 4-way selects + def SVSEL_X2 : SInst<"svsel[_{d}_x2]", "2}22", "cUcsUsiUilUlbhfd", MergeNone, "aarch64_sve_sel_x2", [IsStreaming], []>; + def SVSEL_X4 : SInst<"svsel[_{d}_x4]", "4}44", "cUcsUsiUilUlbhfd", MergeNone, "aarch64_sve_sel_x4", [IsStreaming], []>; + + // SRSHL / URSHL + def SVSRSHL_SINGLE_X2 : SInst<"svrshl[_single_{d}_x2]", "22d", "csil", MergeNone, "aarch64_sve_srshl_single_x2", [IsStreaming], []>; + def SVURSHL_SINGLE_X2 : SInst<"svrshl[_single_{d}_x2]", "22d", "UcUsUiUl", MergeNone, "aarch64_sve_urshl_single_x2", [IsStreaming], []>; + def SVSRSHL_SINGLE_X4 : SInst<"svrshl[_single_{d}_x4]", "44d", "csil", MergeNone, "aarch64_sve_srshl_single_x4", [IsStreaming], []>; + def SVURSHL_SINGLE_X4 : SInst<"svrshl[_single_{d}_x4]", "44d", "UcUsUiUl", MergeNone, "aarch64_sve_urshl_single_x4", [IsStreaming], []>; + + def SVSRSHL_X2 : SInst<"svrshl[_{d}_x2]", "222", "csil", MergeNone, "aarch64_sve_srshl_x2", [IsStreaming], []>; + def SVURSHL_X2 : SInst<"svrshl[_{d}_x2]", "222", "UcUsUiUl", MergeNone, "aarch64_sve_urshl_x2", [IsStreaming], []>; + def SVSRSHL_X4 : SInst<"svrshl[_{d}_x4]", "444", "csil", MergeNone, "aarch64_sve_srshl_x4", [IsStreaming], []>; + def SVURSHL_X4 : SInst<"svrshl[_{d}_x4]", "444", "UcUsUiUl", MergeNone, "aarch64_sve_urshl_x4", [IsStreaming], []>; } let TargetGuard = "sve2p1" in { diff --git a/clang/include/clang/Driver/Multilib.h b/clang/include/clang/Driver/Multilib.h index 1416559414f894b8db92ffcd730fc2d6d9d6447d..6a9533e6dd831f15ffd5acf3d5db6d4a50692adb 100644 --- a/clang/include/clang/Driver/Multilib.h +++ b/clang/include/clang/Driver/Multilib.h @@ -39,13 +39,22 @@ private: std::string IncludeSuffix; flags_list Flags; + // Optionally, a multilib can be assigned a string tag indicating that it's + // part of a group of mutually exclusive possibilities. If two or more + // multilibs have the same non-empty value of ExclusiveGroup, then only the + // last matching one of them will be selected. + // + // Setting this to the empty string is a special case, indicating that the + // directory is not mutually exclusive with anything else. + std::string ExclusiveGroup; + public: /// GCCSuffix, OSSuffix & IncludeSuffix will be appended directly to the /// sysroot string so they must either be empty or begin with a '/' character. /// This is enforced with an assert in the constructor. Multilib(StringRef GCCSuffix = {}, StringRef OSSuffix = {}, - StringRef IncludeSuffix = {}, - const flags_list &Flags = flags_list()); + StringRef IncludeSuffix = {}, const flags_list &Flags = flags_list(), + StringRef ExclusiveGroup = {}); /// Get the detected GCC installation path suffix for the multi-arch /// target variant. Always starts with a '/', unless empty @@ -63,6 +72,9 @@ public: /// All elements begin with either '-' or '!' const flags_list &flags() const { return Flags; } + /// Get the exclusive group label. + const std::string &exclusiveGroup() const { return ExclusiveGroup; } + LLVM_DUMP_METHOD void dump() const; /// print summary of the Multilib void print(raw_ostream &OS) const; diff --git a/clang/include/clang/Driver/OffloadBundler.h b/clang/include/clang/Driver/OffloadBundler.h index 17df31d31071d99cab3c0c6b9e4a7361ad813f09..84349abe185fa4d778585f5832930e650c1b3fb0 100644 --- a/clang/include/clang/Driver/OffloadBundler.h +++ b/clang/include/clang/Driver/OffloadBundler.h @@ -66,7 +66,7 @@ public: llvm::Error UnbundleArchive(); }; -/// Obtain the offload kind, real machine triple, and an optional GPUArch +/// Obtain the offload kind, real machine triple, and an optional TargetID /// out of the target information specified by the user. /// Bundle Entry ID (or, Offload Target String) has following components: /// * Offload Kind - Host, OpenMP, or HIP diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td index 9689f12fd01417bb2de35306d2e9dfa50c846f1f..0eec2b352637623f1841216f5f72887e61a96220 100644 --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -994,7 +994,7 @@ def all__load : Flag<["-"], "all_load">; def allowable__client : Separate<["-"], "allowable_client">; def ansi : Flag<["-", "--"], "ansi">, Group; def arch__errors__fatal : Flag<["-"], "arch_errors_fatal">; -def arch : Separate<["-"], "arch">, Flags<[NoXarchOption]>; +def arch : Separate<["-"], "arch">, Flags<[NoXarchOption,TargetSpecific]>; def arch__only : Separate<["-"], "arch_only">; def autocomplete : Joined<["--"], "autocomplete=">; def bind__at__load : Flag<["-"], "bind_at_load">; @@ -3172,7 +3172,8 @@ def fno_ms_compatibility : Flag<["-"], "fno-ms-compatibility">, Group, def fno_objc_legacy_dispatch : Flag<["-"], "fno-objc-legacy-dispatch">, Group; def fno_objc_weak : Flag<["-"], "fno-objc-weak">, Group, Visibility<[ClangOption, CC1Option]>; -def fno_omit_frame_pointer : Flag<["-"], "fno-omit-frame-pointer">, Group; +def fno_omit_frame_pointer : Flag<["-"], "fno-omit-frame-pointer">, Group, + Visibility<[ClangOption, FlangOption]>; defm operator_names : BoolFOption<"operator-names", LangOpts<"CXXOperatorNames">, Default, NegFlag>; def fomit_frame_pointer : Flag<["-"], "fomit-frame-pointer">, Group, + Visibility<[ClangOption, FlangOption]>, HelpText<"Omit the frame pointer from functions that don't need it. " "Some stack unwinding cases, such as profilers and sanitizers, may prefer specifying -fno-omit-frame-pointer. " "On many targets, -O1 and higher omit the frame pointer by default. " @@ -4721,9 +4723,9 @@ defm amdgpu_ieee : BoolOption<"m", "amdgpu-ieee", def mcode_object_version_EQ : Joined<["-"], "mcode-object-version=">, Group, HelpText<"Specify code object ABI version. Defaults to 4. (AMDGPU only)">, - Visibility<[ClangOption, CC1Option]>, + Visibility<[ClangOption, FlangOption, CC1Option, FC1Option]>, Values<"none,4,5">, - NormalizedValuesScope<"TargetOptions">, + NormalizedValuesScope<"llvm::CodeObjectVersionKind">, NormalizedValues<["COV_None", "COV_4", "COV_5"]>, MarshallingInfoEnum, "COV_4">; @@ -5169,7 +5171,7 @@ def nodriverkitlib : Flag<["-"], "nodriverkitlib">; def nofixprebinding : Flag<["-"], "nofixprebinding">; def nolibc : Flag<["-"], "nolibc">; def nomultidefs : Flag<["-"], "nomultidefs">; -def nopie : Flag<["-"], "nopie">, Visibility<[ClangOption, FlangOption]>; +def nopie : Flag<["-"], "nopie">, Visibility<[ClangOption, FlangOption]>, Flags<[TargetSpecific]>; // OpenBSD def no_pie : Flag<["-"], "no-pie">, Visibility<[ClangOption, FlangOption]>; def noprebind : Flag<["-"], "noprebind">; def noprofilelib : Flag<["-"], "noprofilelib">; @@ -5997,6 +5999,15 @@ def mno_gather : Flag<["-"], "mno-gather">, Group, HelpText<"Disable generation of gather instructions in auto-vectorization(x86 only)">; def mno_scatter : Flag<["-"], "mno-scatter">, Group, HelpText<"Disable generation of scatter instructions in auto-vectorization(x86 only)">; +def mapx_features_EQ : CommaJoined<["-"], "mapx-features=">, Group, + HelpText<"Enable features of APX">, Values<"egpr,push2pop2,ppx,ndd,ccmp,cf">; +def mno_apx_features_EQ : CommaJoined<["-"], "mno-apx-features=">, Group, + HelpText<"Disable features of APX">, Values<"egpr,push2pop2,ppx,ndd,ccmp,cf">; +// Features egpr, push2pop2 and ppx are validated with llvm-test-suite && cpu2017 on Intel SDE. +// For stability, we turn on these features only for -mapxf. After a feature pass the validation, +// we will add it to -mapxf. +def mapxf : Flag<["-"], "mapxf">, Alias, AliasArgs<["egpr","push2pop2","ppx"]>; +def mno_apxf : Flag<["-"], "mno-apxf">, Alias, AliasArgs<["egpr","push2pop2","ppx"]>; } // let Flags = [TargetSpecific] // VE feature flags @@ -6333,9 +6344,6 @@ defm stack_arrays : BoolOptionWithoutMarshalling<"f", "stack-arrays", defm loop_versioning : BoolOptionWithoutMarshalling<"f", "version-loops-for-stride", PosFlag, NegFlag>; -defm alias_analysis : BoolOptionWithoutMarshalling<"f", "alias-analysis", - PosFlag, - NegFlag>; } // let Visibility = [FC1Option, FlangOption] def J : JoinedOrSeparate<["-"], "J">, @@ -6786,10 +6794,6 @@ def new_struct_path_tbaa : Flag<["-"], "new-struct-path-tbaa">, def mdebug_pass : Separate<["-"], "mdebug-pass">, HelpText<"Enable additional debug output">, MarshallingInfoString>; -def mframe_pointer_EQ : Joined<["-"], "mframe-pointer=">, - HelpText<"Specify which frame pointers to retain.">, Values<"all,non-leaf,none">, - NormalizedValuesScope<"CodeGenOptions::FramePointerKind">, NormalizedValues<["All", "NonLeaf", "None"]>, - MarshallingInfoEnum, "None">; def mabi_EQ_ieeelongdouble : Flag<["-"], "mabi=ieeelongdouble">, HelpText<"Use IEEE 754 quadruple-precision for long double">, MarshallingInfoFlag>; @@ -7400,6 +7404,11 @@ def pic_is_pie : Flag<["-"], "pic-is-pie">, HelpText<"File is for a position independent executable">, MarshallingInfoFlag>; +def mframe_pointer_EQ : Joined<["-"], "mframe-pointer=">, + HelpText<"Specify which frame pointers to retain.">, Values<"all,non-leaf,none">, + NormalizedValuesScope<"CodeGenOptions::FramePointerKind">, NormalizedValues<["All", "NonLeaf", "None"]>, + MarshallingInfoEnum, "None">; + def dependent_lib : Joined<["--"], "dependent-lib=">, HelpText<"Add dependent library">, diff --git a/clang/include/clang/Format/.clang-format b/clang/include/clang/Format/.clang-format index 63d45e0307a3dfebe23e458478f608e837174a0b..d7331b3c8cf02ef7944e83a4280476627e33b31d 100644 --- a/clang/include/clang/Format/.clang-format +++ b/clang/include/clang/Format/.clang-format @@ -1,5 +1 @@ -BasedOnStyle: LLVM -InsertBraces: true -InsertNewlineAtEOF: true -LineEnding: LF -RemoveBracesLLVM: true +BasedOnStyle: clang-format diff --git a/clang/include/clang/Format/Format.h b/clang/include/clang/Format/Format.h index 37cb3b5cc06732e38c7147ef15dea0e97a6eb2e3..8604dea689f9371669ed9d7570dea224de4b1dae 100644 --- a/clang/include/clang/Format/Format.h +++ b/clang/include/clang/Format/Format.h @@ -3034,7 +3034,9 @@ struct FormatStyle { bool isJson() const { return Language == LK_Json; } bool isJavaScript() const { return Language == LK_JavaScript; } bool isVerilog() const { return Language == LK_Verilog; } - bool isProto() const { return Language == LK_Proto; } + bool isProto() const { + return Language == LK_Proto || Language == LK_TextProto; + } /// Language, this format style is targeted at. /// \version 3.5 @@ -3262,6 +3264,29 @@ struct FormatStyle { /// \version 11 bool ObjCBreakBeforeNestedBlockParam; + /// The order in which ObjC property attributes should appear. + /// + /// Attributes in code will be sorted in the order specified. Any attributes + /// encountered that are not mentioned in this array will be sorted last, in + /// stable order. Comments between attributes will leave the attributes + /// untouched. + /// \warning + /// Using this option could lead to incorrect code formatting due to + /// clang-format's lack of complete semantic information. As such, extra + /// care should be taken to review code changes made by this option. + /// \endwarning + /// \code{.yaml} + /// ObjCPropertyAttributeOrder: [ + /// class, direct, + /// atomic, nonatomic, + /// assign, retain, strong, copy, weak, unsafe_unretained, + /// readonly, readwrite, getter, setter, + /// nullable, nonnull, null_resettable, null_unspecified + /// ] + /// \endcode + /// \version 18 + std::vector ObjCPropertyAttributeOrder; + /// Add a space after ``@property`` in Objective-C, i.e. use /// ``@property (readonly)`` instead of ``@property(readonly)``. /// \version 3.7 @@ -4819,6 +4844,7 @@ struct FormatStyle { ObjCBlockIndentWidth == R.ObjCBlockIndentWidth && ObjCBreakBeforeNestedBlockParam == R.ObjCBreakBeforeNestedBlockParam && + ObjCPropertyAttributeOrder == R.ObjCPropertyAttributeOrder && ObjCSpaceAfterProperty == R.ObjCSpaceAfterProperty && ObjCSpaceBeforeProtocolList == R.ObjCSpaceBeforeProtocolList && PackConstructorInitializers == R.PackConstructorInitializers && diff --git a/clang/include/clang/Frontend/VerifyDiagnosticConsumer.h b/clang/include/clang/Frontend/VerifyDiagnosticConsumer.h index 500a7e11ab9ac2ad24cc4a30832f75dd5d2ae0f2..ddfae2666c4c397ee9a2dffff6cfe0f13f0ece84 100644 --- a/clang/include/clang/Frontend/VerifyDiagnosticConsumer.h +++ b/clang/include/clang/Frontend/VerifyDiagnosticConsumer.h @@ -32,156 +32,8 @@ class TextDiagnosticBuffer; /// VerifyDiagnosticConsumer - Create a diagnostic client which will use /// markers in the input source to check that all the emitted diagnostics match -/// those expected. -/// -/// INVOKING THE DIAGNOSTIC CHECKER: -/// -/// VerifyDiagnosticConsumer is typically invoked via the "-verify" option to -/// "clang -cc1". "-verify" is equivalent to "-verify=expected", so all -/// diagnostics are typically specified with the prefix "expected". For -/// example: -/// -/// \code -/// int A = B; // expected-error {{use of undeclared identifier 'B'}} -/// \endcode -/// -/// Custom prefixes can be specified as a comma-separated sequence. Each -/// prefix must start with a letter and contain only alphanumeric characters, -/// hyphens, and underscores. For example, given just "-verify=foo,bar", -/// the above diagnostic would be ignored, but the following diagnostics would -/// be recognized: -/// -/// \code -/// int A = B; // foo-error {{use of undeclared identifier 'B'}} -/// int C = D; // bar-error {{use of undeclared identifier 'D'}} -/// \endcode -/// -/// Multiple occurrences accumulate prefixes. For example, -/// "-verify -verify=foo,bar -verify=baz" is equivalent to -/// "-verify=expected,foo,bar,baz". -/// -/// SPECIFYING DIAGNOSTICS: -/// -/// Indicating that a line expects an error or a warning is simple. Put a -/// comment on the line that has the diagnostic, use: -/// -/// \code -/// expected-{error,warning,remark,note} -/// \endcode -/// -/// to tag if it's an expected error, remark or warning, and place the expected -/// text between {{ and }} markers. The full text doesn't have to be included, -/// only enough to ensure that the correct diagnostic was emitted. -/// -/// Here's an example: -/// -/// \code -/// int A = B; // expected-error {{use of undeclared identifier 'B'}} -/// \endcode -/// -/// You can place as many diagnostics on one line as you wish. To make the code -/// more readable, you can use slash-newline to separate out the diagnostics. -/// -/// Alternatively, it is possible to specify the line on which the diagnostic -/// should appear by appending "@" to "expected-", for example: -/// -/// \code -/// #warning some text -/// // expected-warning@10 {{some text}} -/// \endcode -/// -/// The line number may be absolute (as above), or relative to the current -/// line by prefixing the number with either '+' or '-'. -/// -/// If the diagnostic is generated in a separate file, for example in a shared -/// header file, it may be beneficial to be able to declare the file in which -/// the diagnostic will appear, rather than placing the expected-* directive in -/// the actual file itself. This can be done using the following syntax: -/// -/// \code -/// // expected-error@path/include.h:15 {{error message}} -/// \endcode -/// -/// The path can be absolute or relative and the same search paths will be used -/// as for #include directives. The line number in an external file may be -/// substituted with '*' meaning that any line number will match (useful where -/// the included file is, for example, a system header where the actual line -/// number may change and is not critical). -/// -/// As an alternative to specifying a fixed line number, the location of a -/// diagnostic can instead be indicated by a marker of the form "#". -/// Markers are specified by including them in a comment, and then referenced -/// by appending the marker to the diagnostic with "@#": -/// -/// \code -/// #warning some text // #1 -/// // expected-warning@#1 {{some text}} -/// \endcode -/// -/// The name of a marker used in a directive must be unique within the -/// compilation. -/// -/// The simple syntax above allows each specification to match exactly one -/// error. You can use the extended syntax to customize this. The extended -/// syntax is "expected- {{diag text}}", where \ is one of -/// "error", "warning" or "note", and \ is a positive integer. This allows -/// the diagnostic to appear as many times as specified. Example: -/// -/// \code -/// void f(); // expected-note 2 {{previous declaration is here}} -/// \endcode -/// -/// Where the diagnostic is expected to occur a minimum number of times, this -/// can be specified by appending a '+' to the number. Example: -/// -/// \code -/// void f(); // expected-note 0+ {{previous declaration is here}} -/// void g(); // expected-note 1+ {{previous declaration is here}} -/// \endcode -/// -/// In the first example, the diagnostic becomes optional, i.e. it will be -/// swallowed if it occurs, but will not generate an error if it does not -/// occur. In the second example, the diagnostic must occur at least once. -/// As a short-hand, "one or more" can be specified simply by '+'. Example: -/// -/// \code -/// void g(); // expected-note + {{previous declaration is here}} -/// \endcode -/// -/// A range can also be specified by "-". Example: -/// -/// \code -/// void f(); // expected-note 0-1 {{previous declaration is here}} -/// \endcode -/// -/// In this example, the diagnostic may appear only once, if at all. -/// -/// Regex matching mode may be selected by appending '-re' to type and -/// including regexes wrapped in double curly braces in the directive, such as: -/// -/// \code -/// expected-error-re {{format specifies type 'wchar_t **' (aka '{{.+}}')}} -/// \endcode -/// -/// Examples matching error: "variable has incomplete type 'struct s'" -/// -/// \code -/// // expected-error {{variable has incomplete type 'struct s'}} -/// // expected-error {{variable has incomplete type}} -/// -/// // expected-error-re {{variable has type 'struct {{.}}'}} -/// // expected-error-re {{variable has type 'struct {{.*}}'}} -/// // expected-error-re {{variable has type 'struct {{(.*)}}'}} -/// // expected-error-re {{variable has type 'struct{{[[:space:]](.*)}}'}} -/// \endcode -/// -/// VerifyDiagnosticConsumer expects at least one expected-* directive to -/// be found inside the source code. If no diagnostics are expected the -/// following directive can be used to indicate this: -/// -/// \code -/// // expected-no-diagnostics -/// \endcode +/// those expected. See clang/docs/InternalsManual.rst for details about how to +/// write tests to verify diagnostics. /// class VerifyDiagnosticConsumer: public DiagnosticConsumer, public CommentHandler { diff --git a/clang/include/clang/Sema/Overload.h b/clang/include/clang/Sema/Overload.h index 333309a3686a1f9b3c2d2ae4ae85835c28b400a7..6ccabad3af54468a73388183cc281403df491027 100644 --- a/clang/include/clang/Sema/Overload.h +++ b/clang/include/clang/Sema/Overload.h @@ -257,10 +257,7 @@ class Sema; /// sequence (C++ 13.3.3.1.1). A standard conversion sequence /// contains between zero and three conversions. If a particular /// conversion is not needed, it will be set to the identity conversion - /// (ICK_Identity). Note that the three conversions are - /// specified as separate members (rather than in an array) so that - /// we can keep the size of a standard conversion sequence to a - /// single word. + /// (ICK_Identity). class StandardConversionSequence { public: /// First -- The first conversion can be an lvalue-to-rvalue diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 5d786fd5fb9a2c4eed5b70e824229e8ddaf73bce..e745c01ae0ae0f830a55ace3d33712b903ad1ad3 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -3737,6 +3737,9 @@ public: int X, int Y, int Z); HLSLShaderAttr *mergeHLSLShaderAttr(Decl *D, const AttributeCommonInfo &AL, HLSLShaderAttr::ShaderType ShaderType); + HLSLParamModifierAttr * + mergeHLSLParamModifierAttr(Decl *D, const AttributeCommonInfo &AL, + HLSLParamModifierAttr::Spelling Spelling); void mergeDeclAttributes(NamedDecl *New, Decl *Old, AvailabilityMergeKind AMK = AMK_Redeclaration); @@ -3929,6 +3932,11 @@ public: APValue &Value, CCEKind CCE, NamedDecl *Dest = nullptr); + ExprResult + EvaluateConvertedConstantExpression(Expr *E, QualType T, APValue &Value, + CCEKind CCE, bool RequireInt, + const APValue &PreNarrowingValue); + /// Abstract base class used to perform a contextual implicit /// conversion from an expression to any type passing a filter. class ContextualImplicitConverter { diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ConstraintManager.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ConstraintManager.h index 4e94ad49a783360e9dd5da30f4680dcccfac42cb..4de04bc4d397ac4788d6719df2dace5199a76420 100644 --- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ConstraintManager.h +++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ConstraintManager.h @@ -110,6 +110,22 @@ public: return nullptr; } + /// Attempt to return the minimal possible value for a given symbol. Note + /// that a ConstraintManager is not obligated to return a lower bound, it may + /// also return nullptr. + virtual const llvm::APSInt *getSymMinVal(ProgramStateRef state, + SymbolRef sym) const { + return nullptr; + } + + /// Attempt to return the minimal possible value for a given symbol. Note + /// that a ConstraintManager is not obligated to return a lower bound, it may + /// also return nullptr. + virtual const llvm::APSInt *getSymMaxVal(ProgramStateRef state, + SymbolRef sym) const { + return nullptr; + } + /// Scan all symbols referenced by the constraints. If the symbol is not /// alive, remove it. virtual ProgramStateRef removeDeadBindings(ProgramStateRef state, diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h index a64cf7ae4efcb82db450d0f67ed792c461f406af..d7cff49036cb81064e1c24e1cacfc8772e163009 100644 --- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h +++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h @@ -110,6 +110,14 @@ public: /// that value is returned. Otherwise, returns NULL. virtual const llvm::APSInt *getKnownValue(ProgramStateRef state, SVal val) = 0; + /// Tries to get the minimal possible (integer) value of a given SVal. If the + /// constraint manager cannot provide an useful answer, this returns NULL. + virtual const llvm::APSInt *getMinValue(ProgramStateRef state, SVal val) = 0; + + /// Tries to get the maximal possible (integer) value of a given SVal. If the + /// constraint manager cannot provide an useful answer, this returns NULL. + virtual const llvm::APSInt *getMaxValue(ProgramStateRef state, SVal val) = 0; + /// Simplify symbolic expressions within a given SVal. Return an SVal /// that represents the same value, but is hopefully easier to work with /// than the original SVal. diff --git a/clang/include/module.modulemap b/clang/include/module.modulemap index 7dbb7cf02b4f82c19f428348ff9b2f302b4fa294..52395ee9b0fc9ffca68550876c54d846941f3ebb 100644 --- a/clang/include/module.modulemap +++ b/clang/include/module.modulemap @@ -68,6 +68,7 @@ module Clang_Basic { textual header "clang/Basic/BuiltinsX86_64.def" textual header "clang/Basic/BuiltinsXCore.def" textual header "clang/Basic/CodeGenOptions.def" + textual header "clang/Basic/DebugOptions.def" textual header "clang/Basic/DiagnosticOptions.def" textual header "clang/Basic/Features.def" textual header "clang/Basic/FPOptions.def" diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp index a7cee3b7ba2b0db398bb4425d2fc4f4cee5979ac..e877f903b34c6bbbb1089231aec07494e1acf0c2 100644 --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -1627,28 +1627,22 @@ const llvm::fltSemantics &ASTContext::getFloatTypeSemantics(QualType T) const { CharUnits ASTContext::getDeclAlign(const Decl *D, bool ForAlignof) const { unsigned Align = Target->getCharWidth(); - bool UseAlignAttrOnly = false; - if (unsigned AlignFromAttr = D->getMaxAlignment()) { + const unsigned AlignFromAttr = D->getMaxAlignment(); + if (AlignFromAttr) Align = AlignFromAttr; - // __attribute__((aligned)) can increase or decrease alignment - // *except* on a struct or struct member, where it only increases - // alignment unless 'packed' is also specified. - // - // It is an error for alignas to decrease alignment, so we can - // ignore that possibility; Sema should diagnose it. - if (isa(D)) { - UseAlignAttrOnly = D->hasAttr() || - cast(D)->getParent()->hasAttr(); - } else { - UseAlignAttrOnly = true; - } - } - else if (isa(D)) - UseAlignAttrOnly = - D->hasAttr() || - cast(D)->getParent()->hasAttr(); - + // __attribute__((aligned)) can increase or decrease alignment + // *except* on a struct or struct member, where it only increases + // alignment unless 'packed' is also specified. + // + // It is an error for alignas to decrease alignment, so we can + // ignore that possibility; Sema should diagnose it. + bool UseAlignAttrOnly; + if (const FieldDecl *FD = dyn_cast(D)) + UseAlignAttrOnly = + FD->hasAttr() || FD->getParent()->hasAttr(); + else + UseAlignAttrOnly = AlignFromAttr != 0; // If we're using the align attribute only, just ignore everything // else about the declaration and its type. if (UseAlignAttrOnly) { diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp index 3dc33c10af11edf2eaebd96af58c2947172d4fce..f1f335118f37a4d1837f9b96fe148e6924bee292 100644 --- a/clang/lib/AST/ASTImporter.cpp +++ b/clang/lib/AST/ASTImporter.cpp @@ -9073,6 +9073,7 @@ public: ToAttr = FromAttr->clone(Importer.getToContext()); ToAttr->setRange(ToRange); + ToAttr->setAttrName(Importer.Import(FromAttr->getAttrName())); } // Get the result of the previous import attempt (can be used only once). diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index 16697e5f076a8f8d718190a45f5d99f337ffaf4d..986302e1fd225f2194880f02c49a5c5b2715443f 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -4623,11 +4623,13 @@ struct IncDecSubobjectHandler { if (Old) *Old = APValue(Value); APFloat One(Value.getSemantics(), 1); + llvm::RoundingMode RM = getActiveRoundingMode(Info, E); + APFloat::opStatus St; if (AccessKind == AK_Increment) - Value.add(One, APFloat::rmNearestTiesToEven); + St = Value.add(One, RM); else - Value.subtract(One, APFloat::rmNearestTiesToEven); - return true; + St = Value.subtract(One, RM); + return checkFloatingPointResult(Info, E, St); } bool foundPointer(APValue &Subobj, QualType SubobjType) { if (!checkConst(SubobjType)) @@ -8616,7 +8618,7 @@ bool LValueExprEvaluator::VisitMaterializeTemporaryExpr( Result.set(E); } else { Value = &Info.CurrentCall->createTemporary( - E, E->getType(), + E, Inner->getType(), E->getStorageDuration() == SD_FullExpression ? ScopeKind::FullExpression : ScopeKind::Block, Result); diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp index c8e452e2feab0bf96260535136f841f177633b63..160a725939ccd4cb357c4d269e7d94ee0d6a73fe 100644 --- a/clang/lib/AST/Type.cpp +++ b/clang/lib/AST/Type.cpp @@ -2649,6 +2649,8 @@ bool QualType::isTriviallyRelocatableType(const ASTContext &Context) const { if (BaseElementType->isIncompleteType()) { return false; + } else if (!BaseElementType->isObjectType()) { + return false; } else if (const auto *RD = BaseElementType->getAsRecordDecl()) { return RD->canPassInRegisters(); } else { diff --git a/clang/lib/AST/TypePrinter.cpp b/clang/lib/AST/TypePrinter.cpp index e4f5f40cd6259969c9c4615aba2c8d4879c95ee5..f69412429273674e295027724bc4a4cd64197f54 100644 --- a/clang/lib/AST/TypePrinter.cpp +++ b/clang/lib/AST/TypePrinter.cpp @@ -1894,6 +1894,10 @@ void TypePrinter::printAttributedAfter(const AttributedType *T, case attr::ArmMveStrictPolymorphism: OS << "__clang_arm_mve_strict_polymorphism"; break; + + // Nothing to print for this attribute. + case attr::HLSLParamModifier: + break; } OS << "))"; } diff --git a/clang/lib/Analysis/FlowSensitive/DataflowAnalysisContext.cpp b/clang/lib/Analysis/FlowSensitive/DataflowAnalysisContext.cpp index 0a2fcd4ad695a30ccb3ec9b39ff15553eb55c1a4..fa114979c8e3263dd86e04d04dcb6b12ee09758f 100644 --- a/clang/lib/Analysis/FlowSensitive/DataflowAnalysisContext.cpp +++ b/clang/lib/Analysis/FlowSensitive/DataflowAnalysisContext.cpp @@ -68,11 +68,38 @@ StorageLocation &DataflowAnalysisContext::createStorageLocation(QualType Type) { else FieldLocs.insert({Field, &createStorageLocation( Field->getType().getNonReferenceType())}); - return arena().create(Type, std::move(FieldLocs)); + + RecordStorageLocation::SyntheticFieldMap SyntheticFields; + for (const auto &Entry : getSyntheticFields(Type)) + SyntheticFields.insert( + {Entry.getKey(), + &createStorageLocation(Entry.getValue().getNonReferenceType())}); + + return createRecordStorageLocation(Type, std::move(FieldLocs), + std::move(SyntheticFields)); } return arena().create(Type); } +// Returns the keys for a given `StringMap`. +// Can't use `StringSet` as the return type as it doesn't support `operator==`. +template +static llvm::DenseSet getKeys(const llvm::StringMap &Map) { + return llvm::DenseSet(Map.keys().begin(), Map.keys().end()); +} + +RecordStorageLocation &DataflowAnalysisContext::createRecordStorageLocation( + QualType Type, RecordStorageLocation::FieldToLoc FieldLocs, + RecordStorageLocation::SyntheticFieldMap SyntheticFields) { + assert(Type->isRecordType()); + assert(containsSameFields(getModeledFields(Type), FieldLocs)); + assert(getKeys(getSyntheticFields(Type)) == getKeys(SyntheticFields)); + + RecordStorageLocationCreated = true; + return arena().create(Type, std::move(FieldLocs), + std::move(SyntheticFields)); +} + StorageLocation & DataflowAnalysisContext::getStableStorageLocation(const ValueDecl &D) { if (auto *Loc = DeclToLoc.lookup(&D)) @@ -367,3 +394,14 @@ clang::dataflow::FieldSet clang::dataflow::getObjectFields(QualType Type) { getFieldsFromClassHierarchy(Type, Fields); return Fields; } + +bool clang::dataflow::containsSameFields( + const clang::dataflow::FieldSet &Fields, + const clang::dataflow::RecordStorageLocation::FieldToLoc &FieldLocs) { + if (Fields.size() != FieldLocs.size()) + return false; + for ([[maybe_unused]] auto [Field, Loc] : FieldLocs) + if (!Fields.contains(cast_or_null(Field))) + return false; + return true; +} diff --git a/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp b/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp index 525ab188b01b8aa43bdc4c843e514d612d0c41fd..b98037b73645225ab3cf9a35d648181ef275854f 100644 --- a/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp +++ b/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp @@ -300,9 +300,12 @@ static void insertIfFunction(const Decl &D, } static MemberExpr *getMemberForAccessor(const CXXMemberCallExpr &C) { - if (!C.getMethodDecl()) + // Use getCalleeDecl instead of getMethodDecl in order to handle + // pointer-to-member calls. + const auto *MethodDecl = dyn_cast_or_null(C.getCalleeDecl()); + if (!MethodDecl) return nullptr; - auto *Body = dyn_cast_or_null(C.getMethodDecl()->getBody()); + auto *Body = dyn_cast_or_null(MethodDecl->getBody()); if (!Body || Body->size() != 1) return nullptr; if (auto *RS = dyn_cast(*Body->body_begin())) @@ -367,6 +370,59 @@ getFieldsGlobalsAndFuncs(const Stmt &S, FieldSet &Fields, } } +Environment::Environment(DataflowAnalysisContext &DACtx) + : DACtx(&DACtx), + FlowConditionToken(DACtx.arena().makeFlowConditionToken()) {} + +Environment::Environment(DataflowAnalysisContext &DACtx, + const DeclContext &DeclCtx) + : Environment(DACtx) { + CallStack.push_back(&DeclCtx); +} + +void Environment::initialize() { + const DeclContext *DeclCtx = getDeclCtx(); + if (DeclCtx == nullptr) + return; + + if (const auto *FuncDecl = dyn_cast(DeclCtx)) { + assert(FuncDecl->getBody() != nullptr); + + initFieldsGlobalsAndFuncs(FuncDecl); + + for (const auto *ParamDecl : FuncDecl->parameters()) { + assert(ParamDecl != nullptr); + setStorageLocation(*ParamDecl, createObject(*ParamDecl, nullptr)); + } + } + + if (const auto *MethodDecl = dyn_cast(DeclCtx)) { + auto *Parent = MethodDecl->getParent(); + assert(Parent != nullptr); + + if (Parent->isLambda()) { + for (auto Capture : Parent->captures()) { + if (Capture.capturesVariable()) { + const auto *VarDecl = Capture.getCapturedVar(); + assert(VarDecl != nullptr); + setStorageLocation(*VarDecl, createObject(*VarDecl, nullptr)); + } else if (Capture.capturesThis()) { + const auto *SurroundingMethodDecl = + cast(DeclCtx->getNonClosureAncestor()); + QualType ThisPointeeType = + SurroundingMethodDecl->getFunctionObjectParameterType(); + setThisPointeeStorageLocation( + cast(createValue(ThisPointeeType))->getLoc()); + } + } + } else if (MethodDecl->isImplicitObjectMemberFunction()) { + QualType ThisPointeeType = MethodDecl->getFunctionObjectParameterType(); + setThisPointeeStorageLocation( + cast(createValue(ThisPointeeType))->getLoc()); + } + } +} + // FIXME: Add support for resetting globals after function calls to enable // the implementation of sound analyses. void Environment::initFieldsGlobalsAndFuncs(const FunctionDecl *FuncDecl) { @@ -416,59 +472,12 @@ void Environment::initFieldsGlobalsAndFuncs(const FunctionDecl *FuncDecl) { } } -Environment::Environment(DataflowAnalysisContext &DACtx) - : DACtx(&DACtx), - FlowConditionToken(DACtx.arena().makeFlowConditionToken()) {} - Environment Environment::fork() const { Environment Copy(*this); Copy.FlowConditionToken = DACtx->forkFlowCondition(FlowConditionToken); return Copy; } -Environment::Environment(DataflowAnalysisContext &DACtx, - const DeclContext &DeclCtx) - : Environment(DACtx) { - CallStack.push_back(&DeclCtx); - - if (const auto *FuncDecl = dyn_cast(&DeclCtx)) { - assert(FuncDecl->getBody() != nullptr); - - initFieldsGlobalsAndFuncs(FuncDecl); - - for (const auto *ParamDecl : FuncDecl->parameters()) { - assert(ParamDecl != nullptr); - setStorageLocation(*ParamDecl, createObject(*ParamDecl, nullptr)); - } - } - - if (const auto *MethodDecl = dyn_cast(&DeclCtx)) { - auto *Parent = MethodDecl->getParent(); - assert(Parent != nullptr); - - if (Parent->isLambda()) { - for (auto Capture : Parent->captures()) { - if (Capture.capturesVariable()) { - const auto *VarDecl = Capture.getCapturedVar(); - assert(VarDecl != nullptr); - setStorageLocation(*VarDecl, createObject(*VarDecl, nullptr)); - } else if (Capture.capturesThis()) { - const auto *SurroundingMethodDecl = - cast(DeclCtx.getNonClosureAncestor()); - QualType ThisPointeeType = - SurroundingMethodDecl->getFunctionObjectParameterType(); - ThisPointeeLoc = - &cast(createValue(ThisPointeeType))->getLoc(); - } - } - } else if (MethodDecl->isImplicitObjectMemberFunction()) { - QualType ThisPointeeType = MethodDecl->getFunctionObjectParameterType(); - ThisPointeeLoc = - &cast(createValue(ThisPointeeType))->getLoc(); - } - } -} - bool Environment::canDescend(unsigned MaxDepth, const DeclContext *Callee) const { return CallStack.size() <= MaxDepth && !llvm::is_contained(CallStack, Callee); @@ -727,10 +736,6 @@ StorageLocation *Environment::getStorageLocation(const Expr &E) const { return getStorageLocationInternal(E); } -RecordStorageLocation *Environment::getThisPointeeStorageLocation() const { - return ThisPointeeLoc; -} - RecordStorageLocation & Environment::getResultObjectLocation(const Expr &RecordPRValue) { assert(RecordPRValue.getType()->isRecordType()); @@ -852,8 +857,16 @@ Value *Environment::createValueUnlessSelfReferential( CreatedValuesCount)}); } - RecordStorageLocation &Loc = - arena().create(Type, std::move(FieldLocs)); + RecordStorageLocation::SyntheticFieldMap SyntheticFieldLocs; + for (const auto &Entry : DACtx->getSyntheticFields(Type)) { + SyntheticFieldLocs.insert( + {Entry.getKey(), + &createLocAndMaybeValue(Entry.getValue(), Visited, Depth + 1, + CreatedValuesCount)}); + } + + RecordStorageLocation &Loc = DACtx->createRecordStorageLocation( + Type, std::move(FieldLocs), std::move(SyntheticFieldLocs)); RecordValue &RecordVal = create(Loc); // As we already have a storage location for the `RecordValue`, we can and diff --git a/clang/lib/Analysis/FlowSensitive/HTMLLogger.cpp b/clang/lib/Analysis/FlowSensitive/HTMLLogger.cpp index 8329367098b1dbb6ea2544f10c69dea9ad7335a5..7430ef599e032d0c897461a777903d099948932f 100644 --- a/clang/lib/Analysis/FlowSensitive/HTMLLogger.cpp +++ b/clang/lib/Analysis/FlowSensitive/HTMLLogger.cpp @@ -136,6 +136,10 @@ public: if (Value *Val = Env.getValue(*Child.second)) dump(*Val); }); + + for (const auto &SyntheticField : RLoc->synthetic_fields()) + JOS.attributeObject(("sf:" + SyntheticField.first()).str(), + [&] { dump(*SyntheticField.second); }); } } diff --git a/clang/lib/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.cpp b/clang/lib/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.cpp index 55d0713639d90da267f938067cb8391b3162019c..69ac2c2b82cff2407c97e30ce007a7e313f2d033 100644 --- a/clang/lib/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.cpp +++ b/clang/lib/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.cpp @@ -122,12 +122,6 @@ auto nulloptTypeDecl() { auto hasNulloptType() { return hasType(nulloptTypeDecl()); } -// `optional` or `nullopt_t` -auto hasAnyOptionalType() { - return hasType(hasUnqualifiedDesugaredType( - recordType(hasDeclaration(anyOf(nulloptTypeDecl(), optionalClass()))))); -} - auto inPlaceClass() { return recordDecl(hasAnyName("std::in_place_t", "absl::in_place_t", "base::in_place_t", "folly::in_place_t")); @@ -162,11 +156,6 @@ auto isOptionalValueOrConversionAssignment() { argumentCountIs(2), hasArgument(1, unless(hasNulloptType()))); } -auto isNulloptConstructor() { - return cxxConstructExpr(hasNulloptType(), argumentCountIs(1), - hasArgument(0, hasNulloptType())); -} - auto isOptionalNulloptAssignment() { return cxxOperatorCallExpr(hasOverloadedOperatorName("="), callee(cxxMethodDecl(ofClass(optionalClass()))), @@ -246,10 +235,19 @@ const Formula &forceBoolValue(Environment &Env, const Expr &Expr) { return Value->formula(); } +StorageLocation &locForHasValue(const RecordStorageLocation &OptionalLoc) { + return OptionalLoc.getSyntheticField("has_value"); +} + +StorageLocation &locForValue(const RecordStorageLocation &OptionalLoc) { + return OptionalLoc.getSyntheticField("value"); +} + /// Sets `HasValueVal` as the symbolic value that represents the "has_value" -/// property of the optional value `OptionalVal`. -void setHasValue(Value &OptionalVal, BoolValue &HasValueVal) { - OptionalVal.setProperty("has_value", HasValueVal); +/// property of the optional at `OptionalLoc`. +void setHasValue(RecordStorageLocation &OptionalLoc, BoolValue &HasValueVal, + Environment &Env) { + Env.setValue(locForHasValue(OptionalLoc), HasValueVal); } /// Creates a symbolic value for an `optional` value at an existing storage @@ -259,23 +257,22 @@ RecordValue &createOptionalValue(RecordStorageLocation &Loc, BoolValue &HasValueVal, Environment &Env) { auto &OptionalVal = Env.create(Loc); Env.setValue(Loc, OptionalVal); - setHasValue(OptionalVal, HasValueVal); + setHasValue(Loc, HasValueVal, Env); return OptionalVal; } /// Returns the symbolic value that represents the "has_value" property of the -/// optional value `OptionalVal`. Returns null if `OptionalVal` is null. -BoolValue *getHasValue(Environment &Env, Value *OptionalVal) { - if (OptionalVal != nullptr) { - auto *HasValueVal = - cast_or_null(OptionalVal->getProperty("has_value")); - if (HasValueVal == nullptr) { - HasValueVal = &Env.makeAtomicBoolValue(); - OptionalVal->setProperty("has_value", *HasValueVal); - } - return HasValueVal; +/// optional at `OptionalLoc`. Returns null if `OptionalLoc` is null. +BoolValue *getHasValue(Environment &Env, RecordStorageLocation *OptionalLoc) { + if (OptionalLoc == nullptr) + return nullptr; + StorageLocation &HasValueLoc = locForHasValue(*OptionalLoc); + auto *HasValueVal = cast_or_null(Env.getValue(HasValueLoc)); + if (HasValueVal == nullptr) { + HasValueVal = &Env.makeAtomicBoolValue(); + Env.setValue(HasValueLoc, *HasValueVal); } - return nullptr; + return HasValueVal; } /// Returns true if and only if `Type` is an optional type. @@ -302,155 +299,31 @@ int countOptionalWrappers(const ASTContext &ASTCtx, QualType Type) { .getDesugaredType(ASTCtx)); } -/// Tries to initialize the `optional`'s value (that is, contents), and return -/// its location. Returns nullptr if the value can't be represented. -StorageLocation *maybeInitializeOptionalValueMember(QualType Q, - Value &OptionalVal, - Environment &Env) { - // The "value" property represents a synthetic field. As such, it needs - // `StorageLocation`, like normal fields (and other variables). So, we model - // it with a `PointerValue`, since that includes a storage location. Once - // the property is set, it will be shared by all environments that access the - // `Value` representing the optional (here, `OptionalVal`). - if (auto *ValueProp = OptionalVal.getProperty("value")) { - auto *ValuePtr = clang::cast(ValueProp); - auto &ValueLoc = ValuePtr->getPointeeLoc(); - if (Env.getValue(ValueLoc) != nullptr) - return &ValueLoc; - - // The property was previously set, but the value has been lost. This can - // happen in various situations, for example: - // - Because of an environment merge (where the two environments mapped the - // property to different values, which resulted in them both being - // discarded). - // - When two blocks in the CFG, with neither a dominator of the other, - // visit the same optional value. (FIXME: This is something we can and - // should fix -- see also the lengthy FIXME below.) - // - Or even when a block is revisited during testing to collect - // per-statement state. - // FIXME: This situation means that the optional contents are not shared - // between branches and the like. Practically, this lack of sharing - // reduces the precision of the model when the contents are relevant to - // the check, like another optional or a boolean that influences control - // flow. - if (ValueLoc.getType()->isRecordType()) { - refreshRecordValue(cast(ValueLoc), Env); - return &ValueLoc; - } else { - auto *ValueVal = Env.createValue(ValueLoc.getType()); - if (ValueVal == nullptr) - return nullptr; - Env.setValue(ValueLoc, *ValueVal); - return &ValueLoc; - } - } - - auto Ty = Q.getNonReferenceType(); - auto &ValueLoc = Env.createObject(Ty); - auto &ValuePtr = Env.create(ValueLoc); - // FIXME: - // The change we make to the `value` property below may become visible to - // other blocks that aren't successors of the current block and therefore - // don't see the change we made above mapping `ValueLoc` to `ValueVal`. For - // example: - // - // void target(optional oo, bool b) { - // // `oo` is associated with a `RecordValue` here, which we will call - // // `OptionalVal`. - // - // // The `has_value` property is set on `OptionalVal` (but not the - // // `value` property yet). - // if (!oo.has_value()) return; - // - // if (b) { - // // Let's assume we transfer the `if` branch first. - // // - // // This causes us to call `maybeInitializeOptionalValueMember()`, - // // which causes us to set the `value` property on `OptionalVal` - // // (which had not been set until this point). This `value` property - // // refers to a `PointerValue`, which in turn refers to a - // // StorageLocation` that is associated to an `IntegerValue`. - // oo.value(); - // } else { - // // Let's assume we transfer the `else` branch after the `if` branch. - // // - // // We see the `value` property that the `if` branch set on - // // `OptionalVal`, but in the environment for this block, the - // // `StorageLocation` in the `PointerValue` is not associated with any - // // `Value`. - // oo.value(); - // } - // } - // - // This situation is currently "saved" by the code above that checks whether - // the `value` property is already set, and if, the `ValueLoc` is not - // associated with a `ValueVal`, creates a new `ValueVal`. - // - // However, what we should really do is to make sure that the change to the - // `value` property does not "leak" to other blocks that are not successors - // of this block. To do this, instead of simply setting the `value` property - // on the existing `OptionalVal`, we should create a new `Value` for the - // optional, set the property on that, and associate the storage location that - // is currently associated with the existing `OptionalVal` with the newly - // created `Value` instead. - OptionalVal.setProperty("value", ValuePtr); - return &ValueLoc; -} - -void initializeOptionalReference(const Expr *OptionalExpr, - const MatchFinder::MatchResult &, - LatticeTransferState &State) { - if (auto *OptionalVal = State.Env.getValue(*OptionalExpr)) { - if (OptionalVal->getProperty("has_value") == nullptr) { - setHasValue(*OptionalVal, State.Env.makeAtomicBoolValue()); - } +StorageLocation *getLocBehindPossiblePointer(const Expr &E, + const Environment &Env) { + if (E.isPRValue()) { + if (auto *PointerVal = dyn_cast_or_null(Env.getValue(E))) + return &PointerVal->getPointeeLoc(); + return nullptr; } -} - -/// Returns true if and only if `OptionalVal` is initialized and known to be -/// empty in `Env`. -bool isEmptyOptional(const Value &OptionalVal, const Environment &Env) { - auto *HasValueVal = - cast_or_null(OptionalVal.getProperty("has_value")); - return HasValueVal != nullptr && - Env.proves(Env.arena().makeNot(HasValueVal->formula())); -} - -/// Returns true if and only if `OptionalVal` is initialized and known to be -/// non-empty in `Env`. -bool isNonEmptyOptional(const Value &OptionalVal, const Environment &Env) { - auto *HasValueVal = - cast_or_null(OptionalVal.getProperty("has_value")); - return HasValueVal != nullptr && Env.proves(HasValueVal->formula()); -} - -Value *getValueBehindPossiblePointer(const Expr &E, const Environment &Env) { - Value *Val = Env.getValue(E); - if (auto *PointerVal = dyn_cast_or_null(Val)) - return Env.getValue(PointerVal->getPointeeLoc()); - return Val; + return Env.getStorageLocation(E); } void transferUnwrapCall(const Expr *UnwrapExpr, const Expr *ObjectExpr, LatticeTransferState &State) { - if (auto *OptionalVal = - getValueBehindPossiblePointer(*ObjectExpr, State.Env)) { + if (auto *OptionalLoc = cast_or_null( + getLocBehindPossiblePointer(*ObjectExpr, State.Env))) { if (State.Env.getStorageLocation(*UnwrapExpr) == nullptr) - if (auto *Loc = maybeInitializeOptionalValueMember( - UnwrapExpr->getType(), *OptionalVal, State.Env)) - State.Env.setStorageLocation(*UnwrapExpr, *Loc); + State.Env.setStorageLocation(*UnwrapExpr, locForValue(*OptionalLoc)); } } void transferArrowOpCall(const Expr *UnwrapExpr, const Expr *ObjectExpr, LatticeTransferState &State) { - if (auto *OptionalVal = - getValueBehindPossiblePointer(*ObjectExpr, State.Env)) { - if (auto *Loc = maybeInitializeOptionalValueMember( - UnwrapExpr->getType()->getPointeeType(), *OptionalVal, State.Env)) { - State.Env.setValue(*UnwrapExpr, State.Env.create(*Loc)); - } - } + if (auto *OptionalLoc = cast_or_null( + getLocBehindPossiblePointer(*ObjectExpr, State.Env))) + State.Env.setValue( + *UnwrapExpr, State.Env.create(locForValue(*OptionalLoc))); } void transferMakeOptionalCall(const CallExpr *E, @@ -465,8 +338,7 @@ void transferOptionalHasValueCall(const CXXMemberCallExpr *CallExpr, const MatchFinder::MatchResult &, LatticeTransferState &State) { if (auto *HasValueVal = getHasValue( - State.Env, getValueBehindPossiblePointer( - *CallExpr->getImplicitObjectArgument(), State.Env))) { + State.Env, getImplicitObjectLocation(*CallExpr, State.Env))) { State.Env.setValue(*CallExpr, *HasValueVal); } } @@ -480,12 +352,11 @@ void transferValueOrImpl( const Formula &HasValueVal)) { auto &Env = State.Env; - const auto *ObjectArgumentExpr = - Result.Nodes.getNodeAs(ValueOrCallID) - ->getImplicitObjectArgument(); + const auto *MCE = + Result.Nodes.getNodeAs(ValueOrCallID); - auto *HasValueVal = getHasValue( - State.Env, getValueBehindPossiblePointer(*ObjectArgumentExpr, State.Env)); + auto *HasValueVal = + getHasValue(State.Env, getImplicitObjectLocation(*MCE, State.Env)); if (HasValueVal == nullptr) return; @@ -578,7 +449,9 @@ BoolValue &valueOrConversionHasValue(const FunctionDecl &F, const Expr &E, // This is a constructor/assignment call for `optional` with argument of // type `optional` such that `T` is constructible from `U`. - if (auto *HasValueVal = getHasValue(State.Env, State.Env.getValue(E))) + auto *Loc = + cast_or_null(State.Env.getStorageLocation(E)); + if (auto *HasValueVal = getHasValue(State.Env, Loc)) return *HasValueVal; return State.Env.makeAtomicBoolValue(); } @@ -645,11 +518,11 @@ void transferSwap(RecordStorageLocation *Loc1, RecordStorageLocation *Loc2, // allows for local reasoning about the value. To avoid the above, we would // need *lazy* value allocation. // FIXME: allocate values lazily, instead of just creating a fresh value. - BoolValue *BoolVal1 = getHasValue(Env, Env.getValue(*Loc1)); + BoolValue *BoolVal1 = getHasValue(Env, Loc1); if (BoolVal1 == nullptr) BoolVal1 = &Env.makeAtomicBoolValue(); - BoolValue *BoolVal2 = getHasValue(Env, Env.getValue(*Loc2)); + BoolValue *BoolVal2 = getHasValue(Env, Loc2); if (BoolVal2 == nullptr) BoolVal2 = &Env.makeAtomicBoolValue(); @@ -712,20 +585,26 @@ void transferOptionalAndOptionalCmp(const clang::CXXOperatorCallExpr *CmpExpr, Environment &Env = State.Env; auto &A = Env.arena(); auto *CmpValue = &forceBoolValue(Env, *CmpExpr); - if (auto *LHasVal = getHasValue(Env, Env.getValue(*CmpExpr->getArg(0)))) - if (auto *RHasVal = getHasValue(Env, Env.getValue(*CmpExpr->getArg(1)))) { + auto *Arg0Loc = cast_or_null( + Env.getStorageLocation(*CmpExpr->getArg(0))); + if (auto *LHasVal = getHasValue(Env, Arg0Loc)) { + auto *Arg1Loc = cast_or_null( + Env.getStorageLocation(*CmpExpr->getArg(1))); + if (auto *RHasVal = getHasValue(Env, Arg1Loc)) { if (CmpExpr->getOperator() == clang::OO_ExclaimEqual) CmpValue = &A.makeNot(*CmpValue); Env.assume(evaluateEquality(A, *CmpValue, LHasVal->formula(), RHasVal->formula())); } + } } void transferOptionalAndValueCmp(const clang::CXXOperatorCallExpr *CmpExpr, const clang::Expr *E, Environment &Env) { auto &A = Env.arena(); auto *CmpValue = &forceBoolValue(Env, *CmpExpr); - if (auto *HasVal = getHasValue(Env, Env.getValue(*E))) { + auto *Loc = cast_or_null(Env.getStorageLocation(*E)); + if (auto *HasVal = getHasValue(Env, Loc)) { if (CmpExpr->getOperator() == clang::OO_ExclaimEqual) CmpValue = &A.makeNot(*CmpValue); Env.assume( @@ -733,6 +612,19 @@ void transferOptionalAndValueCmp(const clang::CXXOperatorCallExpr *CmpExpr, } } +void transferOptionalAndNulloptCmp(const clang::CXXOperatorCallExpr *CmpExpr, + const clang::Expr *E, Environment &Env) { + auto &A = Env.arena(); + auto *CmpValue = &forceBoolValue(Env, *CmpExpr); + auto *Loc = cast_or_null(Env.getStorageLocation(*E)); + if (auto *HasVal = getHasValue(Env, Loc)) { + if (CmpExpr->getOperator() == clang::OO_ExclaimEqual) + CmpValue = &A.makeNot(*CmpValue); + Env.assume(evaluateEquality(A, *CmpValue, HasVal->formula(), + A.makeLiteral(false))); + } +} + std::optional ignorableOptional(const UncheckedOptionalAccessModelOptions &Options) { if (Options.IgnoreSmartPointerDereference) { @@ -762,12 +654,6 @@ auto buildTransferMatchSwitch() { // lot of duplicated work (e.g. string comparisons), consider providing APIs // that avoid it through memoization. return CFGMatchSwitchBuilder() - // Attach a symbolic "has_value" state to optional values that we see for - // the first time. - .CaseOfCFGStmt( - expr(anyOf(declRefExpr(), memberExpr()), hasOptionalType()), - initializeOptionalReference) - // make_optional .CaseOfCFGStmt(isMakeOptionalCall(), transferMakeOptionalCall) @@ -779,14 +665,6 @@ auto buildTransferMatchSwitch() { constructOptionalValue(*E, State.Env, State.Env.getBoolLiteralValue(true)); }) - // nullopt_t::nullopt_t - .CaseOfCFGStmt( - isNulloptConstructor(), - [](const CXXConstructExpr *E, const MatchFinder::MatchResult &, - LatticeTransferState &State) { - constructOptionalValue(*E, State.Env, - State.Env.getBoolLiteralValue(false)); - }) // optional::optional(nullopt_t) .CaseOfCFGStmt( isOptionalNulloptConstructor(), @@ -887,18 +765,32 @@ auto buildTransferMatchSwitch() { // Comparisons (==, !=): .CaseOfCFGStmt( - isComparisonOperatorCall(hasAnyOptionalType(), hasAnyOptionalType()), + isComparisonOperatorCall(hasOptionalType(), hasOptionalType()), transferOptionalAndOptionalCmp) .CaseOfCFGStmt( - isComparisonOperatorCall(hasOptionalType(), - unless(hasAnyOptionalType())), + isComparisonOperatorCall(hasOptionalType(), hasNulloptType()), + [](const clang::CXXOperatorCallExpr *Cmp, + const MatchFinder::MatchResult &, LatticeTransferState &State) { + transferOptionalAndNulloptCmp(Cmp, Cmp->getArg(0), State.Env); + }) + .CaseOfCFGStmt( + isComparisonOperatorCall(hasNulloptType(), hasOptionalType()), + [](const clang::CXXOperatorCallExpr *Cmp, + const MatchFinder::MatchResult &, LatticeTransferState &State) { + transferOptionalAndNulloptCmp(Cmp, Cmp->getArg(1), State.Env); + }) + .CaseOfCFGStmt( + isComparisonOperatorCall( + hasOptionalType(), + unless(anyOf(hasOptionalType(), hasNulloptType()))), [](const clang::CXXOperatorCallExpr *Cmp, const MatchFinder::MatchResult &, LatticeTransferState &State) { transferOptionalAndValueCmp(Cmp, Cmp->getArg(0), State.Env); }) .CaseOfCFGStmt( - isComparisonOperatorCall(unless(hasAnyOptionalType()), - hasOptionalType()), + isComparisonOperatorCall( + unless(anyOf(hasOptionalType(), hasNulloptType())), + hasOptionalType()), [](const clang::CXXOperatorCallExpr *Cmp, const MatchFinder::MatchResult &, LatticeTransferState &State) { transferOptionalAndValueCmp(Cmp, Cmp->getArg(1), State.Env); @@ -913,8 +805,9 @@ auto buildTransferMatchSwitch() { llvm::SmallVector diagnoseUnwrapCall(const Expr *ObjectExpr, const Environment &Env) { - if (auto *OptionalVal = getValueBehindPossiblePointer(*ObjectExpr, Env)) { - auto *Prop = OptionalVal->getProperty("has_value"); + if (auto *OptionalLoc = cast_or_null( + getLocBehindPossiblePointer(*ObjectExpr, Env))) { + auto *Prop = Env.getValue(locForHasValue(*OptionalLoc)); if (auto *HasValueVal = cast_or_null(Prop)) { if (Env.proves(HasValueVal->formula())) return {}; @@ -960,9 +853,24 @@ UncheckedOptionalAccessModel::optionalClassDecl() { return optionalClass(); } -UncheckedOptionalAccessModel::UncheckedOptionalAccessModel(ASTContext &Ctx) +static QualType valueTypeFromOptionalType(QualType OptionalTy) { + auto *CTSD = + cast(OptionalTy->getAsCXXRecordDecl()); + return CTSD->getTemplateArgs()[0].getAsType(); +} + +UncheckedOptionalAccessModel::UncheckedOptionalAccessModel(ASTContext &Ctx, + Environment &Env) : DataflowAnalysis(Ctx), - TransferMatchSwitch(buildTransferMatchSwitch()) {} + TransferMatchSwitch(buildTransferMatchSwitch()) { + Env.getDataflowAnalysisContext().setSyntheticFieldCallback( + [&Ctx](QualType Ty) -> llvm::StringMap { + if (!isOptionalType(Ty)) + return {}; + return {{"value", valueTypeFromOptionalType(Ty)}, + {"has_value", Ctx.BoolTy}}; + }); +} void UncheckedOptionalAccessModel::transfer(const CFGElement &Elt, NoopLattice &L, Environment &Env) { @@ -970,76 +878,6 @@ void UncheckedOptionalAccessModel::transfer(const CFGElement &Elt, TransferMatchSwitch(Elt, getASTContext(), State); } -ComparisonResult UncheckedOptionalAccessModel::compare( - QualType Type, const Value &Val1, const Environment &Env1, - const Value &Val2, const Environment &Env2) { - if (!isOptionalType(Type)) - return ComparisonResult::Unknown; - bool MustNonEmpty1 = isNonEmptyOptional(Val1, Env1); - bool MustNonEmpty2 = isNonEmptyOptional(Val2, Env2); - if (MustNonEmpty1 && MustNonEmpty2) - return ComparisonResult::Same; - // If exactly one is true, then they're different, no reason to check whether - // they're definitely empty. - if (MustNonEmpty1 || MustNonEmpty2) - return ComparisonResult::Different; - // Check if they're both definitely empty. - return (isEmptyOptional(Val1, Env1) && isEmptyOptional(Val2, Env2)) - ? ComparisonResult::Same - : ComparisonResult::Different; -} - -bool UncheckedOptionalAccessModel::merge(QualType Type, const Value &Val1, - const Environment &Env1, - const Value &Val2, - const Environment &Env2, - Value &MergedVal, - Environment &MergedEnv) { - if (!isOptionalType(Type)) - return true; - // FIXME: uses same approach as join for `BoolValues`. Requires non-const - // values, though, so will require updating the interface. - auto &HasValueVal = MergedEnv.makeAtomicBoolValue(); - bool MustNonEmpty1 = isNonEmptyOptional(Val1, Env1); - bool MustNonEmpty2 = isNonEmptyOptional(Val2, Env2); - if (MustNonEmpty1 && MustNonEmpty2) - MergedEnv.assume(HasValueVal.formula()); - else if ( - // Only make the costly calls to `isEmptyOptional` if we got "unknown" - // (false) for both calls to `isNonEmptyOptional`. - !MustNonEmpty1 && !MustNonEmpty2 && isEmptyOptional(Val1, Env1) && - isEmptyOptional(Val2, Env2)) - MergedEnv.assume(MergedEnv.arena().makeNot(HasValueVal.formula())); - setHasValue(MergedVal, HasValueVal); - return true; -} - -Value *UncheckedOptionalAccessModel::widen(QualType Type, Value &Prev, - const Environment &PrevEnv, - Value &Current, - Environment &CurrentEnv) { - switch (compare(Type, Prev, PrevEnv, Current, CurrentEnv)) { - case ComparisonResult::Same: - return &Prev; - case ComparisonResult::Different: - if (auto *PrevHasVal = - cast_or_null(Prev.getProperty("has_value"))) { - if (isa(PrevHasVal)) - return &Prev; - } - if (auto *CurrentHasVal = - cast_or_null(Current.getProperty("has_value"))) { - if (isa(CurrentHasVal)) - return &Current; - } - return &createOptionalValue(cast(Current).getLoc(), - CurrentEnv.makeTopBoolValue(), CurrentEnv); - case ComparisonResult::Unknown: - return nullptr; - } - llvm_unreachable("all cases covered in switch"); -} - UncheckedOptionalAccessDiagnoser::UncheckedOptionalAccessDiagnoser( UncheckedOptionalAccessModelOptions Options) : DiagnoseMatchSwitch(buildDiagnoseMatchSwitch(Options)) {} diff --git a/clang/lib/Analysis/FlowSensitive/RecordOps.cpp b/clang/lib/Analysis/FlowSensitive/RecordOps.cpp index 38638f8a9e5892d62dadbfb9f6b9ded066c3726e..caaf443382b02cf4ef04eeb4bff0ec7168117a04 100644 --- a/clang/lib/Analysis/FlowSensitive/RecordOps.cpp +++ b/clang/lib/Analysis/FlowSensitive/RecordOps.cpp @@ -54,6 +54,18 @@ void clang::dataflow::copyRecord(RecordStorageLocation &Src, } } + for (const auto &[Name, SynthFieldLoc] : Src.synthetic_fields()) { + if (SynthFieldLoc->getType()->isRecordType()) { + copyRecord(*cast(SynthFieldLoc), + cast(Dst.getSyntheticField(Name)), Env); + } else { + if (Value *Val = Env.getValue(*SynthFieldLoc)) + Env.setValue(Dst.getSyntheticField(Name), *Val); + else + Env.clearValue(Dst.getSyntheticField(Name)); + } + } + RecordValue *SrcVal = cast_or_null(Env.getValue(Src)); RecordValue *DstVal = cast_or_null(Env.getValue(Dst)); @@ -101,6 +113,18 @@ bool clang::dataflow::recordsEqual(const RecordStorageLocation &Loc1, } } + for (const auto &[Name, SynthFieldLoc1] : Loc1.synthetic_fields()) { + if (SynthFieldLoc1->getType()->isRecordType()) { + if (!recordsEqual( + *cast(SynthFieldLoc1), Env1, + cast(Loc2.getSyntheticField(Name)), Env2)) + return false; + } else if (Env1.getValue(*SynthFieldLoc1) != + Env2.getValue(Loc2.getSyntheticField(Name))) { + return false; + } + } + llvm::StringMap Props1, Props2; if (RecordValue *Val1 = cast_or_null(Env1.getValue(Loc1))) diff --git a/clang/lib/Analysis/FlowSensitive/Transfer.cpp b/clang/lib/Analysis/FlowSensitive/Transfer.cpp index 4343af790081301cd7e39065714f04462428bf62..bbf5f12359bc70e91ef4c3f2a1780a594cda0603 100644 --- a/clang/lib/Analysis/FlowSensitive/Transfer.cpp +++ b/clang/lib/Analysis/FlowSensitive/Transfer.cpp @@ -703,20 +703,18 @@ public: // `InitListExpr`, all fields in the class, including those from base // classes, are included in the set of modeled fields. The code above // should therefore populate exactly the modeled fields. - assert([&]() { - auto ModeledFields = - Env.getDataflowAnalysisContext().getModeledFields(Type); - if (ModeledFields.size() != FieldLocs.size()) - return false; - for ([[maybe_unused]] auto [Field, Loc] : FieldLocs) - if (!ModeledFields.contains(cast_or_null(Field))) - return false; - return true; - }()); - - auto &Loc = - Env.getDataflowAnalysisContext().arena().create( - Type, std::move(FieldLocs)); + assert(containsSameFields( + Env.getDataflowAnalysisContext().getModeledFields(Type), FieldLocs)); + + RecordStorageLocation::SyntheticFieldMap SyntheticFieldLocs; + for (const auto &Entry : + Env.getDataflowAnalysisContext().getSyntheticFields(Type)) { + SyntheticFieldLocs.insert( + {Entry.getKey(), &Env.createObject(Entry.getValue())}); + } + + auto &Loc = Env.getDataflowAnalysisContext().createRecordStorageLocation( + Type, std::move(FieldLocs), std::move(SyntheticFieldLocs)); RecordValue &RecordVal = Env.create(Loc); Env.setValue(Loc, RecordVal); diff --git a/clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp b/clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp index ade8c84f19366f4b8dd0becbcfafc9f62bc3d4f7..8c9360235da7c1f670507960917293dbf37966f3 100644 --- a/clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp +++ b/clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp @@ -501,6 +501,14 @@ runTypeErasedDataflowAnalysis( PostVisitCFG) { PrettyStackTraceAnalysis CrashInfo(CFCtx, "runTypeErasedDataflowAnalysis"); + std::optional MaybeStartingEnv; + if (InitEnv.callStackSize() == 1) { + MaybeStartingEnv = InitEnv.fork(); + MaybeStartingEnv->initialize(); + } + const Environment &StartingEnv = + MaybeStartingEnv ? *MaybeStartingEnv : InitEnv; + const clang::CFG &CFG = CFCtx.getCFG(); PostOrderCFGView POV(&CFG); ForwardDataflowWorklist Worklist(CFG, &POV); @@ -511,10 +519,10 @@ runTypeErasedDataflowAnalysis( // The entry basic block doesn't contain statements so it can be skipped. const CFGBlock &Entry = CFG.getEntry(); BlockStates[Entry.getBlockID()] = {Analysis.typeErasedInitialElement(), - InitEnv.fork()}; + StartingEnv.fork()}; Worklist.enqueueSuccessors(&Entry); - AnalysisContext AC(CFCtx, Analysis, InitEnv, BlockStates); + AnalysisContext AC(CFCtx, Analysis, StartingEnv, BlockStates); // Bugs in lattices and transfer functions can prevent the analysis from // converging. To limit the damage (infinite loops) that these bugs can cause, diff --git a/clang/lib/Basic/CodeGenOptions.cpp b/clang/lib/Basic/CodeGenOptions.cpp index 0c609cfa61de18561626e4f0753e0e15cd1c866c..182d0a2fa4d88f212ce607284f3db9b06ef477cb 100644 --- a/clang/lib/Basic/CodeGenOptions.cpp +++ b/clang/lib/Basic/CodeGenOptions.cpp @@ -20,4 +20,39 @@ CodeGenOptions::CodeGenOptions() { memcpy(CoverageVersion, "408*", 4); } +void CodeGenOptions::resetNonModularOptions(StringRef ModuleFormat) { + // First reset all CodeGen options only. The Debug options are handled later. +#define DEBUGOPT(Name, Bits, Default) +#define VALUE_DEBUGOPT(Name, Bits, Default) +#define ENUM_DEBUGOPT(Name, Type, Bits, Default) +#define CODEGENOPT(Name, Bits, Default) Name = Default; +#define ENUM_CODEGENOPT(Name, Type, Bits, Default) set##Name(Default); +#include "clang/Basic/CodeGenOptions.def" + + // Next reset all debug options that can always be reset, because they never + // affect the PCM. +#define DEBUGOPT(Name, Bits, Default) +#define VALUE_DEBUGOPT(Name, Bits, Default) +#define ENUM_DEBUGOPT(Name, Type, Bits, Default) +#define BENIGN_DEBUGOPT(Name, Bits, Default) Name = Default; +#define BENIGN_VALUE_DEBUGOPT(Name, Bits, Default) Name = Default; +#define BENIGN_ENUM_DEBUGOPT(Name, Type, Bits, Default) set##Name(Default); +#include "clang/Basic/DebugOptions.def" + + // Conditionally reset debug options that only matter when the debug info is + // emitted into the PCM (-gmodules). + if (ModuleFormat == "raw" && !DebugTypeExtRefs) { +#define DEBUGOPT(Name, Bits, Default) Name = Default; +#define VALUE_DEBUGOPT(Name, Bits, Default) Name = Default; +#define ENUM_DEBUGOPT(Name, Type, Bits, Default) set##Name(Default); +#define BENIGN_DEBUGOPT(Name, Bits, Default) +#define BENIGN_VALUE_DEBUGOPT(Name, Bits, Default) +#define BENIGN_ENUM_DEBUGOPT(Name, Type, Bits, Default) +#include "clang/Basic/DebugOptions.def" + } + + RelocationModel = llvm::Reloc::PIC_; + memcpy(CoverageVersion, "408*", 4); +} + } // end namespace clang diff --git a/clang/lib/Basic/Targets/RISCV.cpp b/clang/lib/Basic/Targets/RISCV.cpp index d1d9cc1c770e361d23af7774e0afe034f6a48866..13f934e9947212c67535d7fd4cb0e8434e7611e3 100644 --- a/clang/lib/Basic/Targets/RISCV.cpp +++ b/clang/lib/Basic/Targets/RISCV.cpp @@ -236,8 +236,7 @@ ArrayRef RISCVTargetInfo::getTargetBuiltins() const { } static std::vector -collectNonISAExtFeature(const std::vector &FeaturesNeedOverride, - int XLen) { +collectNonISAExtFeature(ArrayRef FeaturesNeedOverride, int XLen) { auto ParseResult = llvm::RISCVISAInfo::parseFeatures(XLen, FeaturesNeedOverride); @@ -265,11 +264,11 @@ resolveTargetAttrOverride(const std::vector &FeaturesVec, if (I == FeaturesVec.end()) return FeaturesVec; - const std::vector FeaturesNeedOverride(FeaturesVec.begin(), I); + ArrayRef FeaturesNeedOverride(&*FeaturesVec.begin(), &*I); std::vector NonISAExtFeature = collectNonISAExtFeature(FeaturesNeedOverride, XLen); - auto ResolvedFeature = std::vector(++I, FeaturesVec.end()); + std::vector ResolvedFeature(++I, FeaturesVec.end()); ResolvedFeature.insert(ResolvedFeature.end(), NonISAExtFeature.begin(), NonISAExtFeature.end()); @@ -377,7 +376,7 @@ bool RISCVTargetInfo::handleTargetFeatures(std::vector &Features, if (ISAInfo->hasExtension("zfh") || ISAInfo->hasExtension("zhinx")) HasLegalHalfType = true; - FastUnalignedAccess = llvm::is_contained(Features, "+unaligned-scalar-mem"); + FastUnalignedAccess = llvm::is_contained(Features, "+fast-unaligned-access"); return true; } @@ -415,8 +414,7 @@ static void handleFullArchString(StringRef FullArchStr, Features.push_back("+" + FullArchStr.str()); } else { std::vector FeatStrings = (*RII)->toFeatureVector(); - for (auto FeatString : FeatStrings) - Features.push_back(FeatString); + Features.insert(Features.end(), FeatStrings.begin(), FeatStrings.end()); } } diff --git a/clang/lib/Basic/Targets/SystemZ.h b/clang/lib/Basic/Targets/SystemZ.h index 9ba255745cf2cc52676208d062cb8cff31bc801a..e4ec338880f21095693fabe84780ff30ebacdef1 100644 --- a/clang/lib/Basic/Targets/SystemZ.h +++ b/clang/lib/Basic/Targets/SystemZ.h @@ -60,7 +60,7 @@ public: resetDataLayout("E-m:e-i1:8:16-i8:8:16-i64:64-f128:64" "-v128:64-a:8:16-n32:64"); } - MaxAtomicPromoteWidth = MaxAtomicInlineWidth = 64; + MaxAtomicPromoteWidth = MaxAtomicInlineWidth = 128; HasStrictFP = true; } diff --git a/clang/lib/Basic/Targets/X86.cpp b/clang/lib/Basic/Targets/X86.cpp index 85d0697ad63cae399a10e8586023cb8191da1402..b97f88647fa49f26e604ff2b99eafa5716944626 100644 --- a/clang/lib/Basic/Targets/X86.cpp +++ b/clang/lib/Basic/Targets/X86.cpp @@ -428,6 +428,18 @@ bool X86TargetInfo::handleTargetFeatures(std::vector &Features, HasX87 = true; } else if (Feature == "+fullbf16") { HasFullBFloat16 = true; + } else if (Feature == "+egpr") { + HasEGPR = true; + } else if (Feature == "+push2pop2") { + HasPush2Pop2 = true; + } else if (Feature == "+ppx") { + HasPPX = true; + } else if (Feature == "+ndd") { + HasNDD = true; + } else if (Feature == "+ccmp") { + HasCCMP = true; + } else if (Feature == "+cf") { + HasCF = true; } X86SSEEnum Level = llvm::StringSwitch(Feature) @@ -927,6 +939,18 @@ void X86TargetInfo::getTargetDefines(const LangOptions &Opts, Builder.defineMacro("__USERMSR__"); if (HasCRC32) Builder.defineMacro("__CRC32__"); + if (HasEGPR) + Builder.defineMacro("__EGPR__"); + if (HasPush2Pop2) + Builder.defineMacro("__PUSH2POP2__"); + if (HasPPX) + Builder.defineMacro("__PPX__"); + if (HasNDD) + Builder.defineMacro("__NDD__"); + if (HasCCMP) + Builder.defineMacro("__CCMP__"); + if (HasCF) + Builder.defineMacro("__CF__"); // Each case falls through to the previous one here. switch (SSELevel) { @@ -1122,6 +1146,12 @@ bool X86TargetInfo::isValidFeatureName(StringRef Name) const { .Case("xsavec", true) .Case("xsaves", true) .Case("xsaveopt", true) + .Case("egpr", true) + .Case("push2pop2", true) + .Case("ppx", true) + .Case("ndd", true) + .Case("ccmp", true) + .Case("cf", true) .Default(false); } @@ -1238,6 +1268,12 @@ bool X86TargetInfo::hasFeature(StringRef Feature) const { .Case("xsaves", HasXSAVES) .Case("xsaveopt", HasXSAVEOPT) .Case("fullbf16", HasFullBFloat16) + .Case("egpr", HasEGPR) + .Case("push2pop2", HasPush2Pop2) + .Case("ppx", HasPPX) + .Case("ndd", HasNDD) + .Case("ccmp", HasCCMP) + .Case("cf", HasCF) .Default(false); } diff --git a/clang/lib/Basic/Targets/X86.h b/clang/lib/Basic/Targets/X86.h index 99a64501d263ce4b330287a09cbf94c40285123b..0ab1c10833db26201e59e7304a4cd32e5f84b880 100644 --- a/clang/lib/Basic/Targets/X86.h +++ b/clang/lib/Basic/Targets/X86.h @@ -168,6 +168,12 @@ class LLVM_LIBRARY_VISIBILITY X86TargetInfo : public TargetInfo { bool HasUINTR = false; bool HasCRC32 = false; bool HasX87 = false; + bool HasEGPR = false; + bool HasPush2Pop2 = false; + bool HasPPX = false; + bool HasNDD = false; + bool HasCCMP = false; + bool HasCF = false; protected: llvm::X86::CPUKind CPU = llvm::X86::CK_None; diff --git a/clang/lib/CodeGen/BackendUtil.cpp b/clang/lib/CodeGen/BackendUtil.cpp index a7a47d17723cb73e29577231ce54e8fe8558e3e5..8c666e2cb463c6ccfee6259d1339d693893f9a07 100644 --- a/clang/lib/CodeGen/BackendUtil.cpp +++ b/clang/lib/CodeGen/BackendUtil.cpp @@ -103,7 +103,7 @@ static cl::opt ClSanitizeOnOptimizerEarlyEP( cl::desc("Insert sanitizers on OptimizerEarlyEP."), cl::init(false)); // Re-link builtin bitcodes after optimization -static cl::opt ClRelinkBuiltinBitcodePostop( +cl::opt ClRelinkBuiltinBitcodePostop( "relink-builtin-bitcode-postop", cl::Optional, cl::desc("Re-link builtin bitcodes after optimization."), cl::init(false)); } @@ -996,9 +996,8 @@ void EmitAssemblyHelper::RunOptimizationPipeline( } if (CodeGenOpts.FatLTO) { - MPM.addPass(PB.buildFatLTODefaultPipeline( - Level, PrepareForThinLTO, - PrepareForThinLTO || shouldEmitRegularLTOSummary())); + assert(CodeGenOpts.UnifiedLTO && "FatLTO requires UnifiedLTO"); + MPM.addPass(PB.buildFatLTODefaultPipeline(Level)); } else if (PrepareForThinLTO) { MPM.addPass(PB.buildThinLTOPreLinkDefaultPipeline(Level)); } else if (PrepareForLTO) { @@ -1042,7 +1041,6 @@ void EmitAssemblyHelper::RunOptimizationPipeline( MPM.addPass(PrintModulePass(*OS, "", CodeGenOpts.EmitLLVMUseLists, /*EmitLTOSummary=*/true)); } - } else { // Emit a module summary by default for Regular LTO except for ld64 // targets @@ -1073,7 +1071,8 @@ void EmitAssemblyHelper::RunOptimizationPipeline( if (!TheModule->getModuleFlag("EnableSplitLTOUnit")) TheModule->addModuleFlag(llvm::Module::Error, "EnableSplitLTOUnit", uint32_t(CodeGenOpts.EnableSplitLTOUnit)); - if (CodeGenOpts.UnifiedLTO && !TheModule->getModuleFlag("UnifiedLTO")) + // FatLTO always means UnifiedLTO + if (!TheModule->getModuleFlag("UnifiedLTO")) TheModule->addModuleFlag(llvm::Module::Error, "UnifiedLTO", uint32_t(1)); } diff --git a/clang/lib/CodeGen/CGAtomic.cpp b/clang/lib/CodeGen/CGAtomic.cpp index 6005d5c51c0e1ac9be09d8dc3d80c8deaf2503dd..379c833af32a2de87e57b5057cafe8026d59220a 100644 --- a/clang/lib/CodeGen/CGAtomic.cpp +++ b/clang/lib/CodeGen/CGAtomic.cpp @@ -383,8 +383,7 @@ static void emitAtomicCmpXchg(CodeGenFunction &CGF, AtomicExpr *E, bool IsWeak, llvm::Value *Desired = CGF.Builder.CreateLoad(Val2); llvm::AtomicCmpXchgInst *Pair = CGF.Builder.CreateAtomicCmpXchg( - Ptr.getPointer(), Expected, Desired, SuccessOrder, FailureOrder, - Scope); + Ptr, Expected, Desired, SuccessOrder, FailureOrder, Scope); Pair->setVolatile(E->isVolatile()); Pair->setWeak(IsWeak); @@ -699,7 +698,7 @@ static void EmitAtomicOp(CodeGenFunction &CGF, AtomicExpr *E, Address Dest, llvm::Value *LoadVal1 = CGF.Builder.CreateLoad(Val1); llvm::AtomicRMWInst *RMWI = - CGF.Builder.CreateAtomicRMW(Op, Ptr.getPointer(), LoadVal1, Order, Scope); + CGF.Builder.CreateAtomicRMW(Op, Ptr, LoadVal1, Order, Scope); RMWI->setVolatile(E->isVolatile()); // For __atomic_*_fetch operations, perform the operation again to @@ -1740,8 +1739,7 @@ std::pair AtomicInfo::EmitAtomicCompareExchangeOp( llvm::AtomicOrdering Success, llvm::AtomicOrdering Failure, bool IsWeak) { // Do the atomic store. Address Addr = getAtomicAddressAsAtomicIntPointer(); - auto *Inst = CGF.Builder.CreateAtomicCmpXchg(Addr.getPointer(), - ExpectedVal, DesiredVal, + auto *Inst = CGF.Builder.CreateAtomicCmpXchg(Addr, ExpectedVal, DesiredVal, Success, Failure); // Other decoration. Inst->setVolatile(LVal.isVolatileQualified()); diff --git a/clang/lib/CodeGen/CGBuilder.h b/clang/lib/CodeGen/CGBuilder.h index 68535920088c49b0018de7d72d3e0cc6d6bdb59c..bf5ab171d720d9b148544ac6467652a5efea0edb 100644 --- a/clang/lib/CodeGen/CGBuilder.h +++ b/clang/lib/CodeGen/CGBuilder.h @@ -126,25 +126,22 @@ public: return CreateAlignedStore(getInt1(Value), Addr, CharUnits::One()); } - // Temporarily use old signature; clang will be updated to an Address overload - // in a subsequent patch. llvm::AtomicCmpXchgInst * - CreateAtomicCmpXchg(llvm::Value *Ptr, llvm::Value *Cmp, llvm::Value *New, + CreateAtomicCmpXchg(Address Addr, llvm::Value *Cmp, llvm::Value *New, llvm::AtomicOrdering SuccessOrdering, llvm::AtomicOrdering FailureOrdering, llvm::SyncScope::ID SSID = llvm::SyncScope::System) { return CGBuilderBaseTy::CreateAtomicCmpXchg( - Ptr, Cmp, New, llvm::MaybeAlign(), SuccessOrdering, FailureOrdering, - SSID); + Addr.getPointer(), Cmp, New, Addr.getAlignment().getAsAlign(), + SuccessOrdering, FailureOrdering, SSID); } - // Temporarily use old signature; clang will be updated to an Address overload - // in a subsequent patch. llvm::AtomicRMWInst * - CreateAtomicRMW(llvm::AtomicRMWInst::BinOp Op, llvm::Value *Ptr, - llvm::Value *Val, llvm::AtomicOrdering Ordering, + CreateAtomicRMW(llvm::AtomicRMWInst::BinOp Op, Address Addr, llvm::Value *Val, + llvm::AtomicOrdering Ordering, llvm::SyncScope::ID SSID = llvm::SyncScope::System) { - return CGBuilderBaseTy::CreateAtomicRMW(Op, Ptr, Val, llvm::MaybeAlign(), + return CGBuilderBaseTy::CreateAtomicRMW(Op, Addr.getPointer(), Val, + Addr.getAlignment().getAsAlign(), Ordering, SSID); } diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp index c83ea966fdeadc6ce11ad992eaead965f5930913..0d8b3e4aaad4705994ea6d0fdc9ebbcd090671ee 100644 --- a/clang/lib/CodeGen/CGBuiltin.cpp +++ b/clang/lib/CodeGen/CGBuiltin.cpp @@ -188,8 +188,7 @@ static Value *EmitFromInt(CodeGenFunction &CGF, llvm::Value *V, return V; } -static llvm::Value *CheckAtomicAlignment(CodeGenFunction &CGF, - const CallExpr *E) { +static Address CheckAtomicAlignment(CodeGenFunction &CGF, const CallExpr *E) { ASTContext &Ctx = CGF.getContext(); Address Ptr = CGF.EmitPointerWithAlignment(E->getArg(0)); unsigned Bytes = Ptr.getElementType()->isPointerTy() @@ -199,8 +198,10 @@ static llvm::Value *CheckAtomicAlignment(CodeGenFunction &CGF, if (Align % Bytes != 0) { DiagnosticsEngine &Diags = CGF.CGM.getDiags(); Diags.Report(E->getBeginLoc(), diag::warn_sync_op_misaligned); + // Force address to be at least naturally-aligned. + return Ptr.withAlignment(CharUnits::fromQuantity(Bytes)); } - return Ptr.getPointer(); + return Ptr; } /// Utility to insert an atomic instruction based on Intrinsic::ID @@ -215,19 +216,17 @@ static Value *MakeBinaryAtomicValue( E->getArg(0)->getType()->getPointeeType())); assert(CGF.getContext().hasSameUnqualifiedType(T, E->getArg(1)->getType())); - llvm::Value *DestPtr = CheckAtomicAlignment(CGF, E); + Address DestAddr = CheckAtomicAlignment(CGF, E); llvm::IntegerType *IntType = llvm::IntegerType::get( CGF.getLLVMContext(), CGF.getContext().getTypeSize(T)); - llvm::Value *Args[2]; - Args[0] = DestPtr; - Args[1] = CGF.EmitScalarExpr(E->getArg(1)); - llvm::Type *ValueType = Args[1]->getType(); - Args[1] = EmitToInt(CGF, Args[1], T, IntType); + llvm::Value *Val = CGF.EmitScalarExpr(E->getArg(1)); + llvm::Type *ValueType = Val->getType(); + Val = EmitToInt(CGF, Val, T, IntType); - llvm::Value *Result = CGF.Builder.CreateAtomicRMW( - Kind, Args[0], Args[1], Ordering); + llvm::Value *Result = + CGF.Builder.CreateAtomicRMW(Kind, DestAddr, Val, Ordering); return EmitFromInt(CGF, Result, T, ValueType); } @@ -270,20 +269,18 @@ static RValue EmitBinaryAtomicPost(CodeGenFunction &CGF, E->getArg(0)->getType()->getPointeeType())); assert(CGF.getContext().hasSameUnqualifiedType(T, E->getArg(1)->getType())); - llvm::Value *DestPtr = CheckAtomicAlignment(CGF, E); + Address DestAddr = CheckAtomicAlignment(CGF, E); llvm::IntegerType *IntType = llvm::IntegerType::get( CGF.getLLVMContext(), CGF.getContext().getTypeSize(T)); - llvm::Value *Args[2]; - Args[1] = CGF.EmitScalarExpr(E->getArg(1)); - llvm::Type *ValueType = Args[1]->getType(); - Args[1] = EmitToInt(CGF, Args[1], T, IntType); - Args[0] = DestPtr; + llvm::Value *Val = CGF.EmitScalarExpr(E->getArg(1)); + llvm::Type *ValueType = Val->getType(); + Val = EmitToInt(CGF, Val, T, IntType); llvm::Value *Result = CGF.Builder.CreateAtomicRMW( - Kind, Args[0], Args[1], llvm::AtomicOrdering::SequentiallyConsistent); - Result = CGF.Builder.CreateBinOp(Op, Result, Args[1]); + Kind, DestAddr, Val, llvm::AtomicOrdering::SequentiallyConsistent); + Result = CGF.Builder.CreateBinOp(Op, Result, Val); if (Invert) Result = CGF.Builder.CreateBinOp(llvm::Instruction::Xor, Result, @@ -309,20 +306,18 @@ static RValue EmitBinaryAtomicPost(CodeGenFunction &CGF, static Value *MakeAtomicCmpXchgValue(CodeGenFunction &CGF, const CallExpr *E, bool ReturnBool) { QualType T = ReturnBool ? E->getArg(1)->getType() : E->getType(); - llvm::Value *DestPtr = CheckAtomicAlignment(CGF, E); + Address DestAddr = CheckAtomicAlignment(CGF, E); llvm::IntegerType *IntType = llvm::IntegerType::get( CGF.getLLVMContext(), CGF.getContext().getTypeSize(T)); - Value *Args[3]; - Args[0] = DestPtr; - Args[1] = CGF.EmitScalarExpr(E->getArg(1)); - llvm::Type *ValueType = Args[1]->getType(); - Args[1] = EmitToInt(CGF, Args[1], T, IntType); - Args[2] = EmitToInt(CGF, CGF.EmitScalarExpr(E->getArg(2)), T, IntType); + Value *Cmp = CGF.EmitScalarExpr(E->getArg(1)); + llvm::Type *ValueType = Cmp->getType(); + Cmp = EmitToInt(CGF, Cmp, T, IntType); + Value *New = EmitToInt(CGF, CGF.EmitScalarExpr(E->getArg(2)), T, IntType); Value *Pair = CGF.Builder.CreateAtomicCmpXchg( - Args[0], Args[1], Args[2], llvm::AtomicOrdering::SequentiallyConsistent, + DestAddr, Cmp, New, llvm::AtomicOrdering::SequentiallyConsistent, llvm::AtomicOrdering::SequentiallyConsistent); if (ReturnBool) // Extract boolean success flag and zext it to int. @@ -358,7 +353,8 @@ Value *EmitAtomicCmpXchgForMSIntrin(CodeGenFunction &CGF, const CallExpr *E, assert(CGF.getContext().hasSameUnqualifiedType(E->getType(), E->getArg(2)->getType())); - auto *Destination = CGF.EmitScalarExpr(E->getArg(0)); + Address DestAddr = CheckAtomicAlignment(CGF, E); + auto *Comparand = CGF.EmitScalarExpr(E->getArg(2)); auto *Exchange = CGF.EmitScalarExpr(E->getArg(1)); @@ -372,8 +368,7 @@ Value *EmitAtomicCmpXchgForMSIntrin(CodeGenFunction &CGF, const CallExpr *E, // _Interlocked* operations in the future, we will have to remove the volatile // marker. auto *Result = CGF.Builder.CreateAtomicCmpXchg( - Destination, Comparand, Exchange, - SuccessOrdering, FailureOrdering); + DestAddr, Comparand, Exchange, SuccessOrdering, FailureOrdering); Result->setVolatile(true); return CGF.Builder.CreateExtractValue(Result, 0); } @@ -386,29 +381,34 @@ Value *EmitAtomicCmpXchgForMSIntrin(CodeGenFunction &CGF, const CallExpr *E, // __int64 _ExchangeHigh, // __int64 _ExchangeLow, // __int64 * _ComparandResult); +// +// Note that Destination is assumed to be at least 16-byte aligned, despite +// being typed int64. + static Value *EmitAtomicCmpXchg128ForMSIntrin(CodeGenFunction &CGF, const CallExpr *E, AtomicOrdering SuccessOrdering) { assert(E->getNumArgs() == 4); - llvm::Value *Destination = CGF.EmitScalarExpr(E->getArg(0)); + llvm::Value *DestPtr = CGF.EmitScalarExpr(E->getArg(0)); llvm::Value *ExchangeHigh = CGF.EmitScalarExpr(E->getArg(1)); llvm::Value *ExchangeLow = CGF.EmitScalarExpr(E->getArg(2)); - llvm::Value *ComparandPtr = CGF.EmitScalarExpr(E->getArg(3)); + Address ComparandAddr = CGF.EmitPointerWithAlignment(E->getArg(3)); - assert(Destination->getType()->isPointerTy()); + assert(DestPtr->getType()->isPointerTy()); assert(!ExchangeHigh->getType()->isPointerTy()); assert(!ExchangeLow->getType()->isPointerTy()); - assert(ComparandPtr->getType()->isPointerTy()); // For Release ordering, the failure ordering should be Monotonic. auto FailureOrdering = SuccessOrdering == AtomicOrdering::Release ? AtomicOrdering::Monotonic : SuccessOrdering; - // Convert to i128 pointers and values. + // Convert to i128 pointers and values. Alignment is also overridden for + // destination pointer. llvm::Type *Int128Ty = llvm::IntegerType::get(CGF.getLLVMContext(), 128); - Address ComparandResult(ComparandPtr, Int128Ty, - CGF.getContext().toCharUnitsFromBits(128)); + Address DestAddr(DestPtr, Int128Ty, + CGF.getContext().toCharUnitsFromBits(128)); + ComparandAddr = ComparandAddr.withElementType(Int128Ty); // (((i128)hi) << 64) | ((i128)lo) ExchangeHigh = CGF.Builder.CreateZExt(ExchangeHigh, Int128Ty); @@ -418,9 +418,9 @@ static Value *EmitAtomicCmpXchg128ForMSIntrin(CodeGenFunction &CGF, llvm::Value *Exchange = CGF.Builder.CreateOr(ExchangeHigh, ExchangeLow); // Load the comparand for the instruction. - llvm::Value *Comparand = CGF.Builder.CreateLoad(ComparandResult); + llvm::Value *Comparand = CGF.Builder.CreateLoad(ComparandAddr); - auto *CXI = CGF.Builder.CreateAtomicCmpXchg(Destination, Comparand, Exchange, + auto *CXI = CGF.Builder.CreateAtomicCmpXchg(DestAddr, Comparand, Exchange, SuccessOrdering, FailureOrdering); // The atomic instruction is marked volatile for consistency with MSVC. This @@ -431,7 +431,7 @@ static Value *EmitAtomicCmpXchg128ForMSIntrin(CodeGenFunction &CGF, // Store the result as an outparameter. CGF.Builder.CreateStore(CGF.Builder.CreateExtractValue(CXI, 0), - ComparandResult); + ComparandAddr); // Get the success boolean and zero extend it to i8. Value *Success = CGF.Builder.CreateExtractValue(CXI, 1); @@ -443,24 +443,21 @@ static Value *EmitAtomicIncrementValue(CodeGenFunction &CGF, const CallExpr *E, assert(E->getArg(0)->getType()->isPointerType()); auto *IntTy = CGF.ConvertType(E->getType()); + Address DestAddr = CheckAtomicAlignment(CGF, E); auto *Result = CGF.Builder.CreateAtomicRMW( - AtomicRMWInst::Add, - CGF.EmitScalarExpr(E->getArg(0)), - ConstantInt::get(IntTy, 1), - Ordering); + AtomicRMWInst::Add, DestAddr, ConstantInt::get(IntTy, 1), Ordering); return CGF.Builder.CreateAdd(Result, ConstantInt::get(IntTy, 1)); } -static Value *EmitAtomicDecrementValue(CodeGenFunction &CGF, const CallExpr *E, +static Value *EmitAtomicDecrementValue( + CodeGenFunction &CGF, const CallExpr *E, AtomicOrdering Ordering = AtomicOrdering::SequentiallyConsistent) { assert(E->getArg(0)->getType()->isPointerType()); auto *IntTy = CGF.ConvertType(E->getType()); + Address DestAddr = CheckAtomicAlignment(CGF, E); auto *Result = CGF.Builder.CreateAtomicRMW( - AtomicRMWInst::Sub, - CGF.EmitScalarExpr(E->getArg(0)), - ConstantInt::get(IntTy, 1), - Ordering); + AtomicRMWInst::Sub, DestAddr, ConstantInt::get(IntTy, 1), Ordering); return CGF.Builder.CreateSub(Result, ConstantInt::get(IntTy, 1)); } @@ -1215,8 +1212,7 @@ static llvm::Value *EmitBitTestIntrinsic(CodeGenFunction &CGF, Mask = CGF.Builder.CreateNot(Mask); RMWOp = llvm::AtomicRMWInst::And; } - OldByte = CGF.Builder.CreateAtomicRMW(RMWOp, ByteAddr.getPointer(), Mask, - Ordering); + OldByte = CGF.Builder.CreateAtomicRMW(RMWOp, ByteAddr, Mask, Ordering); } else { // Emit a plain load for the non-interlocked intrinsics. OldByte = CGF.Builder.CreateLoad(ByteAddr, "bittest.byte"); @@ -4456,14 +4452,13 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID, case Builtin::BI__sync_lock_release_4: case Builtin::BI__sync_lock_release_8: case Builtin::BI__sync_lock_release_16: { - Value *Ptr = CheckAtomicAlignment(*this, E); + Address Ptr = CheckAtomicAlignment(*this, E); QualType ElTy = E->getArg(0)->getType()->getPointeeType(); - CharUnits StoreSize = getContext().getTypeSizeInChars(ElTy); - llvm::Type *ITy = - llvm::IntegerType::get(getLLVMContext(), StoreSize.getQuantity() * 8); + + llvm::Type *ITy = llvm::IntegerType::get(getLLVMContext(), + getContext().getTypeSize(ElTy)); llvm::StoreInst *Store = - Builder.CreateAlignedStore(llvm::Constant::getNullValue(ITy), Ptr, - StoreSize); + Builder.CreateStore(llvm::Constant::getNullValue(ITy), Ptr); Store->setAtomic(llvm::AtomicOrdering::Release); return RValue::get(nullptr); } @@ -4514,7 +4509,9 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID, bool Volatile = PtrTy->castAs()->getPointeeType().isVolatileQualified(); - Value *Ptr = EmitScalarExpr(E->getArg(0)); + Address Ptr = + EmitPointerWithAlignment(E->getArg(0)).withElementType(Int8Ty); + Value *NewVal = Builder.getInt8(1); Value *Order = EmitScalarExpr(E->getArg(1)); if (isa(Order)) { @@ -5035,7 +5032,7 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID, llvm::IntegerType *IntType = IntegerType::get( getLLVMContext(), getContext().getTypeSize(E->getType())); - llvm::Value *Destination = EmitScalarExpr(E->getArg(0)); + Address DestAddr = CheckAtomicAlignment(*this, E); llvm::Value *Exchange = EmitScalarExpr(E->getArg(1)); RTy = Exchange->getType(); @@ -5048,7 +5045,7 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID, BuiltinID == Builtin::BI_InterlockedCompareExchangePointer_nf ? AtomicOrdering::Monotonic : AtomicOrdering::SequentiallyConsistent; - auto Result = Builder.CreateAtomicCmpXchg(Destination, Comparand, Exchange, + auto Result = Builder.CreateAtomicCmpXchg(DestAddr, Comparand, Exchange, Ordering, Ordering); Result->setVolatile(true); @@ -11901,12 +11898,12 @@ Value *CodeGenFunction::EmitAArch64BuiltinExpr(unsigned BuiltinID, } case clang::AArch64::BI_InterlockedAdd: { - Value *Arg0 = EmitScalarExpr(E->getArg(0)); - Value *Arg1 = EmitScalarExpr(E->getArg(1)); - AtomicRMWInst *RMWI = Builder.CreateAtomicRMW( - AtomicRMWInst::Add, Arg0, Arg1, - llvm::AtomicOrdering::SequentiallyConsistent); - return Builder.CreateAdd(RMWI, Arg1); + Address DestAddr = CheckAtomicAlignment(*this, E); + Value *Val = EmitScalarExpr(E->getArg(1)); + AtomicRMWInst *RMWI = + Builder.CreateAtomicRMW(AtomicRMWInst::Add, DestAddr, Val, + llvm::AtomicOrdering::SequentiallyConsistent); + return Builder.CreateAdd(RMWI, Val); } } @@ -17588,7 +17585,7 @@ Value *EmitAMDGPUWorkGroupSize(CodeGenFunction &CGF, unsigned Index) { auto Cov = CGF.getTarget().getTargetOpts().CodeObjectVersion; - if (Cov == clang::TargetOptions::COV_None) { + if (Cov == CodeObjectVersionKind::COV_None) { StringRef Name = "__oclc_ABI_version"; auto *ABIVersionC = CGF.CGM.getModule().getNamedGlobal(Name); if (!ABIVersionC) @@ -17606,7 +17603,7 @@ Value *EmitAMDGPUWorkGroupSize(CodeGenFunction &CGF, unsigned Index) { Value *IsCOV5 = CGF.Builder.CreateICmpSGE( ABIVersion, - llvm::ConstantInt::get(CGF.Int32Ty, clang::TargetOptions::COV_5)); + llvm::ConstantInt::get(CGF.Int32Ty, CodeObjectVersionKind::COV_5)); // Indexing the implicit kernarg segment. Value *ImplicitGEP = CGF.Builder.CreateConstGEP1_32( @@ -17621,7 +17618,7 @@ Value *EmitAMDGPUWorkGroupSize(CodeGenFunction &CGF, unsigned Index) { Address(Result, CGF.Int16Ty, CharUnits::fromQuantity(2))); } else { Value *GEP = nullptr; - if (Cov == clang::TargetOptions::COV_5) { + if (Cov == CodeObjectVersionKind::COV_5) { // Indexing the implicit kernarg segment. GEP = CGF.Builder.CreateConstGEP1_32( CGF.Int8Ty, EmitAMDGPUImplicitArgPtr(CGF), 12 + Index * 2); @@ -18219,7 +18216,7 @@ Value *CodeGenFunction::EmitAMDGPUBuiltinExpr(unsigned BuiltinID, break; } - Value *Ptr = EmitScalarExpr(E->getArg(0)); + Address Ptr = CheckAtomicAlignment(*this, E); Value *Val = EmitScalarExpr(E->getArg(1)); ProcessOrderScopeAMDGCN(EmitScalarExpr(E->getArg(2)), @@ -18337,6 +18334,32 @@ Value *CodeGenFunction::EmitSystemZBuiltinExpr(unsigned BuiltinID, return Builder.CreateCall(F, {X, Undef}); } + case SystemZ::BI__builtin_s390_verllb: + case SystemZ::BI__builtin_s390_verllh: + case SystemZ::BI__builtin_s390_verllf: + case SystemZ::BI__builtin_s390_verllg: { + llvm::Type *ResultType = ConvertType(E->getType()); + llvm::Value *Src = EmitScalarExpr(E->getArg(0)); + llvm::Value *Amt = EmitScalarExpr(E->getArg(1)); + // Splat scalar rotate amount to vector type. + unsigned NumElts = cast(ResultType)->getNumElements(); + Amt = Builder.CreateIntCast(Amt, ResultType->getScalarType(), false); + Amt = Builder.CreateVectorSplat(NumElts, Amt); + Function *F = CGM.getIntrinsic(Intrinsic::fshl, ResultType); + return Builder.CreateCall(F, { Src, Src, Amt }); + } + + case SystemZ::BI__builtin_s390_verllvb: + case SystemZ::BI__builtin_s390_verllvh: + case SystemZ::BI__builtin_s390_verllvf: + case SystemZ::BI__builtin_s390_verllvg: { + llvm::Type *ResultType = ConvertType(E->getType()); + llvm::Value *Src = EmitScalarExpr(E->getArg(0)); + llvm::Value *Amt = EmitScalarExpr(E->getArg(1)); + Function *F = CGM.getIntrinsic(Intrinsic::fshl, ResultType); + return Builder.CreateCall(F, { Src, Src, Amt }); + } + case SystemZ::BI__builtin_s390_vfsqsb: case SystemZ::BI__builtin_s390_vfsqdb: { llvm::Type *ResultType = ConvertType(E->getType()); @@ -19082,9 +19105,10 @@ Value *CodeGenFunction::EmitNVPTXBuiltinExpr(unsigned BuiltinID, case NVPTX::BI__nvvm_atom_add_gen_f: case NVPTX::BI__nvvm_atom_add_gen_d: { - Value *Ptr = EmitScalarExpr(E->getArg(0)); + Address DestAddr = EmitPointerWithAlignment(E->getArg(0)); Value *Val = EmitScalarExpr(E->getArg(1)); - return Builder.CreateAtomicRMW(llvm::AtomicRMWInst::FAdd, Ptr, Val, + + return Builder.CreateAtomicRMW(llvm::AtomicRMWInst::FAdd, DestAddr, Val, AtomicOrdering::SequentiallyConsistent); } diff --git a/clang/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp index 4c2577126e48b3c9b3b3a40f5b157bbe35704741..a24aeea7ae32bfc9b58a8fe5c2b024fb3d46f2e7 100644 --- a/clang/lib/CodeGen/CGCall.cpp +++ b/clang/lib/CodeGen/CGCall.cpp @@ -5608,6 +5608,10 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo, BundleList); EmitBlock(Cont); } + if (CI->getCalledFunction() && CI->getCalledFunction()->hasName() && + CI->getCalledFunction()->getName().startswith("_Z4sqrt")) { + SetSqrtFPAccuracy(CI); + } if (callOrInvoke) *callOrInvoke = CI; diff --git a/clang/lib/CodeGen/CGCoroutine.cpp b/clang/lib/CodeGen/CGCoroutine.cpp index aaf122c0f83bc47c6d39f3dc16092fe3601be1b3..888d30bfb3e1d6a45f1d9e57ba84257dd7f16cfd 100644 --- a/clang/lib/CodeGen/CGCoroutine.cpp +++ b/clang/lib/CodeGen/CGCoroutine.cpp @@ -129,14 +129,48 @@ static SmallString<32> buildSuspendPrefixStr(CGCoroData &Coro, AwaitKind Kind) { return Prefix; } -static bool memberCallExpressionCanThrow(const Expr *E) { - if (const auto *CE = dyn_cast(E)) - if (const auto *Proto = - CE->getMethodDecl()->getType()->getAs()) - if (isNoexceptExceptionSpec(Proto->getExceptionSpecType()) && - Proto->canThrow() == CT_Cannot) - return false; - return true; +// Check if function can throw based on prototype noexcept, also works for +// destructors which are implicitly noexcept but can be marked noexcept(false). +static bool FunctionCanThrow(const FunctionDecl *D) { + const auto *Proto = D->getType()->getAs(); + if (!Proto) { + // Function proto is not found, we conservatively assume throwing. + return true; + } + return !isNoexceptExceptionSpec(Proto->getExceptionSpecType()) || + Proto->canThrow() != CT_Cannot; +} + +static bool ResumeStmtCanThrow(const Stmt *S) { + if (const auto *CE = dyn_cast(S)) { + const auto *Callee = CE->getDirectCallee(); + if (!Callee) + // We don't have direct callee. Conservatively assume throwing. + return true; + + if (FunctionCanThrow(Callee)) + return true; + + // Fall through to visit the children. + } + + if (const auto *TE = dyn_cast(S)) { + // Special handling of CXXBindTemporaryExpr here as calling of Dtor of the + // temporary is not part of `children()` as covered in the fall through. + // We need to mark entire statement as throwing if the destructor of the + // temporary throws. + const auto *Dtor = TE->getTemporary()->getDestructor(); + if (FunctionCanThrow(Dtor)) + return true; + + // Fall through to visit the children. + } + + for (const auto *child : S->children()) + if (ResumeStmtCanThrow(child)) + return true; + + return false; } // Emit suspend expression which roughly looks like: @@ -233,7 +267,7 @@ static LValueOrRValue emitSuspendExpression(CodeGenFunction &CGF, CGCoroData &Co // is marked as 'noexcept', we avoid generating this additional IR. CXXTryStmt *TryStmt = nullptr; if (Coro.ExceptionHandler && Kind == AwaitKind::Init && - memberCallExpressionCanThrow(S.getResumeExpr())) { + ResumeStmtCanThrow(S.getResumeExpr())) { Coro.ResumeEHVar = CGF.CreateTempAlloca(Builder.getInt1Ty(), Prefix + Twine("resume.eh")); Builder.CreateFlagStore(true, Coro.ResumeEHVar); diff --git a/clang/lib/CodeGen/CGDebugInfo.cpp b/clang/lib/CodeGen/CGDebugInfo.cpp index f1f4a1b79b7bcf48f9d2aa742f5e8e7ff1635a4d..602e325e560f98d465c4417f10cddb6878eed87a 100644 --- a/clang/lib/CodeGen/CGDebugInfo.cpp +++ b/clang/lib/CodeGen/CGDebugInfo.cpp @@ -69,6 +69,29 @@ static uint32_t getDeclAlignIfRequired(const Decl *D, const ASTContext &Ctx) { return D->hasAttr() ? D->getMaxAlignment() : 0; } +/// Given a VarDecl corresponding to either the definition or +/// declaration of a C++ static data member, if it has a constant +/// initializer and is evaluatable, return the evaluated value. +/// Returns std::nullopt otherwise. +static std::optional +evaluateConstantInitializer(const clang::VarDecl *VD, + const clang::ASTContext &Ctx) { + assert(VD != nullptr); + + if (!VD->isStaticDataMember()) + return std::nullopt; + + if (!VD->isUsableInConstantExpressions(Ctx)) + return std::nullopt; + + auto const *InitExpr = VD->getAnyInitializer(); + Expr::EvalResult Result; + if (!InitExpr->EvaluateAsConstantExpr(Result, Ctx)) + return std::nullopt; + + return Result.Val; +} + CGDebugInfo::CGDebugInfo(CodeGenModule &CGM) : CGM(CGM), DebugKind(CGM.getCodeGenOpts().getDebugInfo()), DebugTypeExtRefs(CGM.getCodeGenOpts().DebugTypeExtRefs), @@ -5610,14 +5633,11 @@ void CGDebugInfo::EmitGlobalVariable(const VarDecl *VD) { if (VD->hasAttr()) return; - if (!VD->hasInit()) - return; - const auto CacheIt = DeclCache.find(VD); if (CacheIt != DeclCache.end()) return; - auto const *InitVal = VD->evaluateValue(); + const auto InitVal = evaluateConstantInitializer(VD, CGM.getContext()); if (!InitVal) return; diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp index a75f630e1a4c767d7a5bb2427fbcb20616e0886c..69cf7f76be9a7091742e3498c1a904ec838eafe5 100644 --- a/clang/lib/CodeGen/CGExpr.cpp +++ b/clang/lib/CodeGen/CGExpr.cpp @@ -2158,6 +2158,14 @@ RValue CodeGenFunction::EmitLoadOfExtVectorElementLValue(LValue LV) { llvm::Value *Vec = Builder.CreateLoad(LV.getExtVectorAddress(), LV.isVolatileQualified()); + // HLSL allows treating scalars as one-element vectors. Converting the scalar + // IR value to a vector here allows the rest of codegen to behave as normal. + if (getLangOpts().HLSL && !Vec->getType()->isVectorTy()) { + llvm::Type *DstTy = llvm::FixedVectorType::get(Vec->getType(), 1); + llvm::Value *Zero = llvm::Constant::getNullValue(CGM.Int64Ty); + Vec = Builder.CreateInsertElement(DstTy, Vec, Zero, "cast.splat"); + } + const llvm::Constant *Elts = LV.getExtVectorElts(); // If the result of the expression is a non-vector type, we must be extracting @@ -2427,10 +2435,20 @@ void CodeGenFunction::EmitStoreThroughBitfieldLValue(RValue Src, LValue Dst, void CodeGenFunction::EmitStoreThroughExtVectorComponentLValue(RValue Src, LValue Dst) { + // HLSL allows storing to scalar values through ExtVector component LValues. + // To support this we need to handle the case where the destination address is + // a scalar. + Address DstAddr = Dst.getExtVectorAddress(); + if (!DstAddr.getElementType()->isVectorTy()) { + assert(!Dst.getType()->isVectorType() && + "this should only occur for non-vector l-values"); + Builder.CreateStore(Src.getScalarVal(), DstAddr, Dst.isVolatileQualified()); + return; + } + // This access turns into a read/modify/write of the vector. Load the input // value now. - llvm::Value *Vec = Builder.CreateLoad(Dst.getExtVectorAddress(), - Dst.isVolatileQualified()); + llvm::Value *Vec = Builder.CreateLoad(DstAddr, Dst.isVolatileQualified()); const llvm::Constant *Elts = Dst.getExtVectorElts(); llvm::Value *SrcVal = Src.getScalarVal(); @@ -2478,7 +2496,8 @@ void CodeGenFunction::EmitStoreThroughExtVectorComponentLValue(RValue Src, llvm_unreachable("unexpected shorten vector length"); } } else { - // If the Src is a scalar (not a vector) it must be updating one element. + // If the Src is a scalar (not a vector), and the target is a vector it must + // be updating one element. unsigned InIdx = getAccessedFieldNo(0, Elts); llvm::Value *Elt = llvm::ConstantInt::get(SizeTy, InIdx); Vec = Builder.CreateInsertElement(Vec, SrcVal, Elt); @@ -3038,9 +3057,20 @@ LValue CodeGenFunction::EmitDeclRefLValue(const DeclRefExpr *E) { return MakeAddrLValue(CGM.GetAddrOfMSGuidDecl(GD), T, AlignmentSource::Decl); - if (const auto *TPO = dyn_cast(ND)) - return MakeAddrLValue(CGM.GetAddrOfTemplateParamObject(TPO), T, - AlignmentSource::Decl); + if (const auto *TPO = dyn_cast(ND)) { + auto ATPO = CGM.GetAddrOfTemplateParamObject(TPO); + auto AS = getLangASFromTargetAS(ATPO.getAddressSpace()); + + if (AS != T.getAddressSpace()) { + auto TargetAS = getContext().getTargetAddressSpace(T.getAddressSpace()); + auto PtrTy = ATPO.getElementType()->getPointerTo(TargetAS); + auto ASC = getTargetHooks().performAddrSpaceCast( + CGM, ATPO.getPointer(), AS, T.getAddressSpace(), PtrTy); + ATPO = ConstantAddress(ASC, ATPO.getElementType(), ATPO.getAlignment()); + } + + return MakeAddrLValue(ATPO, T, AlignmentSource::Decl); + } llvm_unreachable("Unhandled DeclRefExpr"); } @@ -3803,6 +3833,33 @@ static QualType getFixedSizeElementType(const ASTContext &ctx, return eltType; } +static bool hasBPFPreserveStaticOffset(const RecordDecl *D) { + return D && D->hasAttr(); +} + +static bool hasBPFPreserveStaticOffset(const Expr *E) { + if (!E) + return false; + QualType PointeeType = E->getType()->getPointeeType(); + if (PointeeType.isNull()) + return false; + if (const auto *BaseDecl = PointeeType->getAsRecordDecl()) + return hasBPFPreserveStaticOffset(BaseDecl); + return false; +} + +// Wraps Addr with a call to llvm.preserve.static.offset intrinsic. +static Address wrapWithBPFPreserveStaticOffset(CodeGenFunction &CGF, + Address &Addr) { + if (!CGF.getTarget().getTriple().isBPF()) + return Addr; + + llvm::Function *Fn = + CGF.CGM.getIntrinsic(llvm::Intrinsic::preserve_static_offset); + llvm::CallInst *Call = CGF.Builder.CreateCall(Fn, {Addr.getPointer()}); + return Address(Call, Addr.getElementType(), Addr.getAlignment()); +} + /// Given an array base, check whether its member access belongs to a record /// with preserve_access_index attribute or not. static bool IsPreserveAIArrayBase(CodeGenFunction &CGF, const Expr *ArrayBase) { @@ -3864,6 +3921,9 @@ static Address emitArraySubscriptGEP(CodeGenFunction &CGF, Address addr, CharUnits eltAlign = getArrayElementAlign(addr.getAlignment(), indices.back(), eltSize); + if (hasBPFPreserveStaticOffset(Base)) + addr = wrapWithBPFPreserveStaticOffset(CGF, addr); + llvm::Value *eltPtr; auto LastIndex = dyn_cast(indices.back()); if (!LastIndex || @@ -4492,6 +4552,8 @@ LValue CodeGenFunction::EmitLValueForField(LValue base, Address Addr = base.getAddress(*this); unsigned Idx = RL.getLLVMFieldNo(field); const RecordDecl *rec = field->getParent(); + if (hasBPFPreserveStaticOffset(rec)) + Addr = wrapWithBPFPreserveStaticOffset(*this, Addr); if (!UseVolatile) { if (!IsInPreservedAIRegion && (!getDebugInfo() || !rec->hasAttr())) { @@ -4564,6 +4626,8 @@ LValue CodeGenFunction::EmitLValueForField(LValue base, } Address addr = base.getAddress(*this); + if (hasBPFPreserveStaticOffset(rec)) + addr = wrapWithBPFPreserveStaticOffset(*this, addr); if (auto *ClassDef = dyn_cast(rec)) { if (CGM.getCodeGenOpts().StrictVTablePointers && ClassDef->isDynamicClass()) { @@ -4868,7 +4932,6 @@ LValue CodeGenFunction::EmitCastLValue(const CastExpr *E) { case CK_IntegralToPointer: case CK_PointerToIntegral: case CK_PointerToBoolean: - case CK_VectorSplat: case CK_IntegralCast: case CK_BooleanToSignedIntegral: case CK_IntegralToBoolean: @@ -5033,6 +5096,13 @@ LValue CodeGenFunction::EmitCastLValue(const CastExpr *E) { } case CK_ZeroToOCLOpaqueType: llvm_unreachable("NULL to OpenCL opaque type lvalue cast is not valid"); + + case CK_VectorSplat: { + // LValue results of vector splats are only supported in HLSL. + if (!getLangOpts().HLSL) + return EmitUnsupportedLValue(E, "unexpected cast lvalue"); + return EmitLValue(E->getSubExpr()); + } } llvm_unreachable("Unhandled lvalue cast kind?"); diff --git a/clang/lib/CodeGen/CGExprScalar.cpp b/clang/lib/CodeGen/CGExprScalar.cpp index 378437364767f693a072fe90a6957862efbc4743..41ad2ddac30d2d3b45758fde246a4fa83891011e 100644 --- a/clang/lib/CodeGen/CGExprScalar.cpp +++ b/clang/lib/CodeGen/CGExprScalar.cpp @@ -2571,7 +2571,7 @@ ScalarExprEmitter::EmitScalarPrePostIncDec(const UnaryOperator *E, LValue LV, // For atomic bool increment, we just store true and return it for // preincrement, do an atomic swap with true for postincrement return Builder.CreateAtomicRMW( - llvm::AtomicRMWInst::Xchg, LV.getPointer(CGF), True, + llvm::AtomicRMWInst::Xchg, LV.getAddress(CGF), True, llvm::AtomicOrdering::SequentiallyConsistent); } // Special case for atomic increment / decrement on integers, emit @@ -2589,7 +2589,7 @@ ScalarExprEmitter::EmitScalarPrePostIncDec(const UnaryOperator *E, LValue LV, llvm::Value *amt = CGF.EmitToMemory( llvm::ConstantInt::get(ConvertType(type), 1, true), type); llvm::Value *old = - Builder.CreateAtomicRMW(aop, LV.getPointer(CGF), amt, + Builder.CreateAtomicRMW(aop, LV.getAddress(CGF), amt, llvm::AtomicOrdering::SequentiallyConsistent); return isPre ? Builder.CreateBinOp(op, old, amt) : old; } @@ -3314,7 +3314,7 @@ LValue ScalarExprEmitter::EmitCompoundAssignLValue( E->getExprLoc()), LHSTy); Value *OldVal = Builder.CreateAtomicRMW( - AtomicOp, LHSLV.getPointer(CGF), Amt, + AtomicOp, LHSLV.getAddress(CGF), Amt, llvm::AtomicOrdering::SequentiallyConsistent); // Since operation is atomic, the result type is guaranteed to be the diff --git a/clang/lib/CodeGen/CGObjC.cpp b/clang/lib/CodeGen/CGObjC.cpp index fff89c8939a55a4e057df6f60d00b01e447d614c..acc85165a470be7327e36b6c3865e2aafd154490 100644 --- a/clang/lib/CodeGen/CGObjC.cpp +++ b/clang/lib/CodeGen/CGObjC.cpp @@ -827,11 +827,8 @@ static void emitStructGetterCall(CodeGenFunction &CGF, ObjCIvarDecl *ivar, // sizeof (Type of Ivar), isAtomic, false); CallArgList args; - llvm::Value *dest = - CGF.Builder.CreateBitCast(CGF.ReturnValue.getPointer(), CGF.VoidPtrTy); + llvm::Value *dest = CGF.ReturnValue.getPointer(); args.add(RValue::get(dest), Context.VoidPtrTy); - - src = CGF.Builder.CreateBitCast(src, CGF.VoidPtrTy); args.add(RValue::get(src), Context.VoidPtrTy); CharUnits size = CGF.getContext().getTypeSizeInChars(ivar->getType()); @@ -1098,7 +1095,6 @@ static void emitCPPObjectAtomicGetterCall(CodeGenFunction &CGF, llvm::Value *ivarAddr = CGF.EmitLValueForIvar(CGF.TypeOfSelfObject(), CGF.LoadObjCSelf(), ivar, 0) .getPointer(CGF); - ivarAddr = CGF.Builder.CreateBitCast(ivarAddr, CGF.Int8PtrTy); args.add(RValue::get(ivarAddr), CGF.getContext().VoidPtrTy); // Third argument is the helper function. @@ -1340,7 +1336,6 @@ static void emitStructSetterCall(CodeGenFunction &CGF, ObjCMethodDecl *OMD, argVar->getType().getNonReferenceType(), VK_LValue, SourceLocation()); llvm::Value *argAddr = CGF.EmitLValue(&argRef).getPointer(CGF); - argAddr = CGF.Builder.CreateBitCast(argAddr, CGF.Int8PtrTy); args.add(RValue::get(argAddr), CGF.getContext().VoidPtrTy); // The third argument is the sizeof the type. @@ -1377,7 +1372,6 @@ static void emitCPPObjectAtomicSetterCall(CodeGenFunction &CGF, llvm::Value *ivarAddr = CGF.EmitLValueForIvar(CGF.TypeOfSelfObject(), CGF.LoadObjCSelf(), ivar, 0) .getPointer(CGF); - ivarAddr = CGF.Builder.CreateBitCast(ivarAddr, CGF.Int8PtrTy); args.add(RValue::get(ivarAddr), CGF.getContext().VoidPtrTy); // The second argument is the address of the parameter variable. @@ -1386,7 +1380,6 @@ static void emitCPPObjectAtomicSetterCall(CodeGenFunction &CGF, argVar->getType().getNonReferenceType(), VK_LValue, SourceLocation()); llvm::Value *argAddr = CGF.EmitLValue(&argRef).getPointer(CGF); - argAddr = CGF.Builder.CreateBitCast(argAddr, CGF.Int8PtrTy); args.add(RValue::get(argAddr), CGF.getContext().VoidPtrTy); // Third argument is the helper function. @@ -3685,7 +3678,6 @@ void CodeGenFunction::EmitExtendGCLifetime(llvm::Value *object) { /* constraints */ "r", /* side effects */ true); - object = Builder.CreateBitCast(object, VoidPtrTy); EmitNounwindRuntimeCall(extender, object); } diff --git a/clang/lib/CodeGen/CGOpenMPRuntime.cpp b/clang/lib/CodeGen/CGOpenMPRuntime.cpp index d2be8141a3a4b31a95af680fa544ac0132fae5ce..55648963df36ab97811fecb15502f5a3cc85f421 100644 --- a/clang/lib/CodeGen/CGOpenMPRuntime.cpp +++ b/clang/lib/CodeGen/CGOpenMPRuntime.cpp @@ -1441,6 +1441,7 @@ llvm::Value *CGOpenMPRuntime::getThreadID(CodeGenFunction &CGF, setLocThreadIdInsertPt(CGF); CGBuilderTy::InsertPointGuard IPG(CGF.Builder); CGF.Builder.SetInsertPoint(Elem.second.ServiceInsertPt); + auto DL = ApplyDebugLocation::CreateDefaultArtificial(CGF, Loc); llvm::CallInst *Call = CGF.Builder.CreateCall( OMPBuilder.getOrCreateRuntimeFunction(CGM.getModule(), OMPRTL___kmpc_global_thread_num), diff --git a/clang/lib/CodeGen/CGOpenMPRuntimeGPU.cpp b/clang/lib/CodeGen/CGOpenMPRuntimeGPU.cpp index 7ddc67e8a04ab64f7a9f1b5c28e57c34f984a00f..293ccaa3413cdf372d72a02ff4a93f4c2a28b947 100644 --- a/clang/lib/CodeGen/CGOpenMPRuntimeGPU.cpp +++ b/clang/lib/CodeGen/CGOpenMPRuntimeGPU.cpp @@ -804,7 +804,9 @@ void CGOpenMPRuntimeGPU::emitKernelDeinit(CodeGenFunction &CGF, CGM.getTypes().ConvertTypeForMem(StaticTy); const auto &DL = CGM.getModule().getDataLayout(); uint64_t ReductionDataSize = - DL.getTypeAllocSize(LLVMReductionsBufferTy).getFixedValue(); + TeamsReductions.empty() + ? 0 + : DL.getTypeAllocSize(LLVMReductionsBufferTy).getFixedValue(); CGBuilderTy &Bld = CGF.Builder; OMPBuilder.createTargetDeinit(Bld, ReductionDataSize, C.getLangOpts().OpenMPCUDAReductionBufNum); @@ -3015,11 +3017,7 @@ CGOpenMPRuntimeGPU::getParameterAddress(CodeGenFunction &CGF, QualType TargetTy = TargetParam->getType(); llvm::Value *TargetAddr = CGF.EmitLoadOfScalar(LocalAddr, /*Volatile=*/false, TargetTy, SourceLocation()); - // First cast to generic. - TargetAddr = CGF.Builder.CreatePointerBitCastOrAddrSpaceCast( - TargetAddr, - llvm::PointerType::get(CGF.getLLVMContext(), /*AddrSpace=*/0)); - // Cast from generic to native address space. + // Cast to native address space. TargetAddr = CGF.Builder.CreatePointerBitCastOrAddrSpaceCast( TargetAddr, llvm::PointerType::get(CGF.getLLVMContext(), NativePointeeAddrSpace)); @@ -3046,11 +3044,8 @@ void CGOpenMPRuntimeGPU::emitOutlinedFunctionCall( TargetArgs.emplace_back(NativeArg); continue; } - llvm::Value *TargetArg = CGF.Builder.CreatePointerBitCastOrAddrSpaceCast( - NativeArg, - llvm::PointerType::get(CGF.getLLVMContext(), /*AddrSpace*/ 0)); TargetArgs.emplace_back( - CGF.Builder.CreatePointerBitCastOrAddrSpaceCast(TargetArg, TargetType)); + CGF.Builder.CreatePointerBitCastOrAddrSpaceCast(NativeArg, TargetType)); } CGOpenMPRuntime::emitOutlinedFunctionCall(CGF, Loc, OutlinedFn, TargetArgs); } diff --git a/clang/lib/CodeGen/CGStmtOpenMP.cpp b/clang/lib/CodeGen/CGStmtOpenMP.cpp index 478d6dbf9ca81d9d55971297411d32d0e028a217..ed426098ac691564edd4d2d23d7ffdee5e07c1b0 100644 --- a/clang/lib/CodeGen/CGStmtOpenMP.cpp +++ b/clang/lib/CodeGen/CGStmtOpenMP.cpp @@ -667,11 +667,7 @@ CodeGenFunction::GenerateOpenMPCapturedStmtFunction(const CapturedStmt &S, I->second.first ? I->second.first->getType() : Arg->getType(), AlignmentSource::Decl); if (LV.getType()->isAnyComplexType()) - LV.setAddress(WrapperCGF.Builder.CreatePointerBitCastOrAddrSpaceCast( - LV.getAddress(WrapperCGF), - PI->getType()->getPointerTo( - LV.getAddress(WrapperCGF).getAddressSpace()), - PI->getType())); + LV.setAddress(LV.getAddress(WrapperCGF).withElementType(PI->getType())); CallArg = WrapperCGF.EmitLoadOfScalar(LV, S.getBeginLoc()); } else { auto EI = VLASizes.find(Arg); @@ -2562,9 +2558,9 @@ static void emitOMPSimdRegion(CodeGenFunction &CGF, const OMPLoopDirective &S, (void)CGF.EmitOMPLinearClauseInit(S); { CodeGenFunction::OMPPrivateScope LoopScope(CGF); + CGF.EmitOMPPrivateClause(S, LoopScope); CGF.EmitOMPPrivateLoopCounters(S, LoopScope); CGF.EmitOMPLinearClause(S, LoopScope); - CGF.EmitOMPPrivateClause(S, LoopScope); CGF.EmitOMPReductionClauseInit(S, LoopScope); CGOpenMPRuntime::LastprivateConditionalRAII LPCRegion( CGF, S, CGF.EmitLValue(S.getIterationVariable())); @@ -4828,8 +4824,6 @@ void CodeGenFunction::EmitOMPTaskBasedDirective( } auto *CopyFnTy = llvm::FunctionType::get(CGF.Builder.getVoidTy(), ParamTypes, /*isVarArg=*/false); - CopyFn = CGF.Builder.CreatePointerBitCastOrAddrSpaceCast( - CopyFn, CopyFnTy->getPointerTo()); CGF.CGM.getOpenMPRuntime().emitOutlinedFunctionCall( CGF, S.getBeginLoc(), {CopyFnTy, CopyFn}, CallArgs); for (const auto &Pair : LastprivateDstsOrigs) { @@ -5115,8 +5109,6 @@ void CodeGenFunction::EmitOMPTargetTaskBasedDirective( } auto *CopyFnTy = llvm::FunctionType::get(CGF.Builder.getVoidTy(), ParamTypes, /*isVarArg=*/false); - CopyFn = CGF.Builder.CreatePointerBitCastOrAddrSpaceCast( - CopyFn, CopyFnTy->getPointerTo()); CGF.CGM.getOpenMPRuntime().emitOutlinedFunctionCall( CGF, S.getBeginLoc(), {CopyFnTy, CopyFn}, CallArgs); for (const auto &Pair : PrivatePtrs) { @@ -6214,7 +6206,7 @@ static std::pair emitOMPAtomicRMW(CodeGenFunction &CGF, LValue X, X.getAddress(CGF).getElementType()); } llvm::Value *Res = - CGF.Builder.CreateAtomicRMW(RMWOp, X.getPointer(CGF), UpdateVal, AO); + CGF.Builder.CreateAtomicRMW(RMWOp, X.getAddress(CGF), UpdateVal, AO); return std::make_pair(true, RValue::get(Res)); } diff --git a/clang/lib/CodeGen/CodeGenAction.cpp b/clang/lib/CodeGen/CodeGenAction.cpp index a31a271ed77d1ca9bc11fa7440e3b10e3e19df46..bb6b1a3bc228cf9889bfc3655c48884c20410acb 100644 --- a/clang/lib/CodeGen/CodeGenAction.cpp +++ b/clang/lib/CodeGen/CodeGenAction.cpp @@ -57,6 +57,10 @@ using namespace llvm; #define DEBUG_TYPE "codegenaction" +namespace llvm { +extern cl::opt ClRelinkBuiltinBitcodePostop; +} + namespace clang { class BackendConsumer; class ClangDiagnosticHandler final : public DiagnosticHandler { @@ -251,32 +255,37 @@ bool BackendConsumer::LinkInModules(llvm::Module *M, bool ShouldLinkFiles) { } CurLinkModule = LM.Module.get(); - - // TODO: If CloneModule() is updated to support cloning of unmaterialized - // modules, we can remove this bool Err; - if (Error E = CurLinkModule->materializeAll()) - return false; + + auto DoLink = [&](auto &Mod) { + if (LM.Internalize) { + Err = Linker::linkModules( + *M, std::move(Mod), LM.LinkFlags, + [](llvm::Module &M, const llvm::StringSet<> &GVS) { + internalizeModule(M, [&GVS](const llvm::GlobalValue &GV) { + return !GV.hasName() || (GVS.count(GV.getName()) == 0); + }); + }); + } else + Err = Linker::linkModules(*M, std::move(Mod), LM.LinkFlags); + }; // Create a Clone to move to the linker, which preserves the original // linking modules, allowing them to be linked again in the future - // TODO: Add a ShouldCleanup option to make Cloning optional. When - // set, we can pass the original modules to the linker for cleanup - std::unique_ptr Clone = llvm::CloneModule(*LM.Module); - - if (LM.Internalize) { - Err = Linker::linkModules( - *M, std::move(Clone), LM.LinkFlags, - [](llvm::Module &M, const llvm::StringSet<> &GVS) { - internalizeModule(M, [&GVS](const llvm::GlobalValue &GV) { - return !GV.hasName() || (GVS.count(GV.getName()) == 0); - }); - }); - } else - Err = Linker::linkModules(*M, std::move(Clone), LM.LinkFlags); + if (ClRelinkBuiltinBitcodePostop) { + // TODO: If CloneModule() is updated to support cloning of unmaterialized + // modules, we can remove this + if (Error E = CurLinkModule->materializeAll()) + return false; - if (Err) - return true; + std::unique_ptr Clone = llvm::CloneModule(*LM.Module); + + DoLink(Clone); + } + // Otherwise we can link (and clean up) the original modules + else { + DoLink(LM.Module); + } } return false; // success diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp index 41ff4a992f194ae50e3e61d142d49bd7ae01c75c..dea58a7ff4146a65ec9267308e2e13e83114ee29 100644 --- a/clang/lib/CodeGen/CodeGenModule.cpp +++ b/clang/lib/CodeGen/CodeGenModule.cpp @@ -699,6 +699,7 @@ void CodeGenModule::checkAliases() { void CodeGenModule::clear() { DeferredDeclsToEmit.clear(); EmittedDeferredDecls.clear(); + DeferredAnnotations.clear(); if (OpenMPRuntime) OpenMPRuntime->clear(); } @@ -847,7 +848,7 @@ void CodeGenModule::Release() { // Emit amdgpu_code_object_version module flag, which is code object version // times 100. if (getTarget().getTargetOpts().CodeObjectVersion != - TargetOptions::COV_None) { + llvm::CodeObjectVersionKind::COV_None) { getModule().addModuleFlag(llvm::Module::Error, "amdgpu_code_object_version", getTarget().getTargetOpts().CodeObjectVersion); @@ -1120,6 +1121,15 @@ void CodeGenModule::Release() { "sign-return-address-with-bkey", 1); } + if (CodeGenOpts.StackClashProtector) + getModule().addModuleFlag( + llvm::Module::Override, "probe-stack", + llvm::MDString::get(TheModule.getContext(), "inline-asm")); + + if (CodeGenOpts.StackProbeSize && CodeGenOpts.StackProbeSize != 4096) + getModule().addModuleFlag(llvm::Module::Min, "stack-probe-size", + CodeGenOpts.StackProbeSize); + if (!CodeGenOpts.MemoryProfileOutput.empty()) { llvm::LLVMContext &Ctx = TheModule.getContext(); getModule().addModuleFlag( @@ -2334,6 +2344,10 @@ void CodeGenModule::SetLLVMFunctionAttributesForDefinition(const Decl *D, if (CodeGenOpts.StackClashProtector) B.addAttribute("probe-stack", "inline-asm"); + if (CodeGenOpts.StackProbeSize && CodeGenOpts.StackProbeSize != 4096) + B.addAttribute("stack-probe-size", + std::to_string(CodeGenOpts.StackProbeSize)); + if (!hasUnwindExceptions(LangOpts)) B.addAttribute(llvm::Attribute::NoUnwind); @@ -3165,6 +3179,13 @@ void CodeGenModule::EmitVTablesOpportunistically() { } void CodeGenModule::EmitGlobalAnnotations() { + for (const auto& [MangledName, VD] : DeferredAnnotations) { + llvm::GlobalValue *GV = GetGlobalValue(MangledName); + if (GV) + AddGlobalAnnotations(VD, GV); + } + DeferredAnnotations.clear(); + if (Annotations.empty()) return; @@ -3678,6 +3699,14 @@ void CodeGenModule::EmitGlobal(GlobalDecl GD) { // Ignore declarations, they will be emitted on their first use. if (const auto *FD = dyn_cast(Global)) { + // Update deferred annotations with the latest declaration if the function + // function was already used or defined. + if (FD->hasAttr()) { + StringRef MangledName = getMangledName(GD); + if (GetGlobalValue(MangledName)) + DeferredAnnotations[MangledName] = FD; + } + // Forward declarations are emitted lazily on first use. if (!FD->doesThisDeclarationHaveABody()) { if (!FD->doesDeclarationForceExternallyVisibleDefinition()) @@ -4462,6 +4491,11 @@ llvm::Constant *CodeGenModule::GetOrCreateLLVMFunction( llvm::Function::Create(FTy, llvm::Function::ExternalLinkage, Entry ? StringRef() : MangledName, &getModule()); + // Store the declaration associated with this function so it is potentially + // updated by further declarations or definitions and emitted at the end. + if (D && D->hasAttr()) + DeferredAnnotations[MangledName] = cast(D); + // If we already created a function with the same mangled name (but different // type) before, take its name and add it to the list of functions to be // replaced with F at the end of CodeGen. @@ -5748,8 +5782,6 @@ void CodeGenModule::EmitGlobalFunctionDefinition(GlobalDecl GD, AddGlobalCtor(Fn, CA->getPriority()); if (const DestructorAttr *DA = D->getAttr()) AddGlobalDtor(Fn, DA->getPriority(), true); - if (D->hasAttr()) - AddGlobalAnnotations(D, Fn); if (getLangOpts().OpenMP && D->hasAttr()) getOpenMPRuntime().emitDeclareTargetFunction(D, GV); } diff --git a/clang/lib/CodeGen/CodeGenModule.h b/clang/lib/CodeGen/CodeGenModule.h index e81edc979c208b1aaffd204f9b916b985d1aaeec..ec34680fd3f7e6e40b50b4cff39b19877aaf051a 100644 --- a/clang/lib/CodeGen/CodeGenModule.h +++ b/clang/lib/CodeGen/CodeGenModule.h @@ -431,6 +431,10 @@ private: /// Global annotations. std::vector Annotations; + // Store deferred function annotations so they can be emitted at the end with + // most up to date ValueDecl that will have all the inherited annotations. + llvm::DenseMap DeferredAnnotations; + /// Map used to get unique annotation strings. llvm::StringMap AnnotationStrings; diff --git a/clang/lib/CodeGen/CodeGenTBAA.cpp b/clang/lib/CodeGen/CodeGenTBAA.cpp index 8705d3d65f1a573bb7493c92c083376e36044f09..5906b14dd93cf088bf9c25443d70c9a531416a00 100644 --- a/clang/lib/CodeGen/CodeGenTBAA.cpp +++ b/clang/lib/CodeGen/CodeGenTBAA.cpp @@ -342,7 +342,7 @@ llvm::MDNode *CodeGenTBAA::getBaseTypeInfoHelper(const Type *Ty) { // field. Virtual bases are more complex and omitted, but avoid an // incomplete view for NewStructPathTBAA. if (CodeGenOpts.NewStructPathTBAA && CXXRD->getNumVBases() != 0) - return BaseTypeMetadataCache[Ty] = nullptr; + return nullptr; for (const CXXBaseSpecifier &B : CXXRD->bases()) { if (B.isVirtual()) continue; @@ -354,7 +354,7 @@ llvm::MDNode *CodeGenTBAA::getBaseTypeInfoHelper(const Type *Ty) { ? getBaseTypeInfo(BaseQTy) : getTypeInfo(BaseQTy); if (!TypeNode) - return BaseTypeMetadataCache[Ty] = nullptr; + return nullptr; uint64_t Offset = Layout.getBaseClassOffset(BaseRD).getQuantity(); uint64_t Size = Context.getASTRecordLayout(BaseRD).getDataSize().getQuantity(); @@ -378,7 +378,7 @@ llvm::MDNode *CodeGenTBAA::getBaseTypeInfoHelper(const Type *Ty) { llvm::MDNode *TypeNode = isValidBaseType(FieldQTy) ? getBaseTypeInfo(FieldQTy) : getTypeInfo(FieldQTy); if (!TypeNode) - return BaseTypeMetadataCache[Ty] = nullptr; + return nullptr; uint64_t BitOffset = Layout.getFieldOffset(Field->getFieldIndex()); uint64_t Offset = Context.toCharUnitsFromBits(BitOffset).getQuantity(); @@ -418,14 +418,20 @@ llvm::MDNode *CodeGenTBAA::getBaseTypeInfo(QualType QTy) { return nullptr; const Type *Ty = Context.getCanonicalType(QTy).getTypePtr(); - if (llvm::MDNode *N = BaseTypeMetadataCache[Ty]) - return N; - // Note that the following helper call is allowed to add new nodes to the - // cache, which invalidates all its previously obtained iterators. So we - // first generate the node for the type and then add that node to the cache. + // nullptr is a valid value in the cache, so use find rather than [] + auto I = BaseTypeMetadataCache.find(Ty); + if (I != BaseTypeMetadataCache.end()) + return I->second; + + // First calculate the metadata, before recomputing the insertion point, as + // the helper can recursively call us. llvm::MDNode *TypeNode = getBaseTypeInfoHelper(Ty); - return BaseTypeMetadataCache[Ty] = TypeNode; + LLVM_ATTRIBUTE_UNUSED auto inserted = + BaseTypeMetadataCache.insert({Ty, TypeNode}); + assert(inserted.second && "BaseType metadata was already inserted"); + + return TypeNode; } llvm::MDNode *CodeGenTBAA::getAccessTagInfo(TBAAAccessInfo Info) { diff --git a/clang/lib/CodeGen/MicrosoftCXXABI.cpp b/clang/lib/CodeGen/MicrosoftCXXABI.cpp index 44b9acdee62648a1aa3512b170268269145f29af..172c4c937b97280d30ca5689ec58b600a852a809 100644 --- a/clang/lib/CodeGen/MicrosoftCXXABI.cpp +++ b/clang/lib/CodeGen/MicrosoftCXXABI.cpp @@ -2266,7 +2266,6 @@ MicrosoftCXXABI::performReturnAdjustment(CodeGenFunction &CGF, Address Ret, if (RA.isEmpty()) return Ret.getPointer(); - auto OrigTy = Ret.getType(); Ret = Ret.withElementType(CGF.Int8Ty); llvm::Value *V = Ret.getPointer(); @@ -2283,8 +2282,7 @@ MicrosoftCXXABI::performReturnAdjustment(CodeGenFunction &CGF, Address Ret, if (RA.NonVirtual) V = CGF.Builder.CreateConstInBoundsGEP1_32(CGF.Int8Ty, V, RA.NonVirtual); - // Cast back to the original type. - return CGF.Builder.CreateBitCast(V, OrigTy); + return V; } bool MicrosoftCXXABI::requiresArrayCookie(const CXXDeleteExpr *expr, diff --git a/clang/lib/CodeGen/Targets/AMDGPU.cpp b/clang/lib/CodeGen/Targets/AMDGPU.cpp index 4dd25213dda9fa511c540a35e532115bf996942c..b654e3f12af8d4918e96784083b7b278c1691ca5 100644 --- a/clang/lib/CodeGen/Targets/AMDGPU.cpp +++ b/clang/lib/CodeGen/Targets/AMDGPU.cpp @@ -368,7 +368,7 @@ void AMDGPUTargetCodeGenInfo::emitTargetGlobals( return; if (CGM.getTarget().getTargetOpts().CodeObjectVersion == - clang::TargetOptions::COV_None) + llvm::CodeObjectVersionKind::COV_None) return; auto *Type = llvm::IntegerType::getIntNTy(CGM.getModule().getContext(), 32); diff --git a/clang/lib/Driver/Multilib.cpp b/clang/lib/Driver/Multilib.cpp index 48a494d9fa38db524971892ee37803385e38b5b6..7681c1a3ce6756f6cfc00cc13ed56b796a830ac5 100644 --- a/clang/lib/Driver/Multilib.cpp +++ b/clang/lib/Driver/Multilib.cpp @@ -9,6 +9,7 @@ #include "clang/Driver/Multilib.h" #include "clang/Basic/LLVM.h" #include "clang/Basic/Version.h" +#include "llvm/ADT/DenseSet.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/Compiler.h" @@ -29,9 +30,10 @@ using namespace driver; using namespace llvm::sys; Multilib::Multilib(StringRef GCCSuffix, StringRef OSSuffix, - StringRef IncludeSuffix, const flags_list &Flags) + StringRef IncludeSuffix, const flags_list &Flags, + StringRef ExclusiveGroup) : GCCSuffix(GCCSuffix), OSSuffix(OSSuffix), IncludeSuffix(IncludeSuffix), - Flags(Flags) { + Flags(Flags), ExclusiveGroup(ExclusiveGroup) { assert(GCCSuffix.empty() || (StringRef(GCCSuffix).front() == '/' && GCCSuffix.size() > 1)); assert(OSSuffix.empty() || @@ -96,13 +98,37 @@ bool MultilibSet::select(const Multilib::flags_list &Flags, llvm::SmallVector &Selected) const { llvm::StringSet<> FlagSet(expandFlags(Flags)); Selected.clear(); - llvm::copy_if(Multilibs, std::back_inserter(Selected), - [&FlagSet](const Multilib &M) { - for (const std::string &F : M.flags()) - if (!FlagSet.contains(F)) - return false; - return true; - }); + + // Decide which multilibs we're going to select at all. + llvm::DenseSet ExclusiveGroupsSelected; + for (const Multilib &M : llvm::reverse(Multilibs)) { + // If this multilib doesn't match all our flags, don't select it. + if (!llvm::all_of(M.flags(), [&FlagSet](const std::string &F) { + return FlagSet.contains(F); + })) + continue; + + const std::string &group = M.exclusiveGroup(); + if (!group.empty()) { + // If this multilib has the same ExclusiveGroup as one we've already + // selected, skip it. We're iterating in reverse order, so the group + // member we've selected already is preferred. + // + // Otherwise, add the group name to the set of groups we've already + // selected a member of. + auto [It, Inserted] = ExclusiveGroupsSelected.insert(group); + if (!Inserted) + continue; + } + + // Select this multilib. + Selected.push_back(M); + } + + // We iterated in reverse order, so now put Selected back the right way + // round. + std::reverse(Selected.begin(), Selected.end()); + return !Selected.empty(); } @@ -138,10 +164,39 @@ static const VersionTuple MultilibVersionCurrent(1, 0); struct MultilibSerialization { std::string Dir; std::vector Flags; + std::string Group; +}; + +enum class MultilibGroupType { + /* + * The only group type currently supported is 'Exclusive', which indicates a + * group of multilibs of which at most one may be selected. + */ + Exclusive, + + /* + * Future possibility: a second group type indicating a set of library + * directories that are mutually _dependent_ rather than mutually exclusive: + * if you include one you must include them all. + * + * It might also be useful to allow groups to be members of other groups, so + * that a mutually exclusive group could contain a mutually dependent set of + * library directories, or vice versa. + * + * These additional features would need changes in the implementation, but + * the YAML schema is set up so they can be added without requiring changes + * in existing users' multilib.yaml files. + */ +}; + +struct MultilibGroupSerialization { + std::string Name; + MultilibGroupType Type; }; struct MultilibSetSerialization { llvm::VersionTuple MultilibVersion; + std::vector Groups; std::vector Multilibs; std::vector FlagMatchers; }; @@ -152,6 +207,7 @@ template <> struct llvm::yaml::MappingTraits { static void mapping(llvm::yaml::IO &io, MultilibSerialization &V) { io.mapRequired("Dir", V.Dir); io.mapRequired("Flags", V.Flags); + io.mapOptional("Group", V.Group); } static std::string validate(IO &io, MultilibSerialization &V) { if (StringRef(V.Dir).starts_with("/")) @@ -160,6 +216,19 @@ template <> struct llvm::yaml::MappingTraits { } }; +template <> struct llvm::yaml::ScalarEnumerationTraits { + static void enumeration(IO &io, MultilibGroupType &Val) { + io.enumCase(Val, "Exclusive", MultilibGroupType::Exclusive); + } +}; + +template <> struct llvm::yaml::MappingTraits { + static void mapping(llvm::yaml::IO &io, MultilibGroupSerialization &V) { + io.mapRequired("Name", V.Name); + io.mapRequired("Type", V.Type); + } +}; + template <> struct llvm::yaml::MappingTraits { static void mapping(llvm::yaml::IO &io, MultilibSet::FlagMatcher &M) { io.mapRequired("Match", M.Match); @@ -180,6 +249,7 @@ template <> struct llvm::yaml::MappingTraits { static void mapping(llvm::yaml::IO &io, MultilibSetSerialization &M) { io.mapRequired("MultilibVersion", M.MultilibVersion); io.mapRequired("Variants", M.Multilibs); + io.mapOptional("Groups", M.Groups); io.mapOptional("Mappings", M.FlagMatchers); } static std::string validate(IO &io, MultilibSetSerialization &M) { @@ -191,11 +261,25 @@ template <> struct llvm::yaml::MappingTraits { if (M.MultilibVersion.getMinor() > MultilibVersionCurrent.getMinor()) return "multilib version " + M.MultilibVersion.getAsString() + " is unsupported"; + for (const MultilibSerialization &Lib : M.Multilibs) { + if (!Lib.Group.empty()) { + bool Found = false; + for (const MultilibGroupSerialization &Group : M.Groups) + if (Group.Name == Lib.Group) { + Found = true; + break; + } + if (!Found) + return "multilib \"" + Lib.Dir + + "\" specifies undefined group name \"" + Lib.Group + "\""; + } + } return std::string{}; } }; LLVM_YAML_IS_SEQUENCE_VECTOR(MultilibSerialization) +LLVM_YAML_IS_SEQUENCE_VECTOR(MultilibGroupSerialization) LLVM_YAML_IS_SEQUENCE_VECTOR(MultilibSet::FlagMatcher) llvm::ErrorOr @@ -214,7 +298,11 @@ MultilibSet::parseYaml(llvm::MemoryBufferRef Input, std::string Dir; if (M.Dir != ".") Dir = "/" + M.Dir; - Multilibs.emplace_back(Dir, Dir, Dir, M.Flags); + // We transfer M.Group straight into the ExclusiveGroup parameter for the + // Multilib constructor. If we later support more than one type of group, + // we'll have to look up the group name in MS.Groups, check its type, and + // decide what to do here. + Multilibs.emplace_back(Dir, Dir, Dir, M.Flags, M.Group); } return MultilibSet(std::move(Multilibs), std::move(MS.FlagMatchers)); diff --git a/clang/lib/Driver/OffloadBundler.cpp b/clang/lib/Driver/OffloadBundler.cpp index a7bc15e87ef673b5abd16e0c498f3f64425de2b2..b1091aca5616f876d469c306ea4a37cf3efe965b 100644 --- a/clang/lib/Driver/OffloadBundler.cpp +++ b/clang/lib/Driver/OffloadBundler.cpp @@ -159,51 +159,6 @@ static std::string getDeviceLibraryFileName(StringRef BundleFileName, return Result; } -/// @brief Checks if a code object \p CodeObjectInfo is compatible with a given -/// target \p TargetInfo. -/// @link https://clang.llvm.org/docs/ClangOffloadBundler.html#bundle-entry-id -bool isCodeObjectCompatible(const OffloadTargetInfo &CodeObjectInfo, - const OffloadTargetInfo &TargetInfo) { - - // Compatible in case of exact match. - if (CodeObjectInfo == TargetInfo) { - DEBUG_WITH_TYPE("CodeObjectCompatibility", - dbgs() << "Compatible: Exact match: \t[CodeObject: " - << CodeObjectInfo.str() - << "]\t:\t[Target: " << TargetInfo.str() << "]\n"); - return true; - } - - // Incompatible if Kinds or Triples mismatch. - if (!CodeObjectInfo.isOffloadKindCompatible(TargetInfo.OffloadKind) || - !CodeObjectInfo.Triple.isCompatibleWith(TargetInfo.Triple)) { - DEBUG_WITH_TYPE( - "CodeObjectCompatibility", - dbgs() << "Incompatible: Kind/Triple mismatch \t[CodeObject: " - << CodeObjectInfo.str() << "]\t:\t[Target: " << TargetInfo.str() - << "]\n"); - return false; - } - - // Incompatible if target IDs are incompatible. - if (!clang::isCompatibleTargetID(CodeObjectInfo.TargetID, - TargetInfo.TargetID)) { - DEBUG_WITH_TYPE( - "CodeObjectCompatibility", - dbgs() << "Incompatible: target IDs are incompatible \t[CodeObject: " - << CodeObjectInfo.str() << "]\t:\t[Target: " << TargetInfo.str() - << "]\n"); - return false; - } - - DEBUG_WITH_TYPE( - "CodeObjectCompatibility", - dbgs() << "Compatible: Code Objects are compatible \t[CodeObject: " - << CodeObjectInfo.str() << "]\t:\t[Target: " << TargetInfo.str() - << "]\n"); - return true; -} - namespace { /// Generic file handler interface. class FileHandler { @@ -264,6 +219,20 @@ public: }); } + /// Get bundle IDs in \a Input in \a BundleIds. + virtual Error getBundleIDs(MemoryBuffer &Input, + std::set &BundleIds) { + if (Error Err = ReadHeader(Input)) + return Err; + return forEachBundle(Input, [&](const BundleInfo &Info) -> Error { + BundleIds.insert(Info.BundleID); + Error Err = listBundleIDsCallback(Input, Info); + if (Err) + return Err; + return Error::success(); + }); + } + /// For each bundle in \a Input, do \a Func. Error forEachBundle(MemoryBuffer &Input, std::function Func) { @@ -1112,6 +1081,99 @@ Error OffloadBundler::ListBundleIDsInFile( return FH->listBundleIDs(DecompressedInput); } +/// @brief Checks if a code object \p CodeObjectInfo is compatible with a given +/// target \p TargetInfo. +/// @link https://clang.llvm.org/docs/ClangOffloadBundler.html#bundle-entry-id +bool isCodeObjectCompatible(const OffloadTargetInfo &CodeObjectInfo, + const OffloadTargetInfo &TargetInfo) { + + // Compatible in case of exact match. + if (CodeObjectInfo == TargetInfo) { + DEBUG_WITH_TYPE("CodeObjectCompatibility", + dbgs() << "Compatible: Exact match: \t[CodeObject: " + << CodeObjectInfo.str() + << "]\t:\t[Target: " << TargetInfo.str() << "]\n"); + return true; + } + + // Incompatible if Kinds or Triples mismatch. + if (!CodeObjectInfo.isOffloadKindCompatible(TargetInfo.OffloadKind) || + !CodeObjectInfo.Triple.isCompatibleWith(TargetInfo.Triple)) { + DEBUG_WITH_TYPE( + "CodeObjectCompatibility", + dbgs() << "Incompatible: Kind/Triple mismatch \t[CodeObject: " + << CodeObjectInfo.str() << "]\t:\t[Target: " << TargetInfo.str() + << "]\n"); + return false; + } + + // Incompatible if Processors mismatch. + llvm::StringMap CodeObjectFeatureMap, TargetFeatureMap; + std::optional CodeObjectProc = clang::parseTargetID( + CodeObjectInfo.Triple, CodeObjectInfo.TargetID, &CodeObjectFeatureMap); + std::optional TargetProc = clang::parseTargetID( + TargetInfo.Triple, TargetInfo.TargetID, &TargetFeatureMap); + + // Both TargetProc and CodeObjectProc can't be empty here. + if (!TargetProc || !CodeObjectProc || + CodeObjectProc.value() != TargetProc.value()) { + DEBUG_WITH_TYPE("CodeObjectCompatibility", + dbgs() << "Incompatible: Processor mismatch \t[CodeObject: " + << CodeObjectInfo.str() + << "]\t:\t[Target: " << TargetInfo.str() << "]\n"); + return false; + } + + // Incompatible if CodeObject has more features than Target, irrespective of + // type or sign of features. + if (CodeObjectFeatureMap.getNumItems() > TargetFeatureMap.getNumItems()) { + DEBUG_WITH_TYPE("CodeObjectCompatibility", + dbgs() << "Incompatible: CodeObject has more features " + "than target \t[CodeObject: " + << CodeObjectInfo.str() + << "]\t:\t[Target: " << TargetInfo.str() << "]\n"); + return false; + } + + // Compatible if each target feature specified by target is compatible with + // target feature of code object. The target feature is compatible if the + // code object does not specify it (meaning Any), or if it specifies it + // with the same value (meaning On or Off). + for (const auto &CodeObjectFeature : CodeObjectFeatureMap) { + auto TargetFeature = TargetFeatureMap.find(CodeObjectFeature.getKey()); + if (TargetFeature == TargetFeatureMap.end()) { + DEBUG_WITH_TYPE( + "CodeObjectCompatibility", + dbgs() + << "Incompatible: Value of CodeObject's non-ANY feature is " + "not matching with Target feature's ANY value \t[CodeObject: " + << CodeObjectInfo.str() << "]\t:\t[Target: " << TargetInfo.str() + << "]\n"); + return false; + } else if (TargetFeature->getValue() != CodeObjectFeature.getValue()) { + DEBUG_WITH_TYPE( + "CodeObjectCompatibility", + dbgs() << "Incompatible: Value of CodeObject's non-ANY feature is " + "not matching with Target feature's non-ANY value " + "\t[CodeObject: " + << CodeObjectInfo.str() + << "]\t:\t[Target: " << TargetInfo.str() << "]\n"); + return false; + } + } + + // CodeObject is compatible if all features of Target are: + // - either, present in the Code Object's features map with the same sign, + // - or, the feature is missing from CodeObjects's features map i.e. it is + // set to ANY + DEBUG_WITH_TYPE( + "CodeObjectCompatibility", + dbgs() << "Compatible: Target IDs are compatible \t[CodeObject: " + << CodeObjectInfo.str() << "]\t:\t[Target: " << TargetInfo.str() + << "]\n"); + return true; +} + /// Bundle the files. Return true if an error was found. Error OffloadBundler::BundleFiles() { std::error_code EC; @@ -1353,13 +1415,81 @@ getCompatibleOffloadTargets(OffloadTargetInfo &CodeObjectInfo, return !CompatibleTargets.empty(); } +// Check that each code object file in the input archive conforms to following +// rule: for a specific processor, a feature either shows up in all target IDs, +// or does not show up in any target IDs. Otherwise the target ID combination is +// invalid. +static Error +CheckHeterogeneousArchive(StringRef ArchiveName, + const OffloadBundlerConfig &BundlerConfig) { + std::vector> ArchiveBuffers; + ErrorOr> BufOrErr = + MemoryBuffer::getFileOrSTDIN(ArchiveName, true, false); + if (std::error_code EC = BufOrErr.getError()) + return createFileError(ArchiveName, EC); + + ArchiveBuffers.push_back(std::move(*BufOrErr)); + Expected> LibOrErr = + Archive::create(ArchiveBuffers.back()->getMemBufferRef()); + if (!LibOrErr) + return LibOrErr.takeError(); + + auto Archive = std::move(*LibOrErr); + + Error ArchiveErr = Error::success(); + auto ChildEnd = Archive->child_end(); + + /// Iterate over all bundled code object files in the input archive. + for (auto ArchiveIter = Archive->child_begin(ArchiveErr); + ArchiveIter != ChildEnd; ++ArchiveIter) { + if (ArchiveErr) + return ArchiveErr; + auto ArchiveChildNameOrErr = (*ArchiveIter).getName(); + if (!ArchiveChildNameOrErr) + return ArchiveChildNameOrErr.takeError(); + + auto CodeObjectBufferRefOrErr = (*ArchiveIter).getMemoryBufferRef(); + if (!CodeObjectBufferRefOrErr) + return CodeObjectBufferRefOrErr.takeError(); + + auto CodeObjectBuffer = + MemoryBuffer::getMemBuffer(*CodeObjectBufferRefOrErr, false); + + Expected> FileHandlerOrErr = + CreateFileHandler(*CodeObjectBuffer, BundlerConfig); + if (!FileHandlerOrErr) + return FileHandlerOrErr.takeError(); + + std::unique_ptr &FileHandler = *FileHandlerOrErr; + assert(FileHandler); + + std::set BundleIds; + auto CodeObjectFileError = + FileHandler->getBundleIDs(*CodeObjectBuffer, BundleIds); + if (CodeObjectFileError) + return CodeObjectFileError; + + auto &&ConflictingArchs = clang::getConflictTargetIDCombination(BundleIds); + if (ConflictingArchs) { + std::string ErrMsg = + Twine("conflicting TargetIDs [" + ConflictingArchs.value().first + + ", " + ConflictingArchs.value().second + "] found in " + + ArchiveChildNameOrErr.get() + " of " + ArchiveName) + .str(); + return createStringError(inconvertibleErrorCode(), ErrMsg); + } + } + + return ArchiveErr; +} + /// UnbundleArchive takes an archive file (".a") as input containing bundled /// code object files, and a list of offload targets (not host), and extracts /// the code objects into a new archive file for each offload target. Each /// resulting archive file contains all code object files corresponding to that /// particular offload target. The created archive file does not /// contain an index of the symbols and code object files are named as -/// <->, with ':' replaced with '_'. +/// <->, with ':' replaced with '_'. Error OffloadBundler::UnbundleArchive() { std::vector> ArchiveBuffers; @@ -1378,6 +1508,16 @@ Error OffloadBundler::UnbundleArchive() { StringRef IFName = BundlerConfig.InputFileNames.front(); + if (BundlerConfig.CheckInputArchive) { + // For a specific processor, a feature either shows up in all target IDs, or + // does not show up in any target IDs. Otherwise the target ID combination + // is invalid. + auto ArchiveError = CheckHeterogeneousArchive(IFName, BundlerConfig); + if (ArchiveError) { + return ArchiveError; + } + } + ErrorOr> BufOrErr = MemoryBuffer::getFileOrSTDIN(IFName, true, false); if (std::error_code EC = BufOrErr.getError()) diff --git a/clang/lib/Driver/ToolChains/Arch/RISCV.cpp b/clang/lib/Driver/ToolChains/Arch/RISCV.cpp index 346fb67ff277e9c53f3ef25ce37363653c5eb41b..5d990ba78e5cc880f72bd31d9b250b1cc0a9eefa 100644 --- a/clang/lib/Driver/ToolChains/Arch/RISCV.cpp +++ b/clang/lib/Driver/ToolChains/Arch/RISCV.cpp @@ -65,7 +65,7 @@ static void getRISCFeaturesFromMcpu(const Driver &D, const Arg *A, } if (llvm::RISCV::hasFastUnalignedAccess(Mcpu)) - Features.push_back("+unaligned-scalar-mem"); + Features.push_back("+fast-unaligned-access"); } void riscv::getRISCVTargetFeatures(const Driver &D, const llvm::Triple &Triple, @@ -171,18 +171,12 @@ void riscv::getRISCVTargetFeatures(const Driver &D, const llvm::Triple &Triple, Features.push_back("-save-restore"); // -mno-unaligned-access is default, unless -munaligned-access is specified. - bool HasV = llvm::is_contained(Features, "+zve32x"); if (const Arg *A = Args.getLastArg(options::OPT_munaligned_access, options::OPT_mno_unaligned_access)) { - if (A->getOption().matches(options::OPT_munaligned_access)) { - Features.push_back("+unaligned-scalar-mem"); - if (HasV) - Features.push_back("+unaligned-vector-mem"); - } else { - Features.push_back("-unaligned-scalar-mem"); - if (HasV) - Features.push_back("-unaligned-vector-mem"); - } + if (A->getOption().matches(options::OPT_munaligned_access)) + Features.push_back("+fast-unaligned-access"); + else + Features.push_back("-fast-unaligned-access"); } // Now add any that the user explicitly requested on the command line, diff --git a/clang/lib/Driver/ToolChains/Arch/X86.cpp b/clang/lib/Driver/ToolChains/Arch/X86.cpp index fbe665bdd5c8afb6e5257d046d075d85c302d186..3e51b2b5ce8642e0ac08afc98617e96b630e2816 100644 --- a/clang/lib/Driver/ToolChains/Arch/X86.cpp +++ b/clang/lib/Driver/ToolChains/Arch/X86.cpp @@ -270,6 +270,21 @@ void x86::getX86TargetFeatures(const Driver &D, const llvm::Triple &Triple, } bool IsNegative = Name.startswith("no-"); + if (A->getOption().matches(options::OPT_mapx_features_EQ) || + A->getOption().matches(options::OPT_mno_apx_features_EQ)) { + + for (StringRef Value : A->getValues()) { + if (Value == "egpr" || Value == "push2pop2" || Value == "ppx" || + Value == "ndd" || Value == "ccmp" || Value == "cf") { + Features.push_back( + Args.MakeArgString((IsNegative ? "-" : "+") + Value)); + continue; + } + D.Diag(clang::diag::err_drv_unsupported_option_argument) + << A->getSpelling() << Value; + } + continue; + } if (IsNegative) Name = Name.substr(3); Features.push_back(Args.MakeArgString((IsNegative ? "-" : "+") + Name)); diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp index 2d73f42772a29dcff171b8022750f90545469458..f02f7c841b91f0ba56dd41e377429397404fb9d3 100644 --- a/clang/lib/Driver/ToolChains/Clang.cpp +++ b/clang/lib/Driver/ToolChains/Clang.cpp @@ -409,139 +409,6 @@ static bool ShouldEnableAutolink(const ArgList &Args, const ToolChain &TC, Default); } -static bool mustUseNonLeafFramePointerForTarget(const llvm::Triple &Triple) { - switch (Triple.getArch()){ - default: - return false; - case llvm::Triple::arm: - case llvm::Triple::thumb: - // ARM Darwin targets require a frame pointer to be always present to aid - // offline debugging via backtraces. - return Triple.isOSDarwin(); - } -} - -static bool useFramePointerForTargetByDefault(const ArgList &Args, - const llvm::Triple &Triple) { - if (Args.hasArg(options::OPT_pg) && !Args.hasArg(options::OPT_mfentry)) - return true; - - if (Triple.isAndroid()) { - switch (Triple.getArch()) { - case llvm::Triple::aarch64: - case llvm::Triple::arm: - case llvm::Triple::armeb: - case llvm::Triple::thumb: - case llvm::Triple::thumbeb: - case llvm::Triple::riscv64: - return true; - default: - break; - } - } - - switch (Triple.getArch()) { - case llvm::Triple::xcore: - case llvm::Triple::wasm32: - case llvm::Triple::wasm64: - case llvm::Triple::msp430: - // XCore never wants frame pointers, regardless of OS. - // WebAssembly never wants frame pointers. - return false; - case llvm::Triple::ppc: - case llvm::Triple::ppcle: - case llvm::Triple::ppc64: - case llvm::Triple::ppc64le: - case llvm::Triple::riscv32: - case llvm::Triple::riscv64: - case llvm::Triple::sparc: - case llvm::Triple::sparcel: - case llvm::Triple::sparcv9: - case llvm::Triple::amdgcn: - case llvm::Triple::r600: - case llvm::Triple::csky: - case llvm::Triple::loongarch32: - case llvm::Triple::loongarch64: - return !areOptimizationsEnabled(Args); - default: - break; - } - - if (Triple.isOSFuchsia() || Triple.isOSNetBSD()) { - return !areOptimizationsEnabled(Args); - } - - if (Triple.isOSLinux() || Triple.isOSHurd()) { - switch (Triple.getArch()) { - // Don't use a frame pointer on linux if optimizing for certain targets. - case llvm::Triple::arm: - case llvm::Triple::armeb: - case llvm::Triple::thumb: - case llvm::Triple::thumbeb: - case llvm::Triple::mips64: - case llvm::Triple::mips64el: - case llvm::Triple::mips: - case llvm::Triple::mipsel: - case llvm::Triple::systemz: - case llvm::Triple::x86: - case llvm::Triple::x86_64: - return !areOptimizationsEnabled(Args); - default: - return true; - } - } - - if (Triple.isOSWindows()) { - switch (Triple.getArch()) { - case llvm::Triple::x86: - return !areOptimizationsEnabled(Args); - case llvm::Triple::x86_64: - return Triple.isOSBinFormatMachO(); - case llvm::Triple::arm: - case llvm::Triple::thumb: - // Windows on ARM builds with FPO disabled to aid fast stack walking - return true; - default: - // All other supported Windows ISAs use xdata unwind information, so frame - // pointers are not generally useful. - return false; - } - } - - return true; -} - -static CodeGenOptions::FramePointerKind -getFramePointerKind(const ArgList &Args, const llvm::Triple &Triple) { - // We have 4 states: - // - // 00) leaf retained, non-leaf retained - // 01) leaf retained, non-leaf omitted (this is invalid) - // 10) leaf omitted, non-leaf retained - // (what -momit-leaf-frame-pointer was designed for) - // 11) leaf omitted, non-leaf omitted - // - // "omit" options taking precedence over "no-omit" options is the only way - // to make 3 valid states representable - Arg *A = Args.getLastArg(options::OPT_fomit_frame_pointer, - options::OPT_fno_omit_frame_pointer); - bool OmitFP = A && A->getOption().matches(options::OPT_fomit_frame_pointer); - bool NoOmitFP = - A && A->getOption().matches(options::OPT_fno_omit_frame_pointer); - bool OmitLeafFP = - Args.hasFlag(options::OPT_momit_leaf_frame_pointer, - options::OPT_mno_omit_leaf_frame_pointer, - Triple.isAArch64() || Triple.isPS() || Triple.isVE() || - (Triple.isAndroid() && Triple.isRISCV64())); - if (NoOmitFP || mustUseNonLeafFramePointerForTarget(Triple) || - (!OmitFP && useFramePointerForTargetByDefault(Args, Triple))) { - if (OmitLeafFP) - return CodeGenOptions::FramePointerKind::NonLeaf; - return CodeGenOptions::FramePointerKind::All; - } - return CodeGenOptions::FramePointerKind::None; -} - /// Add a CC1 option to specify the debug compilation directory. static const char *addDebugCompDirArg(const ArgList &Args, ArgStringList &CmdArgs, @@ -3507,7 +3374,7 @@ static void RenderSCPOptions(const ToolChain &TC, const ArgList &Args, return; if (!EffectiveTriple.isX86() && !EffectiveTriple.isSystemZ() && - !EffectiveTriple.isPPC64()) + !EffectiveTriple.isPPC64() && !EffectiveTriple.isAArch64()) return; Args.addOptInFlag(CmdArgs, options::OPT_fstack_clash_protection, @@ -4862,7 +4729,9 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, bool UnifiedLTO = false; if (IsUsingLTO) { UnifiedLTO = Args.hasFlag(options::OPT_funified_lto, - options::OPT_fno_unified_lto, Triple.isPS()); + options::OPT_fno_unified_lto, Triple.isPS()) || + Args.hasFlag(options::OPT_ffat_lto_objects, + options::OPT_fno_fat_lto_objects, false); if (UnifiedLTO) CmdArgs.push_back("-funified-lto"); } @@ -5756,6 +5625,18 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, if (CM == "large" && RelocationModel != llvm::Reloc::Static) D.Diag(diag::err_drv_argument_only_allowed_with) << A->getAsString(Args) << "-fno-pic"; + } else if (Triple.isLoongArch()) { + if (CM == "extreme" && + Args.hasFlagNoClaim(options::OPT_fplt, options::OPT_fno_plt, false)) + D.Diag(diag::err_drv_argument_not_allowed_with) + << A->getAsString(Args) << "-fplt"; + Ok = CM == "normal" || CM == "medium" || CM == "extreme"; + // Convert to LLVM recognizable names. + if (Ok) + CM = llvm::StringSwitch(CM) + .Case("normal", "small") + .Case("extreme", "large") + .Default(CM); } else if (Triple.isPPC64() || Triple.isOSAIX()) { Ok = CM == "small" || CM == "medium" || CM == "large"; } else if (Triple.isRISCV()) { diff --git a/clang/lib/Driver/ToolChains/CommonArgs.cpp b/clang/lib/Driver/ToolChains/CommonArgs.cpp index 1f31c6395206ee8712e6e82d5c0ad2d6cc05124b..0ae8e2dce32e94a8703ea73cfae44f95f4a16a68 100644 --- a/clang/lib/Driver/ToolChains/CommonArgs.cpp +++ b/clang/lib/Driver/ToolChains/CommonArgs.cpp @@ -24,6 +24,7 @@ #include "MSP430.h" #include "Solaris.h" #include "clang/Basic/CharInfo.h" +#include "clang/Basic/CodeGenOptions.h" #include "clang/Basic/LangOptions.h" #include "clang/Basic/ObjCRuntime.h" #include "clang/Basic/Version.h" @@ -71,6 +72,144 @@ using namespace clang::driver::tools; using namespace clang; using namespace llvm::opt; +static bool useFramePointerForTargetByDefault(const llvm::opt::ArgList &Args, + const llvm::Triple &Triple) { + if (Args.hasArg(clang::driver::options::OPT_pg) && + !Args.hasArg(clang::driver::options::OPT_mfentry)) + return true; + + if (Triple.isAndroid()) { + switch (Triple.getArch()) { + case llvm::Triple::aarch64: + case llvm::Triple::arm: + case llvm::Triple::armeb: + case llvm::Triple::thumb: + case llvm::Triple::thumbeb: + case llvm::Triple::riscv64: + return true; + default: + break; + } + } + + switch (Triple.getArch()) { + case llvm::Triple::xcore: + case llvm::Triple::wasm32: + case llvm::Triple::wasm64: + case llvm::Triple::msp430: + // XCore never wants frame pointers, regardless of OS. + // WebAssembly never wants frame pointers. + return false; + case llvm::Triple::ppc: + case llvm::Triple::ppcle: + case llvm::Triple::ppc64: + case llvm::Triple::ppc64le: + case llvm::Triple::riscv32: + case llvm::Triple::riscv64: + case llvm::Triple::sparc: + case llvm::Triple::sparcel: + case llvm::Triple::sparcv9: + case llvm::Triple::amdgcn: + case llvm::Triple::r600: + case llvm::Triple::csky: + case llvm::Triple::loongarch32: + case llvm::Triple::loongarch64: + return !clang::driver::tools::areOptimizationsEnabled(Args); + default: + break; + } + + if (Triple.isOSFuchsia() || Triple.isOSNetBSD()) { + return !clang::driver::tools::areOptimizationsEnabled(Args); + } + + if (Triple.isOSLinux() || Triple.isOSHurd()) { + switch (Triple.getArch()) { + // Don't use a frame pointer on linux if optimizing for certain targets. + case llvm::Triple::arm: + case llvm::Triple::armeb: + case llvm::Triple::thumb: + case llvm::Triple::thumbeb: + case llvm::Triple::mips64: + case llvm::Triple::mips64el: + case llvm::Triple::mips: + case llvm::Triple::mipsel: + case llvm::Triple::systemz: + case llvm::Triple::x86: + case llvm::Triple::x86_64: + return !clang::driver::tools::areOptimizationsEnabled(Args); + default: + return true; + } + } + + if (Triple.isOSWindows()) { + switch (Triple.getArch()) { + case llvm::Triple::x86: + return !clang::driver::tools::areOptimizationsEnabled(Args); + case llvm::Triple::x86_64: + return Triple.isOSBinFormatMachO(); + case llvm::Triple::arm: + case llvm::Triple::thumb: + // Windows on ARM builds with FPO disabled to aid fast stack walking + return true; + default: + // All other supported Windows ISAs use xdata unwind information, so frame + // pointers are not generally useful. + return false; + } + } + + return true; +} + +static bool mustUseNonLeafFramePointerForTarget(const llvm::Triple &Triple) { + switch (Triple.getArch()) { + default: + return false; + case llvm::Triple::arm: + case llvm::Triple::thumb: + // ARM Darwin targets require a frame pointer to be always present to aid + // offline debugging via backtraces. + return Triple.isOSDarwin(); + } +} + +clang::CodeGenOptions::FramePointerKind +getFramePointerKind(const llvm::opt::ArgList &Args, + const llvm::Triple &Triple) { + // We have 4 states: + // + // 00) leaf retained, non-leaf retained + // 01) leaf retained, non-leaf omitted (this is invalid) + // 10) leaf omitted, non-leaf retained + // (what -momit-leaf-frame-pointer was designed for) + // 11) leaf omitted, non-leaf omitted + // + // "omit" options taking precedence over "no-omit" options is the only way + // to make 3 valid states representable + llvm::opt::Arg *A = + Args.getLastArg(clang::driver::options::OPT_fomit_frame_pointer, + clang::driver::options::OPT_fno_omit_frame_pointer); + + bool OmitFP = A && A->getOption().matches( + clang::driver::options::OPT_fomit_frame_pointer); + bool NoOmitFP = A && A->getOption().matches( + clang::driver::options::OPT_fno_omit_frame_pointer); + bool OmitLeafFP = + Args.hasFlag(clang::driver::options::OPT_momit_leaf_frame_pointer, + clang::driver::options::OPT_mno_omit_leaf_frame_pointer, + Triple.isAArch64() || Triple.isPS() || Triple.isVE() || + (Triple.isAndroid() && Triple.isRISCV64())); + if (NoOmitFP || mustUseNonLeafFramePointerForTarget(Triple) || + (!OmitFP && useFramePointerForTargetByDefault(Args, Triple))) { + if (OmitLeafFP) + return clang::CodeGenOptions::FramePointerKind::NonLeaf; + return clang::CodeGenOptions::FramePointerKind::All; + } + return clang::CodeGenOptions::FramePointerKind::None; +} + static void renderRpassOptions(const ArgList &Args, ArgStringList &CmdArgs, const StringRef PluginOptPrefix) { if (const Arg *A = Args.getLastArg(options::OPT_Rpass_EQ)) @@ -977,14 +1116,60 @@ bool tools::addOpenMPRuntime(ArgStringList &CmdArgs, const ToolChain &TC, return true; } -void tools::addFortranRuntimeLibs(const ToolChain &TC, +void tools::addFortranRuntimeLibs(const ToolChain &TC, const ArgList &Args, llvm::opt::ArgStringList &CmdArgs) { // These are handled earlier on Windows by telling the frontend driver to add // the correct libraries to link against as dependents in the object file. if (!TC.getTriple().isKnownWindowsMSVCEnvironment()) { + // The --whole-archive option needs to be part of the link line to + // make sure that the main() function from Fortran_main.a is pulled + // in by the linker. Determine if --whole-archive is active when + // flang will try to link Fortran_main.a. If it is, don't add the + // --whole-archive flag to the link line. If it's not, add a proper + // --whole-archive/--no-whole-archive bracket to the link line. + bool WholeArchiveActive = false; + for (auto *Arg : Args.filtered(options::OPT_Wl_COMMA)) + if (Arg) + for (StringRef ArgValue : Arg->getValues()) { + if (ArgValue == "--whole-archive") + WholeArchiveActive = true; + if (ArgValue == "--no-whole-archive") + WholeArchiveActive = false; + } + + if (!WholeArchiveActive) + CmdArgs.push_back("--whole-archive"); CmdArgs.push_back("-lFortran_main"); + if (!WholeArchiveActive) + CmdArgs.push_back("--no-whole-archive"); + + // Perform regular linkage of the remaining runtime libraries. CmdArgs.push_back("-lFortranRuntime"); CmdArgs.push_back("-lFortranDecimal"); + } else { + unsigned RTOptionID = options::OPT__SLASH_MT; + if (auto *rtl = Args.getLastArg(options::OPT_fms_runtime_lib_EQ)) { + RTOptionID = llvm::StringSwitch(rtl->getValue()) + .Case("static", options::OPT__SLASH_MT) + .Case("static_dbg", options::OPT__SLASH_MTd) + .Case("dll", options::OPT__SLASH_MD) + .Case("dll_dbg", options::OPT__SLASH_MDd) + .Default(options::OPT__SLASH_MT); + } + switch (RTOptionID) { + case options::OPT__SLASH_MT: + CmdArgs.push_back("/WHOLEARCHIVE:Fortran_main.static.lib"); + break; + case options::OPT__SLASH_MTd: + CmdArgs.push_back("/WHOLEARCHIVE:Fortran_main.static_dbg.lib"); + break; + case options::OPT__SLASH_MD: + CmdArgs.push_back("/WHOLEARCHIVE:Fortran_main.dynamic.lib"); + break; + case options::OPT__SLASH_MDd: + CmdArgs.push_back("/WHOLEARCHIVE:Fortran_main.dynamic_dbg.lib"); + break; + } } } diff --git a/clang/lib/Driver/ToolChains/CommonArgs.h b/clang/lib/Driver/ToolChains/CommonArgs.h index f364c9793c9be62d5ced6b068ab7c5cb15ad7c78..25d68345a9f9ebf382c4df5a99bc21b66d0da83a 100644 --- a/clang/lib/Driver/ToolChains/CommonArgs.h +++ b/clang/lib/Driver/ToolChains/CommonArgs.h @@ -9,6 +9,7 @@ #ifndef LLVM_CLANG_LIB_DRIVER_TOOLCHAINS_COMMONARGS_H #define LLVM_CLANG_LIB_DRIVER_TOOLCHAINS_COMMONARGS_H +#include "clang/Basic/CodeGenOptions.h" #include "clang/Driver/Driver.h" #include "clang/Driver/InputInfo.h" #include "clang/Driver/Multilib.h" @@ -116,7 +117,7 @@ bool addOpenMPRuntime(llvm::opt::ArgStringList &CmdArgs, const ToolChain &TC, bool IsOffloadingHost = false, bool GompNeedsRT = false); /// Adds Fortran runtime libraries to \p CmdArgs. -void addFortranRuntimeLibs(const ToolChain &TC, +void addFortranRuntimeLibs(const ToolChain &TC, const llvm::opt::ArgList &Args, llvm::opt::ArgStringList &CmdArgs); /// Adds the path for the Fortran runtime libraries to \p CmdArgs. @@ -215,4 +216,7 @@ void addOpenMPDeviceRTL(const Driver &D, const llvm::opt::ArgList &DriverArgs, } // end namespace driver } // end namespace clang +clang::CodeGenOptions::FramePointerKind +getFramePointerKind(const llvm::opt::ArgList &Args, const llvm::Triple &Triple); + #endif // LLVM_CLANG_LIB_DRIVER_TOOLCHAINS_COMMONARGS_H diff --git a/clang/lib/Driver/ToolChains/Darwin.cpp b/clang/lib/Driver/ToolChains/Darwin.cpp index 1f61bb02c6ae226623a3d3ba04516a1aca2f75e2..f09bc27d7d2c0e6d76aa776e98bb5b3d8ee15c1d 100644 --- a/clang/lib/Driver/ToolChains/Darwin.cpp +++ b/clang/lib/Driver/ToolChains/Darwin.cpp @@ -678,7 +678,7 @@ void darwin::Linker::ConstructJob(Compilation &C, const JobAction &JA, // to generate executables. if (getToolChain().getDriver().IsFlangMode()) { addFortranRuntimeLibraryPath(getToolChain(), Args, CmdArgs); - addFortranRuntimeLibs(getToolChain(), CmdArgs); + addFortranRuntimeLibs(getToolChain(), Args, CmdArgs); } if (!Args.hasArg(options::OPT_nostdlib, options::OPT_nodefaultlibs)) @@ -2073,7 +2073,7 @@ std::optional getDeploymentTargetFromTargetArg( continue; A->claim(); // Accept a -target-variant triple when compiling code that may run on - // macOS or Mac Catalust. + // macOS or Mac Catalyst. if ((Triple.isMacOSX() && TVT.getOS() == llvm::Triple::IOS && TVT.isMacCatalystEnvironment()) || (TVT.isMacOSX() && Triple.getOS() == llvm::Triple::IOS && diff --git a/clang/lib/Driver/ToolChains/DragonFly.cpp b/clang/lib/Driver/ToolChains/DragonFly.cpp index 181d0ad0d834b2b8b95110ed5fc73c9e6ba14d28..9942fc632e0a9170bc858f1c9f7684159063f6e7 100644 --- a/clang/lib/Driver/ToolChains/DragonFly.cpp +++ b/clang/lib/Driver/ToolChains/DragonFly.cpp @@ -153,7 +153,7 @@ void dragonfly::Linker::ConstructJob(Compilation &C, const JobAction &JA, // AddRunTimeLibs). if (D.IsFlangMode()) { addFortranRuntimeLibraryPath(ToolChain, Args, CmdArgs); - addFortranRuntimeLibs(ToolChain, CmdArgs); + addFortranRuntimeLibs(ToolChain, Args, CmdArgs); CmdArgs.push_back("-lm"); } diff --git a/clang/lib/Driver/ToolChains/Flang.cpp b/clang/lib/Driver/ToolChains/Flang.cpp index 86e1c57e485685e4fed760787671576e85c0484e..9b21fe952af7a8293bf92e41758a290563c3c2d1 100644 --- a/clang/lib/Driver/ToolChains/Flang.cpp +++ b/clang/lib/Driver/ToolChains/Flang.cpp @@ -9,6 +9,7 @@ #include "Flang.h" #include "CommonArgs.h" +#include "clang/Basic/CodeGenOptions.h" #include "clang/Driver/Options.h" #include "llvm/Frontend/Debug/Options.h" #include "llvm/Support/FileSystem.h" @@ -142,33 +143,11 @@ void Flang::addCodegenOptions(const ArgList &Args, if (shouldLoopVersion(Args)) CmdArgs.push_back("-fversion-loops-for-stride"); - Arg *aliasAnalysis = Args.getLastArg(options::OPT_falias_analysis, - options::OPT_fno_alias_analysis); - // only pass on the argument if it does not match that implied by the - // optimization level: so if optimization is requested, only forward - // -fno-alias-analysis. If optimization is not requested, only forward - // -falias-analysis. - Arg *optLevel = - Args.getLastArg(options::OPT_Ofast, options::OPT_O, options::OPT_O4); - if (aliasAnalysis) { - bool faliasAnalysis = - aliasAnalysis->getOption().matches(options::OPT_falias_analysis); - if (optLevel && !faliasAnalysis) { - CmdArgs.push_back("-fno-alias-analysis"); - } else { - if (faliasAnalysis) - // requested alias analysis but no optimization enabled - CmdArgs.push_back("-falias-analysis"); - } - } - Args.addAllArgs(CmdArgs, {options::OPT_flang_experimental_hlfir, options::OPT_flang_deprecated_no_hlfir, options::OPT_flang_experimental_polymorphism, options::OPT_fno_ppc_native_vec_elem_order, - options::OPT_fppc_native_vec_elem_order, - options::OPT_falias_analysis, - options::OPT_fno_alias_analysis}); + options::OPT_fppc_native_vec_elem_order}); } void Flang::addPicOptions(const ArgList &Args, ArgStringList &CmdArgs) const { @@ -224,6 +203,30 @@ void Flang::AddAArch64TargetArgs(const ArgList &Args, } } +static void addVSDefines(const ToolChain &TC, const ArgList &Args, + ArgStringList &CmdArgs) { + + unsigned ver = 0; + const VersionTuple vt = TC.computeMSVCVersion(nullptr, Args); + ver = vt.getMajor() * 10000000 + vt.getMinor().value_or(0) * 100000 + + vt.getSubminor().value_or(0); + CmdArgs.push_back(Args.MakeArgString("-D_MSC_VER=" + Twine(ver / 100000))); + CmdArgs.push_back(Args.MakeArgString("-D_MSC_FULL_VER=" + Twine(ver))); + CmdArgs.push_back(Args.MakeArgString("-D_WIN32")); + + llvm::Triple triple = TC.getTriple(); + if (triple.isAArch64()) { + CmdArgs.push_back("-D_M_ARM64=1"); + } else if (triple.isX86() && triple.isArch32Bit()) { + CmdArgs.push_back("-D_M_IX86=600"); + } else if (triple.isX86() && triple.isArch64Bit()) { + CmdArgs.push_back("-D_M_X64=100"); + } else { + llvm_unreachable( + "Flang on Windows only supports X86_32, X86_64 and AArch64"); + } +} + static void processVSRuntimeLibrary(const ToolChain &TC, const ArgList &Args, ArgStringList &CmdArgs) { assert(TC.getTriple().isKnownWindowsMSVCEnvironment() && @@ -277,6 +280,14 @@ static void processVSRuntimeLibrary(const ToolChain &TC, const ArgList &Args, } } +void Flang::AddAMDGPUTargetArgs(const ArgList &Args, + ArgStringList &CmdArgs) const { + if (Arg *A = Args.getLastArg(options::OPT_mcode_object_version_EQ)) { + StringRef Val = A->getValue(); + CmdArgs.push_back(Args.MakeArgString("-mcode-object-version=" + Val)); + } +} + void Flang::addTargetOptions(const ArgList &Args, ArgStringList &CmdArgs) const { const ToolChain &TC = getToolChain(); @@ -300,6 +311,9 @@ void Flang::addTargetOptions(const ArgList &Args, case llvm::Triple::r600: case llvm::Triple::amdgcn: + getTargetFeatures(D, Triple, Args, CmdArgs, /*ForAs*/ false); + AddAMDGPUTargetArgs(Args, CmdArgs); + break; case llvm::Triple::riscv64: case llvm::Triple::x86_64: getTargetFeatures(D, Triple, Args, CmdArgs, /*ForAs*/ false); @@ -342,6 +356,7 @@ void Flang::addTargetOptions(const ArgList &Args, if (Triple.isKnownWindowsMSVCEnvironment()) { processVSRuntimeLibrary(TC, Args, CmdArgs); + addVSDefines(TC, Args, CmdArgs); } // TODO: Add target specific flags, ABI, mtune option etc. @@ -683,6 +698,24 @@ void Flang::ConstructJob(Compilation &C, const JobAction &JA, // Forward -Xflang arguments to -fc1 Args.AddAllArgValues(CmdArgs, options::OPT_Xflang); + CodeGenOptions::FramePointerKind FPKeepKind = + getFramePointerKind(Args, Triple); + + const char *FPKeepKindStr = nullptr; + switch (FPKeepKind) { + case CodeGenOptions::FramePointerKind::None: + FPKeepKindStr = "-mframe-pointer=none"; + break; + case CodeGenOptions::FramePointerKind::NonLeaf: + FPKeepKindStr = "-mframe-pointer=non-leaf"; + break; + case CodeGenOptions::FramePointerKind::All: + FPKeepKindStr = "-mframe-pointer=all"; + break; + } + assert(FPKeepKindStr && "unknown FramePointerKind"); + CmdArgs.push_back(FPKeepKindStr); + // Forward -mllvm options to the LLVM option parser. In practice, this means // forwarding to `-fc1` as that's where the LLVM parser is run. for (const Arg *A : Args.filtered(options::OPT_mllvm)) { diff --git a/clang/lib/Driver/ToolChains/Flang.h b/clang/lib/Driver/ToolChains/Flang.h index 0141240b5d3ac90bff5a49b24070bdcc5d5eb8a7..8d35080e1c0c88bfc21ac699567efce1b2040393 100644 --- a/clang/lib/Driver/ToolChains/Flang.h +++ b/clang/lib/Driver/ToolChains/Flang.h @@ -63,6 +63,13 @@ private: void AddAArch64TargetArgs(const llvm::opt::ArgList &Args, llvm::opt::ArgStringList &CmdArgs) const; + /// Add specific options for AMDGPU target. + /// + /// \param [in] Args The list of input driver arguments + /// \param [out] CmdArgs The list of output command arguments + void AddAMDGPUTargetArgs(const llvm::opt::ArgList &Args, + llvm::opt::ArgStringList &CmdArgs) const; + /// Extract offload options from the driver arguments and add them to /// the command arguments. /// \param [in] C The current compilation for the driver invocation diff --git a/clang/lib/Driver/ToolChains/FreeBSD.cpp b/clang/lib/Driver/ToolChains/FreeBSD.cpp index d08764aeef77539e95048662eeaaa5cc2fa0f3e9..b7c9e0e51cdb666a76585c8dbfcbeb9dc53f0502 100644 --- a/clang/lib/Driver/ToolChains/FreeBSD.cpp +++ b/clang/lib/Driver/ToolChains/FreeBSD.cpp @@ -310,7 +310,7 @@ void freebsd::Linker::ConstructJob(Compilation &C, const JobAction &JA, // AddRunTimeLibs). if (D.IsFlangMode()) { addFortranRuntimeLibraryPath(ToolChain, Args, CmdArgs); - addFortranRuntimeLibs(ToolChain, CmdArgs); + addFortranRuntimeLibs(ToolChain, Args, CmdArgs); if (Profiling) CmdArgs.push_back("-lm_p"); else @@ -400,13 +400,6 @@ FreeBSD::FreeBSD(const Driver &D, const llvm::Triple &Triple, getFilePaths().push_back(concat(getDriver().SysRoot, "/usr/lib")); } -unsigned FreeBSD::GetDefaultDwarfVersion() const { - unsigned Major = getTriple().getOSMajorVersion(); - if (Major >= 12 || Major == 0) - return 4; - return 2; -} - void FreeBSD::AddClangSystemIncludeArgs( const llvm::opt::ArgList &DriverArgs, llvm::opt::ArgStringList &CC1Args) const { @@ -510,13 +503,3 @@ SanitizerMask FreeBSD::getSupportedSanitizers() const { } return Res; } - -void FreeBSD::addClangTargetOptions(const ArgList &DriverArgs, - ArgStringList &CC1Args, - Action::OffloadKind) const { - unsigned Major = getTriple().getOSMajorVersion(); - if (!DriverArgs.hasFlag(options::OPT_fuse_init_array, - options::OPT_fno_use_init_array, - (Major >= 12 || Major == 0))) - CC1Args.push_back("-fno-use-init-array"); -} diff --git a/clang/lib/Driver/ToolChains/FreeBSD.h b/clang/lib/Driver/ToolChains/FreeBSD.h index 959cbf78ddc79d194ef05b22c56e01781e5d3197..7ab63905ed4f991829ffd4c6e3ace523f9579229 100644 --- a/clang/lib/Driver/ToolChains/FreeBSD.h +++ b/clang/lib/Driver/ToolChains/FreeBSD.h @@ -82,14 +82,10 @@ public: getDefaultUnwindTableLevel(const llvm::opt::ArgList &Args) const override; bool isPIEDefault(const llvm::opt::ArgList &Args) const override; SanitizerMask getSupportedSanitizers() const override; - unsigned GetDefaultDwarfVersion() const override; + unsigned GetDefaultDwarfVersion() const override { return 4; } // Until dtrace (via CTF) and LLDB can deal with distributed debug info, // FreeBSD defaults to standalone/full debug info. bool GetDefaultStandaloneDebug() const override { return true; } - void - addClangTargetOptions(const llvm::opt::ArgList &DriverArgs, - llvm::opt::ArgStringList &CC1Args, - Action::OffloadKind DeviceOffloadKind) const override; protected: Tool *buildAssembler() const override; diff --git a/clang/lib/Driver/ToolChains/Gnu.cpp b/clang/lib/Driver/ToolChains/Gnu.cpp index 16cf5707227264d2cee854e29e7cf0d72f4b9d7c..b875991844ffff5240cdb2195a77bf74945dca5e 100644 --- a/clang/lib/Driver/ToolChains/Gnu.cpp +++ b/clang/lib/Driver/ToolChains/Gnu.cpp @@ -564,7 +564,7 @@ void tools::gnutools::Linker::ConstructJob(Compilation &C, const JobAction &JA, // AddRunTimeLibs). if (D.IsFlangMode()) { addFortranRuntimeLibraryPath(ToolChain, Args, CmdArgs); - addFortranRuntimeLibs(ToolChain, CmdArgs); + addFortranRuntimeLibs(ToolChain, Args, CmdArgs); CmdArgs.push_back("-lm"); } diff --git a/clang/lib/Driver/ToolChains/HIPUtility.cpp b/clang/lib/Driver/ToolChains/HIPUtility.cpp index 04efdcba20ea7403e0b209d1ac3cbb98a1f72cf3..f692458b775de2b6c4bcfafa177a9e8c7ff40430 100644 --- a/clang/lib/Driver/ToolChains/HIPUtility.cpp +++ b/clang/lib/Driver/ToolChains/HIPUtility.cpp @@ -150,6 +150,8 @@ void HIP::constructGenerateObjFileFromHIPFatBinary( ObjStream << " .incbin "; llvm::sys::printArg(ObjStream, BundleFile, /*Quote=*/true); ObjStream << "\n"; + if (HostTriple.isOSLinux() && HostTriple.isOSBinFormatELF()) + ObjStream << " .section .note.GNU-stack, \"\", @progbits\n"; ObjStream.flush(); // Dump the contents of the temp object file gen if the user requested that. diff --git a/clang/lib/Driver/ToolChains/Haiku.cpp b/clang/lib/Driver/ToolChains/Haiku.cpp index 3fe92054ac53e15d78b18f3bac0484555eb1c0fb..e0d94035823fd37fa14bd67ebd15805283d99d16 100644 --- a/clang/lib/Driver/ToolChains/Haiku.cpp +++ b/clang/lib/Driver/ToolChains/Haiku.cpp @@ -118,7 +118,7 @@ void haiku::Linker::ConstructJob(Compilation &C, const JobAction &JA, // AddRunTimeLibs). if (D.IsFlangMode()) { addFortranRuntimeLibraryPath(ToolChain, Args, CmdArgs); - addFortranRuntimeLibs(ToolChain, CmdArgs); + addFortranRuntimeLibs(ToolChain, Args, CmdArgs); } CmdArgs.push_back("-lgcc"); diff --git a/clang/lib/Driver/ToolChains/MSVC.cpp b/clang/lib/Driver/ToolChains/MSVC.cpp index 4966d102c51f1acaa5273b894230f2e23588d6d3..8a4a174c90ea8557336cc3258d617628828b6787 100644 --- a/clang/lib/Driver/ToolChains/MSVC.cpp +++ b/clang/lib/Driver/ToolChains/MSVC.cpp @@ -131,7 +131,7 @@ void visualstudio::Linker::ConstructJob(Compilation &C, const JobAction &JA, if (C.getDriver().IsFlangMode()) { addFortranRuntimeLibraryPath(TC, Args, CmdArgs); - addFortranRuntimeLibs(TC, CmdArgs); + addFortranRuntimeLibs(TC, Args, CmdArgs); // Inform the MSVC linker that we're generating a console application, i.e. // one with `main` as the "user-defined" entry point. The `main` function is diff --git a/clang/lib/Driver/ToolChains/MinGW.cpp b/clang/lib/Driver/ToolChains/MinGW.cpp index 39d767795445dbe07bf855b3619dcab6d33b4062..5d7f8675daf8d28c35d62bfd3d9091ba8e0beaf6 100644 --- a/clang/lib/Driver/ToolChains/MinGW.cpp +++ b/clang/lib/Driver/ToolChains/MinGW.cpp @@ -249,7 +249,7 @@ void tools::MinGW::Linker::ConstructJob(Compilation &C, const JobAction &JA, if (C.getDriver().IsFlangMode()) { addFortranRuntimeLibraryPath(TC, Args, CmdArgs); - addFortranRuntimeLibs(TC, CmdArgs); + addFortranRuntimeLibs(TC, Args, CmdArgs); } // TODO: Add profile stuff here diff --git a/clang/lib/Driver/ToolChains/NetBSD.cpp b/clang/lib/Driver/ToolChains/NetBSD.cpp index 90b195a007caa7818a4e7ee3609ed41aaafe6103..240bf5764b9cce293f31380d4484bb96be4c024e 100644 --- a/clang/lib/Driver/ToolChains/NetBSD.cpp +++ b/clang/lib/Driver/ToolChains/NetBSD.cpp @@ -325,7 +325,7 @@ void netbsd::Linker::ConstructJob(Compilation &C, const JobAction &JA, // AddRunTimeLibs). if (D.IsFlangMode()) { addFortranRuntimeLibraryPath(ToolChain, Args, CmdArgs); - addFortranRuntimeLibs(ToolChain, CmdArgs); + addFortranRuntimeLibs(ToolChain, Args, CmdArgs); CmdArgs.push_back("-lm"); } diff --git a/clang/lib/Driver/ToolChains/OpenBSD.cpp b/clang/lib/Driver/ToolChains/OpenBSD.cpp index 133b5ff13a4c7e22581d976d2407537da23317d0..21b9004ef2d52d119ad61e965df970a7f3676c40 100644 --- a/clang/lib/Driver/ToolChains/OpenBSD.cpp +++ b/clang/lib/Driver/ToolChains/OpenBSD.cpp @@ -237,7 +237,7 @@ void openbsd::Linker::ConstructJob(Compilation &C, const JobAction &JA, // AddRunTimeLibs). if (D.IsFlangMode()) { addFortranRuntimeLibraryPath(ToolChain, Args, CmdArgs); - addFortranRuntimeLibs(ToolChain, CmdArgs); + addFortranRuntimeLibs(ToolChain, Args, CmdArgs); if (Profiling) CmdArgs.push_back("-lm_p"); else diff --git a/clang/lib/Driver/ToolChains/Solaris.cpp b/clang/lib/Driver/ToolChains/Solaris.cpp index 32b6cbe647da6addf31291cbbcbe57d1888b70e1..485730da7df1f8cd4c8016a66edb061cc65a449c 100644 --- a/clang/lib/Driver/ToolChains/Solaris.cpp +++ b/clang/lib/Driver/ToolChains/Solaris.cpp @@ -226,7 +226,7 @@ void solaris::Linker::ConstructJob(Compilation &C, const JobAction &JA, // these dependencies need to be listed before the C runtime below. if (D.IsFlangMode()) { addFortranRuntimeLibraryPath(getToolChain(), Args, CmdArgs); - addFortranRuntimeLibs(getToolChain(), CmdArgs); + addFortranRuntimeLibs(getToolChain(), Args, CmdArgs); CmdArgs.push_back("-lm"); } if (Args.hasArg(options::OPT_fstack_protector) || diff --git a/clang/lib/ExtractAPI/ExtractAPIConsumer.cpp b/clang/lib/ExtractAPI/ExtractAPIConsumer.cpp index 3aba3bf44547cf660073f5d7fe4a1571cfee994b..fe282dfb19e8aa7fa9bf6ff43ee2c18b309864a7 100644 --- a/clang/lib/ExtractAPI/ExtractAPIConsumer.cpp +++ b/clang/lib/ExtractAPI/ExtractAPIConsumer.cpp @@ -17,6 +17,7 @@ #include "clang/AST/ASTContext.h" #include "clang/AST/DeclObjC.h" #include "clang/Basic/DiagnosticFrontend.h" +#include "clang/Basic/FileEntry.h" #include "clang/Basic/SourceLocation.h" #include "clang/Basic/SourceManager.h" #include "clang/Basic/TargetInfo.h" @@ -167,6 +168,12 @@ std::optional getRelativeIncludeName(const CompilerInstance &CI, return std::nullopt; } +std::optional getRelativeIncludeName(const CompilerInstance &CI, + FileEntryRef FE, + bool *IsQuoted = nullptr) { + return getRelativeIncludeName(CI, FE.getNameAsRequested(), IsQuoted); +} + struct LocationFileChecker { bool operator()(SourceLocation Loc) { // If the loc refers to a macro expansion we need to first get the file @@ -187,11 +194,9 @@ struct LocationFileChecker { if (ExternalFileEntries.count(*File)) return false; - StringRef FileName = SM.getFileManager().getCanonicalName(*File); - // Try to reduce the include name the same way we tried to include it. bool IsQuoted = false; - if (auto IncludeName = getRelativeIncludeName(CI, FileName, &IsQuoted)) + if (auto IncludeName = getRelativeIncludeName(CI, *File, &IsQuoted)) if (llvm::any_of(KnownFiles, [&IsQuoted, &IncludeName](const auto &KnownFile) { return KnownFile.first.equals(*IncludeName) && diff --git a/clang/lib/Format/.clang-format b/clang/lib/Format/.clang-format index 63d45e0307a3dfebe23e458478f608e837174a0b..d7331b3c8cf02ef7944e83a4280476627e33b31d 100644 --- a/clang/lib/Format/.clang-format +++ b/clang/lib/Format/.clang-format @@ -1,5 +1 @@ -BasedOnStyle: LLVM -InsertBraces: true -InsertNewlineAtEOF: true -LineEnding: LF -RemoveBracesLLVM: true +BasedOnStyle: clang-format diff --git a/clang/lib/Format/CMakeLists.txt b/clang/lib/Format/CMakeLists.txt index 3d3f3c6bf22e732655f4b2a040eec871c7ca069b..015ec7c0cc84e3f68b18c41a4cbd602d09f75e18 100644 --- a/clang/lib/Format/CMakeLists.txt +++ b/clang/lib/Format/CMakeLists.txt @@ -12,6 +12,7 @@ add_clang_library(clangFormat MacroCallReconstructor.cpp MacroExpander.cpp NamespaceEndCommentsFixer.cpp + ObjCPropertyAttributeOrderFixer.cpp QualifierAlignmentFixer.cpp SortJavaScriptImports.cpp TokenAnalyzer.cpp diff --git a/clang/lib/Format/ContinuationIndenter.cpp b/clang/lib/Format/ContinuationIndenter.cpp index 8fffdccd35c059b89a37ece8d763b31a0d592485..cb7dc5f3dd9babcbfdbe15c3278331fbe1d0596f 100644 --- a/clang/lib/Format/ContinuationIndenter.cpp +++ b/clang/lib/Format/ContinuationIndenter.cpp @@ -1208,8 +1208,10 @@ unsigned ContinuationIndenter::getNewLineColumn(const LineState &State) { // Indentation of the statement following a Verilog case label is taken care // of in moveStateToNextToken. - if (Style.isVerilog() && Keywords.isVerilogEndOfLabel(Previous)) + if (Style.isVerilog() && PreviousNonComment && + Keywords.isVerilogEndOfLabel(*PreviousNonComment)) { return State.FirstIndent; + } if (Style.BreakBeforeBraces == FormatStyle::BS_Whitesmiths && State.Line->First->is(tok::kw_enum)) { @@ -1227,9 +1229,7 @@ unsigned ContinuationIndenter::getNewLineColumn(const LineState &State) { return CurrentState.Indent; } if ((Current.isOneOf(tok::r_brace, tok::r_square) || - (Current.is(tok::greater) && - (Style.Language == FormatStyle::LK_Proto || - Style.Language == FormatStyle::LK_TextProto))) && + (Current.is(tok::greater) && Style.isProto())) && State.Stack.size() > 1) { if (Current.closesBlockOrBlockTypeList(Style)) return State.Stack[State.Stack.size() - 2].NestedBlockIndent; @@ -1276,9 +1276,7 @@ unsigned ContinuationIndenter::getNewLineColumn(const LineState &State) { if (Current.is(tok::identifier) && Current.Next && (!Style.isVerilog() || Current.Next->is(tok::colon)) && (Current.Next->is(TT_DictLiteral) || - ((Style.Language == FormatStyle::LK_Proto || - Style.Language == FormatStyle::LK_TextProto) && - Current.Next->isOneOf(tok::less, tok::l_brace)))) { + (Style.isProto() && Current.Next->isOneOf(tok::less, tok::l_brace)))) { return CurrentState.Indent; } if (NextNonComment->is(TT_ObjCStringLiteral) && @@ -1612,6 +1610,7 @@ unsigned ContinuationIndenter::moveStateToNextToken(LineState &State, State.NextToken->MustBreakBefore && Keywords.isVerilogEndOfLabel(Current)) { State.FirstIndent += Style.IndentWidth; + CurrentState.Indent = State.FirstIndent; } unsigned Penalty = @@ -1795,9 +1794,7 @@ void ContinuationIndenter::moveStatePastScopeOpener(LineState &State, Current.MatchingParen->Previous && Current.MatchingParen->Previous->is(tok::comma); AvoidBinPacking = EndsInComma || Current.is(TT_DictLiteral) || - Style.Language == FormatStyle::LK_Proto || - Style.Language == FormatStyle::LK_TextProto || - !Style.BinPackArguments || + Style.isProto() || !Style.BinPackArguments || (NextNonComment && NextNonComment->isOneOf( TT_DesignatedInitializerPeriod, TT_DesignatedInitializerLSquare)); diff --git a/clang/lib/Format/Format.cpp b/clang/lib/Format/Format.cpp index db0cb8a31084952424325034b047ef7a30d9455d..b09487435adb2da7a8f3908006b798d55d47dfc5 100644 --- a/clang/lib/Format/Format.cpp +++ b/clang/lib/Format/Format.cpp @@ -22,6 +22,7 @@ #include "FormatTokenLexer.h" #include "IntegerLiteralSeparatorFixer.h" #include "NamespaceEndCommentsFixer.h" +#include "ObjCPropertyAttributeOrderFixer.h" #include "QualifierAlignmentFixer.h" #include "SortJavaScriptImports.h" #include "TokenAnalyzer.h" @@ -1039,6 +1040,8 @@ template <> struct MappingTraits { IO.mapOptional("ObjCBlockIndentWidth", Style.ObjCBlockIndentWidth); IO.mapOptional("ObjCBreakBeforeNestedBlockParam", Style.ObjCBreakBeforeNestedBlockParam); + IO.mapOptional("ObjCPropertyAttributeOrder", + Style.ObjCPropertyAttributeOrder); IO.mapOptional("ObjCSpaceAfterProperty", Style.ObjCSpaceAfterProperty); IO.mapOptional("ObjCSpaceBeforeProtocolList", Style.ObjCSpaceBeforeProtocolList); @@ -3711,6 +3714,13 @@ reformat(const FormatStyle &Style, StringRef Code, }); } + if (Style.Language == FormatStyle::LK_ObjC && + !Style.ObjCPropertyAttributeOrder.empty()) { + Passes.emplace_back([&](const Environment &Env) { + return ObjCPropertyAttributeOrderFixer(Env, Expanded).process(); + }); + } + if (Style.isJavaScript() && Style.JavaScriptQuotes != FormatStyle::JSQS_Leave) { Passes.emplace_back([&](const Environment &Env) { diff --git a/clang/lib/Format/FormatToken.cpp b/clang/lib/Format/FormatToken.cpp index d994ed0488996cac503c6513dac7a79db9837cbf..7a2df8c53952f9da6eac42dae5209cefdcf1ef19 100644 --- a/clang/lib/Format/FormatToken.cpp +++ b/clang/lib/Format/FormatToken.cpp @@ -100,8 +100,7 @@ bool FormatToken::opensBlockOrBlockTypeList(const FormatStyle &Style) const { (is(tok::l_brace) && (getBlockKind() == BK_Block || is(TT_DictLiteral) || (!Style.Cpp11BracedListStyle && NestingLevel == 0))) || - (is(tok::less) && (Style.Language == FormatStyle::LK_Proto || - Style.Language == FormatStyle::LK_TextProto)); + (is(tok::less) && Style.isProto()); } TokenRole::~TokenRole() {} diff --git a/clang/lib/Format/FormatTokenLexer.cpp b/clang/lib/Format/FormatTokenLexer.cpp index a90ba4af2da84086046d41eb11261f13369a35a1..e4e32e2671df57d925ab5d18af39419f7b0660eb 100644 --- a/clang/lib/Format/FormatTokenLexer.cpp +++ b/clang/lib/Format/FormatTokenLexer.cpp @@ -253,7 +253,8 @@ void FormatTokenLexer::tryMergePreviousTokens() { TT_BinaryOperator)) { return; } - // Module paths in specify blocks and implications in properties. + // Module paths in specify blocks and the implication and boolean equality + // operators. if (tryMergeTokensAny({{tok::plusequal, tok::greater}, {tok::plus, tok::star, tok::greater}, {tok::minusequal, tok::greater}, @@ -265,7 +266,8 @@ void FormatTokenLexer::tryMergePreviousTokens() { {tok::pipe, tok::arrow}, {tok::hash, tok::minus, tok::hash}, {tok::hash, tok::equal, tok::hash}}, - TT_BinaryOperator)) { + TT_BinaryOperator) || + Tokens.back()->is(tok::arrow)) { Tokens.back()->ForcedPrecedence = prec::Comma; return; } @@ -1311,11 +1313,8 @@ void FormatTokenLexer::readRawToken(FormatToken &Tok) { } } - if ((Style.isJavaScript() || Style.Language == FormatStyle::LK_Proto || - Style.Language == FormatStyle::LK_TextProto) && - Tok.is(tok::char_constant)) { + if ((Style.isJavaScript() || Style.isProto()) && Tok.is(tok::char_constant)) Tok.Tok.setKind(tok::string_literal); - } if (Tok.is(tok::comment) && isClangFormatOn(Tok.TokenText)) FormattingDisabled = false; diff --git a/clang/lib/Format/ObjCPropertyAttributeOrderFixer.cpp b/clang/lib/Format/ObjCPropertyAttributeOrderFixer.cpp new file mode 100644 index 0000000000000000000000000000000000000000..c91d6251425eab508c3c196c66ba77774f9fabdb --- /dev/null +++ b/clang/lib/Format/ObjCPropertyAttributeOrderFixer.cpp @@ -0,0 +1,220 @@ +//===--- ObjCPropertyAttributeOrderFixer.cpp -------------------*- C++--*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This file implements ObjCPropertyAttributeOrderFixer, a TokenAnalyzer that +/// adjusts the order of attributes in an ObjC `@property(...)` declaration, +/// depending on the style. +/// +//===----------------------------------------------------------------------===// + +#include "ObjCPropertyAttributeOrderFixer.h" + +#include + +namespace clang { +namespace format { + +ObjCPropertyAttributeOrderFixer::ObjCPropertyAttributeOrderFixer( + const Environment &Env, const FormatStyle &Style) + : TokenAnalyzer(Env, Style) { + // Create an "order priority" map to use to sort properties. + unsigned Index = 0; + for (const auto &Property : Style.ObjCPropertyAttributeOrder) + SortOrderMap[Property] = Index++; +} + +struct ObjCPropertyEntry { + StringRef Attribute; // eg, `readwrite` + StringRef Value; // eg, the `foo` of the attribute `getter=foo` +}; + +void ObjCPropertyAttributeOrderFixer::sortPropertyAttributes( + const SourceManager &SourceMgr, tooling::Replacements &Fixes, + const FormatToken *BeginTok, const FormatToken *EndTok) { + assert(BeginTok); + assert(EndTok); + assert(EndTok->Previous); + + // If there are zero or one tokens, nothing to do. + if (BeginTok == EndTok || BeginTok->Next == EndTok) + return; + + // Use a set to sort attributes and remove duplicates. + std::set Ordinals; + + // Create a "remapping index" on how to reorder the attributes. + SmallVector Indices; + + // Collect the attributes. + SmallVector PropertyAttributes; + bool HasDuplicates = false; + int Index = 0; + for (auto Tok = BeginTok; Tok != EndTok; Tok = Tok->Next) { + assert(Tok); + if (Tok->is(tok::comma)) { + // Ignore the comma separators. + continue; + } + + // Most attributes look like identifiers, but `class` is a keyword. + if (!Tok->isOneOf(tok::identifier, tok::kw_class)) { + // If we hit any other kind of token, just bail. + return; + } + + const StringRef Attribute{Tok->TokenText}; + StringRef Value; + + // Also handle `getter=getFoo` attributes. + // (Note: no check needed against `EndTok`, since its type is not + // BinaryOperator or Identifier) + assert(Tok->Next); + if (Tok->Next->is(tok::equal)) { + Tok = Tok->Next; + assert(Tok->Next); + if (Tok->Next->isNot(tok::identifier)) { + // If we hit any other kind of token, just bail. It's unusual/illegal. + return; + } + Tok = Tok->Next; + Value = Tok->TokenText; + } + + auto It = SortOrderMap.find(Attribute); + if (It == SortOrderMap.end()) + It = SortOrderMap.insert({Attribute, SortOrderMap.size()}).first; + + // Sort the indices based on the priority stored in `SortOrderMap`. + const auto Ordinal = It->second; + if (!Ordinals.insert(Ordinal).second) { + HasDuplicates = true; + continue; + } + + if (Ordinal >= Indices.size()) + Indices.resize(Ordinal + 1); + Indices[Ordinal] = Index++; + + // Memoize the attribute. + PropertyAttributes.push_back({Attribute, Value}); + } + + if (!HasDuplicates) { + // There's nothing to do unless there's more than one attribute. + if (PropertyAttributes.size() < 2) + return; + + int PrevIndex = -1; + bool IsSorted = true; + for (const auto Ordinal : Ordinals) { + const auto Index = Indices[Ordinal]; + if (Index < PrevIndex) { + IsSorted = false; + break; + } + assert(Index > PrevIndex); + PrevIndex = Index; + } + + // If the property order is already correct, then no fix-up is needed. + if (IsSorted) + return; + } + + // Generate the replacement text. + std::string NewText; + bool IsFirst = true; + for (const auto Ordinal : Ordinals) { + if (IsFirst) + IsFirst = false; + else + NewText += ", "; + + const auto &PropertyEntry = PropertyAttributes[Indices[Ordinal]]; + NewText += PropertyEntry.Attribute; + + if (const auto Value = PropertyEntry.Value; !Value.empty()) { + NewText += '='; + NewText += Value; + } + } + + auto Range = CharSourceRange::getCharRange( + BeginTok->getStartOfNonWhitespace(), EndTok->Previous->Tok.getEndLoc()); + auto Replacement = tooling::Replacement(SourceMgr, Range, NewText); + auto Err = Fixes.add(Replacement); + if (Err) { + llvm::errs() << "Error while reodering ObjC property attributes : " + << llvm::toString(std::move(Err)) << "\n"; + } +} + +void ObjCPropertyAttributeOrderFixer::analyzeObjCPropertyDecl( + const SourceManager &SourceMgr, const AdditionalKeywords &Keywords, + tooling::Replacements &Fixes, const FormatToken *Tok) { + assert(Tok); + + // Expect `property` to be the very next token or else just bail early. + const FormatToken *const PropertyTok = Tok->Next; + if (!PropertyTok || PropertyTok->isNot(Keywords.kw_property)) + return; + + // Expect the opening paren to be the next token or else just bail early. + const FormatToken *const LParenTok = PropertyTok->getNextNonComment(); + if (!LParenTok || LParenTok->isNot(tok::l_paren)) + return; + + // Get the matching right-paren, the bounds for property attributes. + const FormatToken *const RParenTok = LParenTok->MatchingParen; + if (!RParenTok) + return; + + sortPropertyAttributes(SourceMgr, Fixes, LParenTok->Next, RParenTok); +} + +std::pair +ObjCPropertyAttributeOrderFixer::analyze( + TokenAnnotator & /*Annotator*/, + SmallVectorImpl &AnnotatedLines, + FormatTokenLexer &Tokens) { + tooling::Replacements Fixes; + const AdditionalKeywords &Keywords = Tokens.getKeywords(); + const SourceManager &SourceMgr = Env.getSourceManager(); + AffectedRangeMgr.computeAffectedLines(AnnotatedLines); + + for (AnnotatedLine *Line : AnnotatedLines) { + assert(Line); + if (!Line->Affected || Line->Type != LT_ObjCProperty) + continue; + FormatToken *First = Line->First; + assert(First); + if (First->Finalized) + continue; + + const auto *Last = Line->Last; + + for (const auto *Tok = First; Tok != Last; Tok = Tok->Next) { + assert(Tok); + + // Skip until the `@` of a `@property` declaration. + if (Tok->isNot(TT_ObjCProperty)) + continue; + + analyzeObjCPropertyDecl(SourceMgr, Keywords, Fixes, Tok); + + // There are never two `@property` in a line (they are split + // by other passes), so this pass can break after just one. + break; + } + } + return {Fixes, 0}; +} + +} // namespace format +} // namespace clang diff --git a/clang/lib/Format/ObjCPropertyAttributeOrderFixer.h b/clang/lib/Format/ObjCPropertyAttributeOrderFixer.h new file mode 100644 index 0000000000000000000000000000000000000000..d9ce85d144afb2b683731c12ae768d198b5ebead --- /dev/null +++ b/clang/lib/Format/ObjCPropertyAttributeOrderFixer.h @@ -0,0 +1,51 @@ +//===--- ObjCPropertyAttributeOrderFixer.h ------------------------------*- C++ +//-*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This file declares ObjCPropertyAttributeOrderFixer, a TokenAnalyzer that +/// adjusts the order of attributes in an ObjC `@property(...)` declaration, +/// depending on the style. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_LIB_FORMAT_OBJCPROPERTYATTRIBUTEORDERFIXER_H +#define LLVM_CLANG_LIB_FORMAT_OBJCPROPERTYATTRIBUTEORDERFIXER_H + +#include "TokenAnalyzer.h" + +namespace clang { +namespace format { + +class ObjCPropertyAttributeOrderFixer : public TokenAnalyzer { + llvm::StringMap SortOrderMap; + + void analyzeObjCPropertyDecl(const SourceManager &SourceMgr, + const AdditionalKeywords &Keywords, + tooling::Replacements &Fixes, + const FormatToken *Tok); + + void sortPropertyAttributes(const SourceManager &SourceMgr, + tooling::Replacements &Fixes, + const FormatToken *BeginTok, + const FormatToken *EndTok); + + std::pair + analyze(TokenAnnotator &Annotator, + SmallVectorImpl &AnnotatedLines, + FormatTokenLexer &Tokens) override; + +public: + ObjCPropertyAttributeOrderFixer(const Environment &Env, + const FormatStyle &Style); +}; + +} // end namespace format +} // end namespace clang + +#endif diff --git a/clang/lib/Format/TokenAnnotator.cpp b/clang/lib/Format/TokenAnnotator.cpp index 4a684e2dfc4bddcafd4ae8a3e154ef88fc19f2bc..eaccb5881ca30f5e381f20b1e17d36894041985d 100644 --- a/clang/lib/Format/TokenAnnotator.cpp +++ b/clang/lib/Format/TokenAnnotator.cpp @@ -227,8 +227,7 @@ private: } if (CurrentToken->isOneOf(tok::r_paren, tok::r_square, tok::r_brace) || (CurrentToken->isOneOf(tok::colon, tok::question) && InExprContext && - !Style.isCSharp() && Style.Language != FormatStyle::LK_Proto && - Style.Language != FormatStyle::LK_TextProto)) { + !Style.isCSharp() && !Style.isProto())) { return false; } // If a && or || is found and interpreted as a binary operator, this set @@ -678,8 +677,7 @@ private: } else if (CurrentToken->is(tok::r_square) && Parent && Parent->is(TT_TemplateCloser)) { Left->setType(TT_ArraySubscriptLSquare); - } else if (Style.Language == FormatStyle::LK_Proto || - Style.Language == FormatStyle::LK_TextProto) { + } else if (Style.isProto()) { // Square braces in LK_Proto can either be message field attributes: // // optional Aaa aaa = 1 [ @@ -906,8 +904,7 @@ private: Previous = Previous->getPreviousNonComment(); if ((CurrentToken->is(tok::colon) && (!Contexts.back().ColonIsDictLiteral || !Style.isCpp())) || - Style.Language == FormatStyle::LK_Proto || - Style.Language == FormatStyle::LK_TextProto) { + Style.isProto()) { OpeningBrace.setType(TT_DictLiteral); if (Previous->Tok.getIdentifierInfo() || Previous->is(tok::string_literal)) { @@ -1049,9 +1046,7 @@ private: Line.First->startsSequence(tok::kw_export, Keywords.kw_module) || Line.First->startsSequence(tok::kw_export, Keywords.kw_import)) { Tok->setType(TT_ModulePartitionColon); - } else if (Contexts.back().ColonIsDictLiteral || - Style.Language == FormatStyle::LK_Proto || - Style.Language == FormatStyle::LK_TextProto) { + } else if (Contexts.back().ColonIsDictLiteral || Style.isProto()) { Tok->setType(TT_DictLiteral); if (Style.Language == FormatStyle::LK_TextProto) { if (FormatToken *Previous = Tok->getPreviousNonComment()) @@ -1290,10 +1285,8 @@ private: Tok->SpacesRequiredBefore = 1; break; case tok::kw_operator: - if (Style.Language == FormatStyle::LK_TextProto || - Style.Language == FormatStyle::LK_Proto) { + if (Style.isProto()) break; - } while (CurrentToken && !CurrentToken->isOneOf(tok::l_paren, tok::semi, tok::r_paren)) { if (CurrentToken->isOneOf(tok::star, tok::amp)) @@ -2028,6 +2021,9 @@ private: } else if (Current.is(tok::arrow) && Style.Language == FormatStyle::LK_Java) { Current.setType(TT_TrailingReturnArrow); + } else if (Current.is(tok::arrow) && Style.isVerilog()) { + // The implication operator. + Current.setType(TT_BinaryOperator); } else if (Current.is(tok::arrow) && AutoFound && Line.MightBeFunctionDecl && Current.NestingLevel == 0 && !Current.Previous->isOneOf(tok::kw_operator, tok::identifier)) { @@ -2874,9 +2870,7 @@ private: return prec::Conditional; if (NextNonComment && Current->is(TT_SelectorName) && (NextNonComment->isOneOf(TT_DictLiteral, TT_JsTypeColon) || - ((Style.Language == FormatStyle::LK_Proto || - Style.Language == FormatStyle::LK_TextProto) && - NextNonComment->is(tok::less)))) { + (Style.isProto() && NextNonComment->is(tok::less)))) { return prec::Assignment; } if (Current->is(TT_JsComputedPropertyName)) @@ -3737,7 +3731,7 @@ unsigned TokenAnnotator::splitPenalty(const AnnotatedLine &Line, // Prefer breaking call chains (".foo") over empty "{}", "[]" or "()". if (Left.opensScope() && Right.closesScope()) return 200; - } else if (Style.isProto()) { + } else if (Style.Language == FormatStyle::LK_Proto) { if (Right.is(tok::l_square)) return 1; if (Right.is(tok::period)) @@ -4191,9 +4185,7 @@ bool TokenAnnotator::spaceRequiredBetween(const AnnotatedLine &Line, const auto SpaceRequiredForArrayInitializerLSquare = [](const FormatToken &LSquareTok, const FormatStyle &Style) { return Style.SpacesInContainerLiterals || - ((Style.Language == FormatStyle::LK_Proto || - Style.Language == FormatStyle::LK_TextProto) && - !Style.Cpp11BracedListStyle && + (Style.isProto() && !Style.Cpp11BracedListStyle && LSquareTok.endsSequence(tok::l_square, tok::colon, TT_SelectorName)); }; @@ -4459,8 +4451,7 @@ bool TokenAnnotator::spaceRequiredBefore(const AnnotatedLine &Line, Right.is(TT_TemplateOpener)) { return true; } - } else if (Style.Language == FormatStyle::LK_Proto || - Style.Language == FormatStyle::LK_TextProto) { + } else if (Style.isProto()) { if (Right.is(tok::period) && Left.isOneOf(Keywords.kw_optional, Keywords.kw_required, Keywords.kw_repeated, Keywords.kw_extend)) { @@ -4717,6 +4708,9 @@ bool TokenAnnotator::spaceRequiredBefore(const AnnotatedLine &Line, (Left.is(TT_VerilogNumberBase) && Right.is(tok::numeric_constant))) { return false; } + // Add spaces around the implication operator `->`. + if (Left.is(tok::arrow) || Right.is(tok::arrow)) + return true; // Don't add spaces between two at signs. Like in a coverage event. // Don't add spaces between at and a sensitivity list like // `@(posedge clk)`. @@ -4727,8 +4721,15 @@ bool TokenAnnotator::spaceRequiredBefore(const AnnotatedLine &Line, Left.isOneOf(TT_VerilogDimensionedTypeName, Keywords.kw_function)) { return true; } + // In a tagged union expression, there should be a space after the tag. + if (Right.isOneOf(tok::period, Keywords.kw_apostrophe) && + Keywords.isVerilogIdentifier(Left) && Left.getPreviousNonComment() && + Left.getPreviousNonComment()->is(Keywords.kw_tagged)) { + return true; + } // Don't add spaces between a casting type and the quote or repetition count - // and the brace. + // and the brace. The case of tagged union expressions is handled by the + // previous rule. if ((Right.is(Keywords.kw_apostrophe) || (Right.is(BK_BracedInit) && Right.is(tok::l_brace))) && !(Left.isOneOf(Keywords.kw_assign, Keywords.kw_unique) || @@ -5079,8 +5080,7 @@ bool TokenAnnotator::mustBreakBefore(const AnnotatedLine &Line, return true; } else if (Style.BreakAdjacentStringLiterals && (Style.isCpp() || Style.isProto() || - Style.Language == FormatStyle::LK_TableGen || - Style.Language == FormatStyle::LK_TextProto)) { + Style.Language == FormatStyle::LK_TableGen)) { if (Left.isStringLiteral() && Right.isStringLiteral()) return true; } @@ -5332,9 +5332,8 @@ bool TokenAnnotator::mustBreakBefore(const AnnotatedLine &Line, // together. // // We ensure elsewhere that extensions are always on their own line. - if ((Style.Language == FormatStyle::LK_Proto || - Style.Language == FormatStyle::LK_TextProto) && - Right.is(TT_SelectorName) && Right.isNot(tok::r_square) && Right.Next) { + if (Style.isProto() && Right.is(TT_SelectorName) && + Right.isNot(tok::r_square) && Right.Next) { // Keep `@submessage` together in: // @submessage { key: value } if (Left.is(tok::at)) @@ -5548,8 +5547,7 @@ bool TokenAnnotator::canBreakBefore(const AnnotatedLine &Line, return false; } if (Left.is(tok::colon) && Left.isOneOf(TT_DictLiteral, TT_ObjCMethodExpr)) { - if (Style.Language == FormatStyle::LK_Proto || - Style.Language == FormatStyle::LK_TextProto) { + if (Style.isProto()) { if (!Style.AlwaysBreakBeforeMultilineStrings && Right.isStringLiteral()) return false; // Prevent cases like: diff --git a/clang/lib/Format/UnwrappedLineFormatter.cpp b/clang/lib/Format/UnwrappedLineFormatter.cpp index 280485d9a90d1bfde4ee2032db5219deb05d81e1..40730cd53529ed356fc5be78005f88e069bbe08d 100644 --- a/clang/lib/Format/UnwrappedLineFormatter.cpp +++ b/clang/lib/Format/UnwrappedLineFormatter.cpp @@ -939,6 +939,12 @@ private: }; static void markFinalized(FormatToken *Tok) { + if (Tok->is(tok::hash) && !Tok->Previous && Tok->Next && + Tok->Next->isOneOf(tok::pp_if, tok::pp_ifdef, tok::pp_ifndef, + tok::pp_elif, tok::pp_elifdef, tok::pp_elifndef, + tok::pp_else, tok::pp_endif)) { + Tok = Tok->Next; + } for (; Tok; Tok = Tok->Next) { if (Tok->MacroCtx && Tok->MacroCtx->Role == MR_ExpandedArg) { // In the first pass we format all macro arguments in the expanded token @@ -1060,6 +1066,8 @@ protected: } Penalty += formatLine(*Child, State.Column + 1, /*FirstStartColumn=*/0, DryRun); + if (!DryRun) + markFinalized(Child->First); State.Column += 1 + Child->Last->TotalLength; return true; @@ -1429,16 +1437,8 @@ unsigned UnwrappedLineFormatter::format( NextLine = Joiner.getNextMergedLine(DryRun, IndentTracker); RangeMinLevel = UINT_MAX; } - if (!DryRun) { - auto *Tok = TheLine.First; - if (Tok->is(tok::hash) && !Tok->Previous && Tok->Next && - Tok->Next->isOneOf(tok::pp_if, tok::pp_ifdef, tok::pp_ifndef, - tok::pp_elif, tok::pp_elifdef, tok::pp_elifndef, - tok::pp_else, tok::pp_endif)) { - Tok = Tok->Next; - } - markFinalized(Tok); - } + if (!DryRun) + markFinalized(TheLine.First); } PenaltyCache[CacheKey] = Penalty; return Penalty; diff --git a/clang/lib/Format/UnwrappedLineParser.cpp b/clang/lib/Format/UnwrappedLineParser.cpp index 7b4ec25a8938fc0c5ffe84dc749a340020a85a78..57126b8dfeac5de62da5b519c1ad0e0b3b5cdc62 100644 --- a/clang/lib/Format/UnwrappedLineParser.cpp +++ b/clang/lib/Format/UnwrappedLineParser.cpp @@ -47,8 +47,7 @@ void printLine(llvm::raw_ostream &OS, const UnwrappedLine &Line, OS << Prefix; NewLine = false; } - OS << I->Tok->Tok.getName() << "[" - << "T=" << (unsigned)I->Tok->getType() + OS << I->Tok->Tok.getName() << "[" << "T=" << (unsigned)I->Tok->getType() << ", OC=" << I->Tok->OriginalColumn << ", \"" << I->Tok->TokenText << "\"] "; for (SmallVectorImpl::const_iterator @@ -438,7 +437,7 @@ bool UnwrappedLineParser::parseLevel(const FormatToken *OpeningBrace, [[fallthrough]]; } case tok::kw_case: - if (Style.isProto() || Style.isVerilog() || + if (Style.Language == FormatStyle::LK_Proto || Style.isVerilog() || (Style.isJavaScript() && Line->MustBeDeclaration)) { // Proto: there are no switch/case statements // Verilog: Case labels don't have this word. We handle case @@ -496,7 +495,7 @@ void UnwrappedLineParser::calculateBraceTypes(bool ExpectClassBody) { do { NextTok = Tokens->getNextToken(); } while (NextTok->is(tok::comment)); - while (NextTok->is(tok::hash)) { + while (NextTok->is(tok::hash) && !Line->InMacroBody) { NextTok = Tokens->getNextToken(); do { NextTok = Tokens->getNextToken(); @@ -1527,7 +1526,7 @@ void UnwrappedLineParser::parseStructuralElement( break; case tok::kw_case: // Proto: there are no switch/case statements. - if (Style.isProto()) { + if (Style.Language == FormatStyle::LK_Proto) { nextToken(); return; } @@ -2034,7 +2033,7 @@ void UnwrappedLineParser::parseStructuralElement( break; case tok::kw_case: // Proto: there are no switch/case statements. - if (Style.isProto()) { + if (Style.Language == FormatStyle::LK_Proto) { nextToken(); return; } diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp index 5a8e4cf9843de2bc827ba64125caa3d178943d0b..56de0f75928ca4245aaa54e900e1636659099274 100644 --- a/clang/lib/Frontend/CompilerInvocation.cpp +++ b/clang/lib/Frontend/CompilerInvocation.cpp @@ -1861,6 +1861,20 @@ bool CompilerInvocation::ParseCodeGenArgs(CodeGenOptions &Opts, ArgList &Args, if (Args.hasArg(OPT_funified_lto)) Opts.PrepareForThinLTO = true; } + if (Arg *A = Args.getLastArg(options::OPT_ffat_lto_objects, + options::OPT_fno_fat_lto_objects)) { + if (A->getOption().matches(options::OPT_ffat_lto_objects)) { + if (Arg *Uni = Args.getLastArg(options::OPT_funified_lto, + options::OPT_fno_unified_lto)) { + if (Uni->getOption().matches(options::OPT_fno_unified_lto)) + Diags.Report(diag::err_drv_incompatible_options) + << A->getAsString(Args) << "-fno-unified-lto"; + } else + Diags.Report(diag::err_drv_argument_only_allowed_with) + << A->getAsString(Args) << "-funified-lto"; + } + } + if (Arg *A = Args.getLastArg(OPT_fthinlto_index_EQ)) { if (IK.getLanguage() != Language::LLVM_IR) Diags.Report(diag::err_drv_argument_only_allowed_with) @@ -4145,10 +4159,10 @@ bool CompilerInvocation::ParseLangArgs(LangOptions &Opts, ArgList &Args, if (Arg *A = Args.getLastArg(OPT_msign_return_address_key_EQ)) { StringRef SignKey = A->getValue(); if (!SignScope.empty() && !SignKey.empty()) { - if (SignKey.equals_insensitive("a_key")) + if (SignKey == "a_key") Opts.setSignReturnAddressKey( LangOptions::SignReturnAddressKeyKind::AKey); - else if (SignKey.equals_insensitive("b_key")) + else if (SignKey == "b_key") Opts.setSignReturnAddressKey( LangOptions::SignReturnAddressKeyKind::BKey); else @@ -4773,6 +4787,18 @@ std::string CompilerInvocation::getModuleHash() const { if (getCodeGenOpts().DebugTypeExtRefs) HBuilder.addRange(getCodeGenOpts().DebugPrefixMap); + // Extend the signature with the affecting debug options. + if (getHeaderSearchOpts().ModuleFormat == "obj") { +#define DEBUGOPT(Name, Bits, Default) HBuilder.add(CodeGenOpts->Name); +#define VALUE_DEBUGOPT(Name, Bits, Default) HBuilder.add(CodeGenOpts->Name); +#define ENUM_DEBUGOPT(Name, Type, Bits, Default) \ + HBuilder.add(static_cast(CodeGenOpts->get##Name())); +#define BENIGN_DEBUGOPT(Name, Bits, Default) +#define BENIGN_VALUE_DEBUGOPT(Name, Bits, Default) +#define BENIGN_ENUM_DEBUGOPT(Name, Type, Bits, Default) +#include "clang/Basic/DebugOptions.def" + } + // Extend the signature with the enabled sanitizers, if at least one is // enabled. Sanitizers which cannot affect AST generation aren't hashed. SanitizerSet SanHash = getLangOpts().Sanitize; @@ -4819,6 +4845,7 @@ std::vector CompilerInvocationBase::getCC1CommandLine() const { void CompilerInvocation::resetNonModularOptions() { getLangOpts().resetNonModularOptions(); getPreprocessorOpts().resetNonModularOptions(); + getCodeGenOpts().resetNonModularOptions(getHeaderSearchOpts().ModuleFormat); } void CompilerInvocation::clearImplicitModuleBuildOptions() { diff --git a/clang/lib/Headers/bmiintrin.h b/clang/lib/Headers/bmiintrin.h index ffb94bea639af6993956c11d4485e64df8759bd8..bc7c8a03c5e2a0de64a2a8bfe7e9a5b563ad7453 100644 --- a/clang/lib/Headers/bmiintrin.h +++ b/clang/lib/Headers/bmiintrin.h @@ -19,7 +19,7 @@ to use it as a potentially faster version of BSF. */ #define __RELAXED_FN_ATTRS __attribute__((__always_inline__, __nodebug__)) -#define _tzcnt_u16(a) (__tzcnt_u16((a))) +#define _tzcnt_u16 __tzcnt_u16 /// Counts the number of trailing zero bits in the operand. /// @@ -71,7 +71,7 @@ _mm_tzcnt_32(unsigned int __X) return (int)__builtin_ia32_tzcnt_u32(__X); } -#define _tzcnt_u32(a) (__tzcnt_u32((a))) +#define _tzcnt_u32 __tzcnt_u32 #ifdef __x86_64__ @@ -109,7 +109,7 @@ _mm_tzcnt_64(unsigned long long __X) return (long long)__builtin_ia32_tzcnt_u64(__X); } -#define _tzcnt_u64(a) (__tzcnt_u64((a))) +#define _tzcnt_u64 __tzcnt_u64 #endif /* __x86_64__ */ @@ -121,14 +121,14 @@ _mm_tzcnt_64(unsigned long long __X) /* Define the default attributes for the functions in this file. */ #define __DEFAULT_FN_ATTRS __attribute__((__always_inline__, __nodebug__, __target__("bmi"))) -#define _andn_u32(a, b) (__andn_u32((a), (b))) +#define _andn_u32 __andn_u32 /* _bextr_u32 != __bextr_u32 */ -#define _blsi_u32(a) (__blsi_u32((a))) +#define _blsi_u32 __blsi_u32 -#define _blsmsk_u32(a) (__blsmsk_u32((a))) +#define _blsmsk_u32 __blsmsk_u32 -#define _blsr_u32(a) (__blsr_u32((a))) +#define _blsr_u32 __blsr_u32 /// Performs a bitwise AND of the second operand with the one's /// complement of the first operand. @@ -272,14 +272,14 @@ __blsr_u32(unsigned int __X) #ifdef __x86_64__ -#define _andn_u64(a, b) (__andn_u64((a), (b))) +#define _andn_u64 __andn_u64 /* _bextr_u64 != __bextr_u64 */ -#define _blsi_u64(a) (__blsi_u64((a))) +#define _blsi_u64 __blsi_u64 -#define _blsmsk_u64(a) (__blsmsk_u64((a))) +#define _blsmsk_u64 __blsmsk_u64 -#define _blsr_u64(a) (__blsr_u64((a))) +#define _blsr_u64 __blsr_u64 /// Performs a bitwise AND of the second operand with the one's /// complement of the first operand. diff --git a/clang/lib/Headers/opencl-c-base.h b/clang/lib/Headers/opencl-c-base.h index d56e5ceae652ad51a00424f403523874da87da9b..2494f6213fc5695d9a4c3b70d29ba4f42e0ed7d0 100644 --- a/clang/lib/Headers/opencl-c-base.h +++ b/clang/lib/Headers/opencl-c-base.h @@ -819,64 +819,6 @@ int printf(__constant const char* st, ...) __attribute__((format(printf, 1, 2))) #endif // cl_intel_device_side_avc_motion_estimation -/** - * Compute square root. - * - * Provide inline implementations using the builtin so that we get appropriate - * !fpmath based on -cl-fp32-correctly-rounded-divide-sqrt, attached to - * llvm.sqrt. The implementation should still provide an external definition. - */ -#define __ovld __attribute__((overloadable)) -#define __cnfn __attribute__((const)) - -inline float __ovld __cnfn sqrt(float __x) { - return __builtin_elementwise_sqrt(__x); -} - -inline float2 __ovld __cnfn sqrt(float2 __x) { - return __builtin_elementwise_sqrt(__x); -} - -inline float3 __ovld __cnfn sqrt(float3 __x) { - return __builtin_elementwise_sqrt(__x); -} - -inline float4 __ovld __cnfn sqrt(float4 __x) { - return __builtin_elementwise_sqrt(__x); -} - -inline float8 __ovld __cnfn sqrt(float8 __x) { - return __builtin_elementwise_sqrt(__x); -} - -inline float16 __ovld __cnfn sqrt(float16 __x) { - return __builtin_elementwise_sqrt(__x); -} - -// We only really want to define the float variants here. However -// -fdeclare-opencl-builtins will not work if some overloads are already - // provided in the base header, so provide all overloads here. - -#ifdef cl_khr_fp64 -double __ovld __cnfn sqrt(double); -double2 __ovld __cnfn sqrt(double2); -double3 __ovld __cnfn sqrt(double3); -double4 __ovld __cnfn sqrt(double4); -double8 __ovld __cnfn sqrt(double8); -double16 __ovld __cnfn sqrt(double16); -#endif //cl_khr_fp64 -#ifdef cl_khr_fp16 -half __ovld __cnfn sqrt(half); -half2 __ovld __cnfn sqrt(half2); -half3 __ovld __cnfn sqrt(half3); -half4 __ovld __cnfn sqrt(half4); -half8 __ovld __cnfn sqrt(half8); -half16 __ovld __cnfn sqrt(half16); -#endif //cl_khr_fp16 - -#undef __cnfn -#undef __ovld - // Disable any extensions we may have enabled previously. #pragma OPENCL EXTENSION all : disable diff --git a/clang/lib/Headers/opencl-c.h b/clang/lib/Headers/opencl-c.h index 1efbbf8f8ee6a01b024986bcff847a1cb8cc17a8..288bb18bc654ebc8208ec6b56fab4fa45019a733 100644 --- a/clang/lib/Headers/opencl-c.h +++ b/clang/lib/Headers/opencl-c.h @@ -8496,6 +8496,32 @@ half8 __ovld __cnfn sinpi(half8); half16 __ovld __cnfn sinpi(half16); #endif //cl_khr_fp16 +/** + * Compute square root. + */ +float __ovld __cnfn sqrt(float); +float2 __ovld __cnfn sqrt(float2); +float3 __ovld __cnfn sqrt(float3); +float4 __ovld __cnfn sqrt(float4); +float8 __ovld __cnfn sqrt(float8); +float16 __ovld __cnfn sqrt(float16); +#ifdef cl_khr_fp64 +double __ovld __cnfn sqrt(double); +double2 __ovld __cnfn sqrt(double2); +double3 __ovld __cnfn sqrt(double3); +double4 __ovld __cnfn sqrt(double4); +double8 __ovld __cnfn sqrt(double8); +double16 __ovld __cnfn sqrt(double16); +#endif //cl_khr_fp64 +#ifdef cl_khr_fp16 +half __ovld __cnfn sqrt(half); +half2 __ovld __cnfn sqrt(half2); +half3 __ovld __cnfn sqrt(half3); +half4 __ovld __cnfn sqrt(half4); +half8 __ovld __cnfn sqrt(half8); +half16 __ovld __cnfn sqrt(half16); +#endif //cl_khr_fp16 + /** * Compute tangent. */ diff --git a/clang/lib/Headers/riscv_crypto.h b/clang/lib/Headers/riscv_crypto.h index 39ee0898af0ff050b6a44346b1700444f8c63c86..7cd2a708f5575635ef9273921ef842ef3f05e1aa 100644 --- a/clang/lib/Headers/riscv_crypto.h +++ b/clang/lib/Headers/riscv_crypto.h @@ -57,7 +57,7 @@ __riscv_aes64esm(uint64_t __x, uint64_t __y) { return __builtin_riscv_aes64esm(__x, __y); } #endif -#endif // defined(__riscv_zknd) +#endif // defined(__riscv_zkne) #if defined(__riscv_zknd) || defined(__riscv_zkne) #if __riscv_xlen == 64 @@ -112,21 +112,11 @@ __riscv_sha512sig1l(uint32_t __x, uint32_t __y) { return __builtin_riscv_sha512sig1l(__x, __y); } -static __inline__ uint32_t __attribute__((__always_inline__, __nodebug__)) -__riscv_sha512sum0l(uint32_t __x, uint32_t __y) { - return __builtin_riscv_sha512sum0l(__x, __y); -} - static __inline__ uint32_t __attribute__((__always_inline__, __nodebug__)) __riscv_sha512sum0r(uint32_t __x, uint32_t __y) { return __builtin_riscv_sha512sum0r(__x, __y); } -static __inline__ uint32_t __attribute__((__always_inline__, __nodebug__)) -__riscv_sha512sum1l(uint32_t __x, uint32_t __y) { - return __builtin_riscv_sha512sum1l(__x, __y); -} - static __inline__ uint32_t __attribute__((__always_inline__, __nodebug__)) __riscv_sha512sum1r(uint32_t __x, uint32_t __y) { return __builtin_riscv_sha512sum1r(__x, __y); @@ -171,7 +161,7 @@ __riscv_sm3p1(uint32_t __x) { #if defined(__riscv_zksed) #define __riscv_sm4ed(x, y, bs) __builtin_riscv_sm4ed(x, y, bs); #define __riscv_sm4ks(x, y, bs) __builtin_riscv_sm4ks(x, y, bs); -#endif // defined(__riscv_zksh) +#endif // defined(__riscv_zksed) #if defined(__cplusplus) } diff --git a/clang/lib/Headers/vecintrin.h b/clang/lib/Headers/vecintrin.h index ec1dbfd015f6ef4d20fe344afb870409bfa6fba6..ecfd6cd1a2f87d87c38d228e6ef67cc2aef2e78d 100644 --- a/clang/lib/Headers/vecintrin.h +++ b/clang/lib/Headers/vecintrin.h @@ -1543,7 +1543,7 @@ vec_load_len(const double *__ptr, unsigned int __len) { #if __ARCH__ >= 12 static inline __ATTRS_ai __vector unsigned char vec_load_len_r(const unsigned char *__ptr, unsigned int __len) { - return (__vector unsigned char)__builtin_s390_vlrl(__len, __ptr); + return (__vector unsigned char)__builtin_s390_vlrlr(__len, __ptr); } #endif @@ -1617,7 +1617,7 @@ vec_store_len(__vector double __vec, double *__ptr, static inline __ATTRS_ai void vec_store_len_r(__vector unsigned char __vec, unsigned char *__ptr, unsigned int __len) { - __builtin_s390_vstrl((__vector signed char)__vec, __len, __ptr); + __builtin_s390_vstrlr((__vector signed char)__vec, __len, __ptr); } #endif @@ -2689,7 +2689,8 @@ vec_cmplt(__vector double __a, __vector double __b) { static inline __ATTRS_o_ai int vec_all_eq(__vector signed char __a, __vector signed char __b) { int __cc; - __builtin_s390_vceqbs(__a, __b, &__cc); + __builtin_s390_vceqbs((__vector unsigned char)__a, + (__vector unsigned char)__b, &__cc); return __cc == 0; } @@ -2697,7 +2698,8 @@ vec_all_eq(__vector signed char __a, __vector signed char __b) { static inline __ATTRS_o_ai int vec_all_eq(__vector signed char __a, __vector __bool char __b) { int __cc; - __builtin_s390_vceqbs(__a, (__vector signed char)__b, &__cc); + __builtin_s390_vceqbs((__vector unsigned char)__a, + (__vector unsigned char)__b, &__cc); return __cc == 0; } @@ -2705,15 +2707,15 @@ vec_all_eq(__vector signed char __a, __vector __bool char __b) { static inline __ATTRS_o_ai int vec_all_eq(__vector __bool char __a, __vector signed char __b) { int __cc; - __builtin_s390_vceqbs((__vector signed char)__a, __b, &__cc); + __builtin_s390_vceqbs((__vector unsigned char)__a, + (__vector unsigned char)__b, &__cc); return __cc == 0; } static inline __ATTRS_o_ai int vec_all_eq(__vector unsigned char __a, __vector unsigned char __b) { int __cc; - __builtin_s390_vceqbs((__vector signed char)__a, - (__vector signed char)__b, &__cc); + __builtin_s390_vceqbs(__a, __b, &__cc); return __cc == 0; } @@ -2721,8 +2723,7 @@ vec_all_eq(__vector unsigned char __a, __vector unsigned char __b) { static inline __ATTRS_o_ai int vec_all_eq(__vector unsigned char __a, __vector __bool char __b) { int __cc; - __builtin_s390_vceqbs((__vector signed char)__a, - (__vector signed char)__b, &__cc); + __builtin_s390_vceqbs(__a, (__vector unsigned char)__b, &__cc); return __cc == 0; } @@ -2730,23 +2731,23 @@ vec_all_eq(__vector unsigned char __a, __vector __bool char __b) { static inline __ATTRS_o_ai int vec_all_eq(__vector __bool char __a, __vector unsigned char __b) { int __cc; - __builtin_s390_vceqbs((__vector signed char)__a, - (__vector signed char)__b, &__cc); + __builtin_s390_vceqbs((__vector unsigned char)__a, __b, &__cc); return __cc == 0; } static inline __ATTRS_o_ai int vec_all_eq(__vector __bool char __a, __vector __bool char __b) { int __cc; - __builtin_s390_vceqbs((__vector signed char)__a, - (__vector signed char)__b, &__cc); + __builtin_s390_vceqbs((__vector unsigned char)__a, + (__vector unsigned char)__b, &__cc); return __cc == 0; } static inline __ATTRS_o_ai int vec_all_eq(__vector signed short __a, __vector signed short __b) { int __cc; - __builtin_s390_vceqhs(__a, __b, &__cc); + __builtin_s390_vceqhs((__vector unsigned short)__a, + (__vector unsigned short)__b, &__cc); return __cc == 0; } @@ -2754,7 +2755,8 @@ vec_all_eq(__vector signed short __a, __vector signed short __b) { static inline __ATTRS_o_ai int vec_all_eq(__vector signed short __a, __vector __bool short __b) { int __cc; - __builtin_s390_vceqhs(__a, (__vector signed short)__b, &__cc); + __builtin_s390_vceqhs((__vector unsigned short)__a, + (__vector unsigned short)__b, &__cc); return __cc == 0; } @@ -2762,15 +2764,15 @@ vec_all_eq(__vector signed short __a, __vector __bool short __b) { static inline __ATTRS_o_ai int vec_all_eq(__vector __bool short __a, __vector signed short __b) { int __cc; - __builtin_s390_vceqhs((__vector signed short)__a, __b, &__cc); + __builtin_s390_vceqhs((__vector unsigned short)__a, + (__vector unsigned short)__b, &__cc); return __cc == 0; } static inline __ATTRS_o_ai int vec_all_eq(__vector unsigned short __a, __vector unsigned short __b) { int __cc; - __builtin_s390_vceqhs((__vector signed short)__a, - (__vector signed short)__b, &__cc); + __builtin_s390_vceqhs(__a, __b, &__cc); return __cc == 0; } @@ -2778,8 +2780,7 @@ vec_all_eq(__vector unsigned short __a, __vector unsigned short __b) { static inline __ATTRS_o_ai int vec_all_eq(__vector unsigned short __a, __vector __bool short __b) { int __cc; - __builtin_s390_vceqhs((__vector signed short)__a, - (__vector signed short)__b, &__cc); + __builtin_s390_vceqhs(__a, (__vector unsigned short)__b, &__cc); return __cc == 0; } @@ -2787,23 +2788,23 @@ vec_all_eq(__vector unsigned short __a, __vector __bool short __b) { static inline __ATTRS_o_ai int vec_all_eq(__vector __bool short __a, __vector unsigned short __b) { int __cc; - __builtin_s390_vceqhs((__vector signed short)__a, - (__vector signed short)__b, &__cc); + __builtin_s390_vceqhs((__vector unsigned short)__a, __b, &__cc); return __cc == 0; } static inline __ATTRS_o_ai int vec_all_eq(__vector __bool short __a, __vector __bool short __b) { int __cc; - __builtin_s390_vceqhs((__vector signed short)__a, - (__vector signed short)__b, &__cc); + __builtin_s390_vceqhs((__vector unsigned short)__a, + (__vector unsigned short)__b, &__cc); return __cc == 0; } static inline __ATTRS_o_ai int vec_all_eq(__vector signed int __a, __vector signed int __b) { int __cc; - __builtin_s390_vceqfs(__a, __b, &__cc); + __builtin_s390_vceqfs((__vector unsigned int)__a, + (__vector unsigned int)__b, &__cc); return __cc == 0; } @@ -2811,7 +2812,8 @@ vec_all_eq(__vector signed int __a, __vector signed int __b) { static inline __ATTRS_o_ai int vec_all_eq(__vector signed int __a, __vector __bool int __b) { int __cc; - __builtin_s390_vceqfs(__a, (__vector signed int)__b, &__cc); + __builtin_s390_vceqfs((__vector unsigned int)__a, + (__vector unsigned int)__b, &__cc); return __cc == 0; } @@ -2819,15 +2821,15 @@ vec_all_eq(__vector signed int __a, __vector __bool int __b) { static inline __ATTRS_o_ai int vec_all_eq(__vector __bool int __a, __vector signed int __b) { int __cc; - __builtin_s390_vceqfs((__vector signed int)__a, __b, &__cc); + __builtin_s390_vceqfs((__vector unsigned int)__a, + (__vector unsigned int)__b, &__cc); return __cc == 0; } static inline __ATTRS_o_ai int vec_all_eq(__vector unsigned int __a, __vector unsigned int __b) { int __cc; - __builtin_s390_vceqfs((__vector signed int)__a, - (__vector signed int)__b, &__cc); + __builtin_s390_vceqfs(__a, __b, &__cc); return __cc == 0; } @@ -2835,8 +2837,7 @@ vec_all_eq(__vector unsigned int __a, __vector unsigned int __b) { static inline __ATTRS_o_ai int vec_all_eq(__vector unsigned int __a, __vector __bool int __b) { int __cc; - __builtin_s390_vceqfs((__vector signed int)__a, - (__vector signed int)__b, &__cc); + __builtin_s390_vceqfs(__a, (__vector unsigned int)__b, &__cc); return __cc == 0; } @@ -2844,23 +2845,23 @@ vec_all_eq(__vector unsigned int __a, __vector __bool int __b) { static inline __ATTRS_o_ai int vec_all_eq(__vector __bool int __a, __vector unsigned int __b) { int __cc; - __builtin_s390_vceqfs((__vector signed int)__a, - (__vector signed int)__b, &__cc); + __builtin_s390_vceqfs((__vector unsigned int)__a, __b, &__cc); return __cc == 0; } static inline __ATTRS_o_ai int vec_all_eq(__vector __bool int __a, __vector __bool int __b) { int __cc; - __builtin_s390_vceqfs((__vector signed int)__a, - (__vector signed int)__b, &__cc); + __builtin_s390_vceqfs((__vector unsigned int)__a, + (__vector unsigned int)__b, &__cc); return __cc == 0; } static inline __ATTRS_o_ai int vec_all_eq(__vector signed long long __a, __vector signed long long __b) { int __cc; - __builtin_s390_vceqgs(__a, __b, &__cc); + __builtin_s390_vceqgs((__vector unsigned long long)__a, + (__vector unsigned long long)__b, &__cc); return __cc == 0; } @@ -2868,7 +2869,8 @@ vec_all_eq(__vector signed long long __a, __vector signed long long __b) { static inline __ATTRS_o_ai int vec_all_eq(__vector signed long long __a, __vector __bool long long __b) { int __cc; - __builtin_s390_vceqgs(__a, (__vector signed long long)__b, &__cc); + __builtin_s390_vceqgs((__vector unsigned long long)__a, + (__vector unsigned long long)__b, &__cc); return __cc == 0; } @@ -2876,15 +2878,15 @@ vec_all_eq(__vector signed long long __a, __vector __bool long long __b) { static inline __ATTRS_o_ai int vec_all_eq(__vector __bool long long __a, __vector signed long long __b) { int __cc; - __builtin_s390_vceqgs((__vector signed long long)__a, __b, &__cc); + __builtin_s390_vceqgs((__vector unsigned long long)__a, + (__vector unsigned long long)__b, &__cc); return __cc == 0; } static inline __ATTRS_o_ai int vec_all_eq(__vector unsigned long long __a, __vector unsigned long long __b) { int __cc; - __builtin_s390_vceqgs((__vector signed long long)__a, - (__vector signed long long)__b, &__cc); + __builtin_s390_vceqgs(__a, __b, &__cc); return __cc == 0; } @@ -2892,8 +2894,7 @@ vec_all_eq(__vector unsigned long long __a, __vector unsigned long long __b) { static inline __ATTRS_o_ai int vec_all_eq(__vector unsigned long long __a, __vector __bool long long __b) { int __cc; - __builtin_s390_vceqgs((__vector signed long long)__a, - (__vector signed long long)__b, &__cc); + __builtin_s390_vceqgs(__a, (__vector unsigned long long)__b, &__cc); return __cc == 0; } @@ -2901,16 +2902,15 @@ vec_all_eq(__vector unsigned long long __a, __vector __bool long long __b) { static inline __ATTRS_o_ai int vec_all_eq(__vector __bool long long __a, __vector unsigned long long __b) { int __cc; - __builtin_s390_vceqgs((__vector signed long long)__a, - (__vector signed long long)__b, &__cc); + __builtin_s390_vceqgs((__vector unsigned long long)__a, __b, &__cc); return __cc == 0; } static inline __ATTRS_o_ai int vec_all_eq(__vector __bool long long __a, __vector __bool long long __b) { int __cc; - __builtin_s390_vceqgs((__vector signed long long)__a, - (__vector signed long long)__b, &__cc); + __builtin_s390_vceqgs((__vector unsigned long long)__a, + (__vector unsigned long long)__b, &__cc); return __cc == 0; } @@ -2935,7 +2935,8 @@ vec_all_eq(__vector double __a, __vector double __b) { static inline __ATTRS_o_ai int vec_all_ne(__vector signed char __a, __vector signed char __b) { int __cc; - __builtin_s390_vceqbs(__a, __b, &__cc); + __builtin_s390_vceqbs((__vector unsigned char)__a, + (__vector unsigned char)__b, &__cc); return __cc == 3; } @@ -2943,7 +2944,8 @@ vec_all_ne(__vector signed char __a, __vector signed char __b) { static inline __ATTRS_o_ai int vec_all_ne(__vector signed char __a, __vector __bool char __b) { int __cc; - __builtin_s390_vceqbs(__a, (__vector signed char)__b, &__cc); + __builtin_s390_vceqbs((__vector unsigned char)__a, + (__vector unsigned char)__b, &__cc); return __cc == 3; } @@ -2951,15 +2953,16 @@ vec_all_ne(__vector signed char __a, __vector __bool char __b) { static inline __ATTRS_o_ai int vec_all_ne(__vector __bool char __a, __vector signed char __b) { int __cc; - __builtin_s390_vceqbs((__vector signed char)__a, __b, &__cc); + __builtin_s390_vceqbs((__vector unsigned char)__a, + (__vector unsigned char)__b, &__cc); return __cc == 3; } static inline __ATTRS_o_ai int vec_all_ne(__vector unsigned char __a, __vector unsigned char __b) { int __cc; - __builtin_s390_vceqbs((__vector signed char)__a, - (__vector signed char)__b, &__cc); + __builtin_s390_vceqbs((__vector unsigned char)__a, + (__vector unsigned char)__b, &__cc); return __cc == 3; } @@ -2967,8 +2970,7 @@ vec_all_ne(__vector unsigned char __a, __vector unsigned char __b) { static inline __ATTRS_o_ai int vec_all_ne(__vector unsigned char __a, __vector __bool char __b) { int __cc; - __builtin_s390_vceqbs((__vector signed char)__a, - (__vector signed char)__b, &__cc); + __builtin_s390_vceqbs(__a, (__vector unsigned char)__b, &__cc); return __cc == 3; } @@ -2976,23 +2978,23 @@ vec_all_ne(__vector unsigned char __a, __vector __bool char __b) { static inline __ATTRS_o_ai int vec_all_ne(__vector __bool char __a, __vector unsigned char __b) { int __cc; - __builtin_s390_vceqbs((__vector signed char)__a, - (__vector signed char)__b, &__cc); + __builtin_s390_vceqbs((__vector unsigned char)__a, __b, &__cc); return __cc == 3; } static inline __ATTRS_o_ai int vec_all_ne(__vector __bool char __a, __vector __bool char __b) { int __cc; - __builtin_s390_vceqbs((__vector signed char)__a, - (__vector signed char)__b, &__cc); + __builtin_s390_vceqbs((__vector unsigned char)__a, + (__vector unsigned char)__b, &__cc); return __cc == 3; } static inline __ATTRS_o_ai int vec_all_ne(__vector signed short __a, __vector signed short __b) { int __cc; - __builtin_s390_vceqhs(__a, __b, &__cc); + __builtin_s390_vceqhs((__vector unsigned short)__a, + (__vector unsigned short)__b, &__cc); return __cc == 3; } @@ -3000,7 +3002,8 @@ vec_all_ne(__vector signed short __a, __vector signed short __b) { static inline __ATTRS_o_ai int vec_all_ne(__vector signed short __a, __vector __bool short __b) { int __cc; - __builtin_s390_vceqhs(__a, (__vector signed short)__b, &__cc); + __builtin_s390_vceqhs((__vector unsigned short)__a, + (__vector unsigned short)__b, &__cc); return __cc == 3; } @@ -3008,15 +3011,15 @@ vec_all_ne(__vector signed short __a, __vector __bool short __b) { static inline __ATTRS_o_ai int vec_all_ne(__vector __bool short __a, __vector signed short __b) { int __cc; - __builtin_s390_vceqhs((__vector signed short)__a, __b, &__cc); + __builtin_s390_vceqhs((__vector unsigned short)__a, + (__vector unsigned short)__b, &__cc); return __cc == 3; } static inline __ATTRS_o_ai int vec_all_ne(__vector unsigned short __a, __vector unsigned short __b) { int __cc; - __builtin_s390_vceqhs((__vector signed short)__a, - (__vector signed short)__b, &__cc); + __builtin_s390_vceqhs(__a, __b, &__cc); return __cc == 3; } @@ -3024,8 +3027,7 @@ vec_all_ne(__vector unsigned short __a, __vector unsigned short __b) { static inline __ATTRS_o_ai int vec_all_ne(__vector unsigned short __a, __vector __bool short __b) { int __cc; - __builtin_s390_vceqhs((__vector signed short)__a, - (__vector signed short)__b, &__cc); + __builtin_s390_vceqhs(__a, (__vector unsigned short)__b, &__cc); return __cc == 3; } @@ -3033,23 +3035,23 @@ vec_all_ne(__vector unsigned short __a, __vector __bool short __b) { static inline __ATTRS_o_ai int vec_all_ne(__vector __bool short __a, __vector unsigned short __b) { int __cc; - __builtin_s390_vceqhs((__vector signed short)__a, - (__vector signed short)__b, &__cc); + __builtin_s390_vceqhs((__vector unsigned short)__a, __b, &__cc); return __cc == 3; } static inline __ATTRS_o_ai int vec_all_ne(__vector __bool short __a, __vector __bool short __b) { int __cc; - __builtin_s390_vceqhs((__vector signed short)__a, - (__vector signed short)__b, &__cc); + __builtin_s390_vceqhs((__vector unsigned short)__a, + (__vector unsigned short)__b, &__cc); return __cc == 3; } static inline __ATTRS_o_ai int vec_all_ne(__vector signed int __a, __vector signed int __b) { int __cc; - __builtin_s390_vceqfs(__a, __b, &__cc); + __builtin_s390_vceqfs((__vector unsigned int)__a, + (__vector unsigned int)__b, &__cc); return __cc == 3; } @@ -3057,7 +3059,8 @@ vec_all_ne(__vector signed int __a, __vector signed int __b) { static inline __ATTRS_o_ai int vec_all_ne(__vector signed int __a, __vector __bool int __b) { int __cc; - __builtin_s390_vceqfs(__a, (__vector signed int)__b, &__cc); + __builtin_s390_vceqfs((__vector unsigned int)__a, + (__vector unsigned int)__b, &__cc); return __cc == 3; } @@ -3065,15 +3068,15 @@ vec_all_ne(__vector signed int __a, __vector __bool int __b) { static inline __ATTRS_o_ai int vec_all_ne(__vector __bool int __a, __vector signed int __b) { int __cc; - __builtin_s390_vceqfs((__vector signed int)__a, __b, &__cc); + __builtin_s390_vceqfs((__vector unsigned int)__a, + (__vector unsigned int)__b, &__cc); return __cc == 3; } static inline __ATTRS_o_ai int vec_all_ne(__vector unsigned int __a, __vector unsigned int __b) { int __cc; - __builtin_s390_vceqfs((__vector signed int)__a, - (__vector signed int)__b, &__cc); + __builtin_s390_vceqfs(__a, __b, &__cc); return __cc == 3; } @@ -3081,8 +3084,7 @@ vec_all_ne(__vector unsigned int __a, __vector unsigned int __b) { static inline __ATTRS_o_ai int vec_all_ne(__vector unsigned int __a, __vector __bool int __b) { int __cc; - __builtin_s390_vceqfs((__vector signed int)__a, - (__vector signed int)__b, &__cc); + __builtin_s390_vceqfs(__a, (__vector unsigned int)__b, &__cc); return __cc == 3; } @@ -3090,23 +3092,23 @@ vec_all_ne(__vector unsigned int __a, __vector __bool int __b) { static inline __ATTRS_o_ai int vec_all_ne(__vector __bool int __a, __vector unsigned int __b) { int __cc; - __builtin_s390_vceqfs((__vector signed int)__a, - (__vector signed int)__b, &__cc); + __builtin_s390_vceqfs((__vector unsigned int)__a, __b, &__cc); return __cc == 3; } static inline __ATTRS_o_ai int vec_all_ne(__vector __bool int __a, __vector __bool int __b) { int __cc; - __builtin_s390_vceqfs((__vector signed int)__a, - (__vector signed int)__b, &__cc); + __builtin_s390_vceqfs((__vector unsigned int)__a, + (__vector unsigned int)__b, &__cc); return __cc == 3; } static inline __ATTRS_o_ai int vec_all_ne(__vector signed long long __a, __vector signed long long __b) { int __cc; - __builtin_s390_vceqgs(__a, __b, &__cc); + __builtin_s390_vceqgs((__vector unsigned long long)__a, + (__vector unsigned long long)__b, &__cc); return __cc == 3; } @@ -3114,7 +3116,8 @@ vec_all_ne(__vector signed long long __a, __vector signed long long __b) { static inline __ATTRS_o_ai int vec_all_ne(__vector signed long long __a, __vector __bool long long __b) { int __cc; - __builtin_s390_vceqgs(__a, (__vector signed long long)__b, &__cc); + __builtin_s390_vceqgs((__vector unsigned long long)__a, + (__vector unsigned long long)__b, &__cc); return __cc == 3; } @@ -3122,15 +3125,15 @@ vec_all_ne(__vector signed long long __a, __vector __bool long long __b) { static inline __ATTRS_o_ai int vec_all_ne(__vector __bool long long __a, __vector signed long long __b) { int __cc; - __builtin_s390_vceqgs((__vector signed long long)__a, __b, &__cc); + __builtin_s390_vceqgs((__vector unsigned long long)__a, + (__vector unsigned long long)__b, &__cc); return __cc == 3; } static inline __ATTRS_o_ai int vec_all_ne(__vector unsigned long long __a, __vector unsigned long long __b) { int __cc; - __builtin_s390_vceqgs((__vector signed long long)__a, - (__vector signed long long)__b, &__cc); + __builtin_s390_vceqgs(__a, __b, &__cc); return __cc == 3; } @@ -3138,8 +3141,7 @@ vec_all_ne(__vector unsigned long long __a, __vector unsigned long long __b) { static inline __ATTRS_o_ai int vec_all_ne(__vector unsigned long long __a, __vector __bool long long __b) { int __cc; - __builtin_s390_vceqgs((__vector signed long long)__a, - (__vector signed long long)__b, &__cc); + __builtin_s390_vceqgs(__a, (__vector unsigned long long)__b, &__cc); return __cc == 3; } @@ -3147,16 +3149,15 @@ vec_all_ne(__vector unsigned long long __a, __vector __bool long long __b) { static inline __ATTRS_o_ai int vec_all_ne(__vector __bool long long __a, __vector unsigned long long __b) { int __cc; - __builtin_s390_vceqgs((__vector signed long long)__a, - (__vector signed long long)__b, &__cc); + __builtin_s390_vceqgs((__vector unsigned long long)__a, __b, &__cc); return __cc == 3; } static inline __ATTRS_o_ai int vec_all_ne(__vector __bool long long __a, __vector __bool long long __b) { int __cc; - __builtin_s390_vceqgs((__vector signed long long)__a, - (__vector signed long long)__b, &__cc); + __builtin_s390_vceqgs((__vector unsigned long long)__a, + (__vector unsigned long long)__b, &__cc); return __cc == 3; } @@ -4241,7 +4242,8 @@ vec_all_numeric(__vector double __a) { static inline __ATTRS_o_ai int vec_any_eq(__vector signed char __a, __vector signed char __b) { int __cc; - __builtin_s390_vceqbs(__a, __b, &__cc); + __builtin_s390_vceqbs((__vector unsigned char)__a, + (__vector unsigned char)__b, &__cc); return __cc <= 1; } @@ -4249,7 +4251,8 @@ vec_any_eq(__vector signed char __a, __vector signed char __b) { static inline __ATTRS_o_ai int vec_any_eq(__vector signed char __a, __vector __bool char __b) { int __cc; - __builtin_s390_vceqbs(__a, (__vector signed char)__b, &__cc); + __builtin_s390_vceqbs((__vector unsigned char)__a, + (__vector unsigned char)__b, &__cc); return __cc <= 1; } @@ -4257,15 +4260,15 @@ vec_any_eq(__vector signed char __a, __vector __bool char __b) { static inline __ATTRS_o_ai int vec_any_eq(__vector __bool char __a, __vector signed char __b) { int __cc; - __builtin_s390_vceqbs((__vector signed char)__a, __b, &__cc); + __builtin_s390_vceqbs((__vector unsigned char)__a, + (__vector unsigned char)__b, &__cc); return __cc <= 1; } static inline __ATTRS_o_ai int vec_any_eq(__vector unsigned char __a, __vector unsigned char __b) { int __cc; - __builtin_s390_vceqbs((__vector signed char)__a, - (__vector signed char)__b, &__cc); + __builtin_s390_vceqbs(__a, __b, &__cc); return __cc <= 1; } @@ -4273,8 +4276,7 @@ vec_any_eq(__vector unsigned char __a, __vector unsigned char __b) { static inline __ATTRS_o_ai int vec_any_eq(__vector unsigned char __a, __vector __bool char __b) { int __cc; - __builtin_s390_vceqbs((__vector signed char)__a, - (__vector signed char)__b, &__cc); + __builtin_s390_vceqbs(__a, (__vector unsigned char)__b, &__cc); return __cc <= 1; } @@ -4282,23 +4284,23 @@ vec_any_eq(__vector unsigned char __a, __vector __bool char __b) { static inline __ATTRS_o_ai int vec_any_eq(__vector __bool char __a, __vector unsigned char __b) { int __cc; - __builtin_s390_vceqbs((__vector signed char)__a, - (__vector signed char)__b, &__cc); + __builtin_s390_vceqbs((__vector unsigned char)__a, __b, &__cc); return __cc <= 1; } static inline __ATTRS_o_ai int vec_any_eq(__vector __bool char __a, __vector __bool char __b) { int __cc; - __builtin_s390_vceqbs((__vector signed char)__a, - (__vector signed char)__b, &__cc); + __builtin_s390_vceqbs((__vector unsigned char)__a, + (__vector unsigned char)__b, &__cc); return __cc <= 1; } static inline __ATTRS_o_ai int vec_any_eq(__vector signed short __a, __vector signed short __b) { int __cc; - __builtin_s390_vceqhs(__a, __b, &__cc); + __builtin_s390_vceqhs((__vector unsigned short)__a, + (__vector unsigned short)__b, &__cc); return __cc <= 1; } @@ -4306,7 +4308,8 @@ vec_any_eq(__vector signed short __a, __vector signed short __b) { static inline __ATTRS_o_ai int vec_any_eq(__vector signed short __a, __vector __bool short __b) { int __cc; - __builtin_s390_vceqhs(__a, (__vector signed short)__b, &__cc); + __builtin_s390_vceqhs((__vector unsigned short)__a, + (__vector unsigned short)__b, &__cc); return __cc <= 1; } @@ -4314,15 +4317,15 @@ vec_any_eq(__vector signed short __a, __vector __bool short __b) { static inline __ATTRS_o_ai int vec_any_eq(__vector __bool short __a, __vector signed short __b) { int __cc; - __builtin_s390_vceqhs((__vector signed short)__a, __b, &__cc); + __builtin_s390_vceqhs((__vector unsigned short)__a, + (__vector unsigned short)__b, &__cc); return __cc <= 1; } static inline __ATTRS_o_ai int vec_any_eq(__vector unsigned short __a, __vector unsigned short __b) { int __cc; - __builtin_s390_vceqhs((__vector signed short)__a, - (__vector signed short)__b, &__cc); + __builtin_s390_vceqhs(__a, __b, &__cc); return __cc <= 1; } @@ -4330,8 +4333,7 @@ vec_any_eq(__vector unsigned short __a, __vector unsigned short __b) { static inline __ATTRS_o_ai int vec_any_eq(__vector unsigned short __a, __vector __bool short __b) { int __cc; - __builtin_s390_vceqhs((__vector signed short)__a, - (__vector signed short)__b, &__cc); + __builtin_s390_vceqhs(__a, (__vector unsigned short)__b, &__cc); return __cc <= 1; } @@ -4339,23 +4341,23 @@ vec_any_eq(__vector unsigned short __a, __vector __bool short __b) { static inline __ATTRS_o_ai int vec_any_eq(__vector __bool short __a, __vector unsigned short __b) { int __cc; - __builtin_s390_vceqhs((__vector signed short)__a, - (__vector signed short)__b, &__cc); + __builtin_s390_vceqhs((__vector unsigned short)__a, __b, &__cc); return __cc <= 1; } static inline __ATTRS_o_ai int vec_any_eq(__vector __bool short __a, __vector __bool short __b) { int __cc; - __builtin_s390_vceqhs((__vector signed short)__a, - (__vector signed short)__b, &__cc); + __builtin_s390_vceqhs((__vector unsigned short)__a, + (__vector unsigned short)__b, &__cc); return __cc <= 1; } static inline __ATTRS_o_ai int vec_any_eq(__vector signed int __a, __vector signed int __b) { int __cc; - __builtin_s390_vceqfs(__a, __b, &__cc); + __builtin_s390_vceqfs((__vector unsigned int)__a, + (__vector unsigned int)__b, &__cc); return __cc <= 1; } @@ -4363,7 +4365,8 @@ vec_any_eq(__vector signed int __a, __vector signed int __b) { static inline __ATTRS_o_ai int vec_any_eq(__vector signed int __a, __vector __bool int __b) { int __cc; - __builtin_s390_vceqfs(__a, (__vector signed int)__b, &__cc); + __builtin_s390_vceqfs((__vector unsigned int)__a, + (__vector unsigned int)__b, &__cc); return __cc <= 1; } @@ -4371,15 +4374,15 @@ vec_any_eq(__vector signed int __a, __vector __bool int __b) { static inline __ATTRS_o_ai int vec_any_eq(__vector __bool int __a, __vector signed int __b) { int __cc; - __builtin_s390_vceqfs((__vector signed int)__a, __b, &__cc); + __builtin_s390_vceqfs((__vector unsigned int)__a, + (__vector unsigned int)__b, &__cc); return __cc <= 1; } static inline __ATTRS_o_ai int vec_any_eq(__vector unsigned int __a, __vector unsigned int __b) { int __cc; - __builtin_s390_vceqfs((__vector signed int)__a, - (__vector signed int)__b, &__cc); + __builtin_s390_vceqfs(__a, __b, &__cc); return __cc <= 1; } @@ -4387,8 +4390,7 @@ vec_any_eq(__vector unsigned int __a, __vector unsigned int __b) { static inline __ATTRS_o_ai int vec_any_eq(__vector unsigned int __a, __vector __bool int __b) { int __cc; - __builtin_s390_vceqfs((__vector signed int)__a, - (__vector signed int)__b, &__cc); + __builtin_s390_vceqfs(__a, (__vector unsigned int)__b, &__cc); return __cc <= 1; } @@ -4396,23 +4398,23 @@ vec_any_eq(__vector unsigned int __a, __vector __bool int __b) { static inline __ATTRS_o_ai int vec_any_eq(__vector __bool int __a, __vector unsigned int __b) { int __cc; - __builtin_s390_vceqfs((__vector signed int)__a, - (__vector signed int)__b, &__cc); + __builtin_s390_vceqfs((__vector unsigned int)__a, __b, &__cc); return __cc <= 1; } static inline __ATTRS_o_ai int vec_any_eq(__vector __bool int __a, __vector __bool int __b) { int __cc; - __builtin_s390_vceqfs((__vector signed int)__a, - (__vector signed int)__b, &__cc); + __builtin_s390_vceqfs((__vector unsigned int)__a, + (__vector unsigned int)__b, &__cc); return __cc <= 1; } static inline __ATTRS_o_ai int vec_any_eq(__vector signed long long __a, __vector signed long long __b) { int __cc; - __builtin_s390_vceqgs(__a, __b, &__cc); + __builtin_s390_vceqgs((__vector unsigned long long)__a, + (__vector unsigned long long)__b, &__cc); return __cc <= 1; } @@ -4420,7 +4422,8 @@ vec_any_eq(__vector signed long long __a, __vector signed long long __b) { static inline __ATTRS_o_ai int vec_any_eq(__vector signed long long __a, __vector __bool long long __b) { int __cc; - __builtin_s390_vceqgs(__a, (__vector signed long long)__b, &__cc); + __builtin_s390_vceqgs((__vector unsigned long long)__a, + (__vector unsigned long long)__b, &__cc); return __cc <= 1; } @@ -4428,15 +4431,15 @@ vec_any_eq(__vector signed long long __a, __vector __bool long long __b) { static inline __ATTRS_o_ai int vec_any_eq(__vector __bool long long __a, __vector signed long long __b) { int __cc; - __builtin_s390_vceqgs((__vector signed long long)__a, __b, &__cc); + __builtin_s390_vceqgs((__vector unsigned long long)__a, + (__vector unsigned long long)__b, &__cc); return __cc <= 1; } static inline __ATTRS_o_ai int vec_any_eq(__vector unsigned long long __a, __vector unsigned long long __b) { int __cc; - __builtin_s390_vceqgs((__vector signed long long)__a, - (__vector signed long long)__b, &__cc); + __builtin_s390_vceqgs(__a, __b, &__cc); return __cc <= 1; } @@ -4444,8 +4447,7 @@ vec_any_eq(__vector unsigned long long __a, __vector unsigned long long __b) { static inline __ATTRS_o_ai int vec_any_eq(__vector unsigned long long __a, __vector __bool long long __b) { int __cc; - __builtin_s390_vceqgs((__vector signed long long)__a, - (__vector signed long long)__b, &__cc); + __builtin_s390_vceqgs(__a, (__vector unsigned long long)__b, &__cc); return __cc <= 1; } @@ -4453,16 +4455,15 @@ vec_any_eq(__vector unsigned long long __a, __vector __bool long long __b) { static inline __ATTRS_o_ai int vec_any_eq(__vector __bool long long __a, __vector unsigned long long __b) { int __cc; - __builtin_s390_vceqgs((__vector signed long long)__a, - (__vector signed long long)__b, &__cc); + __builtin_s390_vceqgs((__vector unsigned long long)__a, __b, &__cc); return __cc <= 1; } static inline __ATTRS_o_ai int vec_any_eq(__vector __bool long long __a, __vector __bool long long __b) { int __cc; - __builtin_s390_vceqgs((__vector signed long long)__a, - (__vector signed long long)__b, &__cc); + __builtin_s390_vceqgs((__vector unsigned long long)__a, + (__vector unsigned long long)__b, &__cc); return __cc <= 1; } @@ -4487,7 +4488,8 @@ vec_any_eq(__vector double __a, __vector double __b) { static inline __ATTRS_o_ai int vec_any_ne(__vector signed char __a, __vector signed char __b) { int __cc; - __builtin_s390_vceqbs(__a, __b, &__cc); + __builtin_s390_vceqbs((__vector unsigned char)__a, + (__vector unsigned char)__b, &__cc); return __cc != 0; } @@ -4495,7 +4497,8 @@ vec_any_ne(__vector signed char __a, __vector signed char __b) { static inline __ATTRS_o_ai int vec_any_ne(__vector signed char __a, __vector __bool char __b) { int __cc; - __builtin_s390_vceqbs(__a, (__vector signed char)__b, &__cc); + __builtin_s390_vceqbs((__vector unsigned char)__a, + (__vector unsigned char)__b, &__cc); return __cc != 0; } @@ -4503,15 +4506,15 @@ vec_any_ne(__vector signed char __a, __vector __bool char __b) { static inline __ATTRS_o_ai int vec_any_ne(__vector __bool char __a, __vector signed char __b) { int __cc; - __builtin_s390_vceqbs((__vector signed char)__a, __b, &__cc); + __builtin_s390_vceqbs((__vector unsigned char)__a, + (__vector unsigned char)__b, &__cc); return __cc != 0; } static inline __ATTRS_o_ai int vec_any_ne(__vector unsigned char __a, __vector unsigned char __b) { int __cc; - __builtin_s390_vceqbs((__vector signed char)__a, - (__vector signed char)__b, &__cc); + __builtin_s390_vceqbs(__a, __b, &__cc); return __cc != 0; } @@ -4519,8 +4522,7 @@ vec_any_ne(__vector unsigned char __a, __vector unsigned char __b) { static inline __ATTRS_o_ai int vec_any_ne(__vector unsigned char __a, __vector __bool char __b) { int __cc; - __builtin_s390_vceqbs((__vector signed char)__a, - (__vector signed char)__b, &__cc); + __builtin_s390_vceqbs(__a, (__vector unsigned char)__b, &__cc); return __cc != 0; } @@ -4528,23 +4530,23 @@ vec_any_ne(__vector unsigned char __a, __vector __bool char __b) { static inline __ATTRS_o_ai int vec_any_ne(__vector __bool char __a, __vector unsigned char __b) { int __cc; - __builtin_s390_vceqbs((__vector signed char)__a, - (__vector signed char)__b, &__cc); + __builtin_s390_vceqbs((__vector unsigned char)__a, __b, &__cc); return __cc != 0; } static inline __ATTRS_o_ai int vec_any_ne(__vector __bool char __a, __vector __bool char __b) { int __cc; - __builtin_s390_vceqbs((__vector signed char)__a, - (__vector signed char)__b, &__cc); + __builtin_s390_vceqbs((__vector unsigned char)__a, + (__vector unsigned char)__b, &__cc); return __cc != 0; } static inline __ATTRS_o_ai int vec_any_ne(__vector signed short __a, __vector signed short __b) { int __cc; - __builtin_s390_vceqhs(__a, __b, &__cc); + __builtin_s390_vceqhs((__vector unsigned short)__a, + (__vector unsigned short)__b, &__cc); return __cc != 0; } @@ -4552,7 +4554,8 @@ vec_any_ne(__vector signed short __a, __vector signed short __b) { static inline __ATTRS_o_ai int vec_any_ne(__vector signed short __a, __vector __bool short __b) { int __cc; - __builtin_s390_vceqhs(__a, (__vector signed short)__b, &__cc); + __builtin_s390_vceqhs((__vector unsigned short)__a, + (__vector unsigned short)__b, &__cc); return __cc != 0; } @@ -4560,15 +4563,15 @@ vec_any_ne(__vector signed short __a, __vector __bool short __b) { static inline __ATTRS_o_ai int vec_any_ne(__vector __bool short __a, __vector signed short __b) { int __cc; - __builtin_s390_vceqhs((__vector signed short)__a, __b, &__cc); + __builtin_s390_vceqhs((__vector unsigned short)__a, + (__vector unsigned short)__b, &__cc); return __cc != 0; } static inline __ATTRS_o_ai int vec_any_ne(__vector unsigned short __a, __vector unsigned short __b) { int __cc; - __builtin_s390_vceqhs((__vector signed short)__a, - (__vector signed short)__b, &__cc); + __builtin_s390_vceqhs(__a, __b, &__cc); return __cc != 0; } @@ -4576,8 +4579,7 @@ vec_any_ne(__vector unsigned short __a, __vector unsigned short __b) { static inline __ATTRS_o_ai int vec_any_ne(__vector unsigned short __a, __vector __bool short __b) { int __cc; - __builtin_s390_vceqhs((__vector signed short)__a, - (__vector signed short)__b, &__cc); + __builtin_s390_vceqhs(__a, (__vector unsigned short)__b, &__cc); return __cc != 0; } @@ -4585,23 +4587,23 @@ vec_any_ne(__vector unsigned short __a, __vector __bool short __b) { static inline __ATTRS_o_ai int vec_any_ne(__vector __bool short __a, __vector unsigned short __b) { int __cc; - __builtin_s390_vceqhs((__vector signed short)__a, - (__vector signed short)__b, &__cc); + __builtin_s390_vceqhs((__vector unsigned short)__a, __b, &__cc); return __cc != 0; } static inline __ATTRS_o_ai int vec_any_ne(__vector __bool short __a, __vector __bool short __b) { int __cc; - __builtin_s390_vceqhs((__vector signed short)__a, - (__vector signed short)__b, &__cc); + __builtin_s390_vceqhs((__vector unsigned short)__a, + (__vector unsigned short)__b, &__cc); return __cc != 0; } static inline __ATTRS_o_ai int vec_any_ne(__vector signed int __a, __vector signed int __b) { int __cc; - __builtin_s390_vceqfs(__a, __b, &__cc); + __builtin_s390_vceqfs((__vector unsigned int)__a, + (__vector unsigned int)__b, &__cc); return __cc != 0; } @@ -4609,7 +4611,8 @@ vec_any_ne(__vector signed int __a, __vector signed int __b) { static inline __ATTRS_o_ai int vec_any_ne(__vector signed int __a, __vector __bool int __b) { int __cc; - __builtin_s390_vceqfs(__a, (__vector signed int)__b, &__cc); + __builtin_s390_vceqfs((__vector unsigned int)__a, + (__vector unsigned int)__b, &__cc); return __cc != 0; } @@ -4617,15 +4620,15 @@ vec_any_ne(__vector signed int __a, __vector __bool int __b) { static inline __ATTRS_o_ai int vec_any_ne(__vector __bool int __a, __vector signed int __b) { int __cc; - __builtin_s390_vceqfs((__vector signed int)__a, __b, &__cc); + __builtin_s390_vceqfs((__vector unsigned int)__a, + (__vector unsigned int)__b, &__cc); return __cc != 0; } static inline __ATTRS_o_ai int vec_any_ne(__vector unsigned int __a, __vector unsigned int __b) { int __cc; - __builtin_s390_vceqfs((__vector signed int)__a, - (__vector signed int)__b, &__cc); + __builtin_s390_vceqfs(__a, __b, &__cc); return __cc != 0; } @@ -4633,8 +4636,7 @@ vec_any_ne(__vector unsigned int __a, __vector unsigned int __b) { static inline __ATTRS_o_ai int vec_any_ne(__vector unsigned int __a, __vector __bool int __b) { int __cc; - __builtin_s390_vceqfs((__vector signed int)__a, - (__vector signed int)__b, &__cc); + __builtin_s390_vceqfs(__a, (__vector unsigned int)__b, &__cc); return __cc != 0; } @@ -4642,23 +4644,23 @@ vec_any_ne(__vector unsigned int __a, __vector __bool int __b) { static inline __ATTRS_o_ai int vec_any_ne(__vector __bool int __a, __vector unsigned int __b) { int __cc; - __builtin_s390_vceqfs((__vector signed int)__a, - (__vector signed int)__b, &__cc); + __builtin_s390_vceqfs((__vector unsigned int)__a, __b, &__cc); return __cc != 0; } static inline __ATTRS_o_ai int vec_any_ne(__vector __bool int __a, __vector __bool int __b) { int __cc; - __builtin_s390_vceqfs((__vector signed int)__a, - (__vector signed int)__b, &__cc); + __builtin_s390_vceqfs((__vector unsigned int)__a, + (__vector unsigned int)__b, &__cc); return __cc != 0; } static inline __ATTRS_o_ai int vec_any_ne(__vector signed long long __a, __vector signed long long __b) { int __cc; - __builtin_s390_vceqgs(__a, __b, &__cc); + __builtin_s390_vceqgs((__vector unsigned long long)__a, + (__vector unsigned long long)__b, &__cc); return __cc != 0; } @@ -4666,7 +4668,8 @@ vec_any_ne(__vector signed long long __a, __vector signed long long __b) { static inline __ATTRS_o_ai int vec_any_ne(__vector signed long long __a, __vector __bool long long __b) { int __cc; - __builtin_s390_vceqgs(__a, (__vector signed long long)__b, &__cc); + __builtin_s390_vceqgs((__vector unsigned long long)__a, + (__vector unsigned long long)__b, &__cc); return __cc != 0; } @@ -4674,15 +4677,15 @@ vec_any_ne(__vector signed long long __a, __vector __bool long long __b) { static inline __ATTRS_o_ai int vec_any_ne(__vector __bool long long __a, __vector signed long long __b) { int __cc; - __builtin_s390_vceqgs((__vector signed long long)__a, __b, &__cc); + __builtin_s390_vceqgs((__vector unsigned long long)__a, + (__vector unsigned long long)__b, &__cc); return __cc != 0; } static inline __ATTRS_o_ai int vec_any_ne(__vector unsigned long long __a, __vector unsigned long long __b) { int __cc; - __builtin_s390_vceqgs((__vector signed long long)__a, - (__vector signed long long)__b, &__cc); + __builtin_s390_vceqgs(__a, __b, &__cc); return __cc != 0; } @@ -4690,8 +4693,7 @@ vec_any_ne(__vector unsigned long long __a, __vector unsigned long long __b) { static inline __ATTRS_o_ai int vec_any_ne(__vector unsigned long long __a, __vector __bool long long __b) { int __cc; - __builtin_s390_vceqgs((__vector signed long long)__a, - (__vector signed long long)__b, &__cc); + __builtin_s390_vceqgs(__a, (__vector unsigned long long)__b, &__cc); return __cc != 0; } @@ -4699,16 +4701,15 @@ vec_any_ne(__vector unsigned long long __a, __vector __bool long long __b) { static inline __ATTRS_o_ai int vec_any_ne(__vector __bool long long __a, __vector unsigned long long __b) { int __cc; - __builtin_s390_vceqgs((__vector signed long long)__a, - (__vector signed long long)__b, &__cc); + __builtin_s390_vceqgs((__vector unsigned long long)__a, __b, &__cc); return __cc != 0; } static inline __ATTRS_o_ai int vec_any_ne(__vector __bool long long __a, __vector __bool long long __b) { int __cc; - __builtin_s390_vceqgs((__vector signed long long)__a, - (__vector signed long long)__b, &__cc); + __builtin_s390_vceqgs((__vector unsigned long long)__a, + (__vector unsigned long long)__b, &__cc); return __cc != 0; } @@ -6565,45 +6566,45 @@ vec_rl(__vector unsigned long long __a, __vector unsigned long long __b) { static inline __ATTRS_o_ai __vector signed char vec_rli(__vector signed char __a, unsigned long __b) { return (__vector signed char)__builtin_s390_verllb( - (__vector unsigned char)__a, (int)__b); + (__vector unsigned char)__a, (unsigned char)__b); } static inline __ATTRS_o_ai __vector unsigned char vec_rli(__vector unsigned char __a, unsigned long __b) { - return __builtin_s390_verllb(__a, (int)__b); + return __builtin_s390_verllb(__a, (unsigned char)__b); } static inline __ATTRS_o_ai __vector signed short vec_rli(__vector signed short __a, unsigned long __b) { return (__vector signed short)__builtin_s390_verllh( - (__vector unsigned short)__a, (int)__b); + (__vector unsigned short)__a, (unsigned char)__b); } static inline __ATTRS_o_ai __vector unsigned short vec_rli(__vector unsigned short __a, unsigned long __b) { - return __builtin_s390_verllh(__a, (int)__b); + return __builtin_s390_verllh(__a, (unsigned char)__b); } static inline __ATTRS_o_ai __vector signed int vec_rli(__vector signed int __a, unsigned long __b) { return (__vector signed int)__builtin_s390_verllf( - (__vector unsigned int)__a, (int)__b); + (__vector unsigned int)__a, (unsigned char)__b); } static inline __ATTRS_o_ai __vector unsigned int vec_rli(__vector unsigned int __a, unsigned long __b) { - return __builtin_s390_verllf(__a, (int)__b); + return __builtin_s390_verllf(__a, (unsigned char)__b); } static inline __ATTRS_o_ai __vector signed long long vec_rli(__vector signed long long __a, unsigned long __b) { return (__vector signed long long)__builtin_s390_verllg( - (__vector unsigned long long)__a, (int)__b); + (__vector unsigned long long)__a, (unsigned char)__b); } static inline __ATTRS_o_ai __vector unsigned long long vec_rli(__vector unsigned long long __a, unsigned long __b) { - return __builtin_s390_verllg(__a, (int)__b); + return __builtin_s390_verllg(__a, (unsigned char)__b); } /*-- vec_rl_mask ------------------------------------------------------------*/ diff --git a/clang/lib/Lex/Lexer.cpp b/clang/lib/Lex/Lexer.cpp index 1c53997527732a93f353cb90e73bd89401466127..f4f1daab857f043696585083e3c99a0839abd1ea 100644 --- a/clang/lib/Lex/Lexer.cpp +++ b/clang/lib/Lex/Lexer.cpp @@ -1990,6 +1990,10 @@ bool Lexer::LexNumericConstant(Token &Result, const char *CurPtr) { while (isPreprocessingNumberBody(C)) { CurPtr = ConsumeChar(CurPtr, Size, Result); PrevCh = C; + if (LangOpts.HLSL && C == '.' && (*CurPtr == 'x' || *CurPtr == 'r')) { + CurPtr -= Size; + break; + } C = getCharAndSize(CurPtr, Size); } diff --git a/clang/lib/Lex/LiteralSupport.cpp b/clang/lib/Lex/LiteralSupport.cpp index 2de307883b97ce75d51dde002655c492d3403b74..0a78638f680511d9cfa39a97ada9e9ebbb070635 100644 --- a/clang/lib/Lex/LiteralSupport.cpp +++ b/clang/lib/Lex/LiteralSupport.cpp @@ -930,7 +930,11 @@ NumericLiteralParser::NumericLiteralParser(StringRef TokSpelling, // and FP constants (specifically, the 'pp-number' regex), and assumes that // the byte at "*end" is both valid and not part of the regex. Because of // this, it doesn't have to check for 'overscan' in various places. - if (isPreprocessingNumberBody(*ThisTokEnd)) { + // Note: For HLSL, the end token is allowed to be '.' which would be in the + // 'pp-number' regex. This is required to support vector swizzles on numeric + // constants (i.e. 1.xx or 1.5f.rrr). + if (isPreprocessingNumberBody(*ThisTokEnd) && + !(LangOpts.HLSL && *ThisTokEnd == '.')) { Diags.Report(TokLoc, diag::err_lexing_numeric); hadError = true; return; diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp index 8cb5b09fd3b0fa6825a1946cdfb9443d4ad8df0b..ece3698967e2f67e0c7044f3a179a3742985b2d4 100644 --- a/clang/lib/Parse/ParseDecl.cpp +++ b/clang/lib/Parse/ParseDecl.cpp @@ -4514,6 +4514,9 @@ void Parser::ParseDeclarationSpecifiers( break; case tok::kw_groupshared: + case tok::kw_in: + case tok::kw_inout: + case tok::kw_out: // NOTE: ParseHLSLQualifiers will consume the qualifier token. ParseHLSLQualifiers(DS.getAttributes()); continue; @@ -5550,7 +5553,6 @@ bool Parser::isTypeSpecifierQualifier() { case tok::kw___read_write: case tok::kw___write_only: case tok::kw___funcref: - case tok::kw_groupshared: return true; case tok::kw_private: @@ -5559,6 +5561,13 @@ bool Parser::isTypeSpecifierQualifier() { // C11 _Atomic case tok::kw__Atomic: return true; + + // HLSL type qualifiers + case tok::kw_groupshared: + case tok::kw_in: + case tok::kw_inout: + case tok::kw_out: + return getLangOpts().HLSL; } } @@ -6058,6 +6067,9 @@ void Parser::ParseTypeQualifierListOpt( break; case tok::kw_groupshared: + case tok::kw_in: + case tok::kw_inout: + case tok::kw_out: // NOTE: ParseHLSLQualifiers will consume the qualifier token. ParseHLSLQualifiers(DS.getAttributes()); continue; diff --git a/clang/lib/Parse/ParseExprCXX.cpp b/clang/lib/Parse/ParseExprCXX.cpp index 79db094e098f8e6c487b04df19c82c79b82a0398..8b86db1bb8fc5d55914c788974ef1979d2ea1e75 100644 --- a/clang/lib/Parse/ParseExprCXX.cpp +++ b/clang/lib/Parse/ParseExprCXX.cpp @@ -3635,10 +3635,12 @@ ExprResult Parser::ParseRequiresExpression() { auto Res = TryParseParameterDeclarationClause(); if (Res != TPResult::False) { // Skip to the closing parenthesis - // FIXME: Don't traverse these tokens twice (here and in - // TryParseParameterDeclarationClause). unsigned Depth = 1; while (Depth != 0) { + bool FoundParen = SkipUntil(tok::l_paren, tok::r_paren, + SkipUntilFlags::StopBeforeMatch); + if (!FoundParen) + break; if (Tok.is(tok::l_paren)) Depth++; else if (Tok.is(tok::r_paren)) diff --git a/clang/lib/Parse/ParseOpenMP.cpp b/clang/lib/Parse/ParseOpenMP.cpp index ca70bb241d6f72346c1867d4cbdb9e39855c614b..da5f6605c6ffac9ed166f8a45553abd34fc2f287 100644 --- a/clang/lib/Parse/ParseOpenMP.cpp +++ b/clang/lib/Parse/ParseOpenMP.cpp @@ -2518,15 +2518,18 @@ StmtResult Parser::ParseOpenMPDeclarativeOrExecutableDirective( switch (DKind) { case OMPD_nothing: - if ((StmtCtx & ParsedStmtContext::AllowStandaloneOpenMPDirectives) == - ParsedStmtContext()) - Diag(Tok, diag::err_omp_immediate_directive) - << getOpenMPDirectiveName(DKind) << 0; ConsumeToken(); - skipUntilPragmaOpenMPEnd(DKind); + // If we are parsing the directive within a metadirective, the directive + // ends with a ')'. + if (ReadDirectiveWithinMetadirective && Tok.is(tok::r_paren)) + while (Tok.isNot(tok::annot_pragma_openmp_end)) + ConsumeAnyToken(); + else + skipUntilPragmaOpenMPEnd(DKind); if (Tok.is(tok::annot_pragma_openmp_end)) ConsumeAnnotationToken(); - break; + // return an empty statement + return StmtEmpty(); case OMPD_metadirective: { ConsumeToken(); SmallVector VMIs; diff --git a/clang/lib/Parse/ParseTemplate.cpp b/clang/lib/Parse/ParseTemplate.cpp index f556d0e6d4f8b6ec5eeb87ffc267c74c1fda296f..64fe4d50bba27bf143d02324e7d7e90401b53702 100644 --- a/clang/lib/Parse/ParseTemplate.cpp +++ b/clang/lib/Parse/ParseTemplate.cpp @@ -1062,8 +1062,7 @@ Parser::ParseNonTypeTemplateParameter(unsigned Depth, unsigned Position) { ++CurTemplateDepthTracker; EnterExpressionEvaluationContext ConstantEvaluated( Actions, Sema::ExpressionEvaluationContext::ConstantEvaluated); - DefaultArg = - Actions.CorrectDelayedTyposInExpr(ParseAssignmentExpression()); + DefaultArg = Actions.CorrectDelayedTyposInExpr(ParseInitializer()); if (DefaultArg.isInvalid()) SkipUntil(tok::comma, tok::greater, StopAtSemi | StopBeforeMatch); } @@ -1582,6 +1581,8 @@ ParsedTemplateArgument Parser::ParseTemplateTemplateArgument() { /// constant-expression /// type-id /// id-expression +/// braced-init-list [C++26, DR] +/// ParsedTemplateArgument Parser::ParseTemplateArgument() { // C++ [temp.arg]p2: // In a template-argument, an ambiguity between a type-id and an @@ -1619,8 +1620,12 @@ ParsedTemplateArgument Parser::ParseTemplateArgument() { } // Parse a non-type template argument. + ExprResult ExprArg; SourceLocation Loc = Tok.getLocation(); - ExprResult ExprArg = ParseConstantExpressionInExprEvalContext(MaybeTypeCast); + if (getLangOpts().CPlusPlus11 && Tok.is(tok::l_brace)) + ExprArg = ParseBraceInitializer(); + else + ExprArg = ParseConstantExpressionInExprEvalContext(MaybeTypeCast); if (ExprArg.isInvalid() || !ExprArg.get()) { return ParsedTemplateArgument(); } diff --git a/clang/lib/Parse/ParseTentative.cpp b/clang/lib/Parse/ParseTentative.cpp index 8b653b1c4f8eaf4bb6b3be06efb56109ed1fb612..242741c15b5ffaccfaefa0ad8766dc329a401e52 100644 --- a/clang/lib/Parse/ParseTentative.cpp +++ b/clang/lib/Parse/ParseTentative.cpp @@ -1529,6 +1529,9 @@ Parser::isCXXDeclarationSpecifier(ImplicitTypenameContext AllowImplicitTypename, // HLSL address space qualifiers case tok::kw_groupshared: + case tok::kw_in: + case tok::kw_inout: + case tok::kw_out: // GNU case tok::kw_restrict: diff --git a/clang/lib/Sema/OpenCLBuiltins.td b/clang/lib/Sema/OpenCLBuiltins.td index 9db450281912d2f983c90e32ffdbbb5e3b86a2f2..0cceba090bd8f26f06f5b42f95e96da8f2db15a4 100644 --- a/clang/lib/Sema/OpenCLBuiltins.td +++ b/clang/lib/Sema/OpenCLBuiltins.td @@ -563,15 +563,12 @@ foreach name = ["acos", "acosh", "acospi", "log", "log2", "log10", "log1p", "logb", "rint", "round", "rsqrt", "sin", "sinh", "sinpi", + "sqrt", "tan", "tanh", "tanpi", "tgamma", "trunc", "lgamma"] in { def : Builtin; } - -// sqrt is handled in opencl-c-base.h to handle -// -cl-fp32-correctly-rounded-divide-sqrt. - foreach name = ["nan"] in { def : Builtin; def : Builtin; diff --git a/clang/lib/Sema/SemaCUDA.cpp b/clang/lib/Sema/SemaCUDA.cpp index 318174f7be8fa95d9f7abf5499d26f6c8402d6d7..6a66ecf6f94c178da3dbf86687175c4f63d76f7d 100644 --- a/clang/lib/Sema/SemaCUDA.cpp +++ b/clang/lib/Sema/SemaCUDA.cpp @@ -225,6 +225,15 @@ Sema::CUDAFunctionPreference Sema::IdentifyCUDAPreference(const FunctionDecl *Caller, const FunctionDecl *Callee) { assert(Callee && "Callee must be valid."); + + // Treat ctor/dtor as host device function in device var initializer to allow + // trivial ctor/dtor without device attr to be used. Non-trivial ctor/dtor + // will be diagnosed by checkAllowedCUDAInitializer. + if (Caller == nullptr && CurCUDATargetCtx.Kind == CTCK_InitGlobalVar && + CurCUDATargetCtx.Target == CFT_Device && + (isa(Callee) || isa(Callee))) + return CFP_HostDevice; + CUDAFunctionTarget CallerTarget = IdentifyCUDATarget(Caller); CUDAFunctionTarget CalleeTarget = IdentifyCUDATarget(Callee); diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp index 9dfff132cd88db375b36a19116b0bb8d8f7d51f7..07ced5ffc3407a317ff1bedbe962542db422d4aa 100644 --- a/clang/lib/Sema/SemaChecking.cpp +++ b/clang/lib/Sema/SemaChecking.cpp @@ -712,8 +712,13 @@ static ExprResult SemaBuiltinDumpStruct(Sema &S, CallExpr *TheCall) { << 1 << TheCall->getDirectCallee() << PtrArgType; return ExprError(); } - const RecordDecl *RD = PtrArgType->getPointeeType()->getAsRecordDecl(); - + QualType Pointee = PtrArgType->getPointeeType(); + const RecordDecl *RD = Pointee->getAsRecordDecl(); + // Try to instantiate the class template as appropriate; otherwise, access to + // its data() may lead to a crash. + if (S.RequireCompleteType(PtrArgResult.get()->getBeginLoc(), Pointee, + diag::err_incomplete_type)) + return ExprError(); // Second argument is a callable, but we can't fully validate it until we try // calling it. QualType FnArgType = TheCall->getArg(1)->getType(); @@ -2993,6 +2998,38 @@ static QualType getNeonEltType(NeonTypeFlags Flags, ASTContext &Context, llvm_unreachable("Invalid NeonTypeFlag!"); } +enum ArmStreamingType { ArmNonStreaming, ArmStreaming, ArmStreamingCompatible }; + +static ArmStreamingType getArmStreamingFnType(const FunctionDecl *FD) { + if (FD->hasAttr()) + return ArmStreaming; + if (const auto *T = FD->getType()->getAs()) { + if (T->getAArch64SMEAttributes() & FunctionType::SME_PStateSMEnabledMask) + return ArmStreaming; + if (T->getAArch64SMEAttributes() & FunctionType::SME_PStateSMCompatibleMask) + return ArmStreamingCompatible; + } + return ArmNonStreaming; +} + +static void checkArmStreamingBuiltin(Sema &S, CallExpr *TheCall, + const FunctionDecl *FD, + ArmStreamingType BuiltinType) { + ArmStreamingType FnType = getArmStreamingFnType(FD); + + if (FnType == ArmStreaming && BuiltinType == ArmNonStreaming) { + S.Diag(TheCall->getBeginLoc(), diag::warn_attribute_arm_sm_incompat_builtin) + << TheCall->getSourceRange() << "streaming"; + } + + if (FnType == ArmStreamingCompatible && + BuiltinType != ArmStreamingCompatible) { + S.Diag(TheCall->getBeginLoc(), diag::warn_attribute_arm_sm_incompat_builtin) + << TheCall->getSourceRange() << "streaming compatible"; + return; + } +} + bool Sema::CheckSVEBuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall) { // Range check SVE intrinsics that take immediate values. SmallVector, 3> ImmChecks; @@ -3148,6 +3185,23 @@ bool Sema::CheckSVEBuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall) { bool Sema::CheckNeonBuiltinFunctionCall(const TargetInfo &TI, unsigned BuiltinID, CallExpr *TheCall) { + if (const FunctionDecl *FD = getCurFunctionDecl()) { + + switch (BuiltinID) { + default: + break; +#define GET_NEON_BUILTINS +#define TARGET_BUILTIN(id, ...) case NEON::BI##id: +#define BUILTIN(id, ...) case NEON::BI##id: +#include "clang/Basic/arm_neon.inc" + checkArmStreamingBuiltin(*this, TheCall, FD, ArmNonStreaming); + break; +#undef TARGET_BUILTIN +#undef BUILTIN +#undef GET_NEON_BUILTINS + } + } + llvm::APSInt Result; uint64_t mask = 0; unsigned TV = 0; diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 23dd8ae15c16583e792f8ca628cdaff22117f866..f12424d33b7da2d1c68880903fac404549127174 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -442,7 +442,6 @@ ParsedType Sema::getTypeName(const IdentifierInfo &II, SourceLocation NameLoc, UsingShadowDecl *FoundUsingShadow = nullptr; switch (Result.getResultKind()) { case LookupResult::NotFound: - case LookupResult::NotFoundInCurrentInstantiation: if (CorrectedII) { TypeNameValidatorCCC CCC(/*AllowInvalid=*/true, isClassName, AllowDeducedTemplate); @@ -482,7 +481,19 @@ ParsedType Sema::getTypeName(const IdentifierInfo &II, SourceLocation NameLoc, } } } - // If typo correction failed or was not performed, fall through + Result.suppressDiagnostics(); + return nullptr; + case LookupResult::NotFoundInCurrentInstantiation: + if (AllowImplicitTypename == ImplicitTypenameContext::Yes) { + QualType T = Context.getDependentNameType(ElaboratedTypeKeyword::None, + SS->getScopeRep(), &II); + TypeLocBuilder TLB; + DependentNameTypeLoc TL = TLB.push(T); + TL.setElaboratedKeywordLoc(SourceLocation()); + TL.setQualifierLoc(SS->getWithLocInContext(Context)); + TL.setNameLoc(NameLoc); + return CreateParsedType(T, TLB.getTypeSourceInfo(Context, T)); + } [[fallthrough]]; case LookupResult::FoundOverloaded: case LookupResult::FoundUnresolvedValue: @@ -3373,6 +3384,26 @@ static void mergeParamDeclAttributes(ParmVarDecl *newDecl, diag::note_carries_dependency_missing_first_decl) << 1/*Param*/; } + // HLSL parameter declarations for inout and out must match between + // declarations. In HLSL inout and out are ambiguous at the call site, but + // have different calling behavior, so you cannot overload a method based on a + // difference between inout and out annotations. + if (S.getLangOpts().HLSL) { + const auto *NDAttr = newDecl->getAttr(); + const auto *ODAttr = oldDecl->getAttr(); + // We don't need to cover the case where one declaration doesn't have an + // attribute. The only possible case there is if one declaration has an `in` + // attribute and the other declaration has no attribute. This case is + // allowed since parameters are `in` by default. + if (NDAttr && ODAttr && + NDAttr->getSpellingListIndex() != ODAttr->getSpellingListIndex()) { + S.Diag(newDecl->getLocation(), diag::err_hlsl_param_qualifier_mismatch) + << NDAttr << newDecl; + S.Diag(oldDecl->getLocation(), diag::note_previous_declaration_as) + << ODAttr; + } + } + if (!oldDecl->hasAttrs()) return; diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp index 8966a301de1db3f21757428e126431252403f0bc..a345978bb8701ce151b8ecf97c0e686c235cf08d 100644 --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -7311,6 +7311,36 @@ static void handleHLSLResourceBindingAttr(Sema &S, Decl *D, D->addAttr(NewAttr); } +static void handleHLSLParamModifierAttr(Sema &S, Decl *D, + const ParsedAttr &AL) { + HLSLParamModifierAttr *NewAttr = S.mergeHLSLParamModifierAttr( + D, AL, + static_cast(AL.getSemanticSpelling())); + if (NewAttr) + D->addAttr(NewAttr); +} + +HLSLParamModifierAttr * +Sema::mergeHLSLParamModifierAttr(Decl *D, const AttributeCommonInfo &AL, + HLSLParamModifierAttr::Spelling Spelling) { + // We can only merge an `in` attribute with an `out` attribute. All other + // combinations of duplicated attributes are ill-formed. + if (HLSLParamModifierAttr *PA = D->getAttr()) { + if ((PA->isIn() && Spelling == HLSLParamModifierAttr::Keyword_out) || + (PA->isOut() && Spelling == HLSLParamModifierAttr::Keyword_in)) { + D->dropAttr(); + SourceRange AdjustedRange = {PA->getLocation(), AL.getRange().getEnd()}; + return HLSLParamModifierAttr::Create( + Context, /*MergedSpelling=*/true, AdjustedRange, + HLSLParamModifierAttr::Keyword_inout); + } + Diag(AL.getLoc(), diag::err_hlsl_duplicate_parameter_modifier) << AL; + Diag(PA->getLocation(), diag::note_conflicting_attribute); + return nullptr; + } + return HLSLParamModifierAttr::Create(Context, AL); +} + static void handleMSInheritanceAttr(Sema &S, Decl *D, const ParsedAttr &AL) { if (!S.LangOpts.CPlusPlus) { S.Diag(AL.getLoc(), diag::err_attribute_not_supported_in_lang) @@ -9006,6 +9036,9 @@ ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, const ParsedAttr &AL, case ParsedAttr::AT_BPFPreserveAccessIndex: handleBPFPreserveAccessIndexAttr(S, D, AL); break; + case ParsedAttr::AT_BPFPreserveStaticOffset: + handleSimpleAttribute(S, D, AL); + break; case ParsedAttr::AT_BTFDeclTag: handleBTFDeclTagAttr(S, D, AL); break; @@ -9461,6 +9494,9 @@ ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, const ParsedAttr &AL, case ParsedAttr::AT_HLSLResourceBinding: handleHLSLResourceBindingAttr(S, D, AL); break; + case ParsedAttr::AT_HLSLParamModifier: + handleHLSLParamModifierAttr(S, D, AL); + break; case ParsedAttr::AT_AbiTag: handleAbiTagAttr(S, D, AL); diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index 7385eac48d8c895b2625f6cae210189252348537..8fedf41d8424ad9f925c4e9ea9a9d7cac78ff3ad 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -12114,6 +12114,24 @@ public: } +static void DiagnoseInvisibleNamespace(const TypoCorrection &Corrected, + Sema &S) { + auto *ND = cast(Corrected.getFoundDecl()); + Module *M = ND->getOwningModule(); + assert(M && "hidden namespace definition not in a module?"); + + if (M->isExplicitGlobalModule()) + S.Diag(Corrected.getCorrectionRange().getBegin(), + diag::err_module_unimported_use_header) + << (int)Sema::MissingImportKind::Declaration << Corrected.getFoundDecl() + << /*Header Name*/ false; + else + S.Diag(Corrected.getCorrectionRange().getBegin(), + diag::err_module_unimported_use) + << (int)Sema::MissingImportKind::Declaration << Corrected.getFoundDecl() + << M->getTopLevelModuleName(); +} + static bool TryNamespaceTypoCorrection(Sema &S, LookupResult &R, Scope *Sc, CXXScopeSpec &SS, SourceLocation IdentLoc, @@ -12123,7 +12141,16 @@ static bool TryNamespaceTypoCorrection(Sema &S, LookupResult &R, Scope *Sc, if (TypoCorrection Corrected = S.CorrectTypo(R.getLookupNameInfo(), R.getLookupKind(), Sc, &SS, CCC, Sema::CTK_ErrorRecovery)) { - if (DeclContext *DC = S.computeDeclContext(SS, false)) { + // Generally we find it is confusing more than helpful to diagnose the + // invisible namespace. + // See https://github.com/llvm/llvm-project/issues/73893. + // + // However, we should diagnose when the users are trying to using an + // invisible namespace. So we handle the case specially here. + if (isa_and_nonnull(Corrected.getFoundDecl()) && + Corrected.requiresImport()) { + DiagnoseInvisibleNamespace(Corrected, S); + } else if (DeclContext *DC = S.computeDeclContext(SS, false)) { std::string CorrectedStr(Corrected.getAsString(S.getLangOpts())); bool DroppedSpecifier = Corrected.WillReplaceSpecifier() && Ident->getName().equals(CorrectedStr); diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index fc39d6149c1cc659bd09e7ac92ad5c9df3308174..d629be083d8c38c2da587dd40210f19ee23deb86 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -1503,16 +1503,22 @@ static void checkEnumArithmeticConversions(Sema &S, Expr *LHS, Expr *RHS, bool IsCompAssign = ACK == Sema::ACK_CompAssign; if ((!IsCompAssign && LEnum && R->isFloatingType()) || (REnum && L->isFloatingType())) { - S.Diag(Loc, S.getLangOpts().CPlusPlus20 + S.Diag(Loc, S.getLangOpts().CPlusPlus26 + ? diag::err_arith_conv_enum_float_cxx26 + : S.getLangOpts().CPlusPlus20 ? diag::warn_arith_conv_enum_float_cxx20 : diag::warn_arith_conv_enum_float) - << LHS->getSourceRange() << RHS->getSourceRange() - << (int)ACK << LEnum << L << R; + << LHS->getSourceRange() << RHS->getSourceRange() << (int)ACK << LEnum + << L << R; } else if (!IsCompAssign && LEnum && REnum && !S.Context.hasSameUnqualifiedType(L, R)) { unsigned DiagID; - if (!L->castAs()->getDecl()->hasNameForLinkage() || - !R->castAs()->getDecl()->hasNameForLinkage()) { + // In C++ 26, usual arithmetic conversions between 2 different enum types + // are ill-formed. + if (S.getLangOpts().CPlusPlus26) + DiagID = diag::err_conv_mixed_enum_types_cxx26; + else if (!L->castAs()->getDecl()->hasNameForLinkage() || + !R->castAs()->getDecl()->hasNameForLinkage()) { // If either enumeration type is unnamed, it's less likely that the // user cares about this, but this situation is still deprecated in // C++2a. Use a different warning group. @@ -6047,9 +6053,14 @@ Sema::CreateBuiltinArraySubscriptExpr(Expr *Base, SourceLocation LLoc, << IndexExpr->getSourceRange()); if ((IndexExpr->getType()->isSpecificBuiltinType(BuiltinType::Char_S) || - IndexExpr->getType()->isSpecificBuiltinType(BuiltinType::Char_U)) - && !IndexExpr->isTypeDependent()) - Diag(LLoc, diag::warn_subscript_is_char) << IndexExpr->getSourceRange(); + IndexExpr->getType()->isSpecificBuiltinType(BuiltinType::Char_U)) && + !IndexExpr->isTypeDependent()) { + std::optional IntegerContantExpr = + IndexExpr->getIntegerConstantExpr(getASTContext()); + if (!IntegerContantExpr.has_value() || + IntegerContantExpr.value().isNegative()) + Diag(LLoc, diag::warn_subscript_is_char) << IndexExpr->getSourceRange(); + } // C99 6.5.2.1p1: "shall have type "pointer to *object* type". Similarly, // C++ [expr.sub]p1: The type "T" shall be a completely-defined object diff --git a/clang/lib/Sema/SemaExprMember.cpp b/clang/lib/Sema/SemaExprMember.cpp index bd85b548c0dd602e79317e06aab23a48af8958a0..473eea55bb6b19e026dd39749bbeb5ad10cccc32 100644 --- a/clang/lib/Sema/SemaExprMember.cpp +++ b/clang/lib/Sema/SemaExprMember.cpp @@ -1714,6 +1714,16 @@ static ExprResult LookupMemberExpr(Sema &S, LookupResult &R, ObjCImpDecl, HasTemplateArgs, TemplateKWLoc); } + // HLSL supports implicit conversion of scalar types to single element vector + // rvalues in member expressions. + if (S.getLangOpts().HLSL && BaseType->isScalarType()) { + QualType VectorTy = S.Context.getExtVectorType(BaseType, 1); + BaseExpr = S.ImpCastExprToType(BaseExpr.get(), VectorTy, CK_VectorSplat, + BaseExpr.get()->getValueKind()); + return LookupMemberExpr(S, R, BaseExpr, IsArrow, OpLoc, SS, ObjCImpDecl, + HasTemplateArgs, TemplateKWLoc); + } + S.Diag(OpLoc, diag::err_typecheck_member_reference_struct_union) << BaseType << BaseExpr.get()->getSourceRange() << MemberLoc; diff --git a/clang/lib/Sema/SemaLookup.cpp b/clang/lib/Sema/SemaLookup.cpp index 54b48f7ab80eb9c93859bbdc7cadd89166e0c0a4..996f8b57233ba223c1003c7357ae562c2d9ceac0 100644 --- a/clang/lib/Sema/SemaLookup.cpp +++ b/clang/lib/Sema/SemaLookup.cpp @@ -5712,6 +5712,11 @@ void Sema::diagnoseMissingImport(SourceLocation UseLoc, const NamedDecl *Decl, MissingImportKind MIK, bool Recover) { assert(!Modules.empty()); + // See https://github.com/llvm/llvm-project/issues/73893. It is generally + // confusing than helpful to show the namespace is not visible. + if (isa(Decl)) + return; + auto NotePrevious = [&] { // FIXME: Suppress the note backtrace even under // -fdiagnostics-show-note-include-stack. We don't care how this diff --git a/clang/lib/Sema/SemaOpenMP.cpp b/clang/lib/Sema/SemaOpenMP.cpp index 3a5f4b12ce296218c0dab6383da4c7505e570592..e400f248d15aa3497b9513603646803f1fb961cb 100644 --- a/clang/lib/Sema/SemaOpenMP.cpp +++ b/clang/lib/Sema/SemaOpenMP.cpp @@ -161,7 +161,7 @@ private: LoopControlVariablesMapTy LCVMap; DefaultDataSharingAttributes DefaultAttr = DSA_unspecified; SourceLocation DefaultAttrLoc; - DefaultmapInfo DefaultmapMap[OMPC_DEFAULTMAP_unknown]; + DefaultmapInfo DefaultmapMap[OMPC_DEFAULTMAP_unknown + 1]; OpenMPDirectiveKind Directive = OMPD_unknown; /// GenericLoopDirective with bind clause is mapped to other directives, /// like for, distribute and simd. Presently, set MappedDirective to @@ -3689,7 +3689,7 @@ class DSAAttrChecker final : public StmtVisitor { bool ErrorFound = false; bool TryCaptureCXXThisMembers = false; CapturedStmt *CS = nullptr; - const static unsigned DefaultmapKindNum = OMPC_DEFAULTMAP_pointer + 1; + const static unsigned DefaultmapKindNum = OMPC_DEFAULTMAP_unknown + 1; llvm::SmallVector ImplicitFirstprivate; llvm::SmallVector ImplicitPrivate; llvm::SmallVector ImplicitMap[DefaultmapKindNum][OMPC_MAP_delete]; @@ -6276,7 +6276,7 @@ StmtResult Sema::ActOnOpenMPExecutableDirective( SmallVector ImplicitPrivates( DSAChecker.getImplicitPrivate().begin(), DSAChecker.getImplicitPrivate().end()); - const unsigned DefaultmapKindNum = OMPC_DEFAULTMAP_pointer + 1; + const unsigned DefaultmapKindNum = OMPC_DEFAULTMAP_unknown + 1; SmallVector ImplicitMaps[DefaultmapKindNum][OMPC_MAP_delete]; SmallVector ImplicitMapModifiers[DefaultmapKindNum]; diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp index 3a3e9234469d3933c165f0dc324c3a1876dbf18a..5026e1d603e5ee24556c6b55273020b3874348b9 100644 --- a/clang/lib/Sema/SemaOverload.cpp +++ b/clang/lib/Sema/SemaOverload.cpp @@ -6123,61 +6123,6 @@ static ExprResult BuildConvertedConstantExpression(Sema &S, Expr *From, return Result; } -/// EvaluateConvertedConstantExpression - Evaluate an Expression -/// That is a converted constant expression -/// (which was built with BuildConvertedConstantExpression) -static ExprResult EvaluateConvertedConstantExpression( - Sema &S, Expr *E, QualType T, APValue &Value, Sema::CCEKind CCE, - bool RequireInt, const APValue &PreNarrowingValue) { - ExprResult Result = E; - // Check the expression is a constant expression. - SmallVector Notes; - Expr::EvalResult Eval; - Eval.Diag = &Notes; - - ConstantExprKind Kind; - if (CCE == Sema::CCEK_TemplateArg && T->isRecordType()) - Kind = ConstantExprKind::ClassTemplateArgument; - else if (CCE == Sema::CCEK_TemplateArg) - Kind = ConstantExprKind::NonClassTemplateArgument; - else - Kind = ConstantExprKind::Normal; - - if (!E->EvaluateAsConstantExpr(Eval, S.Context, Kind) || - (RequireInt && !Eval.Val.isInt())) { - // The expression can't be folded, so we can't keep it at this position in - // the AST. - Result = ExprError(); - } else { - Value = Eval.Val; - - if (Notes.empty()) { - // It's a constant expression. - Expr *E = ConstantExpr::Create(S.Context, Result.get(), Value); - if (!PreNarrowingValue.isAbsent()) - Value = std::move(PreNarrowingValue); - return E; - } - } - - // It's not a constant expression. Produce an appropriate diagnostic. - if (Notes.size() == 1 && - Notes[0].second.getDiagID() == diag::note_invalid_subexpr_in_const_expr) { - S.Diag(Notes[0].first, diag::err_expr_not_cce) << CCE; - } else if (!Notes.empty() && Notes[0].second.getDiagID() == - diag::note_constexpr_invalid_template_arg) { - Notes[0].second.setDiagID(diag::err_constexpr_invalid_template_arg); - for (unsigned I = 0; I < Notes.size(); ++I) - S.Diag(Notes[I].first, Notes[I].second); - } else { - S.Diag(E->getBeginLoc(), diag::err_expr_not_cce) - << CCE << E->getSourceRange(); - for (unsigned I = 0; I < Notes.size(); ++I) - S.Diag(Notes[I].first, Notes[I].second); - } - return ExprError(); -} - /// CheckConvertedConstantExpression - Check that the expression From is a /// converted constant expression of type T, perform the conversion and produce /// the converted expression, per C++11 [expr.const]p3. @@ -6194,8 +6139,8 @@ static ExprResult CheckConvertedConstantExpression(Sema &S, Expr *From, Value = APValue(); return Result; } - return EvaluateConvertedConstantExpression(S, Result.get(), T, Value, CCE, - RequireInt, PreNarrowingValue); + return S.EvaluateConvertedConstantExpression(Result.get(), T, Value, CCE, + RequireInt, PreNarrowingValue); } ExprResult Sema::BuildConvertedConstantExpression(Expr *From, QualType T, @@ -6226,6 +6171,62 @@ ExprResult Sema::CheckConvertedConstantExpression(Expr *From, QualType T, return R; } +/// EvaluateConvertedConstantExpression - Evaluate an Expression +/// That is a converted constant expression +/// (which was built with BuildConvertedConstantExpression) +ExprResult +Sema::EvaluateConvertedConstantExpression(Expr *E, QualType T, APValue &Value, + Sema::CCEKind CCE, bool RequireInt, + const APValue &PreNarrowingValue) { + + ExprResult Result = E; + // Check the expression is a constant expression. + SmallVector Notes; + Expr::EvalResult Eval; + Eval.Diag = &Notes; + + ConstantExprKind Kind; + if (CCE == Sema::CCEK_TemplateArg && T->isRecordType()) + Kind = ConstantExprKind::ClassTemplateArgument; + else if (CCE == Sema::CCEK_TemplateArg) + Kind = ConstantExprKind::NonClassTemplateArgument; + else + Kind = ConstantExprKind::Normal; + + if (!E->EvaluateAsConstantExpr(Eval, Context, Kind) || + (RequireInt && !Eval.Val.isInt())) { + // The expression can't be folded, so we can't keep it at this position in + // the AST. + Result = ExprError(); + } else { + Value = Eval.Val; + + if (Notes.empty()) { + // It's a constant expression. + Expr *E = ConstantExpr::Create(Context, Result.get(), Value); + if (!PreNarrowingValue.isAbsent()) + Value = std::move(PreNarrowingValue); + return E; + } + } + + // It's not a constant expression. Produce an appropriate diagnostic. + if (Notes.size() == 1 && + Notes[0].second.getDiagID() == diag::note_invalid_subexpr_in_const_expr) { + Diag(Notes[0].first, diag::err_expr_not_cce) << CCE; + } else if (!Notes.empty() && Notes[0].second.getDiagID() == + diag::note_constexpr_invalid_template_arg) { + Notes[0].second.setDiagID(diag::err_constexpr_invalid_template_arg); + for (unsigned I = 0; I < Notes.size(); ++I) + Diag(Notes[I].first, Notes[I].second); + } else { + Diag(E->getBeginLoc(), diag::err_expr_not_cce) + << CCE << E->getSourceRange(); + for (unsigned I = 0; I < Notes.size(); ++I) + Diag(Notes[I].first, Notes[I].second); + } + return ExprError(); +} /// dropPointerConversions - If the given standard conversion sequence /// involves any pointer conversions, remove them. This may change diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp index 34d7b8c731e9076eb971612485cfb1c0da81f5ef..09bbf14d39af5ad09f68859261d6a58d944c3745 100644 --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -7313,49 +7313,74 @@ ExprResult Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param, return E; } + QualType CanonParamType = Context.getCanonicalType(ParamType); + // Avoid making a copy when initializing a template parameter of class type + // from a template parameter object of the same type. This is going beyond + // the standard, but is required for soundness: in + // template struct X { X *p; X *q; }; + // ... we need p and q to have the same type. + // + // Similarly, don't inject a call to a copy constructor when initializing + // from a template parameter of the same type. + Expr *InnerArg = Arg->IgnoreParenImpCasts(); + if (ParamType->isRecordType() && isa(InnerArg) && + Context.hasSameUnqualifiedType(ParamType, InnerArg->getType())) { + NamedDecl *ND = cast(InnerArg)->getDecl(); + if (auto *TPO = dyn_cast(ND)) { + + SugaredConverted = TemplateArgument(TPO, ParamType); + CanonicalConverted = + TemplateArgument(TPO->getCanonicalDecl(), CanonParamType); + return Arg; + } + if (isa(ND)) { + SugaredConverted = TemplateArgument(Arg); + CanonicalConverted = + Context.getCanonicalTemplateArgument(SugaredConverted); + return Arg; + } + } + // The initialization of the parameter from the argument is // a constant-evaluated context. EnterExpressionEvaluationContext ConstantEvaluated( *this, Sema::ExpressionEvaluationContext::ConstantEvaluated); - if (getLangOpts().CPlusPlus17) { - QualType CanonParamType = Context.getCanonicalType(ParamType); - - // Avoid making a copy when initializing a template parameter of class type - // from a template parameter object of the same type. This is going beyond - // the standard, but is required for soundness: in - // template struct X { X *p; X *q; }; - // ... we need p and q to have the same type. - // - // Similarly, don't inject a call to a copy constructor when initializing - // from a template parameter of the same type. - Expr *InnerArg = Arg->IgnoreParenImpCasts(); - if (ParamType->isRecordType() && isa(InnerArg) && - Context.hasSameUnqualifiedType(ParamType, InnerArg->getType())) { - NamedDecl *ND = cast(InnerArg)->getDecl(); - if (auto *TPO = dyn_cast(ND)) { - - SugaredConverted = TemplateArgument(TPO, ParamType); - CanonicalConverted = - TemplateArgument(TPO->getCanonicalDecl(), CanonParamType); - return Arg; - } - if (isa(ND)) { - SugaredConverted = TemplateArgument(Arg); - CanonicalConverted = - Context.getCanonicalTemplateArgument(SugaredConverted); - return Arg; - } - } + bool IsConvertedConstantExpression = true; + if (isa(Arg) || ParamType->isRecordType()) { + InitializationKind Kind = InitializationKind::CreateForInit( + Arg->getBeginLoc(), /*DirectInit=*/false, Arg); + Expr *Inits[1] = {Arg}; + InitializedEntity Entity = + InitializedEntity::InitializeTemplateParameter(ParamType, Param); + InitializationSequence InitSeq(*this, Entity, Kind, Inits); + ExprResult Result = InitSeq.Perform(*this, Entity, Kind, Inits); + if (Result.isInvalid() || !Result.get()) + return ExprError(); + Result = ActOnConstantExpression(Result.get()); + if (Result.isInvalid() || !Result.get()) + return ExprError(); + Arg = ActOnFinishFullExpr(Result.get(), Arg->getBeginLoc(), + /*DiscardedValue=*/false, + /*IsConstexpr=*/true, /*IsTemplateArgument=*/true) + .get(); + IsConvertedConstantExpression = false; + } + if (getLangOpts().CPlusPlus17) { // C++17 [temp.arg.nontype]p1: // A template-argument for a non-type template parameter shall be // a converted constant expression of the type of the template-parameter. APValue Value; - ExprResult ArgResult = CheckConvertedConstantExpression( - Arg, ParamType, Value, CCEK_TemplateArg, Param); - if (ArgResult.isInvalid()) - return ExprError(); + ExprResult ArgResult; + if (IsConvertedConstantExpression) { + ArgResult = BuildConvertedConstantExpression(Arg, ParamType, + CCEK_TemplateArg, Param); + if (ArgResult.isInvalid()) + return ExprError(); + } else { + ArgResult = Arg; + } // For a value-dependent argument, CheckConvertedConstantExpression is // permitted (and expected) to be unable to determine a value. @@ -7366,6 +7391,13 @@ ExprResult Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param, return ArgResult; } + APValue PreNarrowingValue; + ArgResult = EvaluateConvertedConstantExpression( + ArgResult.get(), ParamType, Value, CCEK_TemplateArg, /*RequireInt=*/ + false, PreNarrowingValue); + if (ArgResult.isInvalid()) + return ExprError(); + // Convert the APValue to a TemplateArgument. switch (Value.getKind()) { case APValue::None: diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp index 08f4ba00fc9f7de092f5a3cdb42c765341e34680..d768bb72e07c09a3cdd8972f2dd1f7daa75b21ab 100644 --- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -663,6 +663,14 @@ static bool isRelevantAttr(Sema &S, const Decl *D, const Attr *A) { return true; } +static void instantiateDependentHLSLParamModifierAttr( + Sema &S, const MultiLevelTemplateArgumentList &TemplateArgs, + const HLSLParamModifierAttr *Attr, Decl *New) { + ParmVarDecl *P = cast(New); + P->addAttr(Attr->clone(S.getASTContext())); + P->setType(S.getASTContext().getLValueReferenceType(P->getType())); +} + void Sema::InstantiateAttrsForDecl( const MultiLevelTemplateArgumentList &TemplateArgs, const Decl *Tmpl, Decl *New, LateInstantiatedAttrVec *LateAttrs, @@ -784,6 +792,12 @@ void Sema::InstantiateAttrs(const MultiLevelTemplateArgumentList &TemplateArgs, *AMDGPUFlatWorkGroupSize, New); } + if (const auto *ParamAttr = dyn_cast(TmplAttr)) { + instantiateDependentHLSLParamModifierAttr(*this, TemplateArgs, ParamAttr, + New); + continue; + } + // Existing DLL attribute on the instantiation takes precedence. if (TmplAttr->getKind() == attr::DLLExport || TmplAttr->getKind() == attr::DLLImport) { diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp index 560feafa1857cb3ffc4f785de3ec23f04bad313a..56d133f20a2935170486e0a34c23989081a36cb2 100644 --- a/clang/lib/Sema/SemaType.cpp +++ b/clang/lib/Sema/SemaType.cpp @@ -8661,6 +8661,17 @@ static void HandleLifetimeBoundAttr(TypeProcessingState &State, } } +static void HandleHLSLParamModifierAttr(QualType &CurType, + const ParsedAttr &Attr, Sema &S) { + // Don't apply this attribute to template dependent types. It is applied on + // substitution during template instantiation. + if (CurType->isDependentType()) + return; + if (Attr.getSemanticSpelling() == HLSLParamModifierAttr::Keyword_inout || + Attr.getSemanticSpelling() == HLSLParamModifierAttr::Keyword_out) + CurType = S.getASTContext().getLValueReferenceType(CurType); +} + static void processTypeAttrs(TypeProcessingState &state, QualType &type, TypeAttrLocation TAL, const ParsedAttributesView &attrs, @@ -8837,6 +8848,12 @@ static void processTypeAttrs(TypeProcessingState &state, QualType &type, break; } + case ParsedAttr::AT_HLSLParamModifier: { + HandleHLSLParamModifierAttr(type, attr, state.getSema()); + attr.setUsedAsTypeAttr(); + break; + } + MS_TYPE_ATTRS_CASELIST: if (!handleMSPointerTypeQualifierAttr(state, attr, type)) attr.setUsedAsTypeAttr(); diff --git a/clang/lib/Serialization/ASTReaderDecl.cpp b/clang/lib/Serialization/ASTReaderDecl.cpp index 79817b3fb1ec3a0e5fa85b2fcbb2630a527f869d..bc16cfc67a24f9fa45292ca92612aee7b5f1101f 100644 --- a/clang/lib/Serialization/ASTReaderDecl.cpp +++ b/clang/lib/Serialization/ASTReaderDecl.cpp @@ -2032,7 +2032,7 @@ void ASTDeclReader::ReadCXXDefinitionData( break; case LCK_ByCopy: case LCK_ByRef: - auto *Var = readDeclAs(); + auto *Var = readDeclAs(); SourceLocation EllipsisLoc = readSourceLocation(); new (ToCapture) Capture(Loc, IsImplicit, Kind, Var, EllipsisLoc); ToCapture++; diff --git a/clang/lib/StaticAnalyzer/Checkers/ArrayBoundCheckerV2.cpp b/clang/lib/StaticAnalyzer/Checkers/ArrayBoundCheckerV2.cpp index ffc7236d1e2551a8aa7c79444a78fe746fcfe698..6c7a1601402efac74ed7009617e1cd9ff2409aad 100644 --- a/clang/lib/StaticAnalyzer/Checkers/ArrayBoundCheckerV2.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/ArrayBoundCheckerV2.cpp @@ -12,6 +12,7 @@ //===----------------------------------------------------------------------===// #include "clang/AST/CharUnits.h" +#include "clang/AST/ParentMapContext.h" #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" #include "clang/StaticAnalyzer/Checkers/Taint.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" @@ -34,20 +35,46 @@ using llvm::formatv; namespace { enum OOB_Kind { OOB_Precedes, OOB_Exceeds, OOB_Taint }; -class ArrayBoundCheckerV2 : - public Checker { +struct Messages { + std::string Short, Full; +}; + +// NOTE: The `ArraySubscriptExpr` and `UnaryOperator` callbacks are `PostStmt` +// instead of `PreStmt` because the current implementation passes the whole +// expression to `CheckerContext::getSVal()` which only works after the +// symbolic evaluation of the expression. (To turn them into `PreStmt` +// callbacks, we'd need to duplicate the logic that evaluates these +// expressions.) The `MemberExpr` callback would work as `PreStmt` but it's +// defined as `PostStmt` for the sake of consistency with the other callbacks. +class ArrayBoundCheckerV2 : public Checker, + check::PostStmt, + check::PostStmt> { BugType BT{this, "Out-of-bound access"}; BugType TaintBT{this, "Out-of-bound access", categories::TaintedData}; + void performCheck(const Expr *E, CheckerContext &C) const; + void reportOOB(CheckerContext &C, ProgramStateRef ErrorState, OOB_Kind Kind, - NonLoc Offset, std::string RegName, std::string Msg) const; + NonLoc Offset, Messages Msgs) const; static bool isFromCtypeMacro(const Stmt *S, ASTContext &AC); + static bool isInAddressOf(const Stmt *S, ASTContext &AC); + public: - void checkLocation(SVal l, bool isLoad, const Stmt *S, - CheckerContext &C) const; + void checkPostStmt(const ArraySubscriptExpr *E, CheckerContext &C) const { + performCheck(E, C); + } + void checkPostStmt(const UnaryOperator *E, CheckerContext &C) const { + if (E->getOpcode() == UO_Deref) + performCheck(E, C); + } + void checkPostStmt(const MemberExpr *E, CheckerContext &C) const { + if (E->isArrow()) + performCheck(E->getBase(), C); + } }; + } // anonymous namespace /// For a given Location that can be represented as a symbolic expression @@ -149,9 +176,11 @@ getSimplifiedOffsets(NonLoc offset, nonloc::ConcreteInt extent, // where the first one corresponds to "value below threshold" and the second // corresponds to "value at or above threshold". Returns {nullptr, nullptr} in // the case when the evaluation fails. +// If the optional argument CheckEquality is true, then use BO_EQ instead of +// the default BO_LT after consistently applying the same simplification steps. static std::pair compareValueToThreshold(ProgramStateRef State, NonLoc Value, NonLoc Threshold, - SValBuilder &SVB) { + SValBuilder &SVB, bool CheckEquality = false) { if (auto ConcreteThreshold = Threshold.getAs()) { std::tie(Value, Threshold) = getSimplifiedOffsets(Value, *ConcreteThreshold, SVB); } @@ -167,8 +196,10 @@ compareValueToThreshold(ProgramStateRef State, NonLoc Value, NonLoc Threshold, return {nullptr, State}; } } + const BinaryOperatorKind OpKind = CheckEquality ? BO_EQ : BO_LT; auto BelowThreshold = - SVB.evalBinOpNN(State, BO_LT, Value, Threshold, SVB.getConditionType()).getAs(); + SVB.evalBinOpNN(State, OpKind, Value, Threshold, SVB.getConditionType()) + .getAs(); if (BelowThreshold) return State->assume(*BelowThreshold); @@ -217,16 +248,19 @@ static std::string getShortMsg(OOB_Kind Kind, std::string RegName) { return formatv(ShortMsgTemplates[Kind], RegName); } -static std::string getPrecedesMsg(std::string RegName, NonLoc Offset) { +static Messages getPrecedesMsgs(const SubRegion *Region, NonLoc Offset) { + std::string RegName = getRegionName(Region); SmallString<128> Buf; llvm::raw_svector_ostream Out(Buf); Out << "Access of " << RegName << " at negative byte offset"; if (auto ConcreteIdx = Offset.getAs()) Out << ' ' << ConcreteIdx->getValue(); - return std::string(Buf); + return {getShortMsg(OOB_Precedes, RegName), std::string(Buf)}; } -static std::string getExceedsMsg(ASTContext &ACtx, std::string RegName, - NonLoc Offset, NonLoc Extent, SVal Location) { + +static Messages getExceedsMsgs(ASTContext &ACtx, const SubRegion *Region, + NonLoc Offset, NonLoc Extent, SVal Location) { + std::string RegName = getRegionName(Region); const auto *EReg = Location.getAsRegion()->getAs(); assert(EReg && "this checker only handles element access"); QualType ElemType = EReg->getElementType(); @@ -273,20 +307,18 @@ static std::string getExceedsMsg(ASTContext &ACtx, std::string RegName, Out << "s"; } - return std::string(Buf); -} -static std::string getTaintMsg(std::string RegName) { - SmallString<128> Buf; - llvm::raw_svector_ostream Out(Buf); - Out << "Access of " << RegName - << " with a tainted offset that may be too large"; - return std::string(Buf); + return {getShortMsg(OOB_Exceeds, RegName), std::string(Buf)}; } -void ArrayBoundCheckerV2::checkLocation(SVal Location, bool IsLoad, - const Stmt *LoadS, - CheckerContext &C) const { +static Messages getTaintMsgs(const SubRegion *Region, const char *OffsetName) { + std::string RegName = getRegionName(Region); + return {formatv("Potential out of bound access to {0} with tainted {1}", + RegName, OffsetName), + formatv("Access of {0} with a tainted {1} that may be too large", + RegName, OffsetName)}; +} +void ArrayBoundCheckerV2::performCheck(const Expr *E, CheckerContext &C) const { // NOTE: Instead of using ProgramState::assumeInBound(), we are prototyping // some new logic here that reasons directly about memory region extents. // Once that logic is more mature, we can bring it back to assumeInBound() @@ -297,12 +329,14 @@ void ArrayBoundCheckerV2::checkLocation(SVal Location, bool IsLoad, // have some flexibility in defining the base region, we can achieve // various levels of conservatism in our buffer overflow checking. + const SVal Location = C.getSVal(E); + // The header ctype.h (from e.g. glibc) implements the isXXXXX() macros as // #define isXXXXX(arg) (LOOKUP_TABLE[arg] & BITMASK_FOR_XXXXX) // and incomplete analysis of these leads to false positives. As even // accurate reports would be confusing for the users, just disable reports // from these macros: - if (isFromCtypeMacro(LoadS, C.getASTContext())) + if (isFromCtypeMacro(E, C.getASTContext())) return; ProgramStateRef State = C.getState(); @@ -331,9 +365,8 @@ void ArrayBoundCheckerV2::checkLocation(SVal Location, bool IsLoad, if (PrecedesLowerBound && !WithinLowerBound) { // We know that the index definitely precedes the lower bound. - std::string RegName = getRegionName(Reg); - std::string Msg = getPrecedesMsg(RegName, ByteOffset); - reportOOB(C, PrecedesLowerBound, OOB_Precedes, ByteOffset, RegName, Msg); + Messages Msgs = getPrecedesMsgs(Reg, ByteOffset); + reportOOB(C, PrecedesLowerBound, OOB_Precedes, ByteOffset, Msgs); return; } @@ -350,17 +383,38 @@ void ArrayBoundCheckerV2::checkLocation(SVal Location, bool IsLoad, if (ExceedsUpperBound) { if (!WithinUpperBound) { // We know that the index definitely exceeds the upper bound. - std::string RegName = getRegionName(Reg); - std::string Msg = getExceedsMsg(C.getASTContext(), RegName, ByteOffset, - *KnownSize, Location); - reportOOB(C, ExceedsUpperBound, OOB_Exceeds, ByteOffset, RegName, Msg); + if (isa(E) && isInAddressOf(E, C.getASTContext())) { + // ...but this is within an addressof expression, so we need to check + // for the exceptional case that `&array[size]` is valid. + auto [EqualsToThreshold, NotEqualToThreshold] = + compareValueToThreshold(ExceedsUpperBound, ByteOffset, *KnownSize, + SVB, /*CheckEquality=*/true); + if (EqualsToThreshold && !NotEqualToThreshold) { + // We are definitely in the exceptional case, so return early + // instead of reporting a bug. + C.addTransition(EqualsToThreshold); + return; + } + } + Messages Msgs = getExceedsMsgs(C.getASTContext(), Reg, ByteOffset, + *KnownSize, Location); + reportOOB(C, ExceedsUpperBound, OOB_Exceeds, ByteOffset, Msgs); return; } if (isTainted(State, ByteOffset)) { - // Both cases are possible, but the index is tainted, so report. + // Both cases are possible, but the offset is tainted, so report. std::string RegName = getRegionName(Reg); - std::string Msg = getTaintMsg(RegName); - reportOOB(C, ExceedsUpperBound, OOB_Taint, ByteOffset, RegName, Msg); + + // Diagnostic detail: "tainted offset" is always correct, but the + // common case is that 'idx' is tainted in 'arr[idx]' and then it's + // nicer to say "tainted index". + const char *OffsetName = "offset"; + if (const auto *ASE = dyn_cast(E)) + if (isTainted(State, ASE->getIdx(), C.getLocationContext())) + OffsetName = "index"; + + Messages Msgs = getTaintMsgs(Reg, OffsetName); + reportOOB(C, ExceedsUpperBound, OOB_Taint, ByteOffset, Msgs); return; } } @@ -374,17 +428,14 @@ void ArrayBoundCheckerV2::checkLocation(SVal Location, bool IsLoad, void ArrayBoundCheckerV2::reportOOB(CheckerContext &C, ProgramStateRef ErrorState, OOB_Kind Kind, - NonLoc Offset, std::string RegName, - std::string Msg) const { + NonLoc Offset, Messages Msgs) const { ExplodedNode *ErrorNode = C.generateErrorNode(ErrorState); if (!ErrorNode) return; - std::string ShortMsg = getShortMsg(Kind, RegName); - auto BR = std::make_unique( - Kind == OOB_Taint ? TaintBT : BT, ShortMsg, Msg, ErrorNode); + Kind == OOB_Taint ? TaintBT : BT, Msgs.Short, Msgs.Full, ErrorNode); // Track back the propagation of taintedness. if (Kind == OOB_Taint) @@ -413,6 +464,18 @@ bool ArrayBoundCheckerV2::isFromCtypeMacro(const Stmt *S, ASTContext &ACtx) { (MacroName == "isupper") || (MacroName == "isxdigit")); } +bool ArrayBoundCheckerV2::isInAddressOf(const Stmt *S, ASTContext &ACtx) { + ParentMapContext &ParentCtx = ACtx.getParentMapContext(); + do { + const DynTypedNodeList Parents = ParentCtx.getParents(*S); + if (Parents.empty()) + return false; + S = Parents[0].get(); + } while (isa_and_nonnull(S)); + const auto *UnaryOp = dyn_cast_or_null(S); + return UnaryOp && UnaryOp->getOpcode() == UO_AddrOf; +} + void ento::registerArrayBoundCheckerV2(CheckerManager &mgr) { mgr.registerChecker(); } diff --git a/clang/lib/StaticAnalyzer/Checkers/BitwiseShiftChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/BitwiseShiftChecker.cpp index 8a933d124d7700d10d65909a4f28ff385d5cae77..d4aa9fa1339f47cacd307e01f8a61e30fa254807 100644 --- a/clang/lib/StaticAnalyzer/Checkers/BitwiseShiftChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/BitwiseShiftChecker.cpp @@ -172,18 +172,24 @@ BugReportPtr BitwiseShiftValidator::checkOvershift() { const SVal Right = Ctx.getSVal(operandExpr(OperandSide::Right)); - std::string RightOpStr = ""; + std::string RightOpStr = "", LowerBoundStr = ""; if (auto ConcreteRight = Right.getAs()) RightOpStr = formatv(" '{0}'", ConcreteRight->getValue()); + else { + SValBuilder &SVB = Ctx.getSValBuilder(); + if (const llvm::APSInt *MinRight = SVB.getMinValue(FoldedState, Right)) { + LowerBoundStr = formatv(" >= {0},", MinRight->getExtValue()); + } + } std::string ShortMsg = formatv( "{0} shift{1}{2} overflows the capacity of '{3}'", isLeftShift() ? "Left" : "Right", RightOpStr.empty() ? "" : " by", RightOpStr, LHSTy.getAsString()); - std::string Msg = - formatv("The result of {0} shift is undefined because the right " - "operand{1} is not smaller than {2}, the capacity of '{3}'", - shiftDir(), RightOpStr, LHSBitWidth, LHSTy.getAsString()); + std::string Msg = formatv( + "The result of {0} shift is undefined because the right " + "operand{1} is{2} not smaller than {3}, the capacity of '{4}'", + shiftDir(), RightOpStr, LowerBoundStr, LHSBitWidth, LHSTy.getAsString()); return createBugReport(ShortMsg, Msg); } diff --git a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp index eccb2063fad10d0123dffa6393a80a6d4e084a08..a4799b5f762caee2957d9ade00bfbaff85e75d0d 100644 --- a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp @@ -252,7 +252,10 @@ private: std::bind(&StreamChecker::evalFreadFwrite, _1, _2, _3, _4, false), 3}}, {{{"fgetc"}, 1}, {std::bind(&StreamChecker::preReadWrite, _1, _2, _3, _4, true), - std::bind(&StreamChecker::evalFgetc, _1, _2, _3, _4), 0}}, + std::bind(&StreamChecker::evalFgetx, _1, _2, _3, _4, true), 0}}, + {{{"fgets"}, 3}, + {std::bind(&StreamChecker::preReadWrite, _1, _2, _3, _4, true), + std::bind(&StreamChecker::evalFgetx, _1, _2, _3, _4, false), 2}}, {{{"fputc"}, 2}, {std::bind(&StreamChecker::preReadWrite, _1, _2, _3, _4, false), std::bind(&StreamChecker::evalFputx, _1, _2, _3, _4, true), 1}}, @@ -320,8 +323,8 @@ private: void evalFreadFwrite(const FnDescription *Desc, const CallEvent &Call, CheckerContext &C, bool IsFread) const; - void evalFgetc(const FnDescription *Desc, const CallEvent &Call, - CheckerContext &C) const; + void evalFgetx(const FnDescription *Desc, const CallEvent &Call, + CheckerContext &C, bool SingleChar) const; void evalFputx(const FnDescription *Desc, const CallEvent &Call, CheckerContext &C, bool IsSingleChar) const; @@ -760,8 +763,8 @@ void StreamChecker::evalFreadFwrite(const FnDescription *Desc, C.addTransition(StateFailed); } -void StreamChecker::evalFgetc(const FnDescription *Desc, const CallEvent &Call, - CheckerContext &C) const { +void StreamChecker::evalFgetx(const FnDescription *Desc, const CallEvent &Call, + CheckerContext &C, bool SingleChar) const { ProgramStateRef State = C.getState(); SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol(); if (!StreamSym) @@ -778,42 +781,61 @@ void StreamChecker::evalFgetc(const FnDescription *Desc, const CallEvent &Call, assertStreamStateOpened(OldSS); // `fgetc` returns the read character on success, otherwise returns EOF. + // `fgets` returns the read buffer address on success, otherwise returns NULL. - // Generate a transition for the success state of `fgetc`. - // If we know the state to be FEOF at fgetc, do not add a success state. if (OldSS->ErrorState != ErrorFEof) { - NonLoc RetVal = makeRetVal(C, CE).castAs(); - ProgramStateRef StateNotFailed = - State->BindExpr(CE, C.getLocationContext(), RetVal); - SValBuilder &SVB = C.getSValBuilder(); - ASTContext &ASTC = C.getASTContext(); - // The returned 'unsigned char' of `fgetc` is converted to 'int', - // so we need to check if it is in range [0, 255]. - auto CondLow = - SVB.evalBinOp(State, BO_GE, RetVal, SVB.makeZeroVal(ASTC.IntTy), - SVB.getConditionType()) - .getAs(); - auto CondHigh = - SVB.evalBinOp(State, BO_LE, RetVal, - SVB.makeIntVal(SVB.getBasicValueFactory() - .getMaxValue(ASTC.UnsignedCharTy) - .getLimitedValue(), - ASTC.IntTy), - SVB.getConditionType()) - .getAs(); - if (!CondLow || !CondHigh) - return; - StateNotFailed = StateNotFailed->assume(*CondLow, true); - if (!StateNotFailed) - return; - StateNotFailed = StateNotFailed->assume(*CondHigh, true); - if (!StateNotFailed) - return; - C.addTransition(StateNotFailed); + if (SingleChar) { + // Generate a transition for the success state of `fgetc`. + NonLoc RetVal = makeRetVal(C, CE).castAs(); + ProgramStateRef StateNotFailed = + State->BindExpr(CE, C.getLocationContext(), RetVal); + SValBuilder &SVB = C.getSValBuilder(); + ASTContext &ASTC = C.getASTContext(); + // The returned 'unsigned char' of `fgetc` is converted to 'int', + // so we need to check if it is in range [0, 255]. + auto CondLow = + SVB.evalBinOp(State, BO_GE, RetVal, SVB.makeZeroVal(ASTC.IntTy), + SVB.getConditionType()) + .getAs(); + auto CondHigh = + SVB.evalBinOp(State, BO_LE, RetVal, + SVB.makeIntVal(SVB.getBasicValueFactory() + .getMaxValue(ASTC.UnsignedCharTy) + .getLimitedValue(), + ASTC.IntTy), + SVB.getConditionType()) + .getAs(); + if (!CondLow || !CondHigh) + return; + StateNotFailed = StateNotFailed->assume(*CondLow, true); + if (!StateNotFailed) + return; + StateNotFailed = StateNotFailed->assume(*CondHigh, true); + if (!StateNotFailed) + return; + C.addTransition(StateNotFailed); + } else { + // Generate a transition for the success state of `fgets`. + std::optional GetBuf = + Call.getArgSVal(0).getAs(); + if (!GetBuf) + return; + ProgramStateRef StateNotFailed = + State->BindExpr(CE, C.getLocationContext(), *GetBuf); + StateNotFailed = StateNotFailed->set( + StreamSym, StreamState::getOpened(Desc)); + C.addTransition(StateNotFailed); + } } // Add transition for the failed state. - ProgramStateRef StateFailed = bindInt(*EofVal, State, C, CE); + ProgramStateRef StateFailed; + if (SingleChar) + StateFailed = bindInt(*EofVal, State, C, CE); + else + StateFailed = + State->BindExpr(CE, C.getLocationContext(), + C.getSValBuilder().makeNullWithType(CE->getType())); // If a (non-EOF) error occurs, the resulting value of the file position // indicator for the stream is indeterminate. diff --git a/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp b/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp index 5de99384449a4c8c1b3080938c66b5275e4e915f..25d066c4652f2bf1474b61b1835591e1f077cfea 100644 --- a/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp +++ b/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp @@ -1876,6 +1876,12 @@ public: const llvm::APSInt *getSymVal(ProgramStateRef State, SymbolRef Sym) const override; + const llvm::APSInt *getSymMinVal(ProgramStateRef State, + SymbolRef Sym) const override; + + const llvm::APSInt *getSymMaxVal(ProgramStateRef State, + SymbolRef Sym) const override; + ProgramStateRef removeDeadBindings(ProgramStateRef State, SymbolReaper &SymReaper) override; @@ -2863,6 +2869,22 @@ const llvm::APSInt *RangeConstraintManager::getSymVal(ProgramStateRef St, return T ? T->getConcreteValue() : nullptr; } +const llvm::APSInt *RangeConstraintManager::getSymMinVal(ProgramStateRef St, + SymbolRef Sym) const { + const RangeSet *T = getConstraint(St, Sym); + if (!T || T->isEmpty()) + return nullptr; + return &T->getMinValue(); +} + +const llvm::APSInt *RangeConstraintManager::getSymMaxVal(ProgramStateRef St, + SymbolRef Sym) const { + const RangeSet *T = getConstraint(St, Sym); + if (!T || T->isEmpty()) + return nullptr; + return &T->getMaxValue(); +} + //===----------------------------------------------------------------------===// // Remove dead symbols from existing constraints //===----------------------------------------------------------------------===// diff --git a/clang/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp b/clang/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp index 0f2d6c8fc80bb0229fbb4895761625bd17d2998e..45e48d435aca6a2c2d374dd95d074d687f21f2a4 100644 --- a/clang/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp +++ b/clang/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp @@ -28,7 +28,11 @@ class SimpleSValBuilder : public SValBuilder { // returns NULL. // This is an implementation detail. Checkers should use `getKnownValue()` // instead. - const llvm::APSInt *getConstValue(ProgramStateRef state, SVal V); + static const llvm::APSInt *getConstValue(ProgramStateRef state, SVal V); + + // Helper function that returns the value stored in a nonloc::ConcreteInt or + // loc::ConcreteInt. + static const llvm::APSInt *getConcreteValue(SVal V); // With one `simplifySValOnce` call, a compound symbols might collapse to // simpler symbol tree that is still possible to further simplify. Thus, we @@ -76,6 +80,16 @@ public: /// (integer) value, that value is returned. Otherwise, returns NULL. const llvm::APSInt *getKnownValue(ProgramStateRef state, SVal V) override; + /// Evaluates a given SVal by recursively evaluating and simplifying the + /// children SVals, then returns its minimal possible (integer) value. If the + /// constraint manager cannot provide a meaningful answer, this returns NULL. + const llvm::APSInt *getMinValue(ProgramStateRef state, SVal V) override; + + /// Evaluates a given SVal by recursively evaluating and simplifying the + /// children SVals, then returns its maximal possible (integer) value. If the + /// constraint manager cannot provide a meaningful answer, this returns NULL. + const llvm::APSInt *getMaxValue(ProgramStateRef state, SVal V) override; + SVal simplifySVal(ProgramStateRef State, SVal V) override; SVal MakeSymIntVal(const SymExpr *LHS, BinaryOperator::Opcode op, @@ -1182,18 +1196,22 @@ SVal SimpleSValBuilder::evalBinOpLN(ProgramStateRef state, const llvm::APSInt *SimpleSValBuilder::getConstValue(ProgramStateRef state, SVal V) { - if (V.isUnknownOrUndef()) - return nullptr; + if (const llvm::APSInt *Res = getConcreteValue(V)) + return Res; + if (SymbolRef Sym = V.getAsSymbol()) + return state->getConstraintManager().getSymVal(state, Sym); + + return nullptr; +} + +const llvm::APSInt *SimpleSValBuilder::getConcreteValue(SVal V) { if (std::optional X = V.getAs()) return &X->getValue(); if (std::optional X = V.getAs()) return &X->getValue(); - if (SymbolRef Sym = V.getAsSymbol()) - return state->getConstraintManager().getSymVal(state, Sym); - return nullptr; } @@ -1202,6 +1220,32 @@ const llvm::APSInt *SimpleSValBuilder::getKnownValue(ProgramStateRef state, return getConstValue(state, simplifySVal(state, V)); } +const llvm::APSInt *SimpleSValBuilder::getMinValue(ProgramStateRef state, + SVal V) { + V = simplifySVal(state, V); + + if (const llvm::APSInt *Res = getConcreteValue(V)) + return Res; + + if (SymbolRef Sym = V.getAsSymbol()) + return state->getConstraintManager().getSymMinVal(state, Sym); + + return nullptr; +} + +const llvm::APSInt *SimpleSValBuilder::getMaxValue(ProgramStateRef state, + SVal V) { + V = simplifySVal(state, V); + + if (const llvm::APSInt *Res = getConcreteValue(V)) + return Res; + + if (SymbolRef Sym = V.getAsSymbol()) + return state->getConstraintManager().getSymMaxVal(state, Sym); + + return nullptr; +} + SVal SimpleSValBuilder::simplifyUntilFixpoint(ProgramStateRef State, SVal Val) { SVal SimplifiedVal = simplifySValOnce(State, Val); while (SimplifiedVal != Val) { diff --git a/clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp b/clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp index 9099c18391e4d290320697c6a5c39970ecf00dd7..1058ddb8254cde9246e855a27bef64bd69643a09 100644 --- a/clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp +++ b/clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp @@ -316,7 +316,8 @@ void ModuleDepCollector::applyDiscoveredDependencies(CompilerInvocation &CI) { static std::string getModuleContextHash(const ModuleDeps &MD, const CowCompilerInvocation &CI, - bool EagerLoadModules) { + bool EagerLoadModules, + llvm::vfs::FileSystem &VFS) { llvm::HashBuilder, llvm::endianness::native> HashBuilder; SmallString<32> Scratch; @@ -325,6 +326,9 @@ static std::string getModuleContextHash(const ModuleDeps &MD, // will be readable. HashBuilder.add(getClangFullRepositoryVersion()); HashBuilder.add(serialization::VERSION_MAJOR, serialization::VERSION_MINOR); + llvm::ErrorOr CWD = VFS.getCurrentWorkingDirectory(); + if (CWD) + HashBuilder.add(*CWD); // Hash the BuildInvocation without any input files. SmallString<0> ArgVec; @@ -356,7 +360,8 @@ static std::string getModuleContextHash(const ModuleDeps &MD, void ModuleDepCollector::associateWithContextHash( const CowCompilerInvocation &CI, ModuleDeps &Deps) { - Deps.ID.ContextHash = getModuleContextHash(Deps, CI, EagerLoadModules); + Deps.ID.ContextHash = getModuleContextHash( + Deps, CI, EagerLoadModules, ScanInstance.getVirtualFileSystem()); bool Inserted = ModuleDepsByID.insert({Deps.ID, &Deps}).second; (void)Inserted; assert(Inserted && "duplicate module mapping"); diff --git a/clang/test/Analysis/Inputs/system-header-simulator.h b/clang/test/Analysis/Inputs/system-header-simulator.h index 7ef5f29fbf42cb175d38790ecec67d84a2e1c363..7089bd8bfc9d983db384f23a32e74d0d86f1e1c9 100644 --- a/clang/test/Analysis/Inputs/system-header-simulator.h +++ b/clang/test/Analysis/Inputs/system-header-simulator.h @@ -49,6 +49,7 @@ int fclose(FILE *fp); size_t fread(void *restrict, size_t, size_t, FILE *restrict); size_t fwrite(const void *restrict, size_t, size_t, FILE *restrict); int fgetc(FILE *stream); +char *fgets(char *restrict str, int count, FILE *restrict stream); int fputc(int ch, FILE *stream); int fputs(const char *restrict s, FILE *restrict stream); int fseek(FILE *__stream, long int __off, int __whence); diff --git a/clang/test/Analysis/bitwise-shift-common.c b/clang/test/Analysis/bitwise-shift-common.c index fe49f0f992072dd6e17526beba2fef58e593a1c4..39108bc838bf277d7745348d33fd8b531450644a 100644 --- a/clang/test/Analysis/bitwise-shift-common.c +++ b/clang/test/Analysis/bitwise-shift-common.c @@ -1,4 +1,5 @@ // RUN: %clang_analyze_cc1 -analyzer-checker=core.BitwiseShift \ +// RUN: -analyzer-checker=debug.ExprInspection \ // RUN: -analyzer-output=text -verify \ // RUN: -triple x86_64-pc-linux-gnu -x c %s \ // RUN: -Wno-shift-count-negative -Wno-shift-negative-value \ @@ -6,6 +7,7 @@ // RUN: -Wno-shift-sign-overflow // // RUN: %clang_analyze_cc1 -analyzer-checker=core.BitwiseShift \ +// RUN: -analyzer-checker=debug.ExprInspection \ // RUN: -analyzer-config core.BitwiseShift:Pedantic=true \ // RUN: -analyzer-output=text -verify \ // RUN: -triple x86_64-pc-linux-gnu -x c++ -std=c++20 %s \ @@ -85,17 +87,23 @@ int too_large_right_operand_symbolic(int left, int right) { return 0; return left >> right; // expected-warning@-1 {{Right shift overflows the capacity of 'int'}} - // expected-note@-2 {{The result of right shift is undefined because the right operand is not smaller than 32, the capacity of 'int'}} - // NOTE: the messages of the checker are a bit vague in this case, but the - // tracking of the variables reveals our knowledge about them. + // expected-note@-2 {{The result of right shift is undefined because the right operand is >= 32, not smaller than 32, the capacity of 'int'}} } +void clang_analyzer_value(int); int too_large_right_operand_compound(unsigned short arg) { // Note: this would be valid code with an 'unsigned int' because // unsigned addition is allowed to overflow. + clang_analyzer_value(32+arg); + // expected-warning@-1 {{32s:{ [-2147483648, 2147483647] }} + // expected-note@-2 {{32s:{ [-2147483648, 2147483647] }} return 1 << (32 + arg); // expected-warning@-1 {{Left shift overflows the capacity of 'int'}} // expected-note@-2 {{The result of left shift is undefined because the right operand is not smaller than 32, the capacity of 'int'}} + // FIXME: this message should be + // {{The result of left shift is undefined because the right operand is >= 32, not smaller than 32, the capacity of 'int'}} + // but for some reason neither the new logic, nor debug.ExprInspection and + // clang_analyzer_value reports this range information. } // TEST STATE UPDATES diff --git a/clang/test/Analysis/out-of-bounds-diagnostics.c b/clang/test/Analysis/out-of-bounds-diagnostics.c index da1573665fa795120de9cabdcd045b91de5fd405..769a8954f7966915a346a2259e40ea6cef0cb7a3 100644 --- a/clang/test/Analysis/out-of-bounds-diagnostics.c +++ b/clang/test/Analysis/out-of-bounds-diagnostics.c @@ -9,6 +9,14 @@ void arrayUnderflow(void) { // expected-note@-2 {{Access of 'array' at negative byte offset -12}} } +int underflowWithDeref(void) { + int *p = array; + --p; + return *p; + // expected-warning@-1 {{Out of bound access to memory preceding 'array'}} + // expected-note@-2 {{Access of 'array' at negative byte offset -4}} +} + int scanf(const char *restrict fmt, ...); void taintedIndex(void) { @@ -17,6 +25,38 @@ void taintedIndex(void) { // expected-note@-1 {{Taint originated here}} // expected-note@-2 {{Taint propagated to the 2nd argument}} array[index] = 5; + // expected-warning@-1 {{Potential out of bound access to 'array' with tainted index}} + // expected-note@-2 {{Access of 'array' with a tainted index that may be too large}} +} + +int *taintedIndexAfterTheEndPtr(void) { + // NOTE: Technically speaking, this testcase does not trigger any UB because + // &array[10] is the after-the-end pointer which is well-defined; but this is + // a bug-prone situation and far from the idiomatic use of `&array[size]`, so + // it's better to report an error. This report can be easily silenced by + // writing array+index instead of &array[index]. + int index; + scanf("%d", &index); + // expected-note@-1 {{Taint originated here}} + // expected-note@-2 {{Taint propagated to the 2nd argument}} + if (index < 0 || index > 10) + return array; + // expected-note@-2 {{Assuming 'index' is >= 0}} + // expected-note@-3 {{Left side of '||' is false}} + // expected-note@-4 {{Assuming 'index' is <= 10}} + // expected-note@-5 {{Taking false branch}} + return &array[index]; + // expected-warning@-1 {{Potential out of bound access to 'array' with tainted index}} + // expected-note@-2 {{Access of 'array' with a tainted index that may be too large}} +} + +void taintedOffset(void) { + int index; + scanf("%d", &index); + // expected-note@-1 {{Taint originated here}} + // expected-note@-2 {{Taint propagated to the 2nd argument}} + int *p = array + index; + p[0] = 5; // expected-warning@-1 {{Potential out of bound access to 'array' with tainted offset}} // expected-note@-2 {{Access of 'array' with a tainted offset that may be too large}} } @@ -27,6 +67,47 @@ void arrayOverflow(void) { // expected-note@-2 {{Access of 'array' at index 12, while it holds only 10 'int' elements}} } +void flippedOverflow(void) { + 12[array] = 5; + // expected-warning@-1 {{Out of bound access to memory after the end of 'array'}} + // expected-note@-2 {{Access of 'array' at index 12, while it holds only 10 'int' elements}} +} + +int *afterTheEndPtr(void) { + // This is an unusual but standard-compliant way of writing (array + 10). + return &array[10]; // no-warning +} + +int useAfterTheEndPtr(void) { + // ... but dereferencing the after-the-end pointer is still invalid. + return *afterTheEndPtr(); + // expected-warning@-1 {{Out of bound access to memory after the end of 'array'}} + // expected-note@-2 {{Access of 'array' at index 10, while it holds only 10 'int' elements}} +} + +int *afterAfterTheEndPtr(void) { + // This is UB, it's invalid to form an after-after-the-end pointer. + return &array[11]; + // expected-warning@-1 {{Out of bound access to memory after the end of 'array'}} + // expected-note@-2 {{Access of 'array' at index 11, while it holds only 10 'int' elements}} +} + +int *potentialAfterTheEndPtr(int idx) { + if (idx < 10) { /* ...do something... */ } + // expected-note@-1 {{Assuming 'idx' is >= 10}} + // expected-note@-2 {{Taking false branch}} + return &array[idx]; + // expected-warning@-1 {{Out of bound access to memory after the end of 'array'}} + // expected-note@-2 {{Access of 'array' at an overflowing index, while it holds only 10 'int' elements}} + // NOTE: On the idx >= 10 branch the normal "optimistic" behavior would've + // been continuing with the assumption that idx == 10 and the return value is + // a legitimate after-the-end pointer. The checker deviates from this by + // reporting an error because this situation is very suspicious and far from + // the idiomatic `&array[size]` expressions. If the report is FP, the + // developer can easily silence it by writing array+idx instead of + // &array[idx]. +} + int scalar; int scalarOverflow(void) { return (&scalar)[1]; @@ -41,12 +122,6 @@ int oneElementArrayOverflow(void) { // expected-note@-2 {{Access of 'oneElementArray' at index 1, while it holds only a single 'int' element}} } -short convertedArray(void) { - return ((short*)array)[47]; - // expected-warning@-1 {{Out of bound access to memory after the end of 'array'}} - // expected-note@-2 {{Access of 'array' at index 47, while it holds only 20 'short' elements}} -} - struct vec { int len; double elems[64]; @@ -64,6 +139,28 @@ double arrayInStructPtr(struct vec *pv) { // expected-note@-2 {{Access of the field 'elems' at index 64, while it holds only 64 'double' elements}} } +struct item { + int a, b; +} itemArray[20] = {0}; + +int arrayOfStructs(void) { + return itemArray[35].a; + // expected-warning@-1 {{Out of bound access to memory after the end of 'itemArray'}} + // expected-note@-2 {{Access of 'itemArray' at index 35, while it holds only 20 'struct item' elements}} +} + +int arrayOfStructsArrow(void) { + return (itemArray + 35)->b; + // expected-warning@-1 {{Out of bound access to memory after the end of 'itemArray'}} + // expected-note@-2 {{Access of 'itemArray' at index 35, while it holds only 20 'struct item' elements}} +} + +short convertedArray(void) { + return ((short*)array)[47]; + // expected-warning@-1 {{Out of bound access to memory after the end of 'array'}} + // expected-note@-2 {{Access of 'array' at index 47, while it holds only 20 'short' elements}} +} + struct two_bytes { char lo, hi; }; @@ -85,7 +182,9 @@ int intFromString(void) { } int intFromStringDivisible(void) { - // However, this is reported with indices/elements, because the extent happens to be a multiple of 4. + // However, this is reported with indices/elements, because the extent + // (of the string that consists of 'a', 'b', 'c' and '\0') happens to be a + // multiple of 4 bytes (= sizeof(int)). return ((const int*)"abc")[20]; // expected-warning@-1 {{Out of bound access to memory after the end of the string literal}} // expected-note@-2 {{Access of the string literal at index 20, while it holds only a single 'int' element}} diff --git a/clang/test/Analysis/out-of-bounds-new.cpp b/clang/test/Analysis/out-of-bounds-new.cpp index af4ec47d8358aab4f7d3b240a711be9b7bb1eae8..f541bdf810d79ce6ee20360a137f92f94802e39b 100644 --- a/clang/test/Analysis/out-of-bounds-new.cpp +++ b/clang/test/Analysis/out-of-bounds-new.cpp @@ -154,3 +154,29 @@ void test_dynamic_size2(unsigned m,unsigned n){ unsigned *U = nullptr; U = new unsigned[m + n + 1]; } + +//Test creating invalid references, which break the invariant that a reference +//is always holding a value, and could lead to nasty runtime errors. +//(This is not related to operator new, but placed in this file because the +//other test files are not C++.) +int array[10] = {0}; + +void test_after_the_end_reference() { + int &ref = array[10]; // expected-warning{{Out of bound access to memory}} +} + +void test_after_after_the_end_reference() { + int &ref = array[11]; // expected-warning{{Out of bound access to memory}} +} + +int test_reference_that_might_be_after_the_end(int idx) { + // This TC produces no warning because separate analysis of (idx == 10) is + // only introduced _after_ the creation of the reference ref. + if (idx < 0 || idx > 10) + return -2; + int &ref = array[idx]; + if (idx == 10) + return -1; + return ref; +} + diff --git a/clang/test/Analysis/stream-error.c b/clang/test/Analysis/stream-error.c index c90d0cdc5742d372e9802a66b33fbfc8fcdbe579..c8332bcbfa8ca72f8678d47d0226e2d1d99cec44 100644 --- a/clang/test/Analysis/stream-error.c +++ b/clang/test/Analysis/stream-error.c @@ -123,6 +123,29 @@ void error_fgetc(void) { fgetc(F); // expected-warning {{Stream might be already closed}} } +void error_fgets(void) { + FILE *F = tmpfile(); + char Buf[256]; + if (!F) + return; + char *Ret = fgets(Buf, sizeof(Buf), F); + if (Ret == Buf) { + clang_analyzer_eval(feof(F) || ferror(F)); // expected-warning {{FALSE}} + } else { + clang_analyzer_eval(Ret == NULL); // expected-warning {{TRUE}} + clang_analyzer_eval(feof(F) || ferror(F)); // expected-warning {{TRUE}} + if (feof(F)) { + clang_analyzer_eval(ferror(F)); // expected-warning {{FALSE}} + fgets(Buf, sizeof(Buf), F); // expected-warning {{Read function called when stream is in EOF state}} + } else { + clang_analyzer_eval(ferror(F)); // expected-warning {{TRUE}} + fgets(Buf, sizeof(Buf), F); // expected-warning {{might be 'indeterminate'}} + } + } + fclose(F); + fgets(Buf, sizeof(Buf), F); // expected-warning {{Stream might be already closed}} +} + void error_fputc(void) { FILE *F = tmpfile(); if (!F) diff --git a/clang/test/Analysis/stream.c b/clang/test/Analysis/stream.c index b0f39af8eb2f3a306018476b9a2456bef4e80f4b..0b655dbdc571fe21442d66765b1685c70dba5855 100644 --- a/clang/test/Analysis/stream.c +++ b/clang/test/Analysis/stream.c @@ -20,6 +20,13 @@ void check_fgetc(void) { fclose(fp); } +void check_fgets(void) { + FILE *fp = tmpfile(); + char buf[256]; + fgets(buf, sizeof(buf), fp); // expected-warning {{Stream pointer might be NULL}} + fclose(fp); +} + void check_fputc(void) { FILE *fp = tmpfile(); fputc('A', fp); // expected-warning {{Stream pointer might be NULL}} diff --git a/clang/test/Analysis/taint-diagnostic-visitor.c b/clang/test/Analysis/taint-diagnostic-visitor.c index 67dc67e627b3b9c0ffb743bf96809a2710396e36..a3fa1639bffeee5c0e366090edd02749f9309599 100644 --- a/clang/test/Analysis/taint-diagnostic-visitor.c +++ b/clang/test/Analysis/taint-diagnostic-visitor.c @@ -29,8 +29,8 @@ int taintDiagnosticOutOfBound(void) { int Array[] = {1, 2, 3, 4, 5}; scanf("%d", &index); // expected-note {{Taint originated here}} // expected-note@-1 {{Taint propagated to the 2nd argument}} - return Array[index]; // expected-warning {{Potential out of bound access to 'Array' with tainted offset}} - // expected-note@-1 {{Access of 'Array' with a tainted offset that may be too large}} + return Array[index]; // expected-warning {{Potential out of bound access to 'Array' with tainted index}} + // expected-note@-1 {{Access of 'Array' with a tainted index that may be too large}} } int taintDiagnosticDivZero(int operand) { diff --git a/clang/test/CXX/basic/basic.lookup/basic.lookup.argdep/p5-ex2.cpp b/clang/test/CXX/basic/basic.lookup/basic.lookup.argdep/p5-ex2.cpp index c1a824bd51493202cc2ad83696403aa74b437684..a27946bd90a46c88227e9325d6dd2f060067c3e3 100644 --- a/clang/test/CXX/basic/basic.lookup/basic.lookup.argdep/p5-ex2.cpp +++ b/clang/test/CXX/basic/basic.lookup/basic.lookup.argdep/p5-ex2.cpp @@ -50,9 +50,7 @@ void test() { auto x = make(); // error: R and R::f are not visible here - R::f(x); // expected-error {{declaration of 'R' must be imported from module 'N' before it is required}} - // expected-note@N.cpp:4 {{declaration here is not visible}} - // expected-error@-2 {{no type named 'f' in namespace 'R'}} + R::f(x); // expected-error {{no type named 'f' in namespace 'R'}} f(x); // Found by [basic.lookup.argdep] / p4.3 diff --git a/clang/test/CXX/drs/dr0xx.cpp b/clang/test/CXX/drs/dr0xx.cpp index 5a127ff4f4164e172ec2afc5e9d3ac436c2c76e4..e5f675fb448986177b0c5165259461a1aeba92ea 100644 --- a/clang/test/CXX/drs/dr0xx.cpp +++ b/clang/test/CXX/drs/dr0xx.cpp @@ -1,9 +1,9 @@ -// RUN: %clang_cc1 -std=c++98 %s -verify -fexceptions -fcxx-exceptions -pedantic-errors -Wno-bind-to-temporary-copy -// RUN: %clang_cc1 -std=c++11 %s -verify -fexceptions -fcxx-exceptions -pedantic-errors -triple %itanium_abi_triple -// RUN: %clang_cc1 -std=c++14 %s -verify -fexceptions -fcxx-exceptions -pedantic-errors -triple %itanium_abi_triple -// RUN: %clang_cc1 -std=c++17 %s -verify -fexceptions -fcxx-exceptions -pedantic-errors -triple %itanium_abi_triple -// RUN: %clang_cc1 -std=c++20 %s -verify -fexceptions -fcxx-exceptions -pedantic-errors -triple %itanium_abi_triple -// RUN: %clang_cc1 -std=c++23 %s -verify -fexceptions -fcxx-exceptions -pedantic-errors -triple %itanium_abi_triple +// RUN: %clang_cc1 -std=c++98 %s -verify=expected,cxx98,cxx98-14 -fexceptions -fcxx-exceptions -pedantic-errors -Wno-bind-to-temporary-copy +// RUN: %clang_cc1 -std=c++11 %s -verify=expected,since-cxx11,cxx98-14,cxx11-14 -fexceptions -fcxx-exceptions -pedantic-errors -triple %itanium_abi_triple +// RUN: %clang_cc1 -std=c++14 %s -verify=expected,since-cxx11,cxx98-14,cxx11-14 -fexceptions -fcxx-exceptions -pedantic-errors -triple %itanium_abi_triple +// RUN: %clang_cc1 -std=c++17 %s -verify=expected,since-cxx11,since-cxx17 -fexceptions -fcxx-exceptions -pedantic-errors -triple %itanium_abi_triple +// RUN: %clang_cc1 -std=c++20 %s -verify=expected,since-cxx11,since-cxx17 -fexceptions -fcxx-exceptions -pedantic-errors -triple %itanium_abi_triple +// RUN: %clang_cc1 -std=c++23 %s -verify=expected,since-cxx11,since-cxx17 -fexceptions -fcxx-exceptions -pedantic-errors -triple %itanium_abi_triple namespace dr1 { // dr1: no namespace X { extern "C" void dr1_f(int a = 1); } @@ -26,9 +26,11 @@ namespace dr1 { // dr1: no namespace X { void z(int); } - void X::z(int = 1) {} // expected-note {{previous}} + void X::z(int = 1) {} // #dr1-z namespace X { - void z(int = 1); // expected-error {{redefinition of default argument}} + void z(int = 1); + // expected-error@-1 {{redefinition of default argument}} + // expected-note@#dr1-z {{previous definition is here}} } void i(int = 1); @@ -50,21 +52,25 @@ namespace dr1 { // dr1: no namespace dr3 { // dr3: yes template struct A {}; - template void f(T) { A a; } // expected-note {{implicit instantiation}} + template void f(T) { A a; } // #dr3-f-T template void f(int); - template<> struct A {}; // expected-error {{explicit specialization of 'dr3::A' after instantiation}} + template<> struct A {}; + // expected-error@-1 {{explicit specialization of 'dr3::A' after instantiation}} + // expected-note@#dr3-f-T {{implicit instantiation first required here}} } -namespace dr4 { // dr4: yes +namespace dr4 { // dr4: 2.8 extern "C" { static void dr4_f(int) {} static void dr4_f(float) {} - void dr4_g(int) {} // expected-note {{previous}} - void dr4_g(float) {} // expected-error {{conflicting types}} + void dr4_g(int) {} // #dr4-g-int + void dr4_g(float) {} + // expected-error@-1 {{conflicting types for 'dr4_g'}} + // expected-note@#dr4-g-int {{previous definition is here}} } } -namespace dr5 { // dr5: yes +namespace dr5 { // dr5: 3.1 struct A {} a; struct B { B(const A&); @@ -78,16 +84,22 @@ namespace dr5 { // dr5: yes const C c = e; } -namespace dr7 { // dr7: yes +namespace dr7 { // dr7: 3.4 class A { public: ~A(); }; - class B : virtual private A {}; // expected-note 2 {{declared private here}} - class C : public B {} c; // expected-error 2 {{inherited virtual base class 'A' has private destructor}} \ - // expected-note {{implicit default constructor for 'dr7::C' first required here}} \ - // expected-note {{implicit destructor for 'dr7::C' first required here}} + class B : virtual private A {}; // #dr7-B + class C : public B {} c; // #dr7-C + // expected-error@#dr7-C {{inherited virtual base class 'A' has private destructor}} + // expected-note@#dr7-C {{in implicit default constructor for 'dr7::C' first required here}} + // expected-note@#dr7-B {{declared private here}} + // expected-error@#dr7-C {{inherited virtual base class 'A' has private destructor}} + // expected-note@#dr7-C {{in implicit destructor for 'dr7::C' first required here}} + // expected-note@#dr7-B {{declared private here}} class VeryDerivedC : public B, virtual public A {} vdc; - class X { ~X(); }; // expected-note {{here}} - class Y : X { ~Y() {} }; // expected-error {{private destructor}} + class X { ~X(); }; // #dr7-X + class Y : X { ~Y() {} }; + // expected-error@-1 {{base class 'X' has private destructor}} + // expected-note@#dr7-X {{implicitly declared private here}} namespace PR16370 { // This regressed the first time DR7 was fixed. struct S1 { virtual ~S1(); }; @@ -114,16 +126,19 @@ namespace dr8 { // dr8: dup 45 A::T *A::g() { return 0; } } -namespace dr9 { // dr9: yes +namespace dr9 { // dr9: 2.8 struct B { protected: - int m; // expected-note {{here}} + int m; // #dr9-m friend int R1(); }; - struct N : protected B { // expected-note {{protected}} + struct N : protected B { // #dr9-N friend int R2(); } n; - int R1() { return n.m; } // expected-error {{protected member}} + int R1() { return n.m; } + // expected-error@-1 {{'m' is a protected member of 'dr9::B'}} + // expected-note@#dr9-N {{constrained by protected inheritance here}} + // expected-note@#dr9-m {{member is declared here}} int R2() { return n.m; } } @@ -142,7 +157,8 @@ namespace dr11 { // dr11: yes }; template struct B : T { using T::V; - V v; // expected-error {{unknown type name}} + V v; + // expected-error@-1 {{unknown type name 'V'}} }; struct X { typedef int U; }; A ax; @@ -175,7 +191,7 @@ namespace dr13 { // dr13: no int a4 = h(g); } -namespace dr14 { // dr14: yes +namespace dr14 { // dr14: 3.4 namespace X { extern "C" int dr14_f(); } namespace Y { extern "C" int dr14_f(); } using namespace X; @@ -190,27 +206,40 @@ namespace dr14 { // dr14: yes extern "C" int dr14_f() { return c.k; } } - namespace X { typedef int T; typedef int U; } // expected-note {{candidate}} - namespace Y { typedef int T; typedef long U; } // expected-note {{candidate}} + namespace X { typedef int T; typedef int U; } // #dr14-X-U + namespace Y { typedef int T; typedef long U; } // #dr14-Y-U T t; // ok, same type both times - U u; // expected-error {{ambiguous}} + U u; + // expected-error@-1 {{reference to 'U' is ambiguous}} + // expected-note@#dr14-X-U {{candidate found by name lookup is 'dr14::X::U'}} + // expected-note@#dr14-Y-U {{candidate found by name lookup is 'dr14::Y::U'}} } namespace dr15 { // dr15: yes - template void f(int); // expected-note {{previous}} - template void f(int = 0); // expected-error {{default arguments cannot be added}} + template void f(int); // #dr15-f-decl-first + template void f(int = 0); + // expected-error@-1 {{default arguments cannot be added to a function template that has already been declared}} + // expected-note@#dr15-f-decl-first {{previous template declaration is here}} } -namespace dr16 { // dr16: yes - class A { // expected-note {{here}} - void f(); // expected-note {{here}} +namespace dr16 { // dr16: 2.8 + class A { // #dr16-A + void f(); // #dr16-A-f-decl friend class C; }; - class B : A {}; // expected-note 3{{here}} + class B : A {}; // #dr16-B class C : B { void g() { - f(); // expected-error {{private member}} - A::f(); // expected-error {{private member}} expected-error {{private base}} + f(); + // expected-error@-1 {{'f' is a private member of 'dr16::A'}} + // expected-note@#dr16-B {{constrained by implicitly private inheritance here}} + // expected-note@#dr16-A-f-decl {{member is declared here}} + A::f(); // #dr16-A-f-call + // expected-error@#dr16-A-f-call {{'A' is a private member of 'dr16::A'}} + // expected-note@#dr16-B {{constrained by implicitly private inheritance here}} + // expected-note@#dr16-A {{member is declared here}} + // expected-error@#dr16-A-f-call {{cannot cast 'dr16::C' to its private base class 'dr16::A'}} + // expected-note@#dr16-B {{implicitly declared private here}} } }; } @@ -230,104 +259,115 @@ namespace dr17 { // dr17: yes // dr18: sup 577 -namespace dr19 { // dr19: yes +namespace dr19 { // dr19: 3.1 struct A { - int n; // expected-note {{here}} + int n; // #dr19-n }; - struct B : protected A { // expected-note {{here}} + struct B : protected A { // #dr19-B }; struct C : B {} c; struct D : B { - int get1() { return c.n; } // expected-error {{protected member}} + int get1() { return c.n; } + // expected-error@-1 {{'n' is a protected member of 'dr19::A'}} + // expected-note@#dr19-B {{constrained by protected inheritance here}} + // expected-note@#dr19-n {{member is declared here}} int get2() { return ((A&)c).n; } // ok, A is an accessible base of B from here }; } -namespace dr20 { // dr20: yes +namespace dr20 { // dr20: 2.8 class X { public: X(); private: - X(const X&); // expected-note {{here}} + X(const X&); // #dr20-X-ctor }; X &f(); - X x = f(); // expected-error {{private}} + X x = f(); + // expected-error@-1 {{calling a private constructor of class 'dr20::X'}} + // expected-note@#dr20-X-ctor {{declared private here}} } -namespace dr21 { // dr21: yes +namespace dr21 { // dr21: 3.4 template struct A; struct X { - template friend struct A; // expected-error {{default template argument not permitted on a friend template}} - template friend struct B; // expected-error {{default template argument not permitted on a friend template}} + template friend struct A; + // expected-error@-1 {{default template argument not permitted on a friend template}} + template friend struct B; + // expected-error@-1 {{default template argument not permitted on a friend template}} }; } namespace dr22 { // dr22: sup 481 - template struct X; // expected-error {{unknown type name 'dr22_T'}} + template struct X; + // expected-error@-1 {{unknown type name 'dr22_T'}} typedef int T; template struct Y; } namespace dr23 { // dr23: yes - template void f(T, T); // expected-note {{candidate}} - template void f(T, int); // expected-note {{candidate}} - void g() { f(0, 0); } // expected-error {{ambiguous}} + template void f(T, T); // #dr23-f-T-T + template void f(T, int); // #dr23-f-T-int + void g() { f(0, 0); } + // expected-error@-1 {{call to 'f' is ambiguous}} + // expected-note@#dr23-f-T-T {{candidate function [with T = int]}} + // expected-note@#dr23-f-T-int {{candidate function [with T = int]}} } // dr24: na namespace dr25 { // dr25: yes struct A { - void f() throw(int); // expected-error 0-1{{ISO C++17 does not allow}} expected-note 0-1{{use 'noexcept}} + void f() throw(int); + // since-cxx17-error@-1 {{ISO C++17 does not allow dynamic exception specifications}} + // since-cxx17-note@-2 {{use 'noexcept(false)' instead}} }; - void (A::*f)() throw (int); // expected-error 0-1{{ISO C++17 does not allow}} expected-note 0-1{{use 'noexcept}} + void (A::*f)() throw (int); + // since-cxx17-error@-1 {{ISO C++17 does not allow dynamic exception specifications}} + // since-cxx17-note@-2 {{use 'noexcept(false)' instead}} void (A::*g)() throw () = f; -#if __cplusplus <= 201402L - // expected-error@-2 {{is not superset of source}} -#else - // expected-error@-4 {{different exception specifications}} -#endif + // cxx98-14-error@-1 {{target exception specification is not superset of source}} + // since-cxx17-error@-2 {{different exception specifications}} void (A::*g2)() throw () = 0; - void (A::*h)() throw (int, char) = f; // expected-error 0-1{{ISO C++17 does not allow}} expected-note 0-1{{use 'noexcept}} + void (A::*h)() throw (int, char) = f; + // since-cxx17-error@-1 {{ISO C++17 does not allow dynamic exception specifications}} + // since-cxx17-note@-2 {{use 'noexcept(false)' instead}} void (A::*i)() throw () = &A::f; -#if __cplusplus <= 201402L - // expected-error@-2 {{is not superset of source}} -#else - // expected-error@-4 {{different exception specifications}} -#endif + // cxx98-14-error@-1 {{target exception specification is not superset of source}} + // since-cxx17-error@-2 {{different exception specifications}} void (A::*i2)() throw () = 0; - void (A::*j)() throw (int, char) = &A::f; // expected-error 0-1{{ISO C++17 does not allow}} expected-note 0-1{{use 'noexcept}} + void (A::*j)() throw (int, char) = &A::f; + // since-cxx17-error@-1 {{ISO C++17 does not allow dynamic exception specifications}} + // since-cxx17-note@-2 {{use 'noexcept(false)' instead}} void x() { g2 = f; -#if __cplusplus <= 201402L - // expected-error@-2 {{is not superset of source}} -#else - // expected-error@-4 {{different exception specifications}} -#endif + // cxx98-14-error@-1 {{target exception specification is not superset of source}} + // since-cxx17-error@-2 {{different exception specifications}} h = f; i2 = &A::f; -#if __cplusplus <= 201402L - // expected-error@-2 {{is not superset of source}} -#else - // expected-error@-4 {{different exception specifications}} -#endif + // cxx98-14-error@-1 {{target exception specification is not superset of source}} + // since-cxx17-error@-2 {{different exception specifications}} j = &A::f; } } namespace dr26 { // dr26: yes - struct A { A(A, const A & = A()); }; // expected-error {{must pass its first argument by reference}} + struct A { A(A, const A & = A()); }; + // expected-error@-1 {{copy constructor must pass its first argument by reference}} struct B { B(); // FIXME: In C++98, we diagnose this twice. B(const B &, B = B()); -#if __cplusplus <= 201402L - // expected-error@-2 1+{{recursive evaluation of default argument}} expected-note@-2 1+{{used here}} -#endif + // cxx98-14-error@-1 {{recursive evaluation of default argument}} + // cxx98-14-note@-2 {{default argument used here}} + // cxx98-error@-3 {{recursive evaluation of default argument}} + // cxx98-note@-4 {{default argument used here}} }; struct C { static C &f(); - C(const C &, C = f()); // expected-error {{recursive evaluation of default argument}} expected-note {{used here}} + C(const C &, C = f()); + // expected-error@-1 {{recursive evaluation of default argument}} + // expected-note@-2 {{default argument used here}} }; } @@ -339,24 +379,34 @@ namespace dr27 { // dr27: yes // dr28: na lib namespace dr29 { // dr29: 3.4 - void dr29_f0(); // expected-note {{here}} + void dr29_f0(); // #dr29-f0 void g0() { void dr29_f0(); } extern "C++" void g0_cxx() { void dr29_f0(); } - extern "C" void g0_c() { void dr29_f0(); } // expected-error {{different language linkage}} + extern "C" void g0_c() { void dr29_f0(); } + // expected-error@-1 {{declaration of 'dr29_f0' has a different language linkage}} + // expected-note@#dr29-f0 {{previous declaration is here}} - extern "C" void dr29_f1(); // expected-note {{here}} + extern "C" void dr29_f1(); // #dr29-f1 void g1() { void dr29_f1(); } extern "C" void g1_c() { void dr29_f1(); } - extern "C++" void g1_cxx() { void dr29_f1(); } // expected-error {{different language linkage}} + extern "C++" void g1_cxx() { void dr29_f1(); } + // expected-error@-1 {{declaration of 'dr29_f1' has a different language linkage}} + // expected-note@#dr29-f1 {{previous declaration is here}} - void g2() { void dr29_f2(); } // expected-note {{here}} - extern "C" void dr29_f2(); // expected-error {{different language linkage}} + void g2() { void dr29_f2(); } // #dr29-f2 + extern "C" void dr29_f2(); + // expected-error@-1 {{declaration of 'dr29_f2' has a different language linkage}} + // expected-note@#dr29-f2 {{previous declaration is here}} - extern "C" void g3() { void dr29_f3(); } // expected-note {{here}} - extern "C++" void dr29_f3(); // expected-error {{different language linkage}} + extern "C" void g3() { void dr29_f3(); } // #dr29-f3 + extern "C++" void dr29_f3(); + // expected-error@-1 {{declaration of 'dr29_f3' has a different language linkage}} + // expected-note@#dr29-f3 {{previous declaration is here}} - extern "C++" void g4() { void dr29_f4(); } // expected-note {{here}} - extern "C" void dr29_f4(); // expected-error {{different language linkage}} + extern "C++" void g4() { void dr29_f4(); } // #dr29-f4 + extern "C" void dr29_f4(); + // expected-error@-1 {{declaration of 'dr29_f4' has a different language linkage}} + // expected-note@#dr29-f4 {{previous declaration is here}} extern "C" void g5(); extern "C++" void dr29_f5(); @@ -371,15 +421,19 @@ namespace dr29 { // dr29: 3.4 } extern "C" void g7(); - extern "C++" void dr29_f7(); // expected-note {{here}} + extern "C++" void dr29_f7(); // #dr29-f7 extern "C" void g7() { - void dr29_f7(); // expected-error {{different language linkage}} + void dr29_f7(); + // expected-error@-1 {{declaration of 'dr29_f7' has a different language linkage}} + // expected-note@#dr29-f7 {{previous declaration is here}} } extern "C++" void g8(); - extern "C" void dr29_f8(); // expected-note {{here}} + extern "C" void dr29_f8(); // #dr29-f8 extern "C++" void g8() { - void dr29_f8(); // expected-error {{different language linkage}} + void dr29_f8(); + // expected-error@-1 {{declaration of 'dr29_f8' has a different language linkage}} + // expected-note@#dr29-f8 {{previous declaration is here}} } } @@ -387,38 +441,42 @@ namespace dr30 { // dr30: sup 468 c++11 struct A { template static int f(); } a, *p = &a; + // FIXME: It's not clear whether DR468 applies to C++98 too. int x = A::template f<0>(); + // cxx98-error@-1 {{'template' keyword outside of a template}} int y = a.template f<0>(); + // cxx98-error@-1 {{'template' keyword outside of a template}} int z = p->template f<0>(); -#if __cplusplus < 201103L - // FIXME: It's not clear whether DR468 applies to C++98 too. - // expected-error@-5 {{'template' keyword outside of a template}} - // expected-error@-5 {{'template' keyword outside of a template}} - // expected-error@-5 {{'template' keyword outside of a template}} -#endif + // cxx98-error@-1 {{'template' keyword outside of a template}} } -namespace dr31 { // dr31: yes +namespace dr31 { // dr31: 2.8 class X { private: - void operator delete(void*); // expected-note {{here}} + void operator delete(void*); // #dr31-delete }; // We would call X::operator delete if X() threw (even though it can't, // and even though we allocated the X using ::operator delete). - X *p = new X; // expected-error {{private}} + X *p = new X; + // expected-error@-1 {{'operator delete' is a private member of 'dr31::X'}} + // expected-note@#dr31-delete {{declared private here}} } // dr32: na -namespace dr33 { // dr33: yes - namespace X { struct S; void f(void (*)(S)); } // expected-note {{candidate}} - namespace Y { struct T; void f(void (*)(T)); } // expected-note {{candidate}} +namespace dr33 { // dr33: 9 + namespace X { struct S; void f(void (*)(S)); } // #dr33-f-S + namespace Y { struct T; void f(void (*)(T)); } // #dr33-f-T void g(X::S); template Z g(Y::T); - void h() { f(&g); } // expected-error {{ambiguous}} + void h() { f(&g); } + // expected-error@-1 {{call to 'f' is ambiguous}} + // expected-note@#dr33-f-S {{candidate function}} + // expected-note@#dr33-f-T {{candidate function}} template void t(X::S); - template void u(X::S); // expected-error 0-1{{default template argument}} + template void u(X::S); + // expected-error@-1 0-1 {{default template arguments for a function template are a C++11 extension}} void templ() { f(t); f(u); } // Even though v cannot select the first overload, ADL considers it @@ -436,7 +494,8 @@ namespace dr33 { // dr33: yes }; template void f(Y); - int use = X() + f; // expected-error {{invalid operands}} + int use = X() + f; + // expected-error@-1 {{invalid operands to binary expression ('X' and 'void (Y)')}} } namespace member { @@ -451,7 +510,7 @@ namespace dr33 { // dr33: yes // dr34: na // dr35: dup 178 -namespace dr36 { // dr36: yes +namespace dr36 { // dr36: 2.8 namespace example1 { namespace A { int i; @@ -481,17 +540,25 @@ namespace example2 { struct D : virtual B, virtual C { - using B::i; // expected-note {{previous using declaration}} - using B::i; // expected-error {{redeclaration of using declaration}} - - using C::i; // expected-note {{previous using declaration}} - using C::i; // expected-error {{redeclaration of using declaration}} - - using B::j; // expected-note {{previous using declaration}} - using B::j; // expected-error {{redeclaration of using declaration}} - - using C::j; // expected-note {{previous using declaration}} - using C::j; // expected-error {{redeclaration of using declaration}} + using B::i; // #dr36-ex2-B-i-first + using B::i; + // expected-error@-1 {{redeclaration of using declaration}} + // expected-note@#dr36-ex2-B-i-first {{previous using declaration}} + + using C::i; // #dr36-ex2-C-i-first + using C::i; + // expected-error@-1 {{redeclaration of using declaration}} + // expected-note@#dr36-ex2-C-i-first {{previous using declaration}} + + using B::j; // #dr36-ex2-B-j-first + using B::j; + // expected-error@-1 {{redeclaration of using declaration}} + // expected-note@#dr36-ex2-B-j-first {{previous using declaration}} + + using C::j; // #dr36-ex2-C-j-first + using C::j; + // expected-error@-1 {{redeclaration of using declaration}} + // expected-note@#dr36-ex2-C-j-first {{previous using declaration}} }; } @@ -511,17 +578,25 @@ namespace example3 { template struct D : virtual B, virtual C { - using B::i; // expected-note {{previous using declaration}} - using B::i; // expected-error {{redeclaration of using declaration}} - - using C::i; // expected-note {{previous using declaration}} - using C::i; // expected-error {{redeclaration of using declaration}} - - using B::j; // expected-note {{previous using declaration}} - using B::j; // expected-error {{redeclaration of using declaration}} - - using C::j; // expected-note {{previous using declaration}} - using C::j; // expected-error {{redeclaration of using declaration}} + using B::i; // #dr36-ex3-B-i-first + using B::i; + // expected-error@-1 {{redeclaration of using declaration}} + // expected-note@#dr36-ex3-B-i-first {{previous using declaration}} + + using C::i; // #dr36-ex3-C-i-first + using C::i; + // expected-error@-1 {{redeclaration of using declaration}} + // expected-note@#dr36-ex3-C-i-first {{previous using declaration}} + + using B::j; // #dr36-ex3-B-j-first + using B::j; + // expected-error@-1 {{redeclaration of using declaration}} + // expected-note@#dr36-ex3-B-j-first {{previous using declaration}} + + using C::j; // #dr36-ex3-C-j-first + using C::j; + // expected-error@-1 {{redeclaration of using declaration}} + // expected-note@#dr36-ex3-C-j-first {{previous using declaration}} }; } namespace example4 { @@ -532,8 +607,10 @@ namespace example4 { template struct G : E { - using E::k; // expected-note {{previous using declaration}} - using E::k; // expected-error {{redeclaration of using declaration}} + using E::k; // #dr36-E-k-first + using E::k; + // expected-error@-1 {{redeclaration of using declaration}} + // expected-note@#dr36-E-k-first {{previous using declaration}} }; } } @@ -558,24 +635,35 @@ namespace dr39 { // dr39: no namespace example2 { struct A { - int &x(int); // expected-note {{found}} - static int &y(int); // expected-note {{found}} + int &x(int); // #dr39-A-x-decl + static int &y(int); // #dr39-A-y-decl }; struct V { int &z(int); }; struct B : A, virtual V { - using A::x; // expected-note {{found}} + using A::x; // #dr39-using-A-x float &x(float); - using A::y; // expected-note {{found}} + using A::y; // #dr39-using-A-y static float &y(float); using V::z; float &z(float); }; - struct C : A, B, virtual V {} c; // expected-warning {{direct base 'A' is inaccessible due to ambiguity:\n struct dr39::example2::C -> A\n struct dr39::example2::C -> B -> A}} - int &x = c.x(0); // expected-error {{found in multiple base classes}} + struct C : A, B, virtual V {} c; + /* expected-warning@-1 + {{direct base 'A' is inaccessible due to ambiguity: + struct dr39::example2::C -> A + struct dr39::example2::C -> B -> A}} */ + int &x = c.x(0); + // expected-error@-1 {{member 'x' found in multiple base classes of different types}} + // expected-note@#dr39-A-x-decl {{member found by ambiguous name lookup}} + // expected-note@#dr39-using-A-x {{member found by ambiguous name lookup}} + // FIXME: This is valid, because we find the same static data member either way. - int &y = c.y(0); // expected-error {{found in multiple base classes}} + int &y = c.y(0); + // expected-error@-1 {{member 'y' found in multiple base classes of different types}} + // expected-note@#dr39-A-y-decl {{member found by ambiguous name lookup}} + // expected-note@#dr39-using-A-y {{member found by ambiguous name lookup}} int &z = c.z(0); } @@ -588,21 +676,38 @@ namespace dr39 { // dr39: no } namespace example4 { - struct A { int n; }; // expected-note {{found}} + struct A { int n; }; // #dr39-ex4-A-n struct B : A {}; struct C : A {}; - struct D : B, C { int f() { return n; } }; // expected-error {{found in multiple base-class}} + struct D : B, C { int f() { return n; } }; + /* expected-error@-1 + {{non-static member 'n' found in multiple base-class subobjects of type 'A': + struct dr39::example4::D -> B -> A + struct dr39::example4::D -> C -> A}} */ + // expected-note@#dr39-ex4-A-n {{member found by ambiguous name lookup}} } namespace PR5916 { // FIXME: This is valid. - struct A { int n; }; // expected-note +{{found}} + struct A { int n; }; // #dr39-A-n struct B : A {}; struct C : A {}; struct D : B, C {}; - int k = sizeof(D::n); // expected-error {{found in multiple base}} expected-error {{unknown type name}} + int k = sizeof(D::n); // #dr39-sizeof + /* expected-error@#dr39-sizeof + {{non-static member 'n' found in multiple base-class subobjects of type 'A': + struct dr39::PR5916::D -> B -> A + struct dr39::PR5916::D -> C -> A}} */ + // expected-note@#dr39-A-n {{member found by ambiguous name lookup}} + + // expected-error@#dr39-sizeof {{unknown type name}} #if __cplusplus >= 201103L - decltype(D::n) n; // expected-error {{found in multiple base}} + decltype(D::n) n; + /* expected-error@-1 + {{non-static member 'n' found in multiple base-class subobjects of type 'A': + struct dr39::PR5916::D -> B -> A + struct dr39::PR5916::D -> C -> A}} */ + // expected-note@#dr39-A-n {{member found by ambiguous name lookup}} #endif } } @@ -637,15 +742,19 @@ namespace dr45 { // dr45: yes namespace dr46 { // dr46: yes template struct A { template struct B {}; }; - template template struct A::B; // expected-error {{expected unqualified-id}} + template template struct A::B; + // expected-error@-1 {{expected unqualified-id}} } namespace dr47 { // dr47: sup 329 template struct A { - friend void f() { T t; } // expected-error {{redefinition}} expected-note {{previous}} + friend void f() { T t; } // #dr47-f + // expected-error@-1 {{redefinition of 'f'}} + // expected-note@#dr47-b {{in instantiation of template class 'dr47::A' requested here}} + // expected-note@#dr47-f {{previous definition is here}} }; A a; - A b; // expected-note {{instantiation of}} + A b; // #dr47-b void f(); void g() { f(); } @@ -667,43 +776,45 @@ namespace dr48 { // dr48: yes const int &c = S::o; } -namespace dr49 { // dr49: yes - template struct A {}; // expected-note 0-2{{here}} +namespace dr49 { // dr49: 2.8 + template struct A {}; // #dr49-A int k; #if __has_feature(cxx_constexpr) constexpr #endif - int *const p = &k; // expected-note 0-2{{here}} + int *const p = &k; // #dr49-p A<&k> a; - A

b; -#if __cplusplus <= 201402L - // expected-error@-2 {{must have its address taken}} -#endif -#if __cplusplus < 201103L - // expected-error@-5 {{internal linkage}} -#endif - int *q = &k; - A c; -#if __cplusplus < 201103L - // expected-error@-2 {{must have its address taken}} -#else - // expected-error@-4 {{constant expression}} - // expected-note@-5 {{read of non-constexpr}} - // expected-note@-7 {{declared here}} -#endif + A

b; // #dr49-b + // cxx98-error@#dr49-b {{non-type template argument referring to object 'p' with internal linkage is a C++11 extension}} + // cxx98-note@#dr49-p {{non-type template argument refers to object here}} + // cxx98-14-error@#dr49-b {{non-type template argument for template parameter of pointer type 'int *' must have its address taken}} + // cxx98-14-note@#dr49-A {{template parameter is declared here}} + int *q = &k; // #dr49-q + A c; // #dr49-c + // cxx98-error@#dr49-c {{non-type template argument for template parameter of pointer type 'int *' must have its address taken}} + // cxx98-note@#dr49-A {{template parameter is declared here}} + // cxx11-14-error@#dr49-c {{non-type template argument of type 'int *' is not a constant expression}} + // cxx11-14-note@#dr49-c {{read of non-constexpr variable 'q' is not allowed in a constant expression}} + // cxx11-14-note@#dr49-q {{declared here}} + // cxx11-14-note@#dr49-A {{template parameter is declared here}} + // since-cxx17-error@#dr49-c {{non-type template argument is not a constant expression}} + // since-cxx17-note@#dr49-c {{read of non-constexpr variable 'q' is not allowed in a constant expression}} + // since-cxx17-note@#dr49-q {{declared here}} } namespace dr50 { // dr50: yes - struct X; // expected-note {{forward}} + struct X; // #dr50-X extern X *p; X *q = (X*)p; X *r = static_cast(p); X *s = const_cast(p); X *t = reinterpret_cast(p); - X *u = dynamic_cast(p); // expected-error {{incomplete}} + X *u = dynamic_cast(p); + // expected-error@-1 {{'dr50::X' is an incomplete type}} + // expected-note@#dr50-X {{forward declaration of 'dr50::X'}} } -namespace dr51 { // dr51: yes +namespace dr51 { // dr51: 2.8 struct A {}; struct B : A {}; struct S { @@ -713,12 +824,16 @@ namespace dr51 { // dr51: yes A &a = s; } -namespace dr52 { // dr52: yes - struct A { int n; }; // expected-note {{here}} - struct B : private A {} b; // expected-note 2{{private}} +namespace dr52 { // dr52: 2.8 + struct A { int n; }; // #dr52-A + struct B : private A {} b; // #dr52-B + int k = b.A::n; // #dr52-k // FIXME: This first diagnostic is very strangely worded, and seems to be bogus. - int k = b.A::n; // expected-error {{'A' is a private member of 'dr52::A'}} - // expected-error@-1 {{cannot cast 'struct B' to its private base}} + // expected-error@#dr52-k {{'A' is a private member of 'dr52::A'}} + // expected-note@#dr52-B {{constrained by private inheritance here}} + // expected-note@#dr52-A {{member is declared here}} + // expected-error@#dr52-k {{cannot cast 'struct B' to its private base class 'dr52::A'}} + // expected-note@#dr52-B {{declared private here}} } namespace dr53 { // dr53: yes @@ -726,24 +841,40 @@ namespace dr53 { // dr53: yes enum E { e } x = static_cast(n); } -namespace dr54 { // dr54: yes +namespace dr54 { // dr54: 2.8 struct A { int a; } a; struct V { int v; } v; - struct B : private A, virtual V { int b; } b; // expected-note 6{{private here}} - - A &sab = static_cast(b); // expected-error {{private base}} - A *spab = static_cast(&b); // expected-error {{private base}} - int A::*smab = static_cast(&B::b); // expected-error {{private base}} - B &sba = static_cast(a); // expected-error {{private base}} - B *spba = static_cast(&a); // expected-error {{private base}} - int B::*smba = static_cast(&A::a); // expected-error {{private base}} + struct B : private A, virtual V { int b; } b; // #dr54-B + + A &sab = static_cast(b); + // expected-error@-1 {{cannot cast 'struct B' to its private base class 'A'}} + // expected-note@#dr54-B {{declared private here}} + A *spab = static_cast(&b); + // expected-error@-1 {{cannot cast 'struct B' to its private base class 'A'}} + // expected-note@#dr54-B {{declared private here}} + int A::*smab = static_cast(&B::b); + // expected-error@-1 {{cannot cast 'dr54::B' to its private base class 'dr54::A'}} + // expected-note@#dr54-B {{declared private here}} + B &sba = static_cast(a); + // expected-error@-1 {{cannot cast private base class 'dr54::A' to 'dr54::B'}} + // expected-note@#dr54-B {{declared private here}} + B *spba = static_cast(&a); + // expected-error@-1 {{cannot cast private base class 'dr54::A' to 'dr54::B'}} + // expected-note@#dr54-B {{declared private here}} + int B::*smba = static_cast(&A::a); + // expected-error@-1 {{cannot cast private base class 'dr54::A' to 'dr54::B'}} + // expected-note@#dr54-B {{declared private here}} V &svb = static_cast(b); V *spvb = static_cast(&b); - int V::*smvb = static_cast(&B::b); // expected-error {{virtual base}} - B &sbv = static_cast(v); // expected-error {{virtual base}} - B *spbv = static_cast(&v); // expected-error {{virtual base}} - int B::*smbv = static_cast(&V::v); // expected-error {{virtual base}} + int V::*smvb = static_cast(&B::b); + // expected-error@-1 {{conversion from pointer to member of class 'dr54::B' to pointer to member of class 'dr54::V' via virtual base 'dr54::V' is not allowed}} + B &sbv = static_cast(v); + // expected-error@-1 {{cannot cast 'struct V' to 'B &' via virtual base 'dr54::V'}} + B *spbv = static_cast(&v); + // expected-error@-1 {{cannot cast 'dr54::V *' to 'B *' via virtual base 'dr54::V'}} + int B::*smbv = static_cast(&V::v); + // expected-error@-1 {{conversion from pointer to member of class 'dr54::V' to pointer to member of class 'dr54::B' via virtual base 'dr54::V' is not allowed}} A &cab = (A&)(b); A *cpab = (A*)(&b); @@ -754,10 +885,14 @@ namespace dr54 { // dr54: yes V &cvb = (V&)(b); V *cpvb = (V*)(&b); - int V::*cmvb = (int V::*)(&B::b); // expected-error {{virtual base}} - B &cbv = (B&)(v); // expected-error {{virtual base}} - B *cpbv = (B*)(&v); // expected-error {{virtual base}} - int B::*cmbv = (int B::*)(&V::v); // expected-error {{virtual base}} + int V::*cmvb = (int V::*)(&B::b); + // expected-error@-1 {{conversion from pointer to member of class 'dr54::B' to pointer to member of class 'dr54::V' via virtual base 'dr54::V' is not allowed}} + B &cbv = (B&)(v); + // expected-error@-1 {{cannot cast 'struct V' to 'B &' via virtual base 'dr54::V'}} + B *cpbv = (B*)(&v); + // expected-error@-1 {{cannot cast 'dr54::V *' to 'B *' via virtual base 'dr54::V'}} + int B::*cmbv = (int B::*)(&V::v); + // expected-error@-1 {{conversion from pointer to member of class 'dr54::V' to pointer to member of class 'dr54::B' via virtual base 'dr54::V' is not allowed}} } namespace dr55 { // dr55: yes @@ -767,17 +902,21 @@ namespace dr55 { // dr55: yes namespace dr56 { // dr56: yes struct A { - typedef int T; // expected-note {{previous}} - typedef int T; // expected-error {{redefinition}} + typedef int T; // #dr56-typedef-int-T-first + typedef int T; + // expected-error@-1 {{redefinition of 'T'}} + // expected-note@#dr56-typedef-int-T-first {{previous definition is here}} }; struct B { struct X; - typedef X X; // expected-note {{previous}} - typedef X X; // expected-error {{redefinition}} + typedef X X; // #dr56-typedef-X-X-first + typedef X X; + // expected-error@-1 {{redefinition of 'X'}} + // expected-note@#dr56-typedef-X-X-first {{previous definition is here}} }; } -namespace dr58 { // dr58: yes +namespace dr58 { // dr58: 3.1 // FIXME: Ideally, we should have a CodeGen test for this. #if __cplusplus >= 201103L enum E1 { E1_0 = 0, E1_1 = 1 }; @@ -792,32 +931,50 @@ namespace dr59 { // dr59: yes #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdeprecated-volatile" template struct convert_to { operator T() const; }; - struct A {}; // expected-note 5+{{candidate}} - struct B : A {}; // expected-note 0+{{candidate}} + struct A {}; // #dr59-A + struct B : A {}; // #dr59-B A a1 = convert_to(); A a2 = convert_to(); A a3 = convert_to(); A a4 = convert_to(); -#if __cplusplus <= 201402L - // expected-error@-2 {{no viable}} -#endif - A a5 = convert_to(); // expected-error {{no viable}} + // cxx98-14-error@-1 {{no viable constructor copying variable of type 'const volatile dr59::A'}} + // cxx98-14-note@#dr59-A {{candidate constructor (the implicit copy constructor) not viable: 1st argument ('const volatile dr59::A') would lose volatile qualifier}} + // cxx11-14-note@#dr59-A {{candidate constructor (the implicit move constructor) not viable: 1st argument ('const volatile dr59::A') would lose const and volatile qualifiers}} + // cxx98-14-note@#dr59-A {{candidate constructor (the implicit default constructor) not viable: requires 0 arguments, but 1 was provided}} + A a5 = convert_to(); + // expected-error@-1 {{no viable constructor copying variable of type 'const volatile dr59::A'}} + // expected-note@#dr59-A {{candidate constructor (the implicit copy constructor) not viable: 1st argument ('const volatile dr59::A') would lose volatile qualifier}} + // since-cxx11-note@#dr59-A {{candidate constructor (the implicit move constructor) not viable: 1st argument ('const volatile dr59::A') would lose const and volatile qualifiers}} + // expected-note@#dr59-A {{candidate constructor (the implicit default constructor) not viable: requires 0 arguments, but 1 was provided}} B b1 = convert_to(); B b2 = convert_to(); B b3 = convert_to(); B b4 = convert_to(); -#if __cplusplus <= 201402L - // expected-error@-2 {{no viable}} -#endif - B b5 = convert_to(); // expected-error {{no viable}} + // cxx98-14-error@-1 {{no viable constructor copying variable of type 'const volatile dr59::B'}} + // cxx98-14-note@#dr59-B {{candidate constructor (the implicit copy constructor) not viable: 1st argument ('const volatile dr59::B') would lose volatile qualifier}} + // cxx11-14-note@#dr59-B {{candidate constructor (the implicit move constructor) not viable: 1st argument ('const volatile dr59::B') would lose const and volatile qualifiers}} + // cxx98-14-note@#dr59-B {{candidate constructor (the implicit default constructor) not viable: requires 0 arguments, but 1 was provided}} + B b5 = convert_to(); + // expected-error@-1 {{no viable constructor copying variable of type 'const volatile dr59::B'}} + // expected-note@#dr59-B {{candidate constructor (the implicit copy constructor) not viable: 1st argument ('const volatile dr59::B') would lose volatile qualifier}} + // since-cxx11-note@#dr59-B {{candidate constructor (the implicit move constructor) not viable: 1st argument ('const volatile dr59::B') would lose const and volatile qualifiers}} + // expected-note@#dr59-B {{candidate constructor (the implicit default constructor) not viable: requires 0 arguments, but 1 was provided}} A c1 = convert_to(); A c2 = convert_to(); A c3 = convert_to(); - A c4 = convert_to(); // expected-error {{no viable}} - A c5 = convert_to(); // expected-error {{no viable}} + A c4 = convert_to(); + // expected-error@-1 {{no viable constructor copying variable of type 'const volatile dr59::B'}} + // expected-note@#dr59-A {{candidate constructor (the implicit copy constructor) not viable: no known conversion from 'const volatile dr59::B' to 'const A &' for 1st argument}} + // since-cxx11-note@#dr59-A {{candidate constructor (the implicit move constructor) not viable: no known conversion from 'const volatile dr59::B' to 'A &&' for 1st argument}} + // expected-note@#dr59-A {{candidate constructor (the implicit default constructor) not viable: requires 0 arguments, but 1 was provided}} + A c5 = convert_to(); + // expected-error@-1 {{no viable constructor copying variable of type 'const volatile dr59::B'}} + // expected-note@#dr59-A {{candidate constructor (the implicit copy constructor) not viable: no known conversion from 'const volatile dr59::B' to 'const A &' for 1st argument}} + // since-cxx11-note@#dr59-A {{candidate constructor (the implicit move constructor) not viable: no known conversion from 'const volatile dr59::B' to 'A &&' for 1st argument}} + // expected-note@#dr59-A {{candidate constructor (the implicit default constructor) not viable: requires 0 arguments, but 1 was provided}} int n1 = convert_to(); int n2 = convert_to(); @@ -834,7 +991,7 @@ namespace dr60 { // dr60: yes int &n = f(k); } -namespace dr61 { // dr61: yes +namespace dr61 { // dr61: 3.4 struct X { static void f(); } x; @@ -845,11 +1002,13 @@ namespace dr61 { // dr61: yes // This is (presumably) valid, because x.f does not refer to an overloaded // function name. void (*p)() = &x.f; - void (*q)() = &y.f; // expected-error {{cannot create a non-constant pointer to member function}} - void (*r)() = y.f; // expected-error {{cannot create a non-constant pointer to member function}} + void (*q)() = &y.f; + // expected-error@-1 {{cannot create a non-constant pointer to member function}} + void (*r)() = y.f; + // expected-error@-1 {{cannot create a non-constant pointer to member function}} } -namespace dr62 { // dr62: yes +namespace dr62 { // dr62: 2.9 struct A { struct { int n; } b; }; @@ -860,10 +1019,7 @@ namespace dr62 { // dr62: yes X x1; A a = get(); - typedef struct { } *NoNameForLinkagePtr; -#if __cplusplus < 201103L - // expected-note@-2 5{{here}} -#endif + typedef struct { } *NoNameForLinkagePtr; // #dr62-unnamed NoNameForLinkagePtr noNameForLinkagePtr; struct Danger { @@ -871,36 +1027,37 @@ namespace dr62 { // dr62: yes }; X x2; + // cxx98-error@-1 {{template argument uses unnamed type}} + // cxx98-note@#dr62-unnamed {{unnamed type used in template argument was declared here}} X x3; + // cxx98-error@-1 {{template argument uses unnamed type}} + // cxx98-note@#dr62-unnamed {{unnamed type used in template argument was declared here}} NoNameForLinkagePtr p1 = get(); + // cxx98-error@-1 {{template argument uses unnamed type}} + // cxx98-note@#dr62-unnamed {{unnamed type used in template argument was declared here}} NoNameForLinkagePtr p2 = get(); + // cxx98-error@-1 {{template argument uses unnamed type}} + // cxx98-note@#dr62-unnamed {{unnamed type used in template argument was declared here}} int n1 = take(noNameForLinkagePtr); -#if __cplusplus < 201103L - // expected-error@-6 {{uses unnamed type}} - // expected-error@-6 {{uses unnamed type}} - // expected-error@-6 {{uses unnamed type}} - // expected-error@-6 {{uses unnamed type}} - // expected-error@-6 {{uses unnamed type}} -#endif + // cxx98-error@-1 {{template argument uses unnamed type}} + // cxx98-note@#dr62-unnamed {{unnamed type used in template argument was declared here}} X x4; void f() { struct NoLinkage {}; X a; + // cxx98-error@-1 {{template argument uses local type }} X b; + // cxx98-error@-1 {{template argument uses local type }} get(); + // cxx98-error@-1 {{template argument uses local type }} get(); + // cxx98-error@-1 {{template argument uses local type }} X c; + // cxx98-error@-1 {{template argument uses local type }} X d; -#if __cplusplus < 201103L - // expected-error@-7 {{uses local type}} - // expected-error@-7 {{uses local type}} - // expected-error@-7 {{uses local type}} - // expected-error@-7 {{uses local type}} - // expected-error@-7 {{uses local type}} - // expected-error@-7 {{uses local type}} -#endif + // cxx98-error@-1 {{template argument uses local type }} } } @@ -922,7 +1079,7 @@ namespace dr64 { // dr64: yes namespace dr66 { // dr66: no namespace X { - int f(int n); // expected-note 2{{candidate}} + int f(int n); // #dr66-f-first } using X::f; namespace X { @@ -930,51 +1087,47 @@ namespace dr66 { // dr66: no int f(int, int); } // FIXME: The first two calls here should be accepted. - int a = f(); // expected-error {{no matching function}} + int a = f(); + // expected-error@-1 {{no matching function for call to 'f'}} + // expected-note@#dr66-f-first {{candidate function not viable: requires single argument 'n', but no arguments were provided}} int b = f(1); - int c = f(1, 2); // expected-error {{no matching function}} + int c = f(1, 2); + // expected-error@-1 {{no matching function for call to 'f'}} + // expected-note@#dr66-f-first {{candidate function not viable: requires single argument 'n', but 2 arguments were provided}} } // dr67: na -namespace dr68 { // dr68: yes +namespace dr68 { // dr68: 2.8 template struct X {}; struct ::dr68::X x1; struct ::dr68::template X x2; -#if __cplusplus < 201103L - // expected-error@-2 {{'template' keyword outside of a template}} -#endif + // cxx98-error@-1 {{'template' keyword outside of a template}} struct Y { friend struct X; friend struct ::dr68::X; friend struct ::dr68::template X; -#if __cplusplus < 201103L - // expected-error@-2 {{'template' keyword outside of a template}} -#endif + // cxx98-error@-1 {{'template' keyword outside of a template}} }; template struct Z { friend struct ::dr68::template X; friend typename ::dr68::X; -#if __cplusplus < 201103L - // expected-error@-2 {{C++11 extension}} -#endif + // cxx98-error@-1 {{unelaborated friend declaration is a C++11 extension; specify 'struct' to befriend 'typename ::dr68::X'}} }; } -namespace dr69 { // dr69: yes +namespace dr69 { // dr69: 9 template static void f() {} // #dr69-f // FIXME: Should we warn here? inline void g() { f(); } - extern template void f(); // expected-error {{explicit instantiation declaration of 'f' with internal linkage}} -#if __cplusplus < 201103L - // expected-error@-2 {{C++11 extension}} -#endif + extern template void f(); + // cxx98-error@-1 {{extern templates are a C++11 extension}} + // expected-error@-2 {{explicit instantiation declaration of 'f' with internal linkage}} template struct Q {}; Q<&f > q; -#if __cplusplus < 201103L - // expected-error@-2 {{internal linkage}} expected-note@#dr69-f {{here}} -#endif + // cxx98-error@-1 {{non-type template argument referring to function 'f' with internal linkage is a C++11 extension}} + // cxx98-note@#dr69-f {{non-type template argument refers to function here}} } namespace dr70 { // dr70: yes @@ -990,8 +1143,9 @@ namespace dr70 { // dr70: yes #if __cplusplus >= 201103L namespace dr73 { // dr73: sup 1652 int a, b; - static_assert(&a + 1 != &b, ""); // expected-error {{not an integral constant expression}} - // expected-note@-1 {{comparison against pointer '&a + 1' that points past the end of a complete object}} + static_assert(&a + 1 != &b, ""); + // expected-error@-1 {{static assertion expression is not an integral constant expression}} + // expected-note@-2 {{comparison against pointer '&a + 1' that points past the end of a complete object has unspecified value}} } #endif @@ -1002,13 +1156,17 @@ namespace dr74 { // dr74: yes namespace dr75 { // dr75: yes struct S { - static int n = 0; // expected-error {{non-const}} + static int n = 0; + // expected-error@-1 {{non-const static data member must be initialized out of line}} }; } namespace dr76 { // dr76: yes const volatile int n = 1; - int arr[n]; // expected-error +{{variable length array}} expected-note {{read of volatile}} + int arr[n]; // #dr76-vla + // expected-error@#dr76-vla {{variable length arrays in C++ are a Clang extension}} + // expected-note@#dr76-vla {{read of volatile-qualified type 'const volatile int' is not allowed in a constant expression}} + // expected-error@#dr76-vla {{variable length array declaration not allowed at file scope}} } namespace dr77 { // dr77: yes @@ -1021,25 +1179,29 @@ namespace dr77 { // dr77: yes namespace dr78 { // dr78: sup ???? // Under DR78, this is valid, because 'k' has static storage duration, so is // zero-initialized. - const int k; // expected-error {{default initialization of an object of const}} + const int k; + // expected-error@-1 {{default initialization of an object of const type 'const int'}} } // dr79: na -namespace dr80 { // dr80: yes +namespace dr80 { // dr80: 2.9 struct A { int A; }; struct B { - static int B; // expected-error {{same name as its class}} + static int B; + // expected-error@-1 {{member 'B' has the same name as its class}} }; struct C { - int C; // expected-error {{same name as its class}} + int C; + // expected-error@-1 {{member 'C' has the same name as its class}} C(); }; struct D { D(); - int D; // expected-error {{same name as its class}} + int D; + // expected-error@-1 {{member 'D' has the same name as its class}} }; } @@ -1057,44 +1219,54 @@ namespace dr84 { // dr84: yes struct A { operator B() const; }; struct C {}; struct B { - B(B&); // expected-note 0-1{{candidate}} - B(C); // expected-note 0-1{{no known conversion from 'B' to 'C'}} + B(B&); // #dr84-copy-ctor + B(C); // #dr84-ctor-from-C operator C() const; }; A a; // Cannot use B(C) / operator C() pair to construct the B from the B temporary // here. In C++17, we initialize the B object directly using 'A::operator B()'. B b = a; -#if __cplusplus <= 201402L - // expected-error@-2 {{no viable}} -#endif + // cxx98-14-error@-1 {{no viable constructor copying variable of type 'B'}} + // cxx98-14-note@#dr84-copy-ctor {{candidate constructor not viable: expects an lvalue for 1st argument}} + // cxx98-14-note@#dr84-ctor-from-C {{candidate constructor not viable: no known conversion from 'B' to 'C' for 1st argument}} } -namespace dr85 { // dr85: yes +namespace dr85 { // dr85: 3.4 struct A { struct B; - struct B {}; // expected-note{{previous declaration is here}} - struct B; // expected-error{{class member cannot be redeclared}} + struct B {}; // #dr85-B-def + struct B; + // expected-error@-1 {{class member cannot be redeclared}} + // expected-note@#dr85-B-def {{previous declaration is here}} union U; - union U {}; // expected-note{{previous declaration is here}} - union U; // expected-error{{class member cannot be redeclared}} + union U {}; // #dr85-U-def + union U; + // expected-error@-1 {{class member cannot be redeclared}} + // expected-note@#dr85-U-def {{previous declaration is here}} #if __cplusplus >= 201103L enum E1 : int; - enum E1 : int { e1 }; // expected-note{{previous declaration is here}} - enum E1 : int; // expected-error{{class member cannot be redeclared}} + enum E1 : int { e1 }; // #dr85-E1-def + enum E1 : int; + // expected-error@-1 {{class member cannot be redeclared}} + // expected-note@#dr85-E1-def {{previous declaration is here}} enum class E2; - enum class E2 { e2 }; // expected-note{{previous declaration is here}} - enum class E2; // expected-error{{class member cannot be redeclared}} + enum class E2 { e2 }; // #dr85-E2-def + enum class E2; + // expected-error@-1 {{class member cannot be redeclared}} + // expected-note@#dr85-E2-def {{previous declaration is here}} #endif }; template struct C { - struct B {}; // expected-note{{previous declaration is here}} - struct B; // expected-error{{class member cannot be redeclared}} + struct B {}; // #dr85-C-B-def + struct B; + // expected-error@-1 {{class member cannot be redeclared}} + // expected-note@#dr85-C-B-def {{previous declaration is here}} }; } @@ -1109,12 +1281,14 @@ namespace dr87 { // dr87: no X y; } -namespace dr88 { // dr88: yes +namespace dr88 { // dr88: 2.8 template struct S { - static const int a = 1; // expected-note {{previous}} + static const int a = 1; // #dr88-a static const int b; }; - template<> const int S::a = 4; // expected-error {{already has an initializer}} + template<> const int S::a = 4; + // expected-error@-1 {{static data member 'a' already has an initializer}} + // expected-note@#dr88-a {{previous initialization is here}} template<> const int S::b = 4; } @@ -1135,17 +1309,22 @@ namespace dr90 { // dr90: yes void test() { dr90_f(A()); dr90_f(B()); - dr90_f(B::C()); // expected-error {{undeclared identifier}} - dr90_f(B::D()); // expected-error {{undeclared identifier}} + dr90_f(B::C()); + // expected-error@-1 {{use of undeclared identifier 'dr90_f'}} + dr90_f(B::D()); + // expected-error@-1 {{use of undeclared identifier 'dr90_f'}} dr90_f(E()); - dr90_f(F()); // expected-error {{undeclared identifier}} + dr90_f(F()); + // expected-error@-1 {{use of undeclared identifier 'dr90_f'}} - dr90_g(A()); // expected-error {{undeclared identifier}} + dr90_g(A()); + // expected-error@-1 {{use of undeclared identifier 'dr90_g'}} dr90_g(B()); dr90_g(B::C()); dr90_g(B::D()); dr90_g(E()); - dr90_g(F()); // expected-error {{undeclared identifier}} + dr90_g(F()); + // expected-error@-1 {{use of undeclared identifier 'dr90_g'}} } } @@ -1155,25 +1334,32 @@ namespace dr91 { // dr91: yes } namespace dr92 { // dr92: 4 c++17 - void f() throw(int, float); // expected-error 0-1{{ISO C++17 does not allow}} expected-note 0-1{{use 'noexcept}} - void (*p)() throw(int) = &f; // expected-error 0-1{{ISO C++17 does not allow}} expected-note 0-1{{use 'noexcept}} -#if __cplusplus <= 201402L - // expected-error@-2 {{target exception specification is not superset of source}} -#else - // expected-warning@-4 {{target exception specification is not superset of source}} -#endif - void (*q)() throw(int); // expected-error 0-1{{ISO C++17 does not allow}} expected-note 0-1{{use 'noexcept}} + void f() throw(int, float); + // since-cxx17-error@-1 {{ISO C++17 does not allow dynamic exception specifications}} + // since-cxx17-note@-2 {{use 'noexcept(false)' instead}} + void (*p)() throw(int) = &f; // #dr92-p + // since-cxx17-error@#dr92-p {{ISO C++17 does not allow dynamic exception specifications}} + // since-cxx17-note@#dr92-p {{use 'noexcept(false)' instead}} + // cxx98-14-error@#dr92-p {{target exception specification is not superset of source}} + // since-cxx17-warning@#dr92-p {{target exception specification is not superset of source}} + void (*q)() throw(int); + // since-cxx17-error@-1 {{ISO C++17 does not allow dynamic exception specifications}} + // since-cxx17-note@-2 {{use 'noexcept(false)' instead}} void (**pp)() throw() = &q; -#if __cplusplus <= 201402L - // expected-error@-2 {{exception specifications are not allowed}} -#else - // expected-error@-4 {{cannot initialize}} -#endif + // cxx98-14-error@-1 {{exception specifications are not allowed beyond a single level of indirection}} + // since-cxx17-error@-2 {{cannot initialize a variable of type 'void (**)() throw()' with an rvalue of type 'void (**)() throw(int)'}} - void g(void() throw()); // expected-note 0-2 {{no known conversion}} expected-warning 0-1{{mangled name of 'g' will change in C++17}} + void g(void() throw()); // #dr92-g + // cxx98-14-warning@-1 {{mangled name of 'g' will change in C++17 due to non-throwing exception specification in function signature}} void h() throw() { - g(f); // expected-error-re {{{{is not superset|no matching function}}}} - g(q); // expected-error-re {{{{is not superset|no matching function}}}} + g(f); + // cxx98-14-error@-1 {{target exception specification is not superset of source}} + // since-cxx17-error@-2 {{no matching function for call to 'g'}} + // since-cxx17-note@#dr92-g {{candidate function not viable: no known conversion from 'void () throw(int, float)' to 'void (*)() throw()' for 1st argument}} + g(q); + // cxx98-14-error@-1 {{target exception specification is not superset of source}} + // since-cxx17-error@-2 {{no matching function for call to 'g'}} + // since-cxx17-note@#dr92-g {{candidate function not viable: no known conversion from 'void (*)() throw(int)' to 'void (*)() throw()' for 1st argument}} } // Prior to C++17, this is OK because the exception specification is not @@ -1182,11 +1368,11 @@ namespace dr92 { // dr92: 4 c++17 // is part of the type of the parameter, so this is invalid. template struct X {}; X<&f> xp; -#if __cplusplus > 201402L - // expected-error@-2 {{not implicitly convertible}} -#endif + // since-cxx17-error@-1 {{value of type 'void (*)() throw(int, float)' is not implicitly convertible to 'void (*)() throw()'}} - template struct Y {}; // expected-error 0-1{{ISO C++17 does not allow}} expected-note 0-1{{use 'noexcept}} + template struct Y {}; + // since-cxx17-error@-1 {{ISO C++17 does not allow dynamic exception specifications}} + // since-cxx17-note@-2 {{use 'noexcept(false)' instead}} Y<&h> yp; // ok } @@ -1197,19 +1383,21 @@ namespace dr94 { // dr94: yes int arr[A::n]; } -namespace dr95 { // dr95: yes +namespace dr95 { // dr95: 3.3 struct A; struct B; namespace N { class C { friend struct A; friend struct B; - static void f(); // expected-note {{here}} + static void f(); // #dr95-C-f }; struct A *p; // dr95::A, not dr95::N::A. } A *q = N::p; // ok, same type - struct B { void f() { N::C::f(); } }; // expected-error {{private}} + struct B { void f() { N::C::f(); } }; + // expected-error@-1 {{'f' is a private member of 'dr95::N::C'}} + // expected-note@#dr95-C-f {{implicitly declared private here}} } namespace dr96 { // dr96: no @@ -1242,19 +1430,27 @@ namespace dr97 { // dr97: yes namespace dr98 { // dr98: yes void test(int n) { switch (n) { - try { // expected-note 2{{bypasses}} - case 0: // expected-error {{cannot jump}} + try { // #dr98-try + case 0: + // expected-error@-1 {{cannot jump from switch statement to this case label}} + // expected-note@#dr98-try {{jump bypasses initialization of try block}} x: throw n; - } catch (...) { // expected-note 2{{bypasses}} - case 1: // expected-error {{cannot jump}} + } catch (...) { // #dr98-catch + case 1: + // expected-error@-1 {{cannot jump from switch statement to this case label}} + // expected-note@#dr98-catch {{jump bypasses initialization of catch block}} y: throw n; } case 2: - goto x; // expected-error {{cannot jump}} + goto x; + // expected-error@-1 {{cannot jump from this goto statement to its label}} + // expected-note@#dr98-try {{jump bypasses initialization of try block}} case 3: - goto y; // expected-error {{cannot jump}} + goto y; + // expected-error@-1 {{cannot jump from this goto statement to its label}} + // expected-note@#dr98-catch {{jump bypasses initialization of catch block}} } } } diff --git a/clang/test/CXX/drs/dr1xx.cpp b/clang/test/CXX/drs/dr1xx.cpp index 60e80a4c0e1c4f98cde1d2a7fc932d682b9056e5..064ecace59067a1ee43f602f957df21fa076e9a6 100644 --- a/clang/test/CXX/drs/dr1xx.cpp +++ b/clang/test/CXX/drs/dr1xx.cpp @@ -1,30 +1,31 @@ -// RUN: %clang_cc1 -std=c++98 -triple x86_64-unknown-unknown %s -verify -fexceptions -fcxx-exceptions -pedantic-errors -// RUN: %clang_cc1 -std=c++11 -triple x86_64-unknown-unknown %s -verify -fexceptions -fcxx-exceptions -pedantic-errors -// RUN: %clang_cc1 -std=c++14 -triple x86_64-unknown-unknown %s -verify -fexceptions -fcxx-exceptions -pedantic-errors -// RUN: %clang_cc1 -std=c++17 -triple x86_64-unknown-unknown %s -verify -fexceptions -fcxx-exceptions -pedantic-errors -// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-unknown %s -verify -fexceptions -fcxx-exceptions -pedantic-errors -// RUN: %clang_cc1 -std=c++23 -triple x86_64-unknown-unknown %s -verify -fexceptions -fcxx-exceptions -pedantic-errors +// RUN: %clang_cc1 -std=c++98 -triple x86_64-unknown-unknown %s -verify=expected,cxx98,cxx98-11,cxx98-14,cxx98-17 -fexceptions -fcxx-exceptions -pedantic-errors +// RUN: %clang_cc1 -std=c++11 -triple x86_64-unknown-unknown %s -verify=expected,since-cxx11,cxx98-11,cxx98-14,cxx98-17,cxx11-14 -fexceptions -fcxx-exceptions -pedantic-errors +// RUN: %clang_cc1 -std=c++14 -triple x86_64-unknown-unknown %s -verify=expected,since-cxx11,cxx98-14,cxx98-17,cxx11-14 -fexceptions -fcxx-exceptions -pedantic-errors +// RUN: %clang_cc1 -std=c++17 -triple x86_64-unknown-unknown %s -verify=expected,since-cxx11,since-cxx17,cxx98-17 -fexceptions -fcxx-exceptions -pedantic-errors +// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-unknown %s -verify=expected,since-cxx11,since-cxx17 -fexceptions -fcxx-exceptions -pedantic-errors +// RUN: %clang_cc1 -std=c++23 -triple x86_64-unknown-unknown %s -verify=expected,since-cxx11,since-cxx17 -fexceptions -fcxx-exceptions -pedantic-errors namespace dr100 { // dr100: yes - template struct A {}; // expected-note 0-1{{declared here}} - template struct B {}; // expected-note 0-1{{declared here}} - template struct C {}; // expected-note 0-1{{declared here}} - template struct D {}; // expected-note 0-1{{declared here}} - A<&"foo"> a; // #100a - B<"bar"> b; // #100b - C<"baz"> c; // #100c - D<*"quux"> d; // #100d -#if __cplusplus < 201703L - // expected-error@#100a {{does not refer to any declaration}} - // expected-error@#100b {{does not refer to any declaration}} - // expected-error@#100c {{does not refer to any declaration}} - // expected-error@#100d {{does not refer to any declaration}} -#else - // expected-error@#100a {{pointer to string literal is not allowed in a template argument}} - // expected-error@#100b {{reference to string literal is not allowed in a template argument}} - // expected-error@#100c {{pointer to subobject of string literal is not allowed in a template argument}} - // expected-error@#100d {{reference to subobject of string literal is not allowed in a template argument}} -#endif + template struct A {}; // #dr100-A + template struct B {}; // #dr100-B + template struct C {}; // #dr100-C + template struct D {}; // #dr100-D + A<&"foo"> a; // #dr100-a + // cxx98-14-error@#dr100-a {{non-type template argument does not refer to any declaration}} + // cxx98-14-note@#dr100-A {{template parameter is declared here}} + // since-cxx17-error@#dr100-a {{pointer to string literal is not allowed in a template argument}} + B<"bar"> b; // #dr100-b + // cxx98-14-error@#dr100-b {{non-type template argument does not refer to any declaration}} + // cxx98-14-note@#dr100-B {{template parameter is declared here}} + // since-cxx17-error@#dr100-b {{reference to string literal is not allowed in a template argument}} + C<"baz"> c; // #dr100-c + // cxx98-14-error@#dr100-c {{non-type template argument does not refer to any declaration}} + // cxx98-14-note@#dr100-C {{template parameter is declared here}} + // since-cxx17-error@#dr100-c {{pointer to subobject of string literal is not allowed in a template argument}} + D<*"quux"> d; // #dr100-d + // cxx98-14-error@#dr100-d {{non-type template argument does not refer to any declaration}} + // cxx98-14-note@#dr100-D {{template parameter is declared here}} + // since-cxx17-error@#dr100-d {{reference to subobject of string literal is not allowed in a template argument}} } namespace dr101 { // dr101: 3.5 @@ -42,13 +43,16 @@ namespace dr101 { // dr101: 3.5 namespace dr102 { // dr102: yes namespace A { - template T f(T a, T b) { return a + b; } // expected-error {{neither visible in the template definition nor found by argument-dependent lookup}} + template T f(T a, T b) { return a + b; } + // expected-error@-1 {{call to function 'operator+' that is neither visible in the template definition nor found by argument-dependent lookup}} + // expected-note@#dr102-instantiation {{in instantiation of function template specialization 'dr102::A::f' requested here}} + // expected-note@#dr102-operator-plus {{'operator+' should be declared prior to the call site or in namespace 'dr102::B'}} } namespace B { struct S {}; } - B::S operator+(B::S, B::S); // expected-note {{should be declared prior to the call site or in namespace 'dr102::B'}} - template B::S A::f(B::S, B::S); // expected-note {{in instantiation of}} + B::S operator+(B::S, B::S); // #dr102-operator-plus + template B::S A::f(B::S, B::S); // #dr102-instantiation } // dr103: na @@ -58,13 +62,17 @@ namespace dr102 { // dr102: yes namespace dr106 { // dr106: sup 540 typedef int &r1; typedef r1 &r1; - typedef const r1 r1; // expected-warning {{has no effect}} - typedef const r1 &r1; // expected-warning {{has no effect}} + typedef const r1 r1; + // expected-warning@-1 {{'const' qualifier on reference type 'r1' (aka 'int &') has no effect}} + typedef const r1 &r1; + // expected-warning@-1 {{'const' qualifier on reference type 'r1' (aka 'int &') has no effect}} typedef const int &r2; typedef r2 &r2; - typedef const r2 r2; // expected-warning {{has no effect}} - typedef const r2 &r2; // expected-warning {{has no effect}} + typedef const r2 r2; + // expected-warning@-1 {{'const' qualifier on reference type 'r2' (aka 'const int &') has no effect}} + typedef const r2 &r2; + // expected-warning@-1 {{'const' qualifier on reference type 'r2' (aka 'const int &') has no effect}} } namespace dr107 { // dr107: yes @@ -72,14 +80,13 @@ namespace dr107 { // dr107: yes extern "C" S operator+(S, S) { return S(); } } -namespace dr108 { // dr108: yes +namespace dr108 { // dr108: 2.9 template struct A { struct B { typedef int X; }; B::X x; -#if __cplusplus <= 201703L - // expected-error@-2 {{implicit 'typename' is a C++20 extension}} -#endif - struct C : B { X x; }; // expected-error {{unknown type name}} + // cxx98-17-error@-1 {{missing 'typename' prior to dependent type name B::X; implicit 'typename' is a C++20 extension}} + struct C : B { X x; }; + // expected-error@-1 {{unknown type name 'X'}} }; template<> struct A::B { int X; }; } @@ -87,46 +94,55 @@ namespace dr108 { // dr108: yes namespace dr109 { // dr109: yes struct A { template void f(T); }; template struct B : T { - using T::template f; // expected-error {{'template' keyword not permitted here}} - using T::template f; // expected-error {{'template' keyword not permitted here}} expected-error {{using declaration cannot refer to a template specialization}} + using T::template f; + // expected-error@-1 {{'template' keyword not permitted here}} + using T::template f; + // expected-error@-1 {{'template' keyword not permitted here}} + // expected-error@-2 {{using declaration cannot refer to a template specialization}} // FIXME: We shouldn't suggest using the 'template' keyword in a location where it's not valid. - using T::f; // expected-error {{use 'template' keyword}} expected-error {{using declaration cannot refer to a template specialization}} - void g() { this->f(123); } // expected-error {{use 'template' keyword}} + using T::f; + // expected-error@-1 {{use 'template' keyword to treat 'f' as a dependent template name}} + // expected-error@-2 {{using declaration cannot refer to a template specialization}} + void g() { this->f(123); } + // expected-error@-1 {{use 'template' keyword to treat 'f' as a dependent template name}} }; } namespace dr111 { // dr111: dup 535 struct A { A(); A(volatile A&, int = 0); A(A&, const char * = "foo"); }; - struct B : A { B(); }; // expected-note +{{would lose const qualifier}} expected-note {{requires 0 arguments}} + struct B : A { B(); }; // #dr111-B const B b1; - B b2(b1); // expected-error {{no matching constructor}} + B b2(b1); + // expected-error@-1 {{no matching constructor for initialization of 'B'}} + // expected-note@#dr111-B {{candidate constructor (the implicit copy constructor) not viable: 1st argument ('const B') would lose const qualifier}} + // expected-note@#dr111-B {{candidate constructor not viable: requires 0 arguments, but 1 was provided}} } namespace dr112 { // dr112: yes struct T { int n; }; typedef T Arr[1]; - const T a1[1] = {}; + const T a1[1] = {}; // #dr112-a1 volatile T a2[1] = {}; - const Arr a3 = {}; + const Arr a3 = {}; // #dr112-a3 volatile Arr a4 = {}; template struct X {}; + // FIXME: Test this somehow in C++11 and on. X x1; + // cxx98-error@-1 {{non-type template argument referring to object 'a1' with internal linkage is a C++11 extension}} + // cxx98-note@#dr112-a1 {{non-type template argument refers to object here}} X x2; X x3; + // cxx98-error@-1 {{non-type template argument referring to object 'a3' with internal linkage is a C++11 extension}} + // cxx98-note@#dr112-a3 {{non-type template argument refers to object here}} X x4; -#if __cplusplus < 201103L - // expected-error@-5 {{internal linkage}} expected-note@-10 {{here}} - // expected-error@-4 {{internal linkage}} expected-note@-9 {{here}} -#else - // FIXME: Test this somehow. -#endif } namespace dr113 { // dr113: yes extern void (*p)(); void f() { - no_such_function(); // expected-error {{undeclared}} + no_such_function(); + // expected-error@-1 {{use of undeclared identifier 'no_such_function'}} p(); } void g(); @@ -135,31 +151,48 @@ namespace dr113 { // dr113: yes namespace dr114 { // dr114: yes struct A { - virtual void f(int) = 0; // expected-note {{unimplemented}} + virtual void f(int) = 0; // #dr114-A-f }; struct B : A { template void f(T); void g() { f(0); } - } b; // expected-error {{abstract}} + } b; + // expected-error@-1 {{variable type 'struct B' is an abstract class}} + // expected-note@#dr114-A-f {{unimplemented pure virtual method 'f' in 'B'}} } -namespace dr115 { // dr115: yes - template int f(T); // expected-note +{{}} - template int g(T); // expected-note +{{}} - template int g(T, int); // expected-note +{{}} +namespace dr115 { // dr115: 3.0 + template int f(T); // #dr115-f + template int g(T); // #dr115-g + template int g(T, int); // #dr115-g-int - int k1 = f(&f); // expected-error {{no match}} + int k1 = f(&f); + // expected-error@-1 {{no matching function for call to 'f'}} + // expected-note@#dr115-f {{candidate template ignored: couldn't infer template argument 'T'}} int k2 = f(&f); - int k3 = f(&g); // expected-error {{no match}} + int k3 = f(&g); + // expected-error@-1 {{no matching function for call to 'f'}} + // expected-note@#dr115-f {{candidate template ignored: couldn't infer template argument 'T'}} void h() { - (void)&f; // expected-error {{address of overloaded function 'f' cannot be cast to type 'void'}} + (void)&f; + // expected-error@-1 {{address of overloaded function 'f' cannot be cast to type 'void'}} + // expected-note@#dr115-f {{candidate function template}} (void)&f; - (void)&g; // expected-error {{address of overloaded function 'g' cannot be cast to type 'void'}} - - &f; // expected-error {{reference to overloaded function could not be resolved}} - &f; // expected-warning {{unused}} - &g; // expected-error {{reference to overloaded function could not be resolved}} + (void)&g; + // expected-error@-1 {{address of overloaded function 'g' cannot be cast to type 'void'}} + // expected-note@#dr115-g-int {{candidate function template}} + // expected-note@#dr115-g {{candidate function template}} + + &f; + // expected-error@-1 {{reference to overloaded function could not be resolved; did you mean to call it?}} + // expected-note@#dr115-f {{possible target for call}} + &f; + // expected-warning@-1 {{expression result unused}} + &g; + // expected-error@-1 {{reference to overloaded function could not be resolved; did you mean to call it?}} + // expected-note@#dr115-g-int {{possible target for call}} + // expected-note@#dr115-g {{possible target for call}} } struct S { @@ -168,18 +201,25 @@ namespace dr115 { // dr115: yes template static int g(T, int); } s; - int k4 = f(&s.f); // expected-error {{non-constant pointer to member}} + int k4 = f(&s.f); + // expected-error@-1 {{cannot create a non-constant pointer to member function}} int k5 = f(&s.f); - int k6 = f(&s.g); // expected-error {{non-constant pointer to member}} + int k6 = f(&s.g); + // expected-error@-1 {{cannot create a non-constant pointer to member function}} void i() { - (void)&s.f; // expected-error {{non-constant pointer to member}} + (void)&s.f; + // expected-error@-1 {{cannot create a non-constant pointer to member function}} (void)&s.f; - (void)&s.g; // expected-error {{non-constant pointer to member}} - - &s.f; // expected-error {{non-constant pointer to member}} - &s.f; // expected-warning {{unused}} - &s.g; // expected-error {{non-constant pointer to member}} + (void)&s.g; + // expected-error@-1 {{cannot create a non-constant pointer to member function}} + + &s.f; + // expected-error@-1 {{cannot create a non-constant pointer to member function}} + &s.f; + // expected-warning@-1 {{expression result unused}} + &s.g; + // expected-error@-1 {{cannot create a non-constant pointer to member function}} } struct T { @@ -188,40 +228,58 @@ namespace dr115 { // dr115: yes template int g(T, int); } t; - int k7 = f(&s.f); // expected-error {{non-constant pointer to member}} + int k7 = f(&s.f); + // expected-error@-1 {{cannot create a non-constant pointer to member function}} int k8 = f(&s.f); - int k9 = f(&s.g); // expected-error {{non-constant pointer to member}} + int k9 = f(&s.g); + // expected-error@-1 {{cannot create a non-constant pointer to member function}} void j() { - (void)&s.f; // expected-error {{non-constant pointer to member}} + (void)&s.f; + // expected-error@-1 {{cannot create a non-constant pointer to member function}} (void)&s.f; - (void)&s.g; // expected-error {{non-constant pointer to member}} - - &s.f; // expected-error {{non-constant pointer to member}} - &s.f; // expected-warning {{unused}} - &s.g; // expected-error {{non-constant pointer to member}} + (void)&s.g; + // expected-error@-1 {{cannot create a non-constant pointer to member function}} + + &s.f; + // expected-error@-1 {{cannot create a non-constant pointer to member function}} + &s.f; + // expected-warning@-1 {{expression result unused}} + &s.g; + // expected-error@-1 {{cannot create a non-constant pointer to member function}} } #if __cplusplus >= 201103L // Special case kicks in only if a template argument list is specified. - template void with_default(); // expected-note +{{}} - int k10 = f(&with_default); // expected-error {{no matching function}} + template void with_default(); // #dr115-with-default + int k10 = f(&with_default); + // expected-error@-1 {{no matching function for call to 'f'}} + // expected-note@#dr115-f {{candidate template ignored: couldn't infer template argument 'T'}} int k11 = f(&with_default<>); void k() { - (void)&with_default; // expected-error {{overloaded function}} + (void)&with_default; + // expected-error@-1 {{address of overloaded function 'with_default' cannot be cast to type 'void'}} + // expected-note@#dr115-with-default {{candidate function template}} (void)&with_default<>; - &with_default; // expected-error {{overloaded function}} - &with_default<>; // expected-warning {{unused}} + &with_default; + // expected-error@-1 {{reference to overloaded function could not be resolved; did you mean to call it?}} + // expected-note@#dr115-with-default {{possible target for call}} + &with_default<>; + // expected-warning@-1 {{expression result unused}} } #endif } namespace dr116 { // dr116: yes template struct A {}; - template void f(A) {} // expected-note {{previous}} - template void f(A) {} // expected-error {{redefinition}} - template void f(A) {} // expected-note {{previous}} - template void f(A) {} // expected-error {{redefinition}} + template void f(A) {} // #dr116-f-N + template void f(A) {} + // expected-error@-1 {{redefinition of 'f'}} + // expected-note@#dr116-f-N {{previous definition is here}} + template void f(A) {} // #dr116-f-T + template void f(A) {} + // expected-error@-1 {{redefinition of 'f'}} + // expected-note@#dr116-f-T {{previous definition is here}} } // dr117: na @@ -235,7 +293,9 @@ namespace dr121 { // dr121: yes }; template struct Z { X::Y x; - T::Y y; // expected-error +{{}} + T::Y y; + // expected-error@-1 {{use 'template' keyword to treat 'Y' as a dependent template name}} + // cxx98-17-error@-2 {{missing 'typename' prior to dependent type name T::Y; implicit 'typename' is a C++20 extension}} }; Z z; } @@ -249,15 +309,19 @@ namespace dr122 { // dr122: yes // dr124: dup 201 // dr125: yes -struct dr125_A { struct dr125_B {}; }; // expected-note {{here}} +struct dr125_A { struct dr125_B {}; }; // #dr125_B dr125_A::dr125_B dr125_C(); namespace dr125_B { dr125_A dr125_C(); } namespace dr125 { struct X { friend dr125_A::dr125_B (::dr125_C)(); // ok friend dr125_A (::dr125_B::dr125_C)(); // ok - friend dr125_A::dr125_B::dr125_C(); // expected-error {{did you mean the constructor name 'dr125_B'?}} - // expected-error@-1 {{missing exception specification}} + friend dr125_A::dr125_B::dr125_C(); // #dr125_C + // expected-error@#dr125_C {{missing return type for function 'dr125_C'; did you mean the constructor name 'dr125_B'?}} + // cxx98-error@#dr125_C {{'dr125_B' is missing exception specification 'throw()'}} + // cxx98-note@#dr125_B {{previous declaration is here}} + // since-cxx11-error@#dr125_C {{'dr125_B' is missing exception specification 'noexcept'}} + // since-cxx11-note@#dr125_B {{previous declaration is here}} }; } @@ -275,7 +339,6 @@ namespace dr126 { // dr126: partial // So, when catching by non-const (or volatile) reference to pointer, we // should compare the exception type to the caught type and only accept an // exact match. -#if __cplusplus <= 201402L struct C {}; struct D : C {}; struct E : private C { friend class A; friend class B; }; @@ -283,53 +346,64 @@ namespace dr126 { // dr126: partial struct G : C {}; struct H : D, G {}; +#if __cplusplus <= 201402L struct A { virtual void cp() throw(C*); virtual void dp() throw(C*); - virtual void ep() throw(C*); // expected-note {{overridden}} - virtual void fp() throw(C*); // expected-note {{overridden}} + virtual void ep() throw(C*); // #dr126-ep + virtual void fp() throw(C*); // #dr126-fp virtual void gp() throw(C*); - virtual void hp() throw(C*); // expected-note {{overridden}} + virtual void hp() throw(C*); // #dr126-hp virtual void cr() throw(C&); virtual void dr() throw(C&); - virtual void er() throw(C&); // expected-note {{overridden}} - virtual void fr() throw(C&); // expected-note {{overridden}} + virtual void er() throw(C&); // #dr126-er + virtual void fr() throw(C&); // #dr126-fr virtual void gr() throw(C&); - virtual void hr() throw(C&); // expected-note {{overridden}} + virtual void hr() throw(C&); // #dr126-hr virtual void pv() throw(void*); -#if __cplusplus >= 201103L virtual void np() throw(C*); virtual void npm() throw(int C::*); - virtual void nr() throw(C*&); // expected-note {{overridden}} + virtual void nr() throw(C*&); // #dr126-nr virtual void ncr() throw(C*const&); -#endif virtual void ref1() throw(C *const&); virtual void ref2() throw(C *); virtual void v() throw(int); virtual void w() throw(const int); - virtual void x() throw(int*); // expected-note {{overridden}} + virtual void x() throw(int*); // #dr126-x virtual void y() throw(const int*); - virtual void z() throw(int); // expected-note {{overridden}} + virtual void z() throw(int); // #dr126-z }; struct B : A { virtual void cp() throw(C*); virtual void dp() throw(D*); - virtual void ep() throw(E*); // expected-error {{more lax}} - virtual void fp() throw(F*); // expected-error {{more lax}} + virtual void ep() throw(E*); + // cxx98-14-error@-1 {{exception specification of overriding function is more lax than base version}} + // cxx98-14-note@#dr126-ep {{overridden virtual function is here}} + virtual void fp() throw(F*); + // cxx98-14-error@-1 {{exception specification of overriding function is more lax than base version}} + // cxx98-14-note@#dr126-fp {{overridden virtual function is here}} virtual void gp() throw(G*); - virtual void hp() throw(H*); // expected-error {{more lax}} + virtual void hp() throw(H*); + // cxx98-14-error@-1 {{exception specification of overriding function is more lax than base version}} + // cxx98-14-note@#dr126-hp {{overridden virtual function is here}} virtual void cr() throw(C&); virtual void dr() throw(D&); - virtual void er() throw(E&); // expected-error {{more lax}} - virtual void fr() throw(F&); // expected-error {{more lax}} + virtual void er() throw(E&); + // cxx98-14-error@-1 {{exception specification of overriding function is more lax than base version}} + // cxx98-14-note@#dr126-er {{overridden virtual function is here}} + virtual void fr() throw(F&); + // cxx98-14-error@-1 {{exception specification of overriding function is more lax than base version}} + // cxx98-14-note@#dr126-fr {{overridden virtual function is here}} virtual void gr() throw(G&); - virtual void hr() throw(H&); // expected-error {{more lax}} + virtual void hr() throw(H&); + // cxx98-14-error@-1 {{exception specification of overriding function is more lax than base version}} + // cxx98-14-note@#dr126-hr {{overridden virtual function is here}} virtual void pv() throw(C*); @@ -337,34 +411,45 @@ namespace dr126 { // dr126: partial using nullptr_t = decltype(nullptr); virtual void np() throw(nullptr_t); virtual void npm() throw(nullptr_t&); - virtual void nr() throw(nullptr_t); // expected-error {{more lax}} + virtual void nr() throw(nullptr_t); + // cxx11-14-error@-1 {{exception specification of overriding function is more lax than base version}} + // cxx11-14-note@#dr126-nr {{overridden virtual function is here}} virtual void ncr() throw(nullptr_t); -#endif +#endif // __cplusplus >= 201103L virtual void ref1() throw(D *const &); virtual void ref2() throw(D *); virtual void v() throw(const int); virtual void w() throw(int); - virtual void x() throw(const int*); // expected-error {{more lax}} + virtual void x() throw(const int*); + // cxx98-14-error@-1 {{exception specification of overriding function is more lax than base version}} + // cxx98-14-note@#dr126-x {{overridden virtual function is here}} virtual void y() throw(int*); // ok - virtual void z() throw(long); // expected-error {{more lax}} + virtual void z() throw(long); + // cxx98-14-error@-1 {{exception specification of overriding function is more lax than base version}} + // cxx98-14-note@#dr126-z {{overridden virtual function is here}} }; -#else - void f() throw(int); // expected-error {{ISO C++17 does not allow}} expected-note {{use 'noexcept}} -#endif +#endif // __cplusplus <= 201402L + void f() throw(int); + // since-cxx17-error@-1 {{ISO C++17 does not allow dynamic exception specifications}} + // since-cxx17-note@-2 {{use 'noexcept(false)' instead}} } -namespace dr127 { // dr127: yes +namespace dr127 { // dr127: 2.9 __extension__ typedef __decltype(sizeof(0)) size_t; template struct A { A() { throw 0; } void *operator new(size_t, const char * = 0); - void operator delete(void *, const char *) { T::error; } // expected-error 2{{no members}} + void operator delete(void *, const char *) { T::error; } // #dr127-delete-const-char + // expected-error@#dr127-delete-const-char {{type 'void' cannot be used prior to '::' because it has no members}} + // expected-note@#dr127-p {{in instantiation of member function 'dr127::A::operator delete' requested here}} + // expected-error@#dr127-delete-const-char {{type 'int' cannot be used prior to '::' because it has no members}} + // expected-note@#dr127-q {{in instantiation of member function 'dr127::A::operator delete' requested here}} void operator delete(void *) { T::error; } }; - A *p = new A; // expected-note {{instantiat}} - A *q = new ("") A; // expected-note {{instantiat}} + A *p = new A; // #dr127-p + A *q = new ("") A; // #dr127-q } namespace dr128 { // dr128: yes @@ -401,36 +486,50 @@ namespace dr135 { // dr135: yes } namespace dr136 { // dr136: 3.4 - void f(int, int, int = 0); // expected-note {{previous declaration is here}} - void g(int, int, int); // expected-note {{previous declaration is here}} + void f(int, int, int = 0); // #dr136-f + void g(int, int, int); // #dr136-g struct A { - friend void f(int, int = 0, int); // expected-error {{friend declaration specifying a default argument must be the only declaration}} - friend void g(int, int, int = 0); // expected-error {{friend declaration specifying a default argument must be the only declaration}} - friend void h(int, int, int = 0); // expected-error {{friend declaration specifying a default argument must be a definition}} - friend void i(int, int, int = 0) {} // expected-note {{previous declaration is here}} + friend void f(int, int = 0, int); + // expected-error@-1 {{friend declaration specifying a default argument must be the only declaration}} + // expected-note@#dr136-f {{previous declaration is here}} + friend void g(int, int, int = 0); + // expected-error@-1 {{friend declaration specifying a default argument must be the only declaration}} + // expected-note@#dr136-g {{previous declaration is here}} + friend void h(int, int, int = 0); + // expected-error@-1 {{friend declaration specifying a default argument must be a definition}} + friend void i(int, int, int = 0) {} // #dr136-A-i friend void j(int, int, int = 0) {} operator int(); }; - void i(int, int, int); // expected-error {{friend declaration specifying a default argument must be the only declaration}} + void i(int, int, int); + // expected-error@-1 {{friend declaration specifying a default argument must be the only declaration}} + // expected-note@#dr136-A-i {{previous declaration is here}} void q() { j(A(), A()); // ok, has default argument } - extern "C" void k(int, int, int, int); // expected-note 2{{previous declaration is here}} + extern "C" void k(int, int, int, int); // #dr136-k namespace NSA { struct A { - friend void dr136::k(int, int, int, int = 0); // expected-error {{friend declaration specifying a default argument must be the only declaration}} + friend void dr136::k(int, int, int, int = 0); + // expected-error@-1 {{friend declaration specifying a default argument must be the only declaration}} + // expected-note@#dr136-k {{previous declaration is here}} }; } namespace NSB { struct A { - friend void dr136::k(int, int, int = 0, int); // expected-error {{missing default argument on parameter}} expected-error {{must be the only declaration}} + friend void dr136::k(int, int, int = 0, int); // #dr136-friend-k + // expected-error@#dr136-friend-k {{friend declaration specifying a default argument must be the only declaration}} + // expected-note@#dr136-k {{previous declaration is here}} + // expected-error@#dr136-friend-k {{missing default argument on parameter}} }; } struct B { - void f(int); // expected-note {{previous declaration is here}} + void f(int); // #dr136-B-f }; struct C { - friend void B::f(int = 0); // expected-error {{friend declaration specifying a default argument must be the only declaration}} + friend void B::f(int = 0); + // expected-error@-1 {{friend declaration specifying a default argument must be the only declaration}} + // expected-note@#dr136-B-f {{previous declaration is here}} }; } @@ -440,13 +539,18 @@ namespace dr137 { // dr137: yes extern volatile void *vp; extern const volatile void *cvp; int *q = static_cast(p); - int *qc = static_cast(cp); // expected-error {{casts away qualifiers}} - int *qv = static_cast(vp); // expected-error {{casts away qualifiers}} - int *qcv = static_cast(cvp); // expected-error {{casts away qualifiers}} + int *qc = static_cast(cp); + // expected-error@-1 {{static_cast from 'const void *' to 'int *' casts away qualifiers}} + int *qv = static_cast(vp); + // expected-error@-1 {{static_cast from 'volatile void *' to 'int *' casts away qualifiers}} + int *qcv = static_cast(cvp); + // expected-error@-1 {{static_cast from 'const volatile void *' to 'int *' casts away qualifiers}} const int *cq = static_cast(p); const int *cqc = static_cast(cp); - const int *cqv = static_cast(vp); // expected-error {{casts away qualifiers}} - const int *cqcv = static_cast(cvp); // expected-error {{casts away qualifiers}} + const int *cqv = static_cast(vp); + // expected-error@-1 {{static_cast from 'volatile void *' to 'const int *' casts away qualifiers}} + const int *cqcv = static_cast(cvp); + // expected-error@-1 {{static_cast from 'const volatile void *' to 'const int *' casts away qualifiers}} const volatile int *cvq = static_cast(p); const volatile int *cvqc = static_cast(cp); const volatile int *cvqv = static_cast(vp); @@ -455,9 +559,11 @@ namespace dr137 { // dr137: yes namespace dr139 { // dr139: yes namespace example1 { - typedef int f; // expected-note {{previous}} + typedef int f; // #dr139-typedef-f struct A { - friend void f(A &); // expected-error {{different kind of symbol}} + friend void f(A &); + // expected-error@-1 {{redefinition of 'f' as different kind of symbol}} + // expected-note@#dr139-typedef-f {{previous definition is here}} }; } @@ -474,35 +580,41 @@ namespace dr139 { // dr139: yes } namespace dr140 { // dr140: yes - void f(int *const) {} // expected-note {{previous}} - void f(int[3]) {} // expected-error {{redefinition}} + void f(int *const) {} // #dr140-f-first + void f(int[3]) {} + // expected-error@-1 {{redefinition of 'f'}} + // expected-note@#dr140-f-first {{previous definition is here}} void g(const int); void g(int n) { n = 2; } } -namespace dr141 { // dr141: yes +namespace dr141 { // dr141: 3.1 template void f(); - template struct S { int n; }; // expected-note {{'::dr141::S::n' declared here}} + template struct S { int n; }; // #dr141-S struct A : S { template void f(); - template struct S {}; + template struct S {}; // #dr141-A-S } a; struct B : S {} b; void g() { a.f(); - (void)a.S::n; // expected-error {{no member named 'n' in 'dr141::A::S'; did you mean '::dr141::S::n'?}} -#if __cplusplus < 201103L - // expected-error@-2 {{ambiguous}} - // expected-note@-11 {{lookup from the current scope}} - // expected-note@-9 {{lookup in the object type}} -#endif - b.f(); // expected-error {{no member}} expected-error +{{}} + (void)a.S::n; // #dr141-a + // cxx98-error@#dr141-a {{lookup of 'S' in member access expression is ambiguous; using member of 'struct A'}} + // cxx98-note@#dr141-A-S {{lookup in the object type 'struct A' refers here}} + // cxx98-note@#dr141-S {{lookup from the current scope refers here}} + // expected-error@#dr141-a {{no member named 'n' in 'dr141::A::S'; did you mean '::dr141::S::n'?}} + // expected-note@#dr141-S {{'::dr141::S::n' declared here}} + // FIXME: we issue a useful diagnostic first, then some bogus ones. + b.f(); + // expected-error@-1 {{no member named 'f' in 'dr141::B'}} + // expected-error@-2 +{{}} (void)b.S::n; } template struct C { T t; void g() { - t.f(); // expected-error {{use 'template'}} + t.f(); + // expected-error@-1 {{use 'template' keyword to treat 'f' as a dependent template name}} } void h() { (void)t.S::n; // ok @@ -518,29 +630,54 @@ namespace dr141 { // dr141: yes void i() { C().i(); } // ok!! } -namespace dr142 { // dr142: yes - class B { // expected-note +{{here}} +namespace dr142 { // dr142: 2.8 + class B { // #dr142-B public: - int mi; // expected-note +{{here}} - static int si; // expected-note +{{here}} + int mi; // #dr142-B-mi + static int si; // #dr142-B-si }; - class D : private B { // expected-note +{{here}} + class D : private B { // #dr142-D }; class DD : public D { void f(); }; void DD::f() { - mi = 3; // expected-error {{private member}} - si = 3; // expected-error {{private member}} - B b_old; // expected-error {{private member}} + mi = 3; + // expected-error@-1 {{'mi' is a private member of 'dr142::B'}} + // expected-note@#dr142-D {{constrained by private inheritance here}} + // expected-note@#dr142-B-mi {{member is declared here}} + si = 3; + // expected-error@-1 {{'si' is a private member of 'dr142::B'}} + // expected-note@#dr142-D {{constrained by private inheritance here}} + // expected-note@#dr142-B-si {{member is declared here}} + B b_old; + // expected-error@-1 {{'B' is a private member of 'dr142::B'}} + // expected-note@#dr142-D {{constrained by private inheritance here}} + // expected-note@#dr142-B {{member is declared here}} dr142::B b; b.mi = 3; b.si = 3; - B::si = 3; // expected-error {{private member}} + B::si = 3; + // expected-error@-1 {{'B' is a private member of 'dr142::B'}} + // expected-note@#dr142-D {{constrained by private inheritance here}} + // expected-note@#dr142-B {{member is declared here}} dr142::B::si = 3; - B *bp1_old = this; // expected-error {{private member}} expected-error {{private base class}} - dr142::B *bp1 = this; // expected-error {{private base class}} - B *bp2_old = (B*)this; // expected-error 2{{private member}} + B *bp1_old = this; // #dr142-bp1_old + // expected-error@#dr142-bp1_old {{'B' is a private member of 'dr142::B'}} + // expected-note@#dr142-D {{constrained by private inheritance here}} + // expected-note@#dr142-B {{member is declared here}} + // expected-error@#dr142-bp1_old {{cannot cast 'dr142::DD' to its private base class 'B'}} + // expected-note@#dr142-D {{declared private here}} + dr142::B *bp1 = this; + // expected-error@-1 {{cannot cast 'dr142::DD' to its private base class 'dr142::B'}} + // expected-note@#dr142-D {{declared private here}} + B *bp2_old = (B*)this; // #dr142-bp2_old + // expected-error@#dr142-bp2_old {{'B' is a private member of 'dr142::B'}} + // expected-note@#dr142-D {{constrained by private inheritance here}} + // expected-note@#dr142-B {{member is declared here}} + // expected-error@#dr142-bp2_old {{'B' is a private member of 'dr142::B'}} + // expected-note@#dr142-D {{constrained by private inheritance here}} + // expected-note@#dr142-B {{member is declared here}} dr142::B *bp2 = (dr142::B*)this; bp2->mi = 3; } @@ -553,19 +690,19 @@ namespace dr143 { // dr143: yes struct X { friend void B::f(X); }; } void g(A::X x) { - f(x); // expected-error {{undeclared identifier 'f'}} + f(x); + // expected-error@-1 {{use of undeclared identifier 'f'}} } } namespace dr145 { // dr145: yes void f(bool b) { -#if __cplusplus <= 201402L - ++b; // expected-warning {{deprecated}} - b++; // expected-warning {{deprecated}} -#else - ++b; // expected-error {{increment}} - b++; // expected-error {{increment}} -#endif + ++b; + // cxx98-14-warning@-1 {{incrementing expression of type bool is deprecated and incompatible with C++17}} + // since-cxx17-error@-2 {{ISO C++17 does not allow incrementing expression of type bool}} + b++; + // cxx98-14-warning@-1 {{incrementing expression of type bool is deprecated and incompatible with C++17}} + // since-cxx17-error@-2 {{ISO C++17 does not allow incrementing expression of type bool}} } } @@ -576,13 +713,15 @@ namespace dr147 { // dr147: yes }; // Per core issue 1435, this is ill-formed because A::A does not // name the injected-class-name. (A::A does, though.) - template<> template<> A::A(int) {} // expected-error {{out-of-line constructor for 'A' cannot have template arguments}} + template<> template<> A::A(int) {} + // expected-error@-1 {{out-of-line constructor for 'A' cannot have template arguments}} template<> template<> A::A(float) {} } namespace example2 { struct A { A(); }; struct B : A { B(); }; - A::A a1; // expected-error {{is a constructor}} + A::A a1; + // expected-error@-1 {{qualified reference to 'A' is a constructor name rather than a type in this context}} B::A a2; } namespace example3 { @@ -590,7 +729,8 @@ namespace dr147 { // dr147: yes template A(T); static A a; }; - template<> A::A(A::a); // expected-error {{is a constructor}} + template<> A::A(A::a); + // expected-error@-1 {{qualified reference to 'A' is a constructor name rather than a template name in this context}} } } @@ -602,7 +742,7 @@ namespace dr148 { // dr148: yes // dr149: na -namespace dr151 { // dr151: yes +namespace dr151 { // dr151: 3.1 struct X {}; typedef int X::*p; #if __cplusplus < 201103L @@ -616,24 +756,28 @@ namespace dr151 { // dr151: yes namespace dr152 { // dr152: yes struct A { - A(); // expected-note 0-2{{not viable}} - explicit A(const A&); // expected-note 1-2{{not a candidate}} + A(); // #dr152-A-ctor + explicit A(const A&); // #dr152-A-explicit-ctor }; A a1 = A(); -#if __cplusplus <= 201402L - // expected-error@-2 {{no matching constructor}} -#endif + // cxx98-14-error@-1 {{no matching constructor for initialization of 'A'}} + // cxx98-14-note@#dr152-A-explicit-ctor {{explicit constructor is not a candidate}} + // cxx98-14-note@#dr152-A-ctor {{candidate constructor not viable: requires 0 arguments, but 1 was provided}} A a2((A())); A &f(); - A a3 = f(); // expected-error {{no matching constructor}} + A a3 = f(); + // expected-error@-1 {{no matching constructor for initialization of 'A'}} + // expected-note@#dr152-A-explicit-ctor {{explicit constructor is not a candidate}} + // expected-note@#dr152-A-ctor {{candidate constructor not viable: requires 0 arguments, but 1 was provided}} A a4(f()); } // dr153: na namespace dr154 { // dr154: yes - union { int a; }; // expected-error {{must be declared 'static'}} + union { int a; }; + // expected-error@-1 {{nonymous unions at namespace or global scope must be declared 'static'}} namespace { union { int b; }; } @@ -641,7 +785,8 @@ namespace dr154 { // dr154: yes } namespace dr155 { // dr155: dup 632 - struct S { int n; } s = { { 1 } }; // expected-warning {{braces around scalar initializer}} + struct S { int n; } s = { { 1 } }; + // expected-warning@-1 {{braces around scalar initializer}} } // dr158 is in its own file. @@ -649,18 +794,19 @@ namespace dr155 { // dr155: dup 632 namespace dr159 { // dr159: 3.5 namespace X { void f(); } void f(); - void dr159::f() {} // expected-warning {{extra qualification}} + void dr159::f() {} + // expected-warning@-1 {{extra qualification on member 'f'}} void dr159::X::f() {} } // dr160: na -namespace dr161 { // dr161: yes +namespace dr161 { // dr161: 3.1 class A { protected: - struct B { int n; } b; // expected-note 2{{here}} + struct B { int n; } b; // #dr161-B static B bs; - void f(); // expected-note {{here}} + void f(); // #dr161-f static void sf(); }; struct C : A {}; @@ -669,13 +815,19 @@ namespace dr161 { // dr161: yes (void)b.n; B b1; C::B b2; // ok, accessible as a member of A - (void)&C::b; // expected-error {{protected}} + (void)&C::b; + // expected-error@-1 {{'b' is a protected member of 'dr161::A'}} + // expected-note@#dr161-B {{declared protected here}} (void)&C::bs; - (void)c.b; // expected-error {{protected}} + (void)c.b; + // expected-error@-1 {{'b' is a protected member of 'dr161::A'}} + // expected-note@#dr161-B {{declared protected here}} (void)c.bs; f(); sf(); - c.f(); // expected-error {{protected}} + c.f(); + // expected-error@-1 {{protected}} + // expected-note@#dr161-f {{declared protected here}} c.sf(); A::f(); D::f(); @@ -692,13 +844,17 @@ namespace dr162 { // dr162: no static int &f(int); void g() { - int &a = (&A::f)(0); // FIXME: expected-error {{could not be resolved}} - char &b = (&A::f)('0'); // expected-error {{could not be resolved}} + int &a = (&A::f)(0); + // FIXME: expected-error@-1 {{reference to overloaded function could not be resolved; did you mean to call it?}} + char &b = (&A::f)('0'); + // expected-error@-1 {{reference to overloaded function could not be resolved; did you mean to call it?}} } }; - int &c = (&A::f)(0); // FIXME: expected-error {{could not be resolved}} - char &d = (&A::f)('0'); // expected-error {{could not be resolved}} + int &c = (&A::f)(0); + // FIXME: expected-error@-1 {{reference to overloaded function could not be resolved; did you mean to call it?}} + char &d = (&A::f)('0'); + // expected-error@-1 {{reference to overloaded function could not be resolved; did you mean to call it?}} } // dr163: na @@ -724,12 +880,15 @@ namespace dr165 { // dr165: no void N::g() {} } -namespace dr166 { // dr166: yes +namespace dr166 { // dr166: 2.9 namespace A { class X; } template int f(T t) { return t.n; } int g(A::X); - template int h(T t) { return t.n; } // expected-error {{private}} + template int h(T t) { return t.n; } + // expected-error@-1 {{'n' is a private member of 'dr166::A::X'}} + // expected-note@#dr166-h-instantiation {{in instantiation of function template specialization 'dr166::h' requested here}} + // expected-note@#dr166-X-n {{implicitly declared private here}} int i(A::X); namespace A { @@ -738,7 +897,7 @@ namespace dr166 { // dr166: yes friend int dr166::g(X); friend int h(X); friend int i(X); - int n; // expected-note 2{{here}} + int n; // #dr166-X-n }; int h(X x) { return x.n; } @@ -747,8 +906,10 @@ namespace dr166 { // dr166: yes template int f(A::X); int g(A::X x) { return x.n; } - template int h(A::X); // expected-note {{instantiation}} - int i(A::X x) { return x.n; } // expected-error {{private}} + template int h(A::X); // #dr166-h-instantiation + int i(A::X x) { return x.n; } + // expected-error@-1 {{'n' is a private member of 'dr166::A::X'}} + // expected-note@#dr166-X-n {{implicitly declared private here}} } // dr167: sup 1012 @@ -768,23 +929,29 @@ namespace dr169 { // dr169: yes struct B { template struct C; template void f(); - template static int n; // expected-error 0-1{{extension}} + template static int n; + // cxx98-11-error@-1 {{variable templates are a C++14 extension}} }; struct D : A, B { using A::n; - using B::C; // expected-error {{using declaration cannot refer to a template specialization}} - using B::f; // expected-error {{using declaration cannot refer to a template specialization}} - using B::n; // expected-error {{using declaration cannot refer to a template specialization}} + using B::C; + // expected-error@-1 {{using declaration cannot refer to a template specialization}} + using B::f; + // expected-error@-1 {{using declaration cannot refer to a template specialization}} + using B::n; + // expected-error@-1 {{using declaration cannot refer to a template specialization}} }; } -namespace { // dr171: yes +namespace { // dr171: 3.4 int dr171a; } -int dr171b; // expected-note {{here}} +int dr171b; // #dr171b-int namespace dr171 { extern "C" void dr171a(); - extern "C" void dr171b(); // expected-error {{conflicts}} + extern "C" void dr171b(); + // expected-error@-1 {{declaration of 'dr171b' with C language linkage conflicts with declaration in global scope}} + // expected-note@#dr171b-int {{declared in global scope here}} } namespace dr172 { // dr172: yes @@ -810,12 +977,14 @@ namespace dr172 { // dr172: yes int check6a[sizeof(d) == sizeof(unsigned long) ? 1 : -1]; int check6b[-d > 0 ? 1 : -1]; - enum { e = (unsigned long long)-1 / 2 }; // expected-error 0-1{{extension}} - int check7a[sizeof(e) == sizeof(long) ? 1 : -1]; // expected-error 0-1{{extension}} + enum { e = (unsigned long long)-1 / 2 }; + // cxx98-error@-1 {{'long long' is a C++11 extension}} + int check7a[sizeof(e) == sizeof(long) ? 1 : -1]; int check7b[-e < 0 ? 1 : -1]; - enum { f = (unsigned long long)-1 / 2 + 1 }; // expected-error 0-1{{extension}} - int check8a[sizeof(f) == sizeof(unsigned long) ? 1 : -1]; // expected-error 0-1{{extension}} + enum { f = (unsigned long long)-1 / 2 + 1 }; + // cxx98-error@-1 {{'long long' is a C++11 extension}} + int check8a[sizeof(f) == sizeof(unsigned long) ? 1 : -1]; int check8b[-f > 0 ? 1 : -1]; } @@ -827,25 +996,30 @@ namespace dr173 { // dr173: yes // dr174: sup 1012 -namespace dr175 { // dr175: yes - struct A {}; // expected-note {{here}} - struct B : private A {}; // expected-note {{constrained by private inheritance}} +namespace dr175 { // dr175: 2.8 + struct A {}; // #dr175-A + struct B : private A {}; // #dr175-B struct C : B { - A a; // expected-error {{private}} + A a; + // expected-error@-1 {{'A' is a private member of 'dr175::A'}} + // expected-note@#dr175-B {{constrained by private inheritance here}} + // expected-note@#dr175-A {{member is declared here}} dr175::A b; }; } -namespace dr176 { // dr176: yes +namespace dr176 { // dr176: 3.1 template class Y; template<> class Y { void f() { - typedef Y A; // expected-note {{here}} - typedef Y A; // expected-error {{different types ('Y' vs 'Y')}} + typedef Y A; // #dr176-A-first + typedef Y A; + // expected-error@-1 {{typedef redefinition with different types ('Y' vs 'Y')}} + // expected-note@#dr176-A-first {{previous definition is here}} } }; - template struct Base {}; // expected-note 2{{found}} + template struct Base {}; // #dr176-Base template struct Derived : public Base { void f() { typedef typename Derived::template Base A; @@ -855,35 +1029,44 @@ namespace dr176 { // dr176: yes template struct Derived; template struct Derived2 : Base, Base { - typename Derived2::Base b; // expected-error {{found in multiple base classes}} + typename Derived2::Base b; + // expected-error@-1 {{member 'Base' found in multiple base classes of different types}} + // expected-note@#dr176-Base {{member type 'dr176::Base' found by ambiguous name lookup}} + // expected-note@#dr176-Base {{member type 'dr176::Base' found by ambiguous name lookup}} typename Derived2::Base d; }; - template class X { // expected-note {{here}} + template class X { // #dr176-X X *p1; X *p2; X *p3; - dr176::X *p4; // expected-error {{requires template arguments}} + dr176::X *p4; // #dr176-p4 + // cxx98-14-error@#dr176-p4 {{use of class template 'dr176::X' requires template arguments}} + // cxx98-14-note@#dr176-X {{template is declared here}} + // since-cxx17-error@#dr176-p4 {{use of class template 'X' requires template arguments; argument deduction not allowed in non-static class member}} + // since-cxx17-note@#dr176-X {{template is declared here}} }; } namespace dr177 { // dr177: yes struct B {}; struct A { - A(A &); // expected-note 0-1{{not viable: expects an lvalue}} - A(const B &); // expected-note 0-1{{not viable: no known conversion from 'A' to}} + A(A &); // #dr177-A-copy-ctor + A(const B &); // #dr177-A-ctor-from-B }; B b; A a = b; -#if __cplusplus <= 201402L - // expected-error@-2 {{no viable constructor copying variable}} -#endif + // cxx98-14-error@-1 {{no viable constructor copying variable of type 'A'}} + // cxx98-14-note@#dr177-A-copy-ctor {{candidate constructor not viable: expects an lvalue for 1st argument}} + // cxx98-14-note@#dr177-A-ctor-from-B {{candidate constructor not viable: no known conversion from 'A' to 'const B &' for 1st argument}} - struct C { C(C&); }; // expected-note {{not viable: expects an lvalue for 1st argument}} + struct C { C(C&); }; // #dr177-C-copy-ctor struct D : C {}; struct E { operator D(); }; E e; - C c = e; // expected-error {{no viable constructor copying variable of type 'D'}} + C c = e; + // expected-error@-1 {{no viable constructor copying variable of type 'D'}} + // expected-note@#dr177-C-copy-ctor {{candidate constructor not viable: expects an lvalue for 1st argument}} } namespace dr178 { // dr178: yes @@ -901,10 +1084,11 @@ namespace dr178 { // dr178: yes namespace dr179 { // dr179: yes void f(); - int n = &f - &f; // expected-error {{arithmetic on pointers to the function type 'void ()'}} + int n = &f - &f; + // expected-error@-1 {{arithmetic on pointers to the function type 'void ()'}} } -namespace dr180 { // dr180: yes +namespace dr180 { // dr180: 2.8 template struct X : T, T::some_base { X() : T::some_type_that_might_be_T(), T::some_base() {} friend class T::some_class; @@ -916,8 +1100,10 @@ namespace dr180 { // dr180: yes namespace dr181 { // dr181: yes namespace X { - template