diff --git a/.ci/generate-buildkite-pipeline-premerge b/.ci/generate-buildkite-pipeline-premerge index 78a9cb77ff7d90..e1c66ac18e7ac0 100755 --- a/.ci/generate-buildkite-pipeline-premerge +++ b/.ci/generate-buildkite-pipeline-premerge @@ -68,7 +68,7 @@ function compute-projects-to-test() { done ;; clang) - for p in clang-tools-extra compiler-rt flang lldb cross-project-tests; do + for p in clang-tools-extra compiler-rt lldb cross-project-tests; do echo $p done ;; diff --git a/.github/workflows/issue-release-workflow.yml b/.github/workflows/issue-release-workflow.yml index eb88ec6e43c574..5027d4f3ea6f10 100644 --- a/.github/workflows/issue-release-workflow.yml +++ b/.github/workflows/issue-release-workflow.yml @@ -53,7 +53,7 @@ jobs: - name: Setup Environment run: | - pip install -r ./llvm/utils/git/requirements.txt + pip install --require-hashes -r ./llvm/utils/git/requirements.txt ./llvm/utils/git/github-automation.py --token ${{ github.token }} setup-llvmbot-git - name: Backport Commits diff --git a/.github/workflows/issue-subscriber.yml b/.github/workflows/issue-subscriber.yml index ef6cd0674e8085..ef4fdf44181938 100644 --- a/.github/workflows/issue-subscriber.yml +++ b/.github/workflows/issue-subscriber.yml @@ -22,7 +22,7 @@ jobs: - name: Setup Automation Script working-directory: ./llvm/utils/git/ run: | - pip install -r requirements.txt + pip install --require-hashes -r requirements.txt - name: Update watchers working-directory: ./llvm/utils/git/ diff --git a/.github/workflows/llvm-bugs.yml b/.github/workflows/llvm-bugs.yml index f592dd6ccd9033..c392078fa45251 100644 --- a/.github/workflows/llvm-bugs.yml +++ b/.github/workflows/llvm-bugs.yml @@ -14,7 +14,7 @@ jobs: runs-on: ubuntu-latest if: github.repository == 'llvm/llvm-project' steps: - - uses: actions/setup-node@v3 + - uses: actions/setup-node@v4 with: node-version: 18 check-latest: true diff --git a/.github/workflows/merged-prs.yml b/.github/workflows/merged-prs.yml index 37fc6c67f000ba..e29afd4097f9fb 100644 --- a/.github/workflows/merged-prs.yml +++ b/.github/workflows/merged-prs.yml @@ -29,7 +29,7 @@ jobs: - name: Setup Automation Script working-directory: ./llvm/utils/git/ run: | - pip install -r requirements.txt + pip install --require-hashes -r requirements.txt - name: Add Buildbot information comment working-directory: ./llvm/utils/git/ diff --git a/.github/workflows/new-prs.yml b/.github/workflows/new-prs.yml index a60f82ce35d1f3..88175d6f8d64d4 100644 --- a/.github/workflows/new-prs.yml +++ b/.github/workflows/new-prs.yml @@ -43,7 +43,7 @@ jobs: - name: Setup Automation Script working-directory: ./llvm/utils/git/ run: | - pip install -r requirements.txt + pip install --require-hashes -r requirements.txt - name: Greet Author working-directory: ./llvm/utils/git/ diff --git a/.github/workflows/pr-request-release-note.yml b/.github/workflows/pr-request-release-note.yml index 0fcb95f1fe2949..5e48ce7aee2e2b 100644 --- a/.github/workflows/pr-request-release-note.yml +++ b/.github/workflows/pr-request-release-note.yml @@ -29,7 +29,7 @@ jobs: - name: Install Dependencies run: | - pip install -r llvm/utils/git/requirements.txt + pip install --require-hashes -r llvm/utils/git/requirements.txt - name: Request Release Note env: diff --git a/.github/workflows/pr-subscriber.yml b/.github/workflows/pr-subscriber.yml index 3952493bb698fe..272d3e2f9ef8a3 100644 --- a/.github/workflows/pr-subscriber.yml +++ b/.github/workflows/pr-subscriber.yml @@ -22,7 +22,7 @@ jobs: - name: Setup Automation Script working-directory: ./llvm/utils/git/ run: | - pip install -r requirements.txt + pip install --require-hashes -r requirements.txt - name: Update watchers working-directory: ./llvm/utils/git/ diff --git a/.github/workflows/release-binaries.yml b/.github/workflows/release-binaries.yml index 02082a84d8c107..fc497a7de94f7a 100644 --- a/.github/workflows/release-binaries.yml +++ b/.github/workflows/release-binaries.yml @@ -47,7 +47,7 @@ jobs: - name: Install Dependencies run: | - pip install -r ./llvm/utils/git/requirements.txt + pip install --require-hashes -r ./llvm/utils/git/requirements.txt - name: Check Permissions env: diff --git a/.github/workflows/version-check.yml b/.github/workflows/version-check.yml index c6d779080bbe73..4ce6119a407f52 100644 --- a/.github/workflows/version-check.yml +++ b/.github/workflows/version-check.yml @@ -23,7 +23,7 @@ jobs: - name: Install dependencies run: | - pip install -r ./llvm/utils/git/requirements.txt + pip install --require-hashes -r ./llvm/utils/git/requirements.txt - name: Version Check run: | diff --git a/bolt/include/bolt/Core/BinaryContext.h b/bolt/include/bolt/Core/BinaryContext.h index 75765819ac464e..edd0f7d2365a4b 100644 --- a/bolt/include/bolt/Core/BinaryContext.h +++ b/bolt/include/bolt/Core/BinaryContext.h @@ -677,6 +677,9 @@ class BinaryContext { /// have an origin file name available. bool HasSymbolsWithFileName{false}; + /// Does the binary have BAT section. + bool HasBATSection{false}; + /// Sum of execution count of all functions uint64_t SumExecutionCount{0}; diff --git a/bolt/include/bolt/Core/MCPlusBuilder.h b/bolt/include/bolt/Core/MCPlusBuilder.h index f7614cf9ac9777..f7cf538bd0e867 100644 --- a/bolt/include/bolt/Core/MCPlusBuilder.h +++ b/bolt/include/bolt/Core/MCPlusBuilder.h @@ -438,8 +438,8 @@ class MCPlusBuilder { return false; } - /// Check whether we support inverting this branch - virtual bool isUnsupportedBranch(const MCInst &Inst) const { return false; } + /// Check whether this conditional branch can be reversed + virtual bool isReversibleBranch(const MCInst &Inst) const { return true; } /// Return true of the instruction is of pseudo kind. virtual bool isPseudo(const MCInst &Inst) const { diff --git a/bolt/include/bolt/Passes/BinaryPasses.h b/bolt/include/bolt/Passes/BinaryPasses.h index 5d7692559eda88..a07c9130041fdb 100644 --- a/bolt/include/bolt/Passes/BinaryPasses.h +++ b/bolt/include/bolt/Passes/BinaryPasses.h @@ -16,6 +16,7 @@ #include "bolt/Core/BinaryContext.h" #include "bolt/Core/BinaryFunction.h" #include "bolt/Core/DynoStats.h" +#include "bolt/Profile/BoltAddressTranslation.h" #include "llvm/Support/CommandLine.h" #include #include @@ -399,8 +400,11 @@ class PrintProfileStats : public BinaryFunctionPass { /// Prints a list of the top 100 functions sorted by a set of /// dyno stats categories. class PrintProgramStats : public BinaryFunctionPass { + BoltAddressTranslation *BAT = nullptr; + public: - explicit PrintProgramStats() : BinaryFunctionPass(false) {} + explicit PrintProgramStats(BoltAddressTranslation *BAT = nullptr) + : BinaryFunctionPass(false), BAT(BAT) {} const char *getName() const override { return "print-stats"; } bool shouldPrint(const BinaryFunction &) const override { return false; } diff --git a/bolt/include/bolt/Passes/StokeInfo.h b/bolt/include/bolt/Passes/StokeInfo.h index 76417e6a2c3baa..a18c2a05d0153e 100644 --- a/bolt/include/bolt/Passes/StokeInfo.h +++ b/bolt/include/bolt/Passes/StokeInfo.h @@ -87,10 +87,10 @@ struct StokeFuncInfo { << "," << NumBlocks << "," << IsLoopFree << "," << NumLoops << "," << MaxLoopDepth << "," << HotSize << "," << TotalSize << "," << Score << "," << HasCall << ",\"{ "; - for (std::string S : DefIn) + for (const std::string &S : DefIn) Outfile << "%" << S << " "; Outfile << "}\",\"{ "; - for (std::string S : LiveOut) + for (const std::string &S : LiveOut) Outfile << "%" << S << " "; Outfile << "}\"," << HeapOut << "," << StackOut << "," << HasRipAddr << "," << Omitted << "\n"; diff --git a/bolt/include/bolt/Profile/DataAggregator.h b/bolt/include/bolt/Profile/DataAggregator.h index f2fa59bcaa1a3a..6453b3070ceb8d 100644 --- a/bolt/include/bolt/Profile/DataAggregator.h +++ b/bolt/include/bolt/Profile/DataAggregator.h @@ -15,6 +15,7 @@ #define BOLT_PROFILE_DATA_AGGREGATOR_H #include "bolt/Profile/DataReader.h" +#include "bolt/Profile/YAMLProfileWriter.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/Error.h" #include "llvm/Support/Program.h" @@ -122,14 +123,14 @@ class DataAggregator : public DataReader { uint64_t ExternCount{0}; }; - struct BranchInfo { + struct TakenBranchInfo { uint64_t TakenCount{0}; uint64_t MispredCount{0}; }; /// Intermediate storage for profile data. We save the results of parsing /// and use them later for processing and assigning profile. - std::unordered_map BranchLBRs; + std::unordered_map BranchLBRs; std::unordered_map FallthroughLBRs; std::vector AggregatedLBRs; std::unordered_map BasicSamples; @@ -248,7 +249,7 @@ class DataAggregator : public DataReader { BinaryFunction *getBATParentFunction(const BinaryFunction &Func) const; /// Retrieve the location name to be used for samples recorded in \p Func. - StringRef getLocationName(const BinaryFunction &Func) const; + static StringRef getLocationName(const BinaryFunction &Func, bool BAT); /// Semantic actions - parser hooks to interpret parsed perf samples /// Register a sample (non-LBR mode), i.e. a new hit at \p Address @@ -490,6 +491,8 @@ class DataAggregator : public DataReader { /// Parse the output generated by "perf buildid-list" to extract build-ids /// and return a file name matching a given \p FileBuildID. std::optional getFileNameForBuildID(StringRef FileBuildID); + + friend class YAMLProfileWriter; }; } // namespace bolt } // namespace llvm diff --git a/bolt/lib/Core/BinaryContext.cpp b/bolt/lib/Core/BinaryContext.cpp index ad2eb18caf109b..64d160adeee862 100644 --- a/bolt/lib/Core/BinaryContext.cpp +++ b/bolt/lib/Core/BinaryContext.cpp @@ -1322,7 +1322,9 @@ void BinaryContext::processInterproceduralReferences() { InterproceduralReferences) { BinaryFunction &Function = *It.first; uint64_t Address = It.second; - if (!Address || Function.isIgnored()) + // Process interprocedural references from ignored functions in BAT mode + // (non-simple in non-relocation mode) to properly register entry points + if (!Address || (Function.isIgnored() && !HasBATSection)) continue; BinaryFunction *TargetFunction = diff --git a/bolt/lib/Core/BinaryFunction.cpp b/bolt/lib/Core/BinaryFunction.cpp index 4f44ba0d970c0a..1bb05f044fc8bb 100644 --- a/bolt/lib/Core/BinaryFunction.cpp +++ b/bolt/lib/Core/BinaryFunction.cpp @@ -1284,7 +1284,7 @@ Error BinaryFunction::disassemble() { const bool IsCondBranch = MIB->isConditionalBranch(Instruction); MCSymbol *TargetSymbol = nullptr; - if (BC.MIB->isUnsupportedBranch(Instruction)) { + if (!BC.MIB->isReversibleBranch(Instruction)) { setIgnored(); if (BinaryFunction *TargetFunc = BC.getBinaryFunctionContainingAddress(TargetAddress)) @@ -1666,7 +1666,8 @@ void BinaryFunction::postProcessEntryPoints() { // In non-relocation mode there's potentially an external undetectable // reference to the entry point and hence we cannot move this entry // point. Optimizing without moving could be difficult. - if (!BC.HasRelocations) + // In BAT mode, register any known entry points for CFG construction. + if (!BC.HasRelocations && !BC.HasBATSection) setSimple(false); const uint32_t Offset = KV.first; @@ -3381,7 +3382,7 @@ void BinaryFunction::fixBranches() { // Reverse branch condition and swap successors. auto swapSuccessors = [&]() { - if (MIB->isUnsupportedBranch(*CondBranch)) { + if (!MIB->isReversibleBranch(*CondBranch)) { if (opts::Verbosity) { BC.outs() << "BOLT-INFO: unable to swap successors in " << *this << '\n'; diff --git a/bolt/lib/Passes/BinaryPasses.cpp b/bolt/lib/Passes/BinaryPasses.cpp index 867f977cebca72..11e22dea71fb29 100644 --- a/bolt/lib/Passes/BinaryPasses.cpp +++ b/bolt/lib/Passes/BinaryPasses.cpp @@ -674,7 +674,8 @@ static uint64_t fixDoubleJumps(BinaryFunction &Function, bool MarkInvalid) { MCPlusBuilder *MIB = Function.getBinaryContext().MIB.get(); for (BinaryBasicBlock &BB : Function) { auto checkAndPatch = [&](BinaryBasicBlock *Pred, BinaryBasicBlock *Succ, - const MCSymbol *SuccSym) { + const MCSymbol *SuccSym, + std::optional Offset) { // Ignore infinite loop jumps or fallthrough tail jumps. if (Pred == Succ || Succ == &BB) return false; @@ -715,9 +716,11 @@ static uint64_t fixDoubleJumps(BinaryFunction &Function, bool MarkInvalid) { Pred->removeSuccessor(&BB); Pred->eraseInstruction(Pred->findInstruction(Branch)); Pred->addTailCallInstruction(SuccSym); - MCInst *TailCall = Pred->getLastNonPseudoInstr(); - assert(TailCall); - MIB->setOffset(*TailCall, BB.getOffset()); + if (Offset) { + MCInst *TailCall = Pred->getLastNonPseudoInstr(); + assert(TailCall); + MIB->setOffset(*TailCall, *Offset); + } } else { return false; } @@ -760,7 +763,8 @@ static uint64_t fixDoubleJumps(BinaryFunction &Function, bool MarkInvalid) { if (Pred->getSuccessor() == &BB || (Pred->getConditionalSuccessor(true) == &BB && !IsTailCall) || Pred->getConditionalSuccessor(false) == &BB) - if (checkAndPatch(Pred, Succ, SuccSym) && MarkInvalid) + if (checkAndPatch(Pred, Succ, SuccSym, MIB->getOffset(*Inst)) && + MarkInvalid) BB.markValid(BB.pred_size() != 0 || BB.isLandingPad() || BB.isEntryPoint()); } @@ -1386,9 +1390,19 @@ Error PrintProgramStats::runOnFunctions(BinaryContext &BC) { if (Function.isPLTFunction()) continue; + // Adjustment for BAT mode: the profile for BOLT split fragments is combined + // so only count the hot fragment. + const uint64_t Address = Function.getAddress(); + bool IsHotParentOfBOLTSplitFunction = !Function.getFragments().empty() && + BAT && BAT->isBATFunction(Address) && + !BAT->fetchParentAddress(Address); + ++NumRegularFunctions; - if (!Function.isSimple()) { + // In BOLTed binaries split functions are non-simple (due to non-relocation + // mode), but the original function is known to be simple and we have a + // valid profile for it. + if (!Function.isSimple() && !IsHotParentOfBOLTSplitFunction) { if (Function.hasProfile()) ++NumNonSimpleProfiledFunctions; continue; diff --git a/bolt/lib/Passes/Instrumentation.cpp b/bolt/lib/Passes/Instrumentation.cpp index 68acff7e6a867c..14f506f9ca9689 100644 --- a/bolt/lib/Passes/Instrumentation.cpp +++ b/bolt/lib/Passes/Instrumentation.cpp @@ -480,7 +480,7 @@ void Instrumentation::instrumentFunction(BinaryFunction &Function, else if (BC.MIB->isUnconditionalBranch(Inst)) HasUnconditionalBranch = true; else if ((!BC.MIB->isCall(Inst) && !BC.MIB->isConditionalBranch(Inst)) || - BC.MIB->isUnsupportedBranch(Inst)) + !BC.MIB->isReversibleBranch(Inst)) continue; const uint32_t FromOffset = *BC.MIB->getOffset(Inst); diff --git a/bolt/lib/Profile/DataAggregator.cpp b/bolt/lib/Profile/DataAggregator.cpp index 167899ccba1251..c0fd69b98c82d6 100644 --- a/bolt/lib/Profile/DataAggregator.cpp +++ b/bolt/lib/Profile/DataAggregator.cpp @@ -613,7 +613,8 @@ Error DataAggregator::readProfile(BinaryContext &BC) { if (std::error_code EC = writeBATYAML(BC, opts::SaveProfile)) report_error("cannot create output data file", EC); } - BC.logBOLTErrorsAndQuitOnFatal(PrintProgramStats().runOnFunctions(BC)); + PrintProgramStats PPS(BAT); + BC.logBOLTErrorsAndQuitOnFatal(PPS.runOnFunctions(BC)); } return Error::success(); @@ -673,7 +674,8 @@ DataAggregator::getBATParentFunction(const BinaryFunction &Func) const { return nullptr; } -StringRef DataAggregator::getLocationName(const BinaryFunction &Func) const { +StringRef DataAggregator::getLocationName(const BinaryFunction &Func, + bool BAT) { if (!BAT) return Func.getOneName(); @@ -702,7 +704,7 @@ bool DataAggregator::doSample(BinaryFunction &OrigFunc, uint64_t Address, auto I = NamesToSamples.find(Func.getOneName()); if (I == NamesToSamples.end()) { bool Success; - StringRef LocName = getLocationName(Func); + StringRef LocName = getLocationName(Func, BAT); std::tie(I, Success) = NamesToSamples.insert( std::make_pair(Func.getOneName(), FuncSampleData(LocName, FuncSampleData::ContainerTy()))); @@ -722,7 +724,7 @@ bool DataAggregator::doIntraBranch(BinaryFunction &Func, uint64_t From, FuncBranchData *AggrData = getBranchData(Func); if (!AggrData) { AggrData = &NamesToBranches[Func.getOneName()]; - AggrData->Name = getLocationName(Func); + AggrData->Name = getLocationName(Func, BAT); setBranchData(Func, AggrData); } @@ -741,7 +743,7 @@ bool DataAggregator::doInterBranch(BinaryFunction *FromFunc, StringRef SrcFunc; StringRef DstFunc; if (FromFunc) { - SrcFunc = getLocationName(*FromFunc); + SrcFunc = getLocationName(*FromFunc, BAT); FromAggrData = getBranchData(*FromFunc); if (!FromAggrData) { FromAggrData = &NamesToBranches[FromFunc->getOneName()]; @@ -752,7 +754,7 @@ bool DataAggregator::doInterBranch(BinaryFunction *FromFunc, recordExit(*FromFunc, From, Mispreds, Count); } if (ToFunc) { - DstFunc = getLocationName(*ToFunc); + DstFunc = getLocationName(*ToFunc, BAT); ToAggrData = getBranchData(*ToFunc); if (!ToAggrData) { ToAggrData = &NamesToBranches[ToFunc->getOneName()]; @@ -1227,7 +1229,7 @@ ErrorOr DataAggregator::parseLocationOrOffset() { if (Sep == StringRef::npos) return parseOffset(); StringRef LookAhead = ParsingBuf.substr(0, Sep); - if (LookAhead.find_first_of(":") == StringRef::npos) + if (!LookAhead.contains(':')) return parseOffset(); ErrorOr BuildID = parseString(':'); @@ -1464,7 +1466,7 @@ uint64_t DataAggregator::parseLBRSample(const PerfBranchSample &Sample, uint64_t To = getBinaryFunctionContainingAddress(LBR.To) ? LBR.To : 0; if (!From && !To) continue; - BranchInfo &Info = BranchLBRs[Trace(From, To)]; + TakenBranchInfo &Info = BranchLBRs[Trace(From, To)]; ++Info.TakenCount; Info.MispredCount += LBR.Mispred; } @@ -1609,7 +1611,7 @@ void DataAggregator::processBranchEvents() { for (const auto &AggrLBR : BranchLBRs) { const Trace &Loc = AggrLBR.first; - const BranchInfo &Info = AggrLBR.second; + const TakenBranchInfo &Info = AggrLBR.second; doBranch(Loc.From, Loc.To, Info.TakenCount, Info.MispredCount); } } @@ -2253,13 +2255,13 @@ DataAggregator::writeAggregatedFile(StringRef OutputFilename) const { } else { for (const auto &KV : NamesToBranches) { const FuncBranchData &FBD = KV.second; - for (const llvm::bolt::BranchInfo &BI : FBD.Data) { + for (const BranchInfo &BI : FBD.Data) { writeLocation(BI.From); writeLocation(BI.To); OutFile << BI.Mispreds << " " << BI.Branches << "\n"; ++BranchValues; } - for (const llvm::bolt::BranchInfo &BI : FBD.EntryData) { + for (const BranchInfo &BI : FBD.EntryData) { // Do not output if source is a known symbol, since this was already // accounted for in the source function if (BI.From.IsSymbol) @@ -2340,7 +2342,7 @@ std::error_code DataAggregator::writeBATYAML(BinaryContext &BC, continue; BinaryFunction *BF = BC.getBinaryFunctionAtAddress(FuncAddress); assert(BF); - YamlBF.Name = getLocationName(*BF); + YamlBF.Name = getLocationName(*BF, BAT); YamlBF.Id = BF->getFunctionNumber(); YamlBF.Hash = BAT->getBFHash(FuncAddress); YamlBF.ExecCount = BF->getKnownExecutionCount(); @@ -2366,7 +2368,7 @@ std::error_code DataAggregator::writeBATYAML(BinaryContext &BC, return std::pair(BlockIt->first, BlockIt->second.getBBIndex()); }; - for (const llvm::bolt::BranchInfo &BI : Branches.Data) { + for (const BranchInfo &BI : Branches.Data) { using namespace yaml::bolt; const auto &[BlockOffset, BlockIndex] = getBlock(BI.From.Offset); BinaryBasicBlockProfile &YamlBB = YamlBF.Blocks[BlockIndex]; @@ -2388,7 +2390,7 @@ std::error_code DataAggregator::writeBATYAML(BinaryContext &BC, } } // Set entry counts, similar to DataReader::readProfile. - for (const llvm::bolt::BranchInfo &BI : Branches.EntryData) { + for (const BranchInfo &BI : Branches.EntryData) { if (!BlockMap.isInputBlock(BI.To.Offset)) { if (opts::Verbosity >= 1) errs() << "BOLT-WARNING: Unexpected EntryData in " << FuncName diff --git a/bolt/lib/Profile/YAMLProfileReader.cpp b/bolt/lib/Profile/YAMLProfileReader.cpp index 978a7cadfe798f..29d94067f459fc 100644 --- a/bolt/lib/Profile/YAMLProfileReader.cpp +++ b/bolt/lib/Profile/YAMLProfileReader.cpp @@ -99,6 +99,9 @@ bool YAMLProfileReader::parseFunctionProfile( FuncRawBranchCount += YamlSI.Count; BF.setRawBranchCount(FuncRawBranchCount); + if (BF.empty()) + return true; + if (!opts::IgnoreHash && YamlBF.Hash != BF.computeHash(IsDFSOrder, HashFunction)) { if (opts::Verbosity >= 1) diff --git a/bolt/lib/Profile/YAMLProfileWriter.cpp b/bolt/lib/Profile/YAMLProfileWriter.cpp index ef04ba0d21ad75..cf6b61ddd60314 100644 --- a/bolt/lib/Profile/YAMLProfileWriter.cpp +++ b/bolt/lib/Profile/YAMLProfileWriter.cpp @@ -10,6 +10,7 @@ #include "bolt/Core/BinaryBasicBlock.h" #include "bolt/Core/BinaryFunction.h" #include "bolt/Profile/BoltAddressTranslation.h" +#include "bolt/Profile/DataAggregator.h" #include "bolt/Profile/ProfileReaderBase.h" #include "bolt/Rewrite/RewriteInstance.h" #include "llvm/Support/CommandLine.h" @@ -39,6 +40,10 @@ const BinaryFunction *YAMLProfileWriter::setCSIDestination( BC.getFunctionForSymbol(Symbol, &EntryID)) { if (BAT && BAT->isBATFunction(Callee->getAddress())) std::tie(Callee, EntryID) = BAT->translateSymbol(BC, *Symbol, Offset); + else if (const BinaryBasicBlock *BB = + Callee->getBasicBlockContainingOffset(Offset)) + BC.getFunctionForSymbol(Callee->getSecondaryEntryPointSymbol(*BB), + &EntryID); CSI.DestId = Callee->getFunctionNumber(); CSI.EntryDiscriminator = EntryID; return Callee; @@ -59,7 +64,7 @@ YAMLProfileWriter::convert(const BinaryFunction &BF, bool UseDFS, BF.computeHash(UseDFS); BF.computeBlockHashes(); - YamlBF.Name = BF.getPrintName(); + YamlBF.Name = DataAggregator::getLocationName(BF, BAT); YamlBF.Id = BF.getFunctionNumber(); YamlBF.Hash = BF.getHash(); YamlBF.NumBasicBlocks = BF.size(); diff --git a/bolt/lib/Rewrite/RewriteInstance.cpp b/bolt/lib/Rewrite/RewriteInstance.cpp index 85b39176754b64..9cc4c8c8c4fafa 100644 --- a/bolt/lib/Rewrite/RewriteInstance.cpp +++ b/bolt/lib/Rewrite/RewriteInstance.cpp @@ -1988,6 +1988,7 @@ Error RewriteInstance::readSpecialSections() { if (ErrorOr BATSec = BC->getUniqueSectionByName(BoltAddressTranslation::SECTION_NAME)) { + BC->HasBATSection = true; // Do not read BAT when plotting a heatmap if (!opts::HeatmapMode) { if (std::error_code EC = BAT->parse(BC->outs(), BATSec->getContents())) { @@ -4808,6 +4809,40 @@ void RewriteInstance::updateELFSymbolTable( // Create a new symbol based on the existing symbol. ELFSymTy NewSymbol = Symbol; + // Handle special symbols based on their name. + Expected SymbolName = Symbol.getName(StringSection); + assert(SymbolName && "cannot get symbol name"); + + auto updateSymbolValue = [&](const StringRef Name, + std::optional Value = std::nullopt) { + NewSymbol.st_value = Value ? *Value : getNewValueForSymbol(Name); + NewSymbol.st_shndx = ELF::SHN_ABS; + BC->outs() << "BOLT-INFO: setting " << Name << " to 0x" + << Twine::utohexstr(NewSymbol.st_value) << '\n'; + }; + + if (*SymbolName == "__hot_start" || *SymbolName == "__hot_end") { + if (opts::HotText) { + updateSymbolValue(*SymbolName); + ++NumHotTextSymsUpdated; + } + goto registerSymbol; + } + + if (*SymbolName == "__hot_data_start" || *SymbolName == "__hot_data_end") { + if (opts::HotData) { + updateSymbolValue(*SymbolName); + ++NumHotDataSymsUpdated; + } + goto registerSymbol; + } + + if (*SymbolName == "_end") { + if (NextAvailableAddress > Symbol.st_value) + updateSymbolValue(*SymbolName, NextAvailableAddress); + goto registerSymbol; + } + if (Function) { // If the symbol matched a function that was not emitted, update the // corresponding section index but otherwise leave it unchanged. @@ -4904,33 +4939,7 @@ void RewriteInstance::updateELFSymbolTable( } } - // Handle special symbols based on their name. - Expected SymbolName = Symbol.getName(StringSection); - assert(SymbolName && "cannot get symbol name"); - - auto updateSymbolValue = [&](const StringRef Name, - std::optional Value = std::nullopt) { - NewSymbol.st_value = Value ? *Value : getNewValueForSymbol(Name); - NewSymbol.st_shndx = ELF::SHN_ABS; - BC->outs() << "BOLT-INFO: setting " << Name << " to 0x" - << Twine::utohexstr(NewSymbol.st_value) << '\n'; - }; - - if (opts::HotText && - (*SymbolName == "__hot_start" || *SymbolName == "__hot_end")) { - updateSymbolValue(*SymbolName); - ++NumHotTextSymsUpdated; - } - - if (opts::HotData && (*SymbolName == "__hot_data_start" || - *SymbolName == "__hot_data_end")) { - updateSymbolValue(*SymbolName); - ++NumHotDataSymsUpdated; - } - - if (*SymbolName == "_end" && NextAvailableAddress > Symbol.st_value) - updateSymbolValue(*SymbolName, NextAvailableAddress); - + registerSymbol: if (IsDynSym) Write((&Symbol - cantFail(Obj.symbols(&SymTabSection)).begin()) * sizeof(ELFSymTy), diff --git a/bolt/lib/Target/X86/X86MCPlusBuilder.cpp b/bolt/lib/Target/X86/X86MCPlusBuilder.cpp index 8b1894953f3757..8fdacffcb147b6 100644 --- a/bolt/lib/Target/X86/X86MCPlusBuilder.cpp +++ b/bolt/lib/Target/X86/X86MCPlusBuilder.cpp @@ -328,19 +328,19 @@ class X86MCPlusBuilder : public MCPlusBuilder { return false; } - bool isUnsupportedBranch(const MCInst &Inst) const override { + bool isReversibleBranch(const MCInst &Inst) const override { if (isDynamicBranch(Inst)) - return true; + return false; switch (Inst.getOpcode()) { default: - return false; + return true; case X86::LOOP: case X86::LOOPE: case X86::LOOPNE: case X86::JECXZ: case X86::JRCXZ: - return true; + return false; } } @@ -1874,7 +1874,7 @@ class X86MCPlusBuilder : public MCPlusBuilder { } // Handle conditional branches and ignore indirect branches - if (!isUnsupportedBranch(*I) && getCondCode(*I) == X86::COND_INVALID) { + if (isReversibleBranch(*I) && getCondCode(*I) == X86::COND_INVALID) { // Indirect branch return false; } diff --git a/bolt/runtime/instr.cpp b/bolt/runtime/instr.cpp index 16e0bbd55f90b1..d1f8a216badcf2 100644 --- a/bolt/runtime/instr.cpp +++ b/bolt/runtime/instr.cpp @@ -1245,7 +1245,6 @@ void Graph::computeEdgeFrequencies(const uint64_t *Counters, continue; assert(SpanningTreeNodes[Cur].NumInEdges == 1, "must have 1 parent"); - const uint32_t Parent = SpanningTreeNodes[Cur].InEdges[0].Node; const uint32_t ParentEdge = SpanningTreeNodes[Cur].InEdges[0].ID; // Calculate parent edge freq. @@ -1464,9 +1463,8 @@ void visitCallFlowEntry(CallFlowHashTable::MapEntry &Entry, int FD, int openProfile() { // Build the profile name string by appending our PID char Buf[BufSize]; - char *Ptr = Buf; uint64_t PID = __getpid(); - Ptr = strCopy(Buf, __bolt_instr_filename, BufSize); + char *Ptr = strCopy(Buf, __bolt_instr_filename, BufSize); if (__bolt_instr_use_pid) { Ptr = strCopy(Ptr, ".", BufSize - (Ptr - Buf + 1)); Ptr = intToStr(Ptr, PID, 10); diff --git a/bolt/test/X86/bb-with-two-tail-calls.s b/bolt/test/X86/bb-with-two-tail-calls.s index bb2b0cd4cc23a5..b6703e352ff4bf 100644 --- a/bolt/test/X86/bb-with-two-tail-calls.s +++ b/bolt/test/X86/bb-with-two-tail-calls.s @@ -1,8 +1,6 @@ # This reproduces a bug with dynostats when trying to compute branch stats # at a block with two tails calls (one conditional and one unconditional). -# REQUIRES: system-linux - # RUN: llvm-mc -filetype=obj -triple x86_64-unknown-unknown \ # RUN: %s -o %t.o # RUN: link_fdata %s %t.o %t.fdata @@ -13,7 +11,7 @@ # CHECK-NOT: Assertion `BranchInfo.size() == 2 && "could only be called for blocks with 2 successors"' failed. # Two tail calls in the same basic block after SCTC: # CHECK: {{.*}}: ja {{.*}} # TAILCALL # Offset: 7 # CTCTakenCount: 4 -# CHECK-NEXT: {{.*}}: jmp {{.*}} # TAILCALL # Offset: 12 +# CHECK-NEXT: {{.*}}: jmp {{.*}} # TAILCALL # Offset: 13 .globl _start _start: @@ -23,7 +21,9 @@ a: ja b x: ret # FDATA: 1 _start #a# 1 _start #b# 2 4 b: jmp e -c: jmp f +c: + .nops 1 + jmp f .globl e e: diff --git a/bolt/test/X86/bolt-address-translation-yaml.test b/bolt/test/X86/bolt-address-translation-yaml.test index e21513b7dfe592..9f2c2ef3ab9872 100644 --- a/bolt/test/X86/bolt-address-translation-yaml.test +++ b/bolt/test/X86/bolt-address-translation-yaml.test @@ -31,7 +31,8 @@ RUN: perf2bolt %t.out --pa -p %p/Inputs/blarge_new_bat.preagg.txt -w %t.yaml -o RUN: 2>&1 | FileCheck --check-prefix READ-BAT-CHECK %s RUN: FileCheck --input-file %t.yaml --check-prefix YAML-BAT-CHECK %s # Check that YAML converted from fdata matches YAML created directly with BAT. -RUN: llvm-bolt %t.exe -data %t.fdata -w %t.yaml-fdata -o /dev/null +RUN: llvm-bolt %t.exe -data %t.fdata -w %t.yaml-fdata -o /dev/null \ +RUN: 2>&1 | FileCheck --check-prefix READ-BAT-FDATA-CHECK %s RUN: FileCheck --input-file %t.yaml-fdata --check-prefix YAML-BAT-CHECK %s # Test resulting YAML profile with the original binary (no-stale mode) @@ -45,6 +46,8 @@ WRITE-BAT-CHECK: BOLT-INFO: BAT section size (bytes): 384 READ-BAT-CHECK-NOT: BOLT-ERROR: unable to save profile in YAML format for input file processed by BOLT READ-BAT-CHECK: BOLT-INFO: Parsed 5 BAT entries READ-BAT-CHECK: PERF2BOLT: read 79 aggregated LBR entries +READ-BAT-CHECK: BOLT-INFO: 5 out of 21 functions in the binary (23.8%) have non-empty execution profile +READ-BAT-FDATA-CHECK: BOLT-INFO: 5 out of 16 functions in the binary (31.2%) have non-empty execution profile YAML-BAT-CHECK: functions: # Function not covered by BAT - has insns in basic block diff --git a/bolt/test/X86/ignored-interprocedural-reference.s b/bolt/test/X86/ignored-interprocedural-reference.s new file mode 100644 index 00000000000000..12e4fb92adcc0d --- /dev/null +++ b/bolt/test/X86/ignored-interprocedural-reference.s @@ -0,0 +1,49 @@ +# This reproduces a bug with not processing interprocedural references from +# ignored functions. + +# REQUIRES: system-linux + +# RUN: llvm-mc -filetype=obj -triple x86_64-unknown-unknown %s -o %t.o +# RUN: %clang %cflags %t.o -o %t.exe -nostdlib -Wl,-q +# RUN: llvm-bolt %t.exe -o %t.out --enable-bat -funcs=main +# RUN: link_fdata %s %t.out %t.preagg PREAGG +# RUN: perf2bolt %t.out -p %t.preagg --pa -o %t.fdata -w %t.yaml +# RUN: FileCheck %s --input-file=%t.fdata --check-prefix=CHECK-FDATA +# RUN: FileCheck %s --input-file=%t.yaml --check-prefix=CHECK-YAML + +# CHECK-FDATA: 1 main 0 1 foo a 1 1 +# CHECK-YAML: name: main +# CHECK-YAML: calls: {{.*}} disc: 1 + +# PREAGG: B #main# #foo_secondary# 1 1 +# main calls foo at valid instruction offset past nops that are to be stripped. + .globl main +main: + .cfi_startproc + call foo_secondary + ret + .cfi_endproc +.size main,.-main + +# Placeholder cold fragment to force main to be ignored in non-relocation mode. + .globl main.cold +main.cold: + .cfi_startproc + ud2 + .cfi_endproc +.size main.cold,.-main.cold + +# foo is set up to contain a valid instruction at called offset, and trapping +# instructions past that. + .globl foo +foo: + .cfi_startproc + .nops 10 + .globl foo_secondary +foo_secondary: + ret + .rept 20 + int3 + .endr + .cfi_endproc +.size foo,.-foo diff --git a/bolt/test/X86/register-fragments-bolt-symbols.s b/bolt/test/X86/register-fragments-bolt-symbols.s index 6478adf19372b2..90c402b2234d76 100644 --- a/bolt/test/X86/register-fragments-bolt-symbols.s +++ b/bolt/test/X86/register-fragments-bolt-symbols.s @@ -18,6 +18,11 @@ # RUN: FileCheck --input-file %t.bat.fdata --check-prefix=CHECK-FDATA %s # RUN: FileCheck --input-file %t.bat.yaml --check-prefix=CHECK-YAML %s +# RUN: link_fdata --no-redefine %s %t.bolt %t.preagg2 PREAGG2 +# PREAGG2: B X:0 #chain# 1 0 +# RUN: perf2bolt %t.bolt -p %t.preagg2 --pa -o %t.bat2.fdata -w %t.bat2.yaml +# RUN: FileCheck %s --input-file %t.bat2.yaml --check-prefix=CHECK-YAML2 + # CHECK-SYMS: l df *ABS* [[#]] chain.s # CHECK-SYMS: l F .bolt.org.text [[#]] chain # CHECK-SYMS: l F .text.cold [[#]] chain.cold.0 @@ -28,6 +33,9 @@ # CHECK-FDATA: 0 [unknown] 0 1 chain/chain.s/2 10 0 1 # CHECK-YAML: - name: 'chain/chain.s/2' +# CHECK-YAML2: - name: 'chain/chain.s/1' +## non-BAT function has non-zero insns: +# CHECK-YAML2: insns: 1 .file "chain.s" .text diff --git a/bolt/test/X86/yaml-non-simple.test b/bolt/test/X86/yaml-non-simple.test new file mode 100644 index 00000000000000..fef98f692a7103 --- /dev/null +++ b/bolt/test/X86/yaml-non-simple.test @@ -0,0 +1,71 @@ +## Check that YAML profile for non-simple function is not reported as stale. + +# RUN: split-file %s %t +# RUN: llvm-mc -filetype=obj -triple x86_64-unknown-unknown %t/main.s -o %t.o +# RUN: %clang %cflags %t.o -o %t.exe -nostdlib +# RUN: llvm-bolt %t.exe -o %t.out --data %t/yaml --profile-ignore-hash -v=1 \ +# RUN: --report-stale 2>&1 | FileCheck %s + +# CHECK: BOLT-INFO: could not disassemble function main. Will ignore. +# CHECK: BOLT-INFO: could not disassemble function main.cold. Will ignore. +# CHECK: BOLT-INFO: 0 out of 2 functions in the binary (0.0%) have non-empty execution profile +# CHECK: BOLT-INFO: 1 function with profile could not be optimized + +#--- main.s +.globl main +.type main, @function +main: + .cfi_startproc +.LBB00: + pushq %rbp + movq %rsp, %rbp + subq $16, %rsp + testq %rax, %rax + js .LBB03 +.LBB01: + jne .LBB04 +.LBB02: + nop +.LBB03: + xorl %eax, %eax + addq $16, %rsp + popq %rbp + retq +.LBB04: + xorl %eax, %eax + addq $16, %rsp + popq %rbp + retq + .cfi_endproc + .size main, .-main + +.globl main.cold +.type main.cold, @function +main.cold: + .cfi_startproc + nop + .cfi_endproc + .size main.cold, .-main.cold + +#--- yaml +--- +header: + profile-version: 1 + binary-name: 'yaml-non-simple.s.tmp.exe' + binary-build-id: '' + profile-flags: [ lbr ] + profile-origin: branch profile reader + profile-events: '' + dfs-order: false + hash-func: xxh3 +functions: + - name: main + fid: 0 + hash: 0x0000000000000000 + exec: 1 + nblocks: 5 + blocks: + - bid: 1 + insns: 1 + succ: [ { bid: 3, cnt: 1} ] +... diff --git a/bolt/test/link_fdata.py b/bolt/test/link_fdata.py index 0232dd3211e9bb..3837e394ccc87b 100755 --- a/bolt/test/link_fdata.py +++ b/bolt/test/link_fdata.py @@ -19,6 +19,7 @@ parser.add_argument("prefix", nargs="?", default="FDATA", help="Custom FDATA prefix") parser.add_argument("--nmtool", default="nm", help="Path to nm tool") parser.add_argument("--no-lbr", action="store_true") +parser.add_argument("--no-redefine", action="store_true") args = parser.parse_args() @@ -90,6 +91,8 @@ symbols = {} for symline in nm_output.splitlines(): symval, _, symname = symline.split(maxsplit=2) + if symname in symbols and args.no_redefine: + continue symbols[symname] = symval diff --git a/bolt/test/runtime/X86/hot-end-symbol.s b/bolt/test/runtime/X86/hot-end-symbol.s index e6d83d77167acd..6ae771cead7568 100755 --- a/bolt/test/runtime/X86/hot-end-symbol.s +++ b/bolt/test/runtime/X86/hot-end-symbol.s @@ -12,6 +12,7 @@ # RUN: %clang %cflags -no-pie %t.o -o %t.exe -Wl,-q # RUN: llvm-bolt %t.exe --relocs=1 --hot-text --reorder-functions=hfsort \ +# RUN: --split-functions --split-strategy=all \ # RUN: --data %t.fdata -o %t.out | FileCheck %s # RUN: %t.out 1 @@ -30,12 +31,12 @@ # CHECK-OUTPUT: __hot_start # CHECK-OUTPUT-NEXT: main # CHECK-OUTPUT-NEXT: __hot_end +# CHECK-OUTPUT-NOT: __hot_start.cold .text .globl main .type main, %function .globl __hot_start - .type __hot_start, %object .p2align 4 main: __hot_start: diff --git a/clang-tools-extra/clang-query/CMakeLists.txt b/clang-tools-extra/clang-query/CMakeLists.txt index 8a58d4224e049d..34f018c4a03f38 100644 --- a/clang-tools-extra/clang-query/CMakeLists.txt +++ b/clang-tools-extra/clang-query/CMakeLists.txt @@ -20,7 +20,6 @@ clang_target_link_libraries(clangQuery clangBasic clangDynamicASTMatchers clangFrontend - clangTooling clangSerialization ) diff --git a/clang-tools-extra/clang-query/Query.cpp b/clang-tools-extra/clang-query/Query.cpp index 9d5807a52fa8ed..93f4104d39db8c 100644 --- a/clang-tools-extra/clang-query/Query.cpp +++ b/clang-tools-extra/clang-query/Query.cpp @@ -13,7 +13,6 @@ #include "clang/ASTMatchers/ASTMatchFinder.h" #include "clang/Frontend/ASTUnit.h" #include "clang/Frontend/TextDiagnostic.h" -#include "clang/Tooling/NodeIntrospection.h" #include "llvm/Support/raw_ostream.h" #include @@ -69,8 +68,6 @@ bool HelpQuery::run(llvm::raw_ostream &OS, QuerySession &QS) const { "Diagnostic location for bound nodes.\n" " detailed-ast " "Detailed AST output for bound nodes.\n" - " srcloc " - "Source locations and ranges for bound nodes.\n" " dump " "Detailed AST output for bound nodes (alias of detailed-ast).\n\n"; return true; @@ -91,90 +88,6 @@ struct CollectBoundNodes : MatchFinder::MatchCallback { } }; -void dumpLocations(llvm::raw_ostream &OS, DynTypedNode Node, ASTContext &Ctx, - const DiagnosticsEngine &Diags, SourceManager const &SM) { - auto Locs = clang::tooling::NodeIntrospection::GetLocations(Node); - - auto PrintLocations = [](llvm::raw_ostream &OS, auto Iter, auto End) { - auto CommonEntry = Iter->first; - auto Scout = Iter; - SmallVector LocationStrings; - while (Scout->first == CommonEntry) { - LocationStrings.push_back( - tooling::LocationCallFormatterCpp::format(*Iter->second)); - if (Scout == End) - break; - ++Scout; - if (Scout->first == CommonEntry) - ++Iter; - } - llvm::sort(LocationStrings); - for (auto &LS : LocationStrings) { - OS << " * \"" << LS << "\"\n"; - } - return Iter; - }; - - TextDiagnostic TD(OS, Ctx.getLangOpts(), &Diags.getDiagnosticOptions()); - - for (auto Iter = Locs.LocationAccessors.begin(); - Iter != Locs.LocationAccessors.end(); ++Iter) { - if (!Iter->first.isValid()) - continue; - - TD.emitDiagnostic(FullSourceLoc(Iter->first, SM), DiagnosticsEngine::Note, - "source locations here", std::nullopt, std::nullopt); - - Iter = PrintLocations(OS, Iter, Locs.LocationAccessors.end()); - OS << '\n'; - } - - for (auto Iter = Locs.RangeAccessors.begin(); - Iter != Locs.RangeAccessors.end(); ++Iter) { - - if (!Iter->first.getBegin().isValid()) - continue; - - if (SM.getPresumedLineNumber(Iter->first.getBegin()) != - SM.getPresumedLineNumber(Iter->first.getEnd())) - continue; - - TD.emitDiagnostic( - FullSourceLoc(Iter->first.getBegin(), SM), DiagnosticsEngine::Note, - "source ranges here " + Iter->first.printToString(SM), - CharSourceRange::getTokenRange(Iter->first), std::nullopt); - - Iter = PrintLocations(OS, Iter, Locs.RangeAccessors.end()); - } - for (auto Iter = Locs.RangeAccessors.begin(); - Iter != Locs.RangeAccessors.end(); ++Iter) { - - if (!Iter->first.getBegin().isValid()) - continue; - - if (SM.getPresumedLineNumber(Iter->first.getBegin()) == - SM.getPresumedLineNumber(Iter->first.getEnd())) - continue; - - TD.emitDiagnostic( - FullSourceLoc(Iter->first.getBegin(), SM), DiagnosticsEngine::Note, - "source range " + Iter->first.printToString(SM) + " starting here...", - CharSourceRange::getTokenRange(Iter->first), std::nullopt); - - auto ColNum = SM.getPresumedColumnNumber(Iter->first.getEnd()); - auto LastLineLoc = Iter->first.getEnd().getLocWithOffset(-(ColNum - 1)); - - TD.emitDiagnostic(FullSourceLoc(Iter->first.getEnd(), SM), - DiagnosticsEngine::Note, "... ending here", - CharSourceRange::getTokenRange( - SourceRange(LastLineLoc, Iter->first.getEnd())), - std::nullopt); - - Iter = PrintLocations(OS, Iter, Locs.RangeAccessors.end()); - } - OS << "\n"; -} - } // namespace bool MatchQuery::run(llvm::raw_ostream &OS, QuerySession &QS) const { @@ -195,8 +108,7 @@ bool MatchQuery::run(llvm::raw_ostream &OS, QuerySession &QS) const { return false; } - auto &Ctx = AST->getASTContext(); - const auto &SM = Ctx.getSourceManager(); + ASTContext &Ctx = AST->getASTContext(); Ctx.getParentMapContext().setTraversalKind(QS.TK); Finder.matchAST(Ctx); @@ -244,19 +156,11 @@ bool MatchQuery::run(llvm::raw_ostream &OS, QuerySession &QS) const { } if (QS.DetailedASTOutput) { OS << "Binding for \"" << BI->first << "\":\n"; - const ASTContext &Ctx = AST->getASTContext(); ASTDumper Dumper(OS, Ctx, AST->getDiagnostics().getShowColors()); Dumper.SetTraversalKind(QS.TK); Dumper.Visit(BI->second); OS << "\n"; } - if (QS.SrcLocOutput) { - OS << "\n \"" << BI->first << "\" Source locations\n"; - OS << " " << std::string(19 + BI->first.size(), '-') << '\n'; - - dumpLocations(OS, BI->second, Ctx, AST->getDiagnostics(), SM); - OS << "\n"; - } } if (MI->getMap().empty()) diff --git a/clang-tools-extra/clang-query/Query.h b/clang-tools-extra/clang-query/Query.h index 7242479633c24f..af250fbe13ce3f 100644 --- a/clang-tools-extra/clang-query/Query.h +++ b/clang-tools-extra/clang-query/Query.h @@ -17,7 +17,7 @@ namespace clang { namespace query { -enum OutputKind { OK_Diag, OK_Print, OK_DetailedAST, OK_SrcLoc }; +enum OutputKind { OK_Diag, OK_Print, OK_DetailedAST }; enum QueryKind { QK_Invalid, @@ -149,7 +149,6 @@ struct SetExclusiveOutputQuery : Query { QS.DiagOutput = false; QS.DetailedASTOutput = false; QS.PrintOutput = false; - QS.SrcLocOutput = false; QS.*Var = true; return true; } diff --git a/clang-tools-extra/clang-query/QueryParser.cpp b/clang-tools-extra/clang-query/QueryParser.cpp index 85a442bdd7deda..1d0b7d9bc6fc84 100644 --- a/clang-tools-extra/clang-query/QueryParser.cpp +++ b/clang-tools-extra/clang-query/QueryParser.cpp @@ -11,7 +11,6 @@ #include "QuerySession.h" #include "clang/ASTMatchers/Dynamic/Parser.h" #include "clang/Basic/CharInfo.h" -#include "clang/Tooling/NodeIntrospection.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/StringSwitch.h" #include @@ -104,19 +103,16 @@ QueryRef QueryParser::parseSetBool(bool QuerySession::*Var) { template QueryRef QueryParser::parseSetOutputKind() { StringRef ValStr; - bool HasIntrospection = tooling::NodeIntrospection::hasIntrospectionSupport(); - unsigned OutKind = - LexOrCompleteWord(this, ValStr) - .Case("diag", OK_Diag) - .Case("print", OK_Print) - .Case("detailed-ast", OK_DetailedAST) - .Case("srcloc", OK_SrcLoc, /*IsCompletion=*/HasIntrospection) - .Case("dump", OK_DetailedAST) - .Default(~0u); + unsigned OutKind = LexOrCompleteWord(this, ValStr) + .Case("diag", OK_Diag) + .Case("print", OK_Print) + .Case("detailed-ast", OK_DetailedAST) + .Case("dump", OK_DetailedAST) + .Default(~0u); if (OutKind == ~0u) { - return new InvalidQuery("expected 'diag', 'print', 'detailed-ast'" + - StringRef(HasIntrospection ? ", 'srcloc'" : "") + - " or 'dump', got '" + ValStr + "'"); + return new InvalidQuery("expected 'diag', 'print', 'detailed-ast' or " + "'dump', got '" + + ValStr + "'"); } switch (OutKind) { @@ -126,10 +122,6 @@ template QueryRef QueryParser::parseSetOutputKind() { return new QueryType(&QuerySession::DiagOutput); case OK_Print: return new QueryType(&QuerySession::PrintOutput); - case OK_SrcLoc: - if (HasIntrospection) - return new QueryType(&QuerySession::SrcLocOutput); - return new InvalidQuery("'srcloc' output support is not available."); } llvm_unreachable("Invalid output kind"); diff --git a/clang-tools-extra/clang-query/QuerySession.h b/clang-tools-extra/clang-query/QuerySession.h index 9a08289a253449..31a4900e26190b 100644 --- a/clang-tools-extra/clang-query/QuerySession.h +++ b/clang-tools-extra/clang-query/QuerySession.h @@ -25,15 +25,14 @@ class QuerySession { public: QuerySession(llvm::ArrayRef> ASTs) : ASTs(ASTs), PrintOutput(false), DiagOutput(true), - DetailedASTOutput(false), SrcLocOutput(false), BindRoot(true), - PrintMatcher(false), Terminate(false), TK(TK_AsIs) {} + DetailedASTOutput(false), BindRoot(true), PrintMatcher(false), + Terminate(false), TK(TK_AsIs) {} llvm::ArrayRef> ASTs; bool PrintOutput; bool DiagOutput; bool DetailedASTOutput; - bool SrcLocOutput; bool BindRoot; bool PrintMatcher; diff --git a/clang-tools-extra/clang-tidy/bugprone/ForwardingReferenceOverloadCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/ForwardingReferenceOverloadCheck.cpp index 36687a8e761e85..c87b3ea7e26163 100644 --- a/clang-tools-extra/clang-tidy/bugprone/ForwardingReferenceOverloadCheck.cpp +++ b/clang-tools-extra/clang-tidy/bugprone/ForwardingReferenceOverloadCheck.cpp @@ -54,7 +54,9 @@ AST_MATCHER(QualType, isEnableIf) { AST_MATCHER_P(TemplateTypeParmDecl, hasDefaultArgument, clang::ast_matchers::internal::Matcher, TypeMatcher) { return Node.hasDefaultArgument() && - TypeMatcher.matches(Node.getDefaultArgument(), Finder, Builder); + TypeMatcher.matches( + Node.getDefaultArgument().getArgument().getAsType(), Finder, + Builder); } AST_MATCHER(TemplateDecl, hasAssociatedConstraints) { return Node.hasAssociatedConstraints(); diff --git a/clang-tools-extra/clang-tidy/bugprone/IncorrectEnableIfCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/IncorrectEnableIfCheck.cpp index 09aaf3e31d5dd7..75f1107904fcec 100644 --- a/clang-tools-extra/clang-tidy/bugprone/IncorrectEnableIfCheck.cpp +++ b/clang-tools-extra/clang-tidy/bugprone/IncorrectEnableIfCheck.cpp @@ -19,10 +19,11 @@ namespace { AST_MATCHER_P(TemplateTypeParmDecl, hasUnnamedDefaultArgument, ast_matchers::internal::Matcher, InnerMatcher) { if (Node.getIdentifier() != nullptr || !Node.hasDefaultArgument() || - Node.getDefaultArgumentInfo() == nullptr) + Node.getDefaultArgument().getArgument().isNull()) return false; - TypeLoc DefaultArgTypeLoc = Node.getDefaultArgumentInfo()->getTypeLoc(); + TypeLoc DefaultArgTypeLoc = + Node.getDefaultArgument().getTypeSourceInfo()->getTypeLoc(); return InnerMatcher.matches(DefaultArgTypeLoc, Finder, Builder); } diff --git a/clang-tools-extra/clang-tidy/bugprone/SizeofExpressionCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/SizeofExpressionCheck.cpp index a1cffbc6661992..5e64d23874ec17 100644 --- a/clang-tools-extra/clang-tidy/bugprone/SizeofExpressionCheck.cpp +++ b/clang-tools-extra/clang-tidy/bugprone/SizeofExpressionCheck.cpp @@ -144,16 +144,13 @@ void SizeofExpressionCheck::registerMatchers(MatchFinder *Finder) { unaryOperator(hasUnaryOperand(ArrayExpr), unless(hasOperatorName("*"))), binaryOperator(hasEitherOperand(ArrayExpr)), castExpr(hasSourceExpression(ArrayExpr)))); - const auto PointerToArrayExpr = ignoringParenImpCasts( - hasType(hasCanonicalType(pointerType(pointee(arrayType()))))); + const auto PointerToArrayExpr = + hasType(hasCanonicalType(pointerType(pointee(arrayType())))); - const auto StructAddrOfExpr = unaryOperator( - hasOperatorName("&"), hasUnaryOperand(ignoringParenImpCasts( - hasType(hasCanonicalType(recordType()))))); const auto PointerToStructType = hasUnqualifiedDesugaredType(pointerType(pointee(recordType()))); - const auto PointerToStructExpr = ignoringParenImpCasts(expr( - hasType(hasCanonicalType(PointerToStructType)), unless(cxxThisExpr()))); + const auto PointerToStructExpr = expr( + hasType(hasCanonicalType(PointerToStructType)), unless(cxxThisExpr())); const auto ArrayOfPointersExpr = ignoringParenImpCasts( hasType(hasCanonicalType(arrayType(hasElementType(pointerType())) @@ -166,18 +163,19 @@ void SizeofExpressionCheck::registerMatchers(MatchFinder *Finder) { ignoringParenImpCasts(arraySubscriptExpr( hasBase(ArrayOfSamePointersExpr), hasIndex(ZeroLiteral))); const auto ArrayLengthExprDenom = - expr(hasParent(expr(ignoringParenImpCasts(binaryOperator( - hasOperatorName("/"), hasLHS(ignoringParenImpCasts(sizeOfExpr( - has(ArrayOfPointersExpr)))))))), + expr(hasParent(binaryOperator(hasOperatorName("/"), + hasLHS(ignoringParenImpCasts(sizeOfExpr( + has(ArrayOfPointersExpr)))))), sizeOfExpr(has(ArrayOfSamePointersZeroSubscriptExpr))); - Finder->addMatcher(expr(anyOf(sizeOfExpr(has(ignoringParenImpCasts(anyOf( - ArrayCastExpr, PointerToArrayExpr, - StructAddrOfExpr, PointerToStructExpr)))), - sizeOfExpr(has(PointerToStructType))), - unless(ArrayLengthExprDenom)) - .bind("sizeof-pointer-to-aggregate"), - this); + Finder->addMatcher( + expr(sizeOfExpr(anyOf( + has(ignoringParenImpCasts(anyOf( + ArrayCastExpr, PointerToArrayExpr, PointerToStructExpr))), + has(PointerToStructType))), + unless(ArrayLengthExprDenom)) + .bind("sizeof-pointer-to-aggregate"), + this); } // Detect expression like: sizeof(expr) <= k for a suspicious constant 'k'. diff --git a/clang-tools-extra/clang-tidy/modernize/UseConstraintsCheck.cpp b/clang-tools-extra/clang-tidy/modernize/UseConstraintsCheck.cpp index 7a021fe14436a1..ea4d99586c7110 100644 --- a/clang-tools-extra/clang-tidy/modernize/UseConstraintsCheck.cpp +++ b/clang-tools-extra/clang-tidy/modernize/UseConstraintsCheck.cpp @@ -177,9 +177,11 @@ matchTrailingTemplateParam(const FunctionTemplateDecl *FunctionTemplate) { dyn_cast(LastParam)) { if (LastTemplateParam->hasDefaultArgument() && LastTemplateParam->getIdentifier() == nullptr) { - return {matchEnableIfSpecialization( - LastTemplateParam->getDefaultArgumentInfo()->getTypeLoc()), - LastTemplateParam}; + return { + matchEnableIfSpecialization(LastTemplateParam->getDefaultArgument() + .getTypeSourceInfo() + ->getTypeLoc()), + LastTemplateParam}; } } return {}; diff --git a/clang-tools-extra/clang-tidy/utils/RenamerClangTidyCheck.cpp b/clang-tools-extra/clang-tidy/utils/RenamerClangTidyCheck.cpp index e811f5519de2c1..88e4886cd0df93 100644 --- a/clang-tools-extra/clang-tidy/utils/RenamerClangTidyCheck.cpp +++ b/clang-tools-extra/clang-tidy/utils/RenamerClangTidyCheck.cpp @@ -123,6 +123,9 @@ static const NamedDecl *getFailureForNamedDecl(const NamedDecl *ND) { if (const auto *Method = dyn_cast(ND)) { if (const CXXMethodDecl *Overridden = getOverrideMethod(Method)) Canonical = cast(Overridden->getCanonicalDecl()); + else if (const FunctionTemplateDecl *Primary = Method->getPrimaryTemplate()) + if (const FunctionDecl *TemplatedDecl = Primary->getTemplatedDecl()) + Canonical = cast(TemplatedDecl->getCanonicalDecl()); if (Canonical != ND) return Canonical; diff --git a/clang-tools-extra/clangd/Hover.cpp b/clang-tools-extra/clangd/Hover.cpp index 06b949bc4a2b55..2ec0994e846e94 100644 --- a/clang-tools-extra/clangd/Hover.cpp +++ b/clang-tools-extra/clangd/Hover.cpp @@ -247,8 +247,12 @@ fetchTemplateParameters(const TemplateParameterList *Params, if (!TTP->getName().empty()) P.Name = TTP->getNameAsString(); - if (TTP->hasDefaultArgument()) - P.Default = TTP->getDefaultArgument().getAsString(PP); + if (TTP->hasDefaultArgument()) { + P.Default.emplace(); + llvm::raw_string_ostream Out(*P.Default); + TTP->getDefaultArgument().getArgument().print(PP, Out, + /*IncludeType=*/false); + } } else if (const auto *NTTP = dyn_cast(Param)) { P.Type = printType(NTTP, PP); diff --git a/clang-tools-extra/clangd/test/infinite-instantiation.test b/clang-tools-extra/clangd/test/infinite-instantiation.test index 85a1b656f49086..a9c787c77027c7 100644 --- a/clang-tools-extra/clangd/test/infinite-instantiation.test +++ b/clang-tools-extra/clangd/test/infinite-instantiation.test @@ -1,5 +1,6 @@ -// RUN: cp %s %t.cpp -// RUN: not clangd -check=%t.cpp 2>&1 | FileCheck -strict-whitespace %s +// RUN: rm -rf %t.dir && mkdir -p %t.dir +// RUN: echo '[{"directory": "%/t.dir", "command": "clang -ftemplate-depth=100 -x c++ %/s", "file": "%/s"}]' > %t.dir/compile_commands.json +// RUN: not clangd --compile-commands-dir=%t.dir -check=%s 2>&1 | FileCheck -strict-whitespace %s // CHECK: [template_recursion_depth_exceeded] diff --git a/clang-tools-extra/clangd/unittests/FindTargetTests.cpp b/clang-tools-extra/clangd/unittests/FindTargetTests.cpp index 0b2273f0a9a6e3..3220a5a6a98250 100644 --- a/clang-tools-extra/clangd/unittests/FindTargetTests.cpp +++ b/clang-tools-extra/clangd/unittests/FindTargetTests.cpp @@ -836,7 +836,9 @@ TEST_F(TargetDeclTest, OverloadExpr) { [[delete]] x; } )cpp"; - EXPECT_DECLS("CXXDeleteExpr", "void operator delete(void *) noexcept"); + // Sized deallocation is enabled by default in C++14 onwards. + EXPECT_DECLS("CXXDeleteExpr", + "void operator delete(void *, unsigned long) noexcept"); } TEST_F(TargetDeclTest, DependentExprs) { diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst index 71734617bf7aa8..741abc0a199a77 100644 --- a/clang-tools-extra/docs/ReleaseNotes.rst +++ b/clang-tools-extra/docs/ReleaseNotes.rst @@ -94,6 +94,8 @@ Improvements to clang-query from an external file, allowing the cost of reading the compilation database and building the AST to be imposed just once for faster prototyping. +- Removed support for ``enable output srcloc``. Fixes #GH82591 + Improvements to clang-rename ---------------------------- @@ -373,7 +375,8 @@ Changes in existing checks ` check in `GetConfigPerFile` mode by resolving symbolic links to header files. Fixed handling of Hungarian Prefix when configured to `LowerCase`. Added support for renaming designated - initializers. Added support for renaming macro arguments. + initializers. Added support for renaming macro arguments. Fixed renaming + conflicts arising from out-of-line member function template definitions. - Improved :doc:`readability-implicit-bool-conversion ` check to provide diff --git a/clang-tools-extra/modularize/ModularizeUtilities.cpp b/clang-tools-extra/modularize/ModularizeUtilities.cpp index 53e8a49d1a5489..b202b3aae8f8a3 100644 --- a/clang-tools-extra/modularize/ModularizeUtilities.cpp +++ b/clang-tools-extra/modularize/ModularizeUtilities.cpp @@ -435,11 +435,9 @@ static std::string replaceDotDot(StringRef Path) { llvm::sys::path::const_iterator B = llvm::sys::path::begin(Path), E = llvm::sys::path::end(Path); while (B != E) { - if (B->compare(".") == 0) { - } - else if (B->compare("..") == 0) + if (*B == "..") llvm::sys::path::remove_filename(Buffer); - else + else if (*B != ".") llvm::sys::path::append(Buffer, *B); ++B; } diff --git a/clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/pro-type-member-init-no-crash.cpp b/clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/pro-type-member-init-no-crash.cpp index 300fff6cb179bf..2e2964dda1dafd 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/pro-type-member-init-no-crash.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/pro-type-member-init-no-crash.cpp @@ -5,3 +5,11 @@ struct X { // CHECK-MESSAGES: :[[@LINE-1]]:5: error: field has incomplete type 'X' [clang-diagnostic-error] int a = 10; }; + +template class NoCrash { + // CHECK-MESSAGES: :[[@LINE+2]]:20: error: base class has incomplete type + // CHECK-MESSAGES: :[[@LINE-2]]:29: note: definition of 'NoCrash' is not complete until the closing '}' + class B : public NoCrash { + template B(U u) {} + }; +}; diff --git a/clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/pro-type-member-init.cpp b/clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/pro-type-member-init.cpp index 8d6992afef08a9..eaa73b906ce092 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/pro-type-member-init.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/pro-type-member-init.cpp @@ -463,12 +463,6 @@ struct NegativeIncompleteArrayMember { char e[]; }; -template class NoCrash { - class B : public NoCrash { - template B(U u) {} - }; -}; - struct PositiveBitfieldMember { PositiveBitfieldMember() {} // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: constructor does not initialize these fields: F diff --git a/clang-tools-extra/test/clang-tidy/checkers/misc/new-delete-overloads.cpp b/clang-tools-extra/test/clang-tidy/checkers/misc/new-delete-overloads.cpp index 78f021144b2e19..f86fe8a4c5b14f 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/misc/new-delete-overloads.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/misc/new-delete-overloads.cpp @@ -12,16 +12,6 @@ struct S { // CHECK-MESSAGES: :[[@LINE+1]]:7: warning: declaration of 'operator new' has no matching declaration of 'operator delete' at the same scope void *operator new(size_t size) noexcept(false); -struct T { - // Sized deallocations are not enabled by default, and so this new/delete pair - // does not match. However, we expect only one warning, for the new, because - // the operator delete is a placement delete and we do not warn on mismatching - // placement operations. - // CHECK-MESSAGES: :[[@LINE+1]]:9: warning: declaration of 'operator new' has no matching declaration of 'operator delete' at the same scope - void *operator new(size_t size) noexcept; - void operator delete(void *ptr, size_t) noexcept; // ok only if sized deallocation is enabled -}; - struct U { void *operator new(size_t size) noexcept; void operator delete(void *ptr) noexcept; diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/make-unique.cpp b/clang-tools-extra/test/clang-tidy/checkers/modernize/make-unique.cpp index 7934c6e93ffbd3..fe512a8f3bf321 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/modernize/make-unique.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/make-unique.cpp @@ -606,11 +606,8 @@ void invoke_template() { template_fun(foo); } -void no_fix_for_invalid_new_loc() { - // FIXME: Although the code is valid, the end location of `new struct Base` is - // invalid. Correct it once https://bugs.llvm.org/show_bug.cgi?id=35952 is - // fixed. +void fix_for_c_style_struct() { auto T = std::unique_ptr(new struct Base); // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: use std::make_unique instead - // CHECK-FIXES: auto T = std::unique_ptr(new struct Base); + // CHECK-FIXES: auto T = std::make_unique(); } diff --git a/clang-tools-extra/test/clang-tidy/checkers/readability/identifier-naming-outofline.cpp b/clang-tools-extra/test/clang-tidy/checkers/readability/identifier-naming-outofline.cpp new file mode 100644 index 00000000000000..f807875e27698d --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/readability/identifier-naming-outofline.cpp @@ -0,0 +1,30 @@ +// RUN: %check_clang_tidy %s readability-identifier-naming %t -std=c++20 \ +// RUN: --config='{CheckOptions: { \ +// RUN: readability-identifier-naming.MethodCase: CamelCase, \ +// RUN: }}' + +namespace SomeNamespace { +namespace Inner { + +class SomeClass { +public: + template + int someMethod(); +// CHECK-MESSAGES: :[[@LINE-1]]:9: warning: invalid case style for method 'someMethod' [readability-identifier-naming] +// CHECK-FIXES: {{^}} int SomeMethod(); +}; +template +int SomeClass::someMethod() { +// CHECK-FIXES: {{^}}int SomeClass::SomeMethod() { + return 5; +} + +} // namespace Inner + +void someFunc() { + Inner::SomeClass S; + S.someMethod(); +// CHECK-FIXES: {{^}} S.SomeMethod(); +} + +} // namespace SomeNamespace diff --git a/clang-tools-extra/unittests/clang-query/QueryParserTest.cpp b/clang-tools-extra/unittests/clang-query/QueryParserTest.cpp index b561e2bb983321..e414587c568b73 100644 --- a/clang-tools-extra/unittests/clang-query/QueryParserTest.cpp +++ b/clang-tools-extra/unittests/clang-query/QueryParserTest.cpp @@ -9,7 +9,6 @@ #include "QueryParser.h" #include "Query.h" #include "QuerySession.h" -#include "clang/Tooling/NodeIntrospection.h" #include "llvm/LineEditor/LineEditor.h" #include "gtest/gtest.h" @@ -61,7 +60,6 @@ TEST_F(QueryParserTest, Quit) { TEST_F(QueryParserTest, Set) { - bool HasIntrospection = tooling::NodeIntrospection::hasIntrospectionSupport(); QueryRef Q = parse("set"); ASSERT_TRUE(isa(Q)); EXPECT_EQ("expected variable name", cast(Q)->ErrStr); @@ -72,13 +70,8 @@ TEST_F(QueryParserTest, Set) { Q = parse("set output"); ASSERT_TRUE(isa(Q)); - if (HasIntrospection) - EXPECT_EQ( - "expected 'diag', 'print', 'detailed-ast', 'srcloc' or 'dump', got ''", - cast(Q)->ErrStr); - else - EXPECT_EQ("expected 'diag', 'print', 'detailed-ast' or 'dump', got ''", - cast(Q)->ErrStr); + EXPECT_EQ("expected 'diag', 'print', 'detailed-ast' or 'dump', got ''", + cast(Q)->ErrStr); Q = parse("set bind-root true foo"); ASSERT_TRUE(isa(Q)); @@ -86,13 +79,8 @@ TEST_F(QueryParserTest, Set) { Q = parse("set output foo"); ASSERT_TRUE(isa(Q)); - if (HasIntrospection) - EXPECT_EQ("expected 'diag', 'print', 'detailed-ast', 'srcloc' or 'dump', " - "got 'foo'", - cast(Q)->ErrStr); - else - EXPECT_EQ("expected 'diag', 'print', 'detailed-ast' or 'dump', got 'foo'", - cast(Q)->ErrStr); + EXPECT_EQ("expected 'diag', 'print', 'detailed-ast' or 'dump', got 'foo'", + cast(Q)->ErrStr); Q = parse("set output dump"); ASSERT_TRUE(isa(Q)); @@ -232,10 +220,8 @@ TEST_F(QueryParserTest, Complete) { EXPECT_EQ("output ", Comps[0].TypedText); EXPECT_EQ("output", Comps[0].DisplayText); - bool HasIntrospection = tooling::NodeIntrospection::hasIntrospectionSupport(); - Comps = QueryParser::complete("enable output ", 14, QS); - ASSERT_EQ(HasIntrospection ? 5u : 4u, Comps.size()); + ASSERT_EQ(4u, Comps.size()); EXPECT_EQ("diag ", Comps[0].TypedText); EXPECT_EQ("diag", Comps[0].DisplayText); @@ -243,12 +229,8 @@ TEST_F(QueryParserTest, Complete) { EXPECT_EQ("print", Comps[1].DisplayText); EXPECT_EQ("detailed-ast ", Comps[2].TypedText); EXPECT_EQ("detailed-ast", Comps[2].DisplayText); - if (HasIntrospection) { - EXPECT_EQ("srcloc ", Comps[3].TypedText); - EXPECT_EQ("srcloc", Comps[3].DisplayText); - } - EXPECT_EQ("dump ", Comps[HasIntrospection ? 4 : 3].TypedText); - EXPECT_EQ("dump", Comps[HasIntrospection ? 4 : 3].DisplayText); + EXPECT_EQ("dump ", Comps[3].TypedText); + EXPECT_EQ("dump", Comps[3].DisplayText); Comps = QueryParser::complete("set traversal ", 14, QS); ASSERT_EQ(2u, Comps.size()); diff --git a/clang/cmake/caches/HLSL.cmake b/clang/cmake/caches/HLSL.cmake index 84850c86f12cd7..ed813f60c9c699 100644 --- a/clang/cmake/caches/HLSL.cmake +++ b/clang/cmake/caches/HLSL.cmake @@ -8,6 +8,12 @@ set(LLVM_EXPERIMENTAL_TARGETS_TO_BUILD "DirectX;SPIRV" CACHE STRING "") # HLSL support is currently limted to clang, eventually it will expand to # clang-tools-extra too. -set(LLVM_ENABLE_PROJECTS "clang" CACHE STRING "") +set(LLVM_ENABLE_PROJECTS "clang;clang-tools-extra" CACHE STRING "") set(CLANG_ENABLE_HLSL On CACHE BOOL "") + +if (HLSL_ENABLE_DISTRIBUTION) + set(LLVM_DISTRIBUTION_COMPONENTS + "clang;hlsl-resource-headers;clangd" + CACHE STRING "") +endif() diff --git a/clang/docs/HLSL/AvailabilityDiagnostics.rst b/clang/docs/HLSL/AvailabilityDiagnostics.rst new file mode 100644 index 00000000000000..bb9d02f21dde62 --- /dev/null +++ b/clang/docs/HLSL/AvailabilityDiagnostics.rst @@ -0,0 +1,137 @@ +============================= +HLSL Availability Diagnostics +============================= + +.. contents:: + :local: + +Introduction +============ + +HLSL availability diagnostics emits errors or warning when unavailable shader APIs are used. Unavailable shader APIs are APIs that are exposed in HLSL code but are not available in the target shader stage or shader model version. + +There are three modes of HLSL availability diagnostic: + +#. **Default mode** - compiler emits an error when an unavailable API is found in a code that is reachable from the shader entry point function or from an exported library function (when compiling a shader library) + +#. **Relaxed mode** - same as default mode except the compiler emits a warning. This mode is enabled by ``-Wno-error=hlsl-availability``. + +#. **Strict mode** - compiler emits an error when an unavailable API is found in parsed code regardless of whether it can be reached from the shader entry point or exported functions, or not. This mode is enabled by ``-fhlsl-strict-availability``. + +Implementation Details +====================== + +Environment Parameter +--------------------- + +In order to encode API availability based on the shader model version and shader model stage a new ``environment`` parameter was added to the existing Clang ``availability`` attribute. + +The values allowed for this parameter are a subset of values allowed as the ``llvm::Triple`` environment component. If the environment parameters is present, the declared availability attribute applies only to targets with the same platform and environment. + +Default and Relaxed Diagnostic Modes +------------------------------------ + +This mode is implemented in ``DiagnoseHLSLAvailability`` class in ``SemaHLSL.cpp`` and it is invoked after the whole translation unit is parsed (from ``Sema::ActOnEndOfTranslationUnit``). The implementation iterates over all shader entry points and exported library functions in the translation unit and performs an AST traversal of each function body. + +When a reference to another function or member method is found (``DeclRefExpr`` or ``MemberExpr``) and it has a body, the AST of the referenced function is also scanned. This chain of AST traversals will reach all of the code that is reachable from the initial shader entry point or exported library function and avoids the need to generate a call graph. + +All shader APIs have an availability attribute that specifies the shader model version (and environment, if applicable) when this API was first introduced.When a reference to a function without a definition is found and it has an availability attribute, the version of the attribute is checked against the target shader model version and shader stage (if shader stage context is known), and an appropriate diagnostic is generated as needed. + +All shader entry functions have ``HLSLShaderAttr`` attribute that specifies what type of shader this function represents. However, for exported library functions the target shader stage is unknown, so in this case the HLSL API availability will be only checked against the shader model version. It means that for exported library functions the diagnostic of APIs with availability specific to shader stage will be deferred until DXIL linking time. + +A list of functions that were already scanned is kept in order to avoid duplicate scans and diagnostics (see ``DiagnoseHLSLAvailability::ScannedDecls``). It might happen that a shader library has multiple shader entry points for different shader stages that all call into the same shared function. It is therefore important to record not just that a function has been scanned, but also in which shader stage context. This is done by using ``llvm::DenseMap`` that maps ``FunctionDecl *`` to a ``unsigned`` bitmap that represents a set of shader stages (or environments) the function has been scanned for. The ``N``'th bit in the set is set if the function has been scanned in shader environment whose ``HLSLShaderAttr::ShaderType`` integer value equals ``N``. + +The emitted diagnostic messages belong to ``hlsl-availability`` diagnostic group and are reported as errors by default. With ``-Wno-error=hlsl-availability`` flag they become warning, making it relaxed HLSL diagnostics mode. + +Strict Diagnostic Mode +---------------------- + +When strict HLSL availability diagnostic mode is enabled the compiler must report all HLSL API availability issues regardless of code reachability. The implementation of this mode takes advantage of an existing diagnostic scan in ``DiagnoseUnguardedAvailability`` class which is already traversing AST of each function as soon as the function body has been parsed. For HLSL, this pass was only slightly modified, such as making sure diagnostic messages are in the ``hlsl-availability`` group and that availability checks based on shader stage are not included if the shader stage context is unknown. + +If the compilation target is a shader library, only availability based on shader model version can be diagnosed during this scan. To diagnose availability based on shader stage, the compiler needs to run the AST traversals implementated in ``DiagnoseHLSLAvailability`` at the end of the translation unit as described above. + +As a result, availability based on specific shader stage will only be diagnosed in code that is reachable from a shader entry point or library export function. It also means that function bodies might be scanned multiple time. When that happens, care should be taken not to produce duplicated diagnostics. + +======== +Examples +======== + +**Note** +For the example below, the ``WaveActiveCountBits`` API function became available in shader model 6.0 and ``WaveMultiPrefixSum`` in shader model 6.5. + +The availability of ``ddx`` function depends on a shader stage. It is available for pixel shaders in shader model 2.1 and higher, for compute, mesh and amplification shaders in shader model 6.6 and higher. For any other shader stages it is not available. + +Compute shader example +====================== + +.. code-block:: c++ + + float unusedFunction(float f) { + return ddx(f); + } + + [numthreads(4, 4, 1)] + void main(uint3 threadId : SV_DispatchThreadId) { + float f1 = ddx(threadId.x); + float f2 = WaveActiveCountBits(threadId.y == 1.0); + } + +When compiled as compute shader for shader model version 5.0, Clang will emit the following error by default: + +.. code-block:: console + + <>:7:13: error: 'ddx' is only available in compute shader environment on Shader Model 6.6 or newer + <>:8:13: error: 'WaveActiveCountBits' is only available on Shader Model 6.5 or newer + +With relaxed diagnostic mode this errors will become warnings. + +With strict diagnostic mode, in addition to the 2 errors above Clang will also emit error for the ``ddx`` call in ``unusedFunction``.: + +.. code-block:: console + + <>:2:9: error: 'ddx' is only available in compute shader environment on Shader Model 6.5 or newer + <>:7:13: error: 'ddx' is only available in compute shader environment on Shader Model 6.5 or newer + <>:7:13: error: 'WaveActiveCountBits' is only available on Shader Model 6.5 or newer + +Shader library example +====================== + +.. code-block:: c++ + + float myFunction(float f) { + return ddx(f); + } + + float unusedFunction(float f) { + return WaveMultiPrefixSum(f, 1.0); + } + + [shader("compute")] + [numthreads(4, 4, 1)] + void main(uint3 threadId : SV_DispatchThreadId) { + float f = 3; + float e = myFunction(f); + } + + [shader("pixel")] + void main() { + float f = 3; + float e = myFunction(f); + } + +When compiled as shader library vshader model version 6.4, Clang will emit the following error by default: + +.. code-block:: console + + <>:2:9: error: 'ddx' is only available in compute shader environment on Shader Model 6.5 or newer + +With relaxed diagnostic mode this errors will become warnings. + +With strict diagnostic mode Clang will also emit errors for availability issues in code that is not used by any of the entry points: + +.. code-block:: console + + <>2:9: error: 'ddx' is only available in compute shader environment on Shader Model 6.6 or newer + <>:6:9: error: 'WaveActiveCountBits' is only available on Shader Model 6.5 or newer + +Note that ``myFunction`` is reachable from both pixel and compute shader entry points is therefore scanned twice - once for each context. The diagnostic is emitted only for the compute shader context. diff --git a/clang/docs/HLSL/HLSLDocs.rst b/clang/docs/HLSL/HLSLDocs.rst index 97b2425f013b34..1e50a66d984b53 100644 --- a/clang/docs/HLSL/HLSLDocs.rst +++ b/clang/docs/HLSL/HLSLDocs.rst @@ -16,3 +16,4 @@ HLSL Design and Implementation ResourceTypes EntryFunctions FunctionCalls + AvailabilityDiagnostics diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index be4cded276321f..2899bc5ed35ad0 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -155,6 +155,11 @@ C++17 Feature Support files because they may not be stable across multiple TUs (the values may vary based on compiler version as well as CPU tuning). #GH60174 +C++14 Feature Support +^^^^^^^^^^^^^^^^^^^^^ +- Sized deallocation is enabled by default in C++14 onwards. The user may specify + ``-fno-sized-deallocation`` to disable it if there are some regressions. + C++20 Feature Support ^^^^^^^^^^^^^^^^^^^^^ @@ -226,6 +231,9 @@ Resolutions to C++ Defect Reports - Clang now diagnoses declarative nested-name-specifiers with pack-index-specifiers. (`CWG2858: Declarative nested-name-specifiers and pack-index-specifiers `_). +- Clang now allows attributes on concepts. + (`CWG2428: Deprecating a concept `_). + - P0522 implementation is enabled by default in all language versions, and provisional wording for CWG2398 is implemented. @@ -400,6 +408,10 @@ Attribute Changes in Clang - Clang now warns that the ``exclude_from_explicit_instantiation`` attribute is ignored when applied to a local class or a member thereof. +- The ``clspv_libclc_builtin`` attribute has been added to allow clspv + (`OpenCL-C to Vulkan SPIR-V compiler `_) to identify functions coming from libclc + (`OpenCL-C builtin library `_). + Improvements to Clang's diagnostics ----------------------------------- - Clang now applies syntax highlighting to the code snippets it @@ -491,9 +503,15 @@ Improvements to Clang's diagnostics } }; +- Clang emits a ``-Wparentheses`` warning for expressions with consecutive comparisons like ``x < y < z``. + Fixes #GH20456. + Improvements to Clang's time-trace ---------------------------------- +- Clang now specifies that using ``auto`` in a lambda parameter is a C++14 extension when + appropriate. (`#46059: `_). + Bug Fixes in This Version ------------------------- - Clang's ``-Wundefined-func-template`` no longer warns on pure virtual @@ -573,6 +591,9 @@ Bug Fixes in This Version - Clang now correctly disallows VLA type compound literals, e.g. ``(int[size]){}``, as the C standard mandates. (#GH89835) +- ``__is_array`` and ``__is_bounded_array`` no longer return ``true`` for + zero-sized arrays. Fixes (#GH54705). + Bug Fixes to Compiler Builtins ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -723,6 +744,16 @@ Bug Fixes to C++ Support - Clang now ignores template parameters only used within the exception specification of candidate function templates during partial ordering when deducing template arguments from a function declaration or when taking the address of a function template. +- Fix a bug with checking constrained non-type template parameters for equivalence. Fixes (#GH77377). +- Fix a bug where the last argument was not considered when considering the most viable function for + explicit object argument member functions. Fixes (#GH92188). +- Fix a C++11 crash when a non-const non-static member function is defined out-of-line with + the ``constexpr`` specifier. Fixes (#GH61004). +- Clang no longer transforms dependent qualified names into implicit class member access expressions + until it can be determined whether the name is that of a non-static member. +- Clang now correctly diagnoses when the current instantiation is used as an incomplete base class. +- Clang no longer treats ``constexpr`` class scope function template specializations of non-static members + as implicitly ``const`` in language modes after C++11. Bug Fixes to AST Handling ^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/clang/docs/analyzer/checkers.rst b/clang/docs/analyzer/checkers.rst index eb8b58323da4d7..b4bd9dac1cbc2d 100644 --- a/clang/docs/analyzer/checkers.rst +++ b/clang/docs/analyzer/checkers.rst @@ -1179,6 +1179,47 @@ security.insecureAPI.DeprecatedOrUnsafeBufferHandling (C) strncpy(buf, "a", 1); // warn } +security.SetgidSetuidOrder (C) +"""""""""""""""""""""""""""""" +When dropping user-level and group-level privileges in a program by using +``setuid`` and ``setgid`` calls, it is important to reset the group-level +privileges (with ``setgid``) first. Function ``setgid`` will likely fail if +the superuser privileges are already dropped. + +The checker checks for sequences of ``setuid(getuid())`` and +``setgid(getgid())`` calls (in this order). If such a sequence is found and +there is no other privilege-changing function call (``seteuid``, ``setreuid``, +``setresuid`` and the GID versions of these) in between, a warning is +generated. The checker finds only exactly ``setuid(getuid())`` calls (and the +GID versions), not for example if the result of ``getuid()`` is stored in a +variable. + +.. code-block:: c + + void test1() { + // ... + // end of section with elevated privileges + // reset privileges (user and group) to normal user + if (setuid(getuid()) != 0) { + handle_error(); + return; + } + if (setgid(getgid()) != 0) { // warning: A 'setgid(getgid())' call following a 'setuid(getuid())' call is likely to fail + handle_error(); + return; + } + // user-ID and group-ID are reset to normal user now + // ... + } + +In the code above the problem is that ``setuid(getuid())`` removes superuser +privileges before ``setgid(getgid())`` is called. To fix the problem the +``setgid(getgid())`` should be called first. Further attention is needed to +avoid code like ``setgid(getuid())`` (this checker does not detect bugs like +this) and always check the return value of these calls. + +This check corresponds to SEI CERT Rule `POS36-C `_. + .. _unix-checkers: unix diff --git a/clang/docs/tools/clang-formatted-files.txt b/clang/docs/tools/clang-formatted-files.txt index eaeadf2656b0bf..dee51e402b687f 100644 --- a/clang/docs/tools/clang-formatted-files.txt +++ b/clang/docs/tools/clang-formatted-files.txt @@ -124,6 +124,7 @@ clang/include/clang/Analysis/Analyses/CFGReachabilityAnalysis.h clang/include/clang/Analysis/Analyses/ExprMutationAnalyzer.h clang/include/clang/Analysis/FlowSensitive/AdornedCFG.h clang/include/clang/Analysis/FlowSensitive/ASTOps.h +clang/include/clang/Analysis/FlowSensitive/CNFFormula.h clang/include/clang/Analysis/FlowSensitive/DataflowAnalysis.h clang/include/clang/Analysis/FlowSensitive/DataflowAnalysisContext.h clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h @@ -252,7 +253,6 @@ clang/include/clang/Tooling/CompilationDatabasePluginRegistry.h clang/include/clang/Tooling/DiagnosticsYaml.h clang/include/clang/Tooling/Execution.h clang/include/clang/Tooling/JSONCompilationDatabase.h -clang/include/clang/Tooling/NodeIntrospection.h clang/include/clang/Tooling/Refactoring.h clang/include/clang/Tooling/StandaloneExecution.h clang/include/clang/Tooling/ToolExecutorPluginRegistry.h @@ -562,15 +562,11 @@ clang/lib/Tooling/Execution.cpp clang/lib/Tooling/ExpandResponseFilesCompilationDatabase.cpp clang/lib/Tooling/FixIt.cpp clang/lib/Tooling/GuessTargetAndModeCompilationDatabase.cpp -clang/lib/Tooling/NodeIntrospection.cpp clang/lib/Tooling/StandaloneExecution.cpp clang/lib/Tooling/DependencyScanning/DependencyScanningFilesystem.cpp clang/lib/Tooling/DependencyScanning/DependencyScanningService.cpp clang/lib/Tooling/DependencyScanning/DependencyScanningTool.cpp clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp -clang/lib/Tooling/DumpTool/APIData.h -clang/lib/Tooling/DumpTool/ASTSrcLocProcessor.h -clang/lib/Tooling/DumpTool/ClangSrcLocDump.cpp clang/lib/Tooling/Inclusions/HeaderIncludes.cpp clang/lib/Tooling/Inclusions/IncludeStyle.cpp clang/lib/Tooling/Inclusions/StandardLibrary.cpp @@ -626,6 +622,7 @@ clang/tools/libclang/CXCursor.h clang/tools/scan-build-py/tests/functional/src/include/clean-one.h clang/unittests/Analysis/CFGBuildResult.h clang/unittests/Analysis/MacroExpansionContextTest.cpp +clang/unittests/Analysis/FlowSensitive/CNFFormula.cpp clang/unittests/Analysis/FlowSensitive/DataflowAnalysisContextTest.cpp clang/unittests/Analysis/FlowSensitive/DataflowEnvironmentTest.cpp clang/unittests/Analysis/FlowSensitive/MapLatticeTest.cpp @@ -637,6 +634,7 @@ clang/unittests/Analysis/FlowSensitive/TestingSupport.cpp clang/unittests/Analysis/FlowSensitive/TestingSupport.h clang/unittests/Analysis/FlowSensitive/TestingSupportTest.cpp clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp +clang/unittests/Analysis/FlowSensitive/WatchedLiteralsSolver.cpp clang/unittests/Analysis/FlowSensitive/WatchedLiteralsSolverTest.cpp clang/unittests/AST/ASTImporterFixtures.cpp clang/unittests/AST/ASTImporterFixtures.h diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h index e03b1121947867..2ce2b810d36364 100644 --- a/clang/include/clang/AST/ASTContext.h +++ b/clang/include/clang/AST/ASTContext.h @@ -2611,7 +2611,7 @@ class ASTContext : public RefCountedBase { /// /// \returns if this is an array type, the completely unqualified array type /// that corresponds to it. Otherwise, returns T.getUnqualifiedType(). - QualType getUnqualifiedArrayType(QualType T, Qualifiers &Quals); + QualType getUnqualifiedArrayType(QualType T, Qualifiers &Quals) const; /// Determine whether the given types are equivalent after /// cvr-qualifiers have been removed. diff --git a/clang/include/clang/AST/ASTNodeTraverser.h b/clang/include/clang/AST/ASTNodeTraverser.h index bf7c204e4ad73a..98db1cb5789909 100644 --- a/clang/include/clang/AST/ASTNodeTraverser.h +++ b/clang/include/clang/AST/ASTNodeTraverser.h @@ -695,7 +695,7 @@ class ASTNodeTraverser if (const auto *TC = D->getTypeConstraint()) Visit(TC->getImmediatelyDeclaredConstraint()); if (D->hasDefaultArgument()) - Visit(D->getDefaultArgument(), SourceRange(), + Visit(D->getDefaultArgument().getArgument(), SourceRange(), D->getDefaultArgStorage().getInheritedFrom(), D->defaultArgumentWasInherited() ? "inherited from" : "previous"); } diff --git a/clang/include/clang/AST/Decl.h b/clang/include/clang/AST/Decl.h index de8b923645f8d6..5e485ccb85a139 100644 --- a/clang/include/clang/AST/Decl.h +++ b/clang/include/clang/AST/Decl.h @@ -2096,13 +2096,12 @@ class FunctionDecl : public DeclaratorDecl, /// /// \param PointOfInstantiation point at which the function template /// specialization was first instantiated. - void setFunctionTemplateSpecialization(ASTContext &C, - FunctionTemplateDecl *Template, - const TemplateArgumentList *TemplateArgs, - void *InsertPos, - TemplateSpecializationKind TSK, - const TemplateArgumentListInfo *TemplateArgsAsWritten, - SourceLocation PointOfInstantiation); + void setFunctionTemplateSpecialization( + ASTContext &C, FunctionTemplateDecl *Template, + TemplateArgumentList *TemplateArgs, void *InsertPos, + TemplateSpecializationKind TSK, + const TemplateArgumentListInfo *TemplateArgsAsWritten, + SourceLocation PointOfInstantiation); /// Specify that this record is an instantiation of the /// member function FD. @@ -2981,12 +2980,12 @@ class FunctionDecl : public DeclaratorDecl, /// /// \param PointOfInstantiation point at which the function template /// specialization was first instantiated. - void setFunctionTemplateSpecialization(FunctionTemplateDecl *Template, - const TemplateArgumentList *TemplateArgs, - void *InsertPos, - TemplateSpecializationKind TSK = TSK_ImplicitInstantiation, - const TemplateArgumentListInfo *TemplateArgsAsWritten = nullptr, - SourceLocation PointOfInstantiation = SourceLocation()) { + void setFunctionTemplateSpecialization( + FunctionTemplateDecl *Template, TemplateArgumentList *TemplateArgs, + void *InsertPos, + TemplateSpecializationKind TSK = TSK_ImplicitInstantiation, + TemplateArgumentListInfo *TemplateArgsAsWritten = nullptr, + SourceLocation PointOfInstantiation = SourceLocation()) { setFunctionTemplateSpecialization(getASTContext(), Template, TemplateArgs, InsertPos, TSK, TemplateArgsAsWritten, PointOfInstantiation); diff --git a/clang/include/clang/AST/DeclTemplate.h b/clang/include/clang/AST/DeclTemplate.h index 268aeacf2f20fe..07b08b5ed43ca4 100644 --- a/clang/include/clang/AST/DeclTemplate.h +++ b/clang/include/clang/AST/DeclTemplate.h @@ -478,7 +478,7 @@ class FunctionTemplateSpecializationInfo final public: /// The template arguments used to produce the function template /// specialization from the function template. - const TemplateArgumentList *TemplateArguments; + TemplateArgumentList *TemplateArguments; /// The template arguments as written in the sources, if provided. /// FIXME: Normally null; tail-allocate this. @@ -491,7 +491,7 @@ class FunctionTemplateSpecializationInfo final private: FunctionTemplateSpecializationInfo( FunctionDecl *FD, FunctionTemplateDecl *Template, - TemplateSpecializationKind TSK, const TemplateArgumentList *TemplateArgs, + TemplateSpecializationKind TSK, TemplateArgumentList *TemplateArgs, const ASTTemplateArgumentListInfo *TemplateArgsAsWritten, SourceLocation POI, MemberSpecializationInfo *MSInfo) : Function(FD, MSInfo ? true : false), Template(Template, TSK - 1), @@ -511,8 +511,7 @@ class FunctionTemplateSpecializationInfo final static FunctionTemplateSpecializationInfo * Create(ASTContext &C, FunctionDecl *FD, FunctionTemplateDecl *Template, - TemplateSpecializationKind TSK, - const TemplateArgumentList *TemplateArgs, + TemplateSpecializationKind TSK, TemplateArgumentList *TemplateArgs, const TemplateArgumentListInfo *TemplateArgsAsWritten, SourceLocation POI, MemberSpecializationInfo *MSInfo); @@ -1186,7 +1185,7 @@ class TemplateTypeParmDecl final : public TypeDecl, /// The default template argument, if any. using DefArgStorage = - DefaultArgStorage; + DefaultArgStorage; DefArgStorage DefaultArgument; TemplateTypeParmDecl(DeclContext *DC, SourceLocation KeyLoc, @@ -1226,13 +1225,9 @@ class TemplateTypeParmDecl final : public TypeDecl, bool hasDefaultArgument() const { return DefaultArgument.isSet(); } /// Retrieve the default argument, if any. - QualType getDefaultArgument() const { - return DefaultArgument.get()->getType(); - } - - /// Retrieves the default argument's source information, if any. - TypeSourceInfo *getDefaultArgumentInfo() const { - return DefaultArgument.get(); + const TemplateArgumentLoc &getDefaultArgument() const { + static const TemplateArgumentLoc NoneLoc; + return DefaultArgument.isSet() ? *DefaultArgument.get() : NoneLoc; } /// Retrieves the location of the default argument declaration. @@ -1245,9 +1240,8 @@ class TemplateTypeParmDecl final : public TypeDecl, } /// Set the default argument for this template parameter. - void setDefaultArgument(TypeSourceInfo *DefArg) { - DefaultArgument.set(DefArg); - } + void setDefaultArgument(const ASTContext &C, + const TemplateArgumentLoc &DefArg); /// Set that this default argument was inherited from another /// parameter. diff --git a/clang/include/clang/AST/ExprCXX.h b/clang/include/clang/AST/ExprCXX.h index fac65628ffede8..dbf693611a7fa5 100644 --- a/clang/include/clang/AST/ExprCXX.h +++ b/clang/include/clang/AST/ExprCXX.h @@ -4377,15 +4377,21 @@ class PackIndexingExpr final // The pack being indexed, followed by the index Stmt *SubExprs[2]; - size_t TransformedExpressions; + // The size of the trailing expressions. + unsigned TransformedExpressions : 31; + + LLVM_PREFERRED_TYPE(bool) + unsigned ExpandedToEmptyPack : 1; PackIndexingExpr(QualType Type, SourceLocation EllipsisLoc, SourceLocation RSquareLoc, Expr *PackIdExpr, Expr *IndexExpr, - ArrayRef SubstitutedExprs = {}) + ArrayRef SubstitutedExprs = {}, + bool ExpandedToEmptyPack = false) : Expr(PackIndexingExprClass, Type, VK_LValue, OK_Ordinary), EllipsisLoc(EllipsisLoc), RSquareLoc(RSquareLoc), SubExprs{PackIdExpr, IndexExpr}, - TransformedExpressions(SubstitutedExprs.size()) { + TransformedExpressions(SubstitutedExprs.size()), + ExpandedToEmptyPack(ExpandedToEmptyPack) { auto *Exprs = getTrailingObjects(); std::uninitialized_copy(SubstitutedExprs.begin(), SubstitutedExprs.end(), @@ -4408,10 +4414,14 @@ class PackIndexingExpr final SourceLocation EllipsisLoc, SourceLocation RSquareLoc, Expr *PackIdExpr, Expr *IndexExpr, std::optional Index, - ArrayRef SubstitutedExprs = {}); + ArrayRef SubstitutedExprs = {}, + bool ExpandedToEmptyPack = false); static PackIndexingExpr *CreateDeserialized(ASTContext &Context, unsigned NumTransformedExprs); + /// Determine if the expression was expanded to empty. + bool expandsToEmptyPack() const { return ExpandedToEmptyPack; } + /// Determine the location of the 'sizeof' keyword. SourceLocation getEllipsisLoc() const { return EllipsisLoc; } @@ -4445,6 +4455,7 @@ class PackIndexingExpr final return getTrailingObjects()[*Index]; } + /// Return the trailing expressions, regardless of the expansion. ArrayRef getExpressions() const { return {getTrailingObjects(), TransformedExpressions}; } diff --git a/clang/include/clang/AST/OpenACCClause.h b/clang/include/clang/AST/OpenACCClause.h index 607a2b9d653678..28ff8c44bd2569 100644 --- a/clang/include/clang/AST/OpenACCClause.h +++ b/clang/include/clang/AST/OpenACCClause.h @@ -677,6 +677,35 @@ class OpenACCCreateClause final ArrayRef VarList, SourceLocation EndLoc); }; +class OpenACCReductionClause final + : public OpenACCClauseWithVarList, + public llvm::TrailingObjects { + OpenACCReductionOperator Op; + + OpenACCReductionClause(SourceLocation BeginLoc, SourceLocation LParenLoc, + OpenACCReductionOperator Operator, + ArrayRef VarList, SourceLocation EndLoc) + : OpenACCClauseWithVarList(OpenACCClauseKind::Reduction, BeginLoc, + LParenLoc, EndLoc), + Op(Operator) { + std::uninitialized_copy(VarList.begin(), VarList.end(), + getTrailingObjects()); + setExprs(MutableArrayRef(getTrailingObjects(), VarList.size())); + } + +public: + static bool classof(const OpenACCClause *C) { + return C->getClauseKind() == OpenACCClauseKind::Reduction; + } + + static OpenACCReductionClause * + Create(const ASTContext &C, SourceLocation BeginLoc, SourceLocation LParenLoc, + OpenACCReductionOperator Operator, ArrayRef VarList, + SourceLocation EndLoc); + + OpenACCReductionOperator getReductionOp() const { return Op; } +}; + template class OpenACCClauseVisitor { Impl &getDerived() { return static_cast(*this); } diff --git a/clang/include/clang/AST/RecursiveASTVisitor.h b/clang/include/clang/AST/RecursiveASTVisitor.h index 782f60844506f4..659e4cdd1037b1 100644 --- a/clang/include/clang/AST/RecursiveASTVisitor.h +++ b/clang/include/clang/AST/RecursiveASTVisitor.h @@ -736,13 +736,27 @@ bool RecursiveASTVisitor::TraverseDecl(Decl *D) { // As a syntax visitor, by default we want to ignore declarations for // implicit declarations (ones not typed explicitly by the user). - if (!getDerived().shouldVisitImplicitCode() && D->isImplicit()) { - // For an implicit template type parameter, its type constraints are not - // implicit and are not represented anywhere else. We still need to visit - // them. - if (auto *TTPD = dyn_cast(D)) - return TraverseTemplateTypeParamDeclConstraints(TTPD); - return true; + if (!getDerived().shouldVisitImplicitCode()) { + if (D->isImplicit()) { + // For an implicit template type parameter, its type constraints are not + // implicit and are not represented anywhere else. We still need to visit + // them. + if (auto *TTPD = dyn_cast(D)) + return TraverseTemplateTypeParamDeclConstraints(TTPD); + return true; + } + + // Deduction guides for alias templates are always synthesized, so they + // should not be traversed unless shouldVisitImplicitCode() returns true. + // + // It's important to note that checking the implicit bit is not efficient + // for the alias case. For deduction guides synthesized from explicit + // user-defined deduction guides, we must maintain the explicit bit to + // ensure correct overload resolution. + if (auto *FTD = dyn_cast(D)) + if (llvm::isa_and_present( + FTD->getDeclName().getCXXDeductionGuideTemplate())) + return true; } switch (D->getKind()) { @@ -1946,7 +1960,7 @@ DEF_TRAVERSE_DECL(TemplateTypeParmDecl, { TRY_TO(TraverseType(QualType(D->getTypeForDecl(), 0))); TRY_TO(TraverseTemplateTypeParamDeclConstraints(D)); if (D->hasDefaultArgument() && !D->defaultArgumentWasInherited()) - TRY_TO(TraverseTypeLoc(D->getDefaultArgumentInfo()->getTypeLoc())); + TRY_TO(TraverseTemplateArgumentLoc(D->getDefaultArgument())); }) DEF_TRAVERSE_DECL(TypedefDecl, { diff --git a/clang/include/clang/AST/Type.h b/clang/include/clang/AST/Type.h index da3834f19ca044..9a5c6e8d562c34 100644 --- a/clang/include/clang/AST/Type.h +++ b/clang/include/clang/AST/Type.h @@ -2523,6 +2523,7 @@ class alignas(TypeAlignment) Type : public ExtQualsTypeCommonBase { bool isVectorType() const; // GCC vector type. bool isExtVectorType() const; // Extended vector type. bool isExtVectorBoolType() const; // Extended vector type with bool element. + bool isSubscriptableVectorType() const; bool isMatrixType() const; // Matrix type. bool isConstantMatrixType() const; // Constant matrix type. bool isDependentAddressSpaceType() const; // value-dependent address space qualifier @@ -7729,6 +7730,10 @@ inline bool Type::isExtVectorBoolType() const { return cast(CanonicalType)->getElementType()->isBooleanType(); } +inline bool Type::isSubscriptableVectorType() const { + return isVectorType() || isSveVLSBuiltinType(); +} + inline bool Type::isMatrixType() const { return isa(CanonicalType); } diff --git a/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h b/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h index 5d16dcc824c50c..228b4ae1e3e115 100644 --- a/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h +++ b/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h @@ -106,6 +106,11 @@ class UnsafeBufferUsageHandler { virtual void handleUnsafeOperation(const Stmt *Operation, bool IsRelatedToDecl, ASTContext &Ctx) = 0; + /// Invoked when an unsafe operation with a std container is found. + virtual void handleUnsafeOperationInContainer(const Stmt *Operation, + bool IsRelatedToDecl, + ASTContext &Ctx) = 0; + /// Invoked when a fix is suggested against a variable. This function groups /// all variables that must be fixed together (i.e their types must be changed /// to the same target type to prevent type mismatches) into a single fixit. diff --git a/clang/include/clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def b/clang/include/clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def index 3273c642eed517..242ad763ba62b9 100644 --- a/clang/include/clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def +++ b/clang/include/clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def @@ -36,6 +36,7 @@ WARNING_GADGET(Decrement) WARNING_GADGET(ArraySubscript) WARNING_GADGET(PointerArithmetic) WARNING_GADGET(UnsafeBufferUsageAttr) +WARNING_GADGET(UnsafeBufferUsageCtorAttr) WARNING_GADGET(DataInvocation) WARNING_CONTAINER_GADGET(SpanTwoParamConstructor) // Uses of `std::span(arg0, arg1)` FIXABLE_GADGET(ULCArraySubscript) // `DRE[any]` in an Unspecified Lvalue Context diff --git a/clang/include/clang/Analysis/FlowSensitive/CNFFormula.h b/clang/include/clang/Analysis/FlowSensitive/CNFFormula.h new file mode 100644 index 00000000000000..fb13e774c67fe7 --- /dev/null +++ b/clang/include/clang/Analysis/FlowSensitive/CNFFormula.h @@ -0,0 +1,179 @@ +//===- CNFFormula.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 +// +//===----------------------------------------------------------------------===// +// +// A representation of a boolean formula in 3-CNF. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_ANALYSIS_FLOWSENSITIVE_CNFFORMULA_H +#define LLVM_CLANG_ANALYSIS_FLOWSENSITIVE_CNFFORMULA_H + +#include +#include + +#include "clang/Analysis/FlowSensitive/Formula.h" + +namespace clang { +namespace dataflow { + +/// Boolean variables are represented as positive integers. +using Variable = uint32_t; + +/// A null boolean variable is used as a placeholder in various data structures +/// and algorithms. +constexpr Variable NullVar = 0; + +/// Literals are represented as positive integers. Specifically, for a boolean +/// variable `V` that is represented as the positive integer `I`, the positive +/// literal `V` is represented as the integer `2*I` and the negative literal +/// `!V` is represented as the integer `2*I+1`. +using Literal = uint32_t; + +/// A null literal is used as a placeholder in various data structures and +/// algorithms. +constexpr Literal NullLit = 0; + +/// Clause identifiers are represented as positive integers. +using ClauseID = uint32_t; + +/// A null clause identifier is used as a placeholder in various data structures +/// and algorithms. +constexpr ClauseID NullClause = 0; + +/// Returns the positive literal `V`. +inline constexpr Literal posLit(Variable V) { return 2 * V; } + +/// Returns the negative literal `!V`. +inline constexpr Literal negLit(Variable V) { return 2 * V + 1; } + +/// Returns whether `L` is a positive literal. +inline constexpr bool isPosLit(Literal L) { return 0 == (L & 1); } + +/// Returns whether `L` is a negative literal. +inline constexpr bool isNegLit(Literal L) { return 1 == (L & 1); } + +/// Returns the negated literal `!L`. +inline constexpr Literal notLit(Literal L) { return L ^ 1; } + +/// Returns the variable of `L`. +inline constexpr Variable var(Literal L) { return L >> 1; } + +/// A boolean formula in 3-CNF (conjunctive normal form with at most 3 literals +/// per clause). +class CNFFormula { + /// `LargestVar` is equal to the largest positive integer that represents a + /// variable in the formula. + const Variable LargestVar; + + /// Literals of all clauses in the formula. + /// + /// The element at index 0 stands for the literal in the null clause. It is + /// set to 0 and isn't used. Literals of clauses in the formula start from the + /// element at index 1. + /// + /// For example, for the formula `(L1 v L2) ^ (L2 v L3 v L4)` the elements of + /// `Clauses` will be `[0, L1, L2, L2, L3, L4]`. + std::vector Clauses; + + /// Start indices of clauses of the formula in `Clauses`. + /// + /// The element at index 0 stands for the start index of the null clause. It + /// is set to 0 and isn't used. Start indices of clauses in the formula start + /// from the element at index 1. + /// + /// For example, for the formula `(L1 v L2) ^ (L2 v L3 v L4)` the elements of + /// `ClauseStarts` will be `[0, 1, 3]`. Note that the literals of the first + /// clause always start at index 1. The start index for the literals of the + /// second clause depends on the size of the first clause and so on. + std::vector ClauseStarts; + + /// Indicates that we already know the formula is unsatisfiable. + /// During construction, we catch simple cases of conflicting unit-clauses. + bool KnownContradictory; + +public: + explicit CNFFormula(Variable LargestVar); + + /// Adds the `L1 v ... v Ln` clause to the formula. + /// Requirements: + /// + /// `Li` must not be `NullLit`. + /// + /// All literals in the input that are not `NullLit` must be distinct. + void addClause(ArrayRef lits); + + /// Returns whether the formula is known to be contradictory. + /// This is the case if any of the clauses is empty. + bool knownContradictory() const { return KnownContradictory; } + + /// Returns the largest variable in the formula. + Variable largestVar() const { return LargestVar; } + + /// Returns the number of clauses in the formula. + /// Valid clause IDs are in the range [1, `numClauses()`]. + ClauseID numClauses() const { return ClauseStarts.size() - 1; } + + /// Returns the number of literals in clause `C`. + size_t clauseSize(ClauseID C) const { + return C == ClauseStarts.size() - 1 ? Clauses.size() - ClauseStarts[C] + : ClauseStarts[C + 1] - ClauseStarts[C]; + } + + /// Returns the literals of clause `C`. + /// If `knownContradictory()` is false, each clause has at least one literal. + llvm::ArrayRef clauseLiterals(ClauseID C) const { + size_t S = clauseSize(C); + if (S == 0) + return llvm::ArrayRef(); + return llvm::ArrayRef(&Clauses[ClauseStarts[C]], S); + } + + /// An iterator over all literals of all clauses in the formula. + /// The iterator allows mutation of the literal through the `*` operator. + /// This is to support solvers that mutate the formula during solving. + class Iterator { + friend class CNFFormula; + CNFFormula *CNF; + size_t Idx; + Iterator(CNFFormula *CNF, size_t Idx) : CNF(CNF), Idx(Idx) {} + + public: + Iterator(const Iterator &) = default; + Iterator &operator=(const Iterator &) = default; + + Iterator &operator++() { + ++Idx; + assert(Idx < CNF->Clauses.size() && "Iterator out of bounds"); + return *this; + } + + Iterator next() const { + Iterator I = *this; + ++I; + return I; + } + + Literal &operator*() const { return CNF->Clauses[Idx]; } + }; + friend class Iterator; + + /// Returns an iterator to the first literal of clause `C`. + Iterator startOfClause(ClauseID C) { return Iterator(this, ClauseStarts[C]); } +}; + +/// Converts the conjunction of `Vals` into a formula in conjunctive normal +/// form where each clause has at least one and at most three literals. +/// `Atomics` is populated with a mapping from `Variables` to the corresponding +/// `Atom`s for atomic booleans in the input formulas. +CNFFormula buildCNF(const llvm::ArrayRef &Formulas, + llvm::DenseMap &Atomics); + +} // namespace dataflow +} // namespace clang + +#endif // LLVM_CLANG_ANALYSIS_FLOWSENSITIVE_CNFFORMULA_H diff --git a/clang/include/clang/Analysis/FlowSensitive/WatchedLiteralsSolver.h b/clang/include/clang/Analysis/FlowSensitive/WatchedLiteralsSolver.h index b5cd7aa10fd7d2..d74380b78e935d 100644 --- a/clang/include/clang/Analysis/FlowSensitive/WatchedLiteralsSolver.h +++ b/clang/include/clang/Analysis/FlowSensitive/WatchedLiteralsSolver.h @@ -17,16 +17,17 @@ #include "clang/Analysis/FlowSensitive/Formula.h" #include "clang/Analysis/FlowSensitive/Solver.h" #include "llvm/ADT/ArrayRef.h" -#include namespace clang { namespace dataflow { /// A SAT solver that is an implementation of Algorithm D from Knuth's The Art /// of Computer Programming Volume 4: Satisfiability, Fascicle 6. It is based on -/// the Davis-Putnam-Logemann-Loveland (DPLL) algorithm, keeps references to a -/// single "watched" literal per clause, and uses a set of "active" variables +/// the Davis-Putnam-Logemann-Loveland (DPLL) algorithm [1], keeps references to +/// a single "watched" literal per clause, and uses a set of "active" variables /// for unit propagation. +// +// [1] https://en.wikipedia.org/wiki/DPLL_algorithm class WatchedLiteralsSolver : public Solver { // Count of the iterations of the main loop of the solver. This spans *all* // calls to the underlying solver across the life of this object. It is diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td index 52552ba488560b..7008bea483c872 100644 --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -999,7 +999,7 @@ def Availability : InheritableAttr { VersionArgument<"deprecated">, VersionArgument<"obsoleted">, BoolArgument<"unavailable">, StringArgument<"message">, BoolArgument<"strict">, StringArgument<"replacement">, - IntArgument<"priority">]; + IntArgument<"priority">, IdentifierArgument<"environment">]; let AdditionalMembers = [{static llvm::StringRef getPrettyPlatformName(llvm::StringRef Platform) { return llvm::StringSwitch(Platform) @@ -1019,7 +1019,7 @@ def Availability : InheritableAttr { .Case("xros", "visionOS") .Case("xros_app_extension", "visionOS (App Extension)") .Case("swift", "Swift") - .Case("shadermodel", "HLSL ShaderModel") + .Case("shadermodel", "Shader Model") .Case("ohos", "OpenHarmony OS") .Default(llvm::StringRef()); } @@ -1059,7 +1059,34 @@ static llvm::StringRef canonicalizePlatformName(llvm::StringRef Platform) { .Case("visionos_app_extension", "xros_app_extension") .Case("ShaderModel", "shadermodel") .Default(Platform); -} }]; +} +static llvm::StringRef getPrettyEnviromentName(llvm::StringRef Environment) { + return llvm::StringSwitch(Environment) + .Case("pixel", "pixel shader") + .Case("vertex", "vertex shader") + .Case("geometry", "geometry shader") + .Case("hull", "hull shader") + .Case("domain", "domain shader") + .Case("compute", "compute shader") + .Case("mesh", "mesh shader") + .Case("amplification", "amplification shader") + .Case("library", "shader library") + .Default(Environment); +} +static llvm::Triple::EnvironmentType getEnvironmentType(llvm::StringRef Environment) { + return llvm::StringSwitch(Environment) + .Case("pixel", llvm::Triple::Pixel) + .Case("vertex", llvm::Triple::Vertex) + .Case("geometry", llvm::Triple::Geometry) + .Case("hull", llvm::Triple::Hull) + .Case("domain", llvm::Triple::Domain) + .Case("compute", llvm::Triple::Compute) + .Case("mesh", llvm::Triple::Mesh) + .Case("amplification", llvm::Triple::Amplification) + .Case("library", llvm::Triple::Library) + .Default(llvm::Triple::UnknownEnvironment); +} +}]; let HasCustomParsing = 1; let InheritEvenIfAlreadyPresent = 1; let Subjects = SubjectList<[Named]>; @@ -4561,3 +4588,10 @@ def CodeAlign: StmtAttr { static constexpr int MaximumAlignment = 4096; }]; } + +def ClspvLibclcBuiltin: InheritableAttr { + let Spellings = [Clang<"clspv_libclc_builtin">]; + let Subjects = SubjectList<[Function]>; + let Documentation = [ClspvLibclcBuiltinDoc]; + let SimpleHandler = 1; +} diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td index f351822ac74bd5..54197d588eb450 100644 --- a/clang/include/clang/Basic/AttrDocs.td +++ b/clang/include/clang/Basic/AttrDocs.td @@ -1593,6 +1593,11 @@ replacement=\ *string-literal* a warning about use of a deprecated declaration. The Fix-It will replace the deprecated declaration with the new declaration specified. +environment=\ *identifier* + Target environment in which this declaration is available. If present, + the availability attribute applies only to targets with the same platform + and environment. The parameter is currently supported only in HLSL. + Multiple availability attributes can be placed on a declaration, which may correspond to different platforms. For most platforms, the availability attribute with the platform corresponding to the target platform will be used; @@ -8087,3 +8092,17 @@ requirement: } }]; } + +def ClspvLibclcBuiltinDoc : Documentation { + let Category = DocCatFunction; + let Content = [{ +Attribute used by `clspv`_ (OpenCL-C to Vulkan SPIR-V compiler) to identify functions coming from `libclc`_ (OpenCL-C builtin library). + +.. code-block:: c + + void __attribute__((clspv_libclc_builtin)) libclc_builtin() {} + +.. _`clspv`: https://github.com/google/clspv +.. _`libclc`: https://libclc.llvm.org +}]; +} diff --git a/clang/include/clang/Basic/BuiltinsAMDGPU.def b/clang/include/clang/Basic/BuiltinsAMDGPU.def index 3e21a2fe2ac6b3..efa652eee99015 100644 --- a/clang/include/clang/Basic/BuiltinsAMDGPU.def +++ b/clang/include/clang/Basic/BuiltinsAMDGPU.def @@ -240,6 +240,7 @@ TARGET_BUILTIN(__builtin_amdgcn_flat_atomic_fadd_v2bf16, "V2sV2s*0V2s", "t", "at TARGET_BUILTIN(__builtin_amdgcn_global_atomic_fadd_v2bf16, "V2sV2s*1V2s", "t", "atomic-global-pk-add-bf16-inst") TARGET_BUILTIN(__builtin_amdgcn_ds_atomic_fadd_v2bf16, "V2sV2s*3V2s", "t", "atomic-ds-pk-add-16-insts") TARGET_BUILTIN(__builtin_amdgcn_ds_atomic_fadd_v2f16, "V2hV2h*3V2h", "t", "atomic-ds-pk-add-16-insts") +TARGET_BUILTIN(__builtin_amdgcn_global_load_lds, "vv*1v*3UiiUi", "t", "gfx940-insts") //===----------------------------------------------------------------------===// // Deep learning builtins. diff --git a/clang/include/clang/Basic/DiagnosticDriverKinds.td b/clang/include/clang/Basic/DiagnosticDriverKinds.td index 9d97a75f696f66..50d3b42c0f8661 100644 --- a/clang/include/clang/Basic/DiagnosticDriverKinds.td +++ b/clang/include/clang/Basic/DiagnosticDriverKinds.td @@ -599,9 +599,6 @@ def warn_drv_unsupported_gpopt : Warning< "ignoring '-mgpopt' option as it cannot be used with %select{|the implicit" " usage of }0-mabicalls">, InGroup; -def warn_drv_unsupported_tocdata: Warning< - "ignoring '-mtocdata' as it is only supported for -mcmodel=small">, - InGroup; def warn_drv_unsupported_sdata : Warning< "ignoring '-msmall-data-limit=' with -mcmodel=large for -fpic or RV64">, InGroup; diff --git a/clang/include/clang/Basic/DiagnosticInstallAPIKinds.td b/clang/include/clang/Basic/DiagnosticInstallAPIKinds.td index 674742431dcb2d..944b2a38b6e965 100644 --- a/clang/include/clang/Basic/DiagnosticInstallAPIKinds.td +++ b/clang/include/clang/Basic/DiagnosticInstallAPIKinds.td @@ -24,7 +24,7 @@ def err_no_matching_target : Error<"no matching target found for target variant def err_unsupported_vendor : Error<"vendor '%0' is not supported: '%1'">; def err_unsupported_environment : Error<"environment '%0' is not supported: '%1'">; def err_unsupported_os : Error<"os '%0' is not supported: '%1'">; -def err_cannot_read_input_list : Error<"could not read %select{alias list|filelist}0 '%1': %2">; +def err_cannot_read_input_list : Error<"could not read %0 input list '%1': %2">; def err_invalid_label: Error<"label '%0' is reserved: use a different label name for -X