[llvm-branch-commits] [mlir] [MLIR] Add apply_patterns.vector.arm_sve.lower_contraction TD Op (PR #140572)
@@ -93,3 +99,15 @@ func.func @test_vector_contract_to_usmmla( return %2 : vector<4x[4]xi32> } + +module attributes {transform.with_named_sequence} { + transform.named_sequence @__transform_main(%module: !transform.any_op {transform.readonly}) { +%func = transform.structured.match ops{["func.func"]} in %module : (!transform.any_op) -> !transform.op<"func.func"> + +transform.apply_patterns to %func { + transform.apply_patterns.vector.arm_sve.lower_contraction +} : !transform.op<"func.func"> + +transform.yield + } +} ftynse wrote: Nit: trailing newline please (may require configuring your editor appropriately). https://github.com/llvm/llvm-project/pull/140572 ___ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
[llvm-branch-commits] [mlir] [MLIR] Add apply_patterns.vector.arm_sve.lower_contraction TD Op (PR #140572)
https://github.com/ftynse approved this pull request. https://github.com/llvm/llvm-project/pull/140572 ___ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
[llvm-branch-commits] [llvm] release/20.x: [SDAG] Ensure load is included in output chain of sincos expansion (#140525) (PR #140703)
https://github.com/RKSimon approved this pull request. LGTM https://github.com/llvm/llvm-project/pull/140703 ___ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
[llvm-branch-commits] [llvm] release/20.x: [SDAG] Ensure load is included in output chain of sincos expansion (#140525) (PR #140703)
https://github.com/arsenm approved this pull request. https://github.com/llvm/llvm-project/pull/140703 ___ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
[llvm-branch-commits] [llvm] [BOLT] Gadget scanner: detect authentication oracles (PR #135663)
https://github.com/atrosinenko updated https://github.com/llvm/llvm-project/pull/135663 >From def05034a2fb735f281325b40d0448586fe1e715 Mon Sep 17 00:00:00 2001 From: Anatoly Trosinenko Date: Sat, 5 Apr 2025 14:54:01 +0300 Subject: [PATCH 1/5] [BOLT] Gadget scanner: detect authentication oracles Implement the detection of authentication instructions whose results can be inspected by an attacker to know whether authentication succeeded. As the properties of output registers of authentication instructions are inspected, add a second set of analysis-related classes to iterate over the instructions in reverse order. --- bolt/include/bolt/Passes/PAuthGadgetScanner.h | 12 + bolt/lib/Passes/PAuthGadgetScanner.cpp| 543 + .../AArch64/gs-pauth-authentication-oracles.s | 723 ++ .../AArch64/gs-pauth-debug-output.s | 78 ++ 4 files changed, 1356 insertions(+) create mode 100644 bolt/test/binary-analysis/AArch64/gs-pauth-authentication-oracles.s diff --git a/bolt/include/bolt/Passes/PAuthGadgetScanner.h b/bolt/include/bolt/Passes/PAuthGadgetScanner.h index 98a49df862ebd..a3b39fcd5dc02 100644 --- a/bolt/include/bolt/Passes/PAuthGadgetScanner.h +++ b/bolt/include/bolt/Passes/PAuthGadgetScanner.h @@ -284,6 +284,15 @@ class ClobberingInfo : public ExtraInfo { void print(raw_ostream &OS, const MCInstReference Location) const override; }; +class LeakageInfo : public ExtraInfo { + SmallVector LeakingInstrs; + +public: + LeakageInfo(const ArrayRef Instrs) : LeakingInstrs(Instrs) {} + + void print(raw_ostream &OS, const MCInstReference Location) const override; +}; + /// A brief version of a report that can be further augmented with the details. /// /// A half-baked report produced on the first run of the analysis. An extra, @@ -324,6 +333,9 @@ class FunctionAnalysisContext { void findUnsafeUses(SmallVector> &Reports); void augmentUnsafeUseReports(ArrayRef> Reports); + void findUnsafeDefs(SmallVector> &Reports); + void augmentUnsafeDefReports(ArrayRef> Reports); + /// Process the reports which do not have to be augmented, and remove them /// from Reports. void handleSimpleReports(SmallVector> &Reports); diff --git a/bolt/lib/Passes/PAuthGadgetScanner.cpp b/bolt/lib/Passes/PAuthGadgetScanner.cpp index 24ad4844ffb7d..1dd2e3824e385 100644 --- a/bolt/lib/Passes/PAuthGadgetScanner.cpp +++ b/bolt/lib/Passes/PAuthGadgetScanner.cpp @@ -717,6 +717,459 @@ SrcSafetyAnalysis::create(BinaryFunction &BF, RegsToTrackInstsFor); } +/// A state representing which registers are safe to be used as the destination +/// operand of an authentication instruction. +/// +/// Similar to SrcState, it is the analysis that should take register aliasing +/// into account. +/// +/// Depending on the implementation, it may be possible that an authentication +/// instruction returns an invalid pointer on failure instead of terminating +/// the program immediately (assuming the program will crash as soon as that +/// pointer is dereferenced). To prevent brute-forcing the correct signature, +/// it should be impossible for an attacker to test if a pointer is correctly +/// signed - either the program should be terminated on authentication failure +/// or it should be impossible to tell whether authentication succeeded or not. +/// +/// For that reason, a restricted set of operations is allowed on any register +/// containing a value derived from the result of an authentication instruction +/// until that register is either wiped or checked not to contain a result of a +/// failed authentication. +/// +/// Specifically, the safety property for a register is computed by iterating +/// the instructions in backward order: the source register Xn of an instruction +/// Inst is safe if at least one of the following is true: +/// * Inst checks if Xn contains the result of a successful authentication and +/// terminates the program on failure. Note that Inst can either naturally +/// dereference Xn (load, branch, return, etc. instructions) or be the first +/// instruction of an explicit checking sequence. +/// * Inst performs safe address arithmetic AND both source and result +/// registers, as well as any temporary registers, must be safe after +/// execution of Inst (temporaries are not used on AArch64 and thus not +/// currently supported/allowed). +/// See MCPlusBuilder::analyzeAddressArithmeticsForPtrAuth for the details. +/// * Inst fully overwrites Xn with an unrelated value. +struct DstState { + /// The set of registers whose values cannot be inspected by an attacker in + /// a way usable as an authentication oracle. The results of authentication + /// instructions should be written to such registers. + BitVector CannotEscapeUnchecked; + + std::vector> FirstInstLeakingReg; + + /// Construct an empty state. + DstState() {} + + DstState(unsigned NumRegs, unsigned NumRegsToTrack) + : Cannot
[llvm-branch-commits] [llvm] [BOLT] Gadget scanner: improve handling of unreachable basic blocks (PR #136183)
jacobbramley wrote: Just a thought: if BOLT has an incomplete CFG such that there are apparently-unreachable basic blocks, then either there's some genuine dead code, or some control flow that BOLT doesn't understand. If the basic block begins with `BTI j` (or an implicit alternative) then a computed branch is probably intended, and in that case, don't we have a potential problem for all basic blocks? That is, an attacker could divert a computed branch to _any_ `BTI j(c)`. A warning is probably the right approach for now, anyway. The code looks broadly sensible to me but I'm not sure if I'm the right person to do an implementation review here, so I just looked at a high level. https://github.com/llvm/llvm-project/pull/136183 ___ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
[llvm-branch-commits] [llvm] [BOLT] Factor out MCInstReference from gadget scanner (NFC) (PR #138655)
https://github.com/atrosinenko updated https://github.com/llvm/llvm-project/pull/138655 >From 69b6e028492a787934a4d5a4f23644988cb35af9 Mon Sep 17 00:00:00 2001 From: Anatoly Trosinenko Date: Mon, 28 Apr 2025 18:35:48 +0300 Subject: [PATCH] [BOLT] Factor out MCInstReference from gadget scanner (NFC) Move MCInstReference representing a constant reference to an instruction inside a parent entity - either inside a basic block (which has a reference to its parent function) or directly to the function (when CFG information is not available). --- bolt/include/bolt/Core/MCInstUtils.h | 168 + bolt/include/bolt/Passes/PAuthGadgetScanner.h | 178 +- bolt/lib/Core/CMakeLists.txt | 1 + bolt/lib/Core/MCInstUtils.cpp | 57 ++ bolt/lib/Passes/PAuthGadgetScanner.cpp| 102 +- 5 files changed, 269 insertions(+), 237 deletions(-) create mode 100644 bolt/include/bolt/Core/MCInstUtils.h create mode 100644 bolt/lib/Core/MCInstUtils.cpp diff --git a/bolt/include/bolt/Core/MCInstUtils.h b/bolt/include/bolt/Core/MCInstUtils.h new file mode 100644 index 0..69bf5e6159b74 --- /dev/null +++ b/bolt/include/bolt/Core/MCInstUtils.h @@ -0,0 +1,168 @@ +//===- bolt/Core/MCInstUtils.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 +// +//===--===// + +#ifndef BOLT_CORE_MCINSTUTILS_H +#define BOLT_CORE_MCINSTUTILS_H + +#include "bolt/Core/BinaryBasicBlock.h" + +#include +#include +#include + +namespace llvm { +namespace bolt { + +class BinaryFunction; + +/// MCInstReference represents a reference to a constant MCInst as stored either +/// in a BinaryFunction (i.e. before a CFG is created), or in a BinaryBasicBlock +/// (after a CFG is created). +class MCInstReference { + using nocfg_const_iterator = std::map::const_iterator; + + // Two cases are possible: + // * functions with CFG reconstructed - a function stores a collection of + // basic blocks, each basic block stores a contiguous vector of MCInst + // * functions without CFG - there are no basic blocks created, + // the instructions are directly stored in std::map in BinaryFunction + // + // In both cases, the direct parent of MCInst is stored together with an + // iterator pointing to the instruction. + + // Helper struct: CFG is available, the direct parent is a basic block, + // iterator's type is `MCInst *`. + struct RefInBB { +RefInBB(const BinaryBasicBlock *BB, const MCInst *Inst) +: BB(BB), It(Inst) {} +RefInBB(const RefInBB &Other) = default; +RefInBB &operator=(const RefInBB &Other) = default; + +const BinaryBasicBlock *BB; +BinaryBasicBlock::const_iterator It; + +bool operator<(const RefInBB &Other) const { + return std::tie(BB, It) < std::tie(Other.BB, Other.It); +} + +bool operator==(const RefInBB &Other) const { + return BB == Other.BB && It == Other.It; +} + }; + + // Helper struct: CFG is *not* available, the direct parent is a function, + // iterator's type is std::map::iterator (the mapped value + // is an instruction's offset). + struct RefInBF { +RefInBF(const BinaryFunction *BF, nocfg_const_iterator It) +: BF(BF), It(It) {} +RefInBF(const RefInBF &Other) = default; +RefInBF &operator=(const RefInBF &Other) = default; + +const BinaryFunction *BF; +nocfg_const_iterator It; + +bool operator<(const RefInBF &Other) const { + return std::tie(BF, It->first) < std::tie(Other.BF, Other.It->first); +} + +bool operator==(const RefInBF &Other) const { + return BF == Other.BF && It->first == Other.It->first; +} + }; + + std::variant Reference; + + // Utility methods to be used like this: + // + // if (auto *Ref = tryGetRefInBB()) + // return Ref->doSomething(...); + // return getRefInBF().doSomethingElse(...); + const RefInBB *tryGetRefInBB() const { +assert(std::get_if(&Reference) || + std::get_if(&Reference)); +return std::get_if(&Reference); + } + const RefInBF &getRefInBF() const { +assert(std::get_if(&Reference)); +return *std::get_if(&Reference); + } + +public: + /// Constructs an empty reference. + MCInstReference() : Reference(RefInBB(nullptr, nullptr)) {} + /// Constructs a reference to the instruction inside the basic block. + MCInstReference(const BinaryBasicBlock *BB, const MCInst *Inst) + : Reference(RefInBB(BB, Inst)) { +assert(BB && Inst && "Neither BB nor Inst should be nullptr"); + } + /// Constructs a reference to the instruction inside the basic block. + MCInstReference(const BinaryBasicBlock *BB, unsigned Index) + : Reference(RefInBB(BB, &BB->getInstructionAtIndex(I
[llvm-branch-commits] [clang] [llvm] [HLSL][RootSignature] Add parsing of flags to RootParam (PR #140152)
@@ -46,6 +46,14 @@ enum class RootFlags : uint32_t { ValidFlags = 0x0fff }; +enum class RootDescriptorFlags : unsigned { + None = 0, + DataVolatile = 0x2, + DataStaticWhileSetAtExecute = 0x4, + DataStatic = 0x8, + ValidFlags = 0xe, +}; joaosaffran wrote: Those values are also defined in `BinaryFormat/DXContainer.h`, in case you wanna reuse it. https://github.com/llvm/llvm-project/pull/140152 ___ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
[llvm-branch-commits] [llvm] [HLSL] Adding support for root descriptors in root signature metadata representation (PR #139781)
@@ -155,6 +217,16 @@ static bool verifyVersion(uint32_t Version) { return (Version == 1 || Version == 2); } +static bool verifyRegisterValue(uint32_t RegisterValue) { + return !(RegisterValue == 0x); +} + +static bool verifyRegisterSpace(uint32_t RegisterSpace) { + return !(RegisterSpace >= 0xFFF0 && RegisterSpace <= 0x); +} + +static bool verifyDescriptorFlag(uint32_t Flags) { return (Flags & ~0xE) == 0; } inbelic wrote: self-note: this is only called on v2, so it implicitly handles the different verification based on version. https://github.com/llvm/llvm-project/pull/139781 ___ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
[llvm-branch-commits] [llvm] [HLSL] Adding support for root descriptors in root signature metadata representation (PR #139781)
@@ -105,6 +113,56 @@ static bool parseRootConstants(LLVMContext *Ctx, mcdxbc::RootSignatureDesc &RSD, return false; } +static bool parseRootDescriptors(LLVMContext *Ctx, + mcdxbc::RootSignatureDesc &RSD, + MDNode *RootDescriptorNode) { + + if (RootDescriptorNode->getNumOperands() != 5) +return reportError(Ctx, "Invalid format for RootConstants Element"); + + std::optional ElementText = + extractMdStringValue(RootDescriptorNode, 0); inbelic wrote: We should add a check that this is not `std::nullopt` here and create an error. Otherwise, the below assert or the deference below will caush a crash. https://github.com/llvm/llvm-project/pull/139781 ___ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
[llvm-branch-commits] [llvm] [HLSL] Adding support for root descriptors in root signature metadata representation (PR #139781)
@@ -105,6 +113,56 @@ static bool parseRootConstants(LLVMContext *Ctx, mcdxbc::RootSignatureDesc &RSD, return false; } +static bool parseRootDescriptors(LLVMContext *Ctx, + mcdxbc::RootSignatureDesc &RSD, + MDNode *RootDescriptorNode) { + + if (RootDescriptorNode->getNumOperands() != 5) +return reportError(Ctx, "Invalid format for RootConstants Element"); + + std::optional ElementText = + extractMdStringValue(RootDescriptorNode, 0); + assert(!ElementText->empty()); + + dxbc::RootParameterHeader Header; + Header.ParameterType = + StringSwitch(*ElementText) + .Case("RootCBV", llvm::to_underlying(dxbc::RootParameterType::CBV)) + .Case("RootSRV", llvm::to_underlying(dxbc::RootParameterType::SRV)) + .Case("RootUAV", llvm::to_underlying(dxbc::RootParameterType::UAV)); inbelic wrote: Without a default case, this is equivalent to `llvm_unreachable` right? https://github.com/llvm/llvm-project/pull/139781 ___ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
[llvm-branch-commits] [llvm] [NFC] Refactoring DXContainerYaml Root Parameter representation (PR #138318)
@@ -112,98 +113,17 @@ struct DescriptorTableYaml { SmallVector Ranges; }; +using ParameterData = +std::variant; + struct RootParameterYamlDesc { uint32_t Type; inbelic wrote: This field can be removed now right? IIUC, it is held in the variant edit: Ah wait, it seems we use it when constructing the object from YAML. Is there an easy way we can just use the variant type to do so? https://github.com/llvm/llvm-project/pull/138318 ___ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
[llvm-branch-commits] [llvm] [NFC] Refactoring DXContainerYaml Root Parameter representation (PR #138318)
@@ -278,33 +280,40 @@ void DXContainerWriter::writeParts(raw_ostream &OS) { auto Header = dxbc::RootParameterHeader{Param.Type, Param.Visibility, Param.Offset}; -switch (Param.Type) { -case llvm::to_underlying(dxbc::RootParameterType::Constants32Bit): +if (std::holds_alternative( +Param.Data)) { + auto ConstantYaml = + std::get(Param.Data); + dxbc::RootConstants Constants; - Constants.Num32BitValues = Param.Constants.Num32BitValues; - Constants.RegisterSpace = Param.Constants.RegisterSpace; - Constants.ShaderRegister = Param.Constants.ShaderRegister; + Constants.Num32BitValues = ConstantYaml.Num32BitValues; + Constants.RegisterSpace = ConstantYaml.RegisterSpace; + Constants.ShaderRegister = ConstantYaml.ShaderRegister; RS.ParametersContainer.addParameter(Header, Constants); - break; -case llvm::to_underlying(dxbc::RootParameterType::SRV): -case llvm::to_underlying(dxbc::RootParameterType::UAV): -case llvm::to_underlying(dxbc::RootParameterType::CBV): +} else if (std::holds_alternative( + Param.Data)) { + auto DescriptorYaml = + std::get(Param.Data); inbelic wrote: I think you can use the `std::get_if(&Param.Data)` notations here? https://github.com/llvm/llvm-project/pull/138318 ___ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
[llvm-branch-commits] [clang] [llvm] [HLSL][RootSignature] Add parsing of flags to RootParam (PR #140152)
https://github.com/joaosaffran edited https://github.com/llvm/llvm-project/pull/140152 ___ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
[llvm-branch-commits] [clang] [llvm] [HLSL][RootSignature] Add parsing of flags to RootParam (PR #140152)
@@ -91,6 +99,23 @@ struct RootParam { Register Reg; uint32_t Space = 0; ShaderVisibility Visibility = ShaderVisibility::All; + RootDescriptorFlags Flags; + + void setDefaultFlags() { +assert(Type != ParamType::Sampler && + "Sampler is not a valid type of ParamType"); +switch (Type) { +case ParamType::CBuffer: +case ParamType::SRV: + Flags = RootDescriptorFlags::DataStaticWhileSetAtExecute; + break; +case ParamType::UAV: + Flags = RootDescriptorFlags::DataVolatile; + break; +case ParamType::Sampler: + break; +} + } joaosaffran wrote: Are we planning to modify that when we add support for versioning ? Something like: ```C++ if(Version == 1) Flags = 0; ``` https://github.com/llvm/llvm-project/pull/140152 ___ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
[llvm-branch-commits] [clang] [llvm] [HLSL][RootSignature] Add parsing of flags to RootParam (PR #140152)
https://github.com/joaosaffran approved this pull request. LGTM, Just some minor comments and questions for my own clarification https://github.com/llvm/llvm-project/pull/140152 ___ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
[llvm-branch-commits] [llvm] [HLSL] Adding support for root descriptors in root signature metadata representation (PR #139781)
@@ -308,6 +413,21 @@ PreservedAnalyses RootSignatureAnalysisPrinter::run(Module &M, << "Shader Register: " << Constants->ShaderRegister << "\n"; OS << indent(Space + 2) << "Num 32 Bit Values: " << Constants->Num32BitValues << "\n"; + } else if (std::holds_alternative( + *P)) { +auto *Constants = std::get(*P); +OS << indent(Space + 2) + << "Register Space: " << Constants->RegisterSpace << "\n"; +OS << indent(Space + 2) + << "Shader Register: " << Constants->ShaderRegister << "\n"; + } else if (std::holds_alternative( + *P)) { +auto *Constants = std::get(*P); +OS << indent(Space + 2) + << "Register Space: " << Constants->RegisterSpace << "\n"; +OS << indent(Space + 2) + << "Shader Register: " << Constants->ShaderRegister << "\n"; +OS << indent(Space + 2) << "Flags: " << Constants->Flags << "\n"; inbelic wrote: This doesn't seem to be invoked in any of the test cases? The test cases seems to only output the YAML representation https://github.com/llvm/llvm-project/pull/139781 ___ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
[llvm-branch-commits] [llvm] [NFCI] Avoid adding duplicated SpecialCaseList::Sections. (PR #140821)
https://github.com/qinkunbao updated https://github.com/llvm/llvm-project/pull/140821 >From bb0e2f0bb26a47602978d1ac6f3d73a770075900 Mon Sep 17 00:00:00 2001 From: Qinkun Bao Date: Wed, 21 May 2025 00:28:47 + Subject: [PATCH 1/2] Remove lineno. Created using spr 1.3.6 --- llvm/include/llvm/Support/SpecialCaseList.h | 1 - llvm/lib/Support/SpecialCaseList.cpp| 3 +-- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/llvm/include/llvm/Support/SpecialCaseList.h b/llvm/include/llvm/Support/SpecialCaseList.h index baa5c917220e3..fc6dc93651f38 100644 --- a/llvm/include/llvm/Support/SpecialCaseList.h +++ b/llvm/include/llvm/Support/SpecialCaseList.h @@ -138,7 +138,6 @@ class SpecialCaseList { std::unique_ptr SectionMatcher; SectionEntries Entries; std::string SectionStr; -unsigned LineNo; }; std::vector Sections; diff --git a/llvm/lib/Support/SpecialCaseList.cpp b/llvm/lib/Support/SpecialCaseList.cpp index e8dac4680f96f..2d84f7f22cd7b 100644 --- a/llvm/lib/Support/SpecialCaseList.cpp +++ b/llvm/lib/Support/SpecialCaseList.cpp @@ -139,13 +139,12 @@ SpecialCaseList::addSection(StringRef SectionStr, unsigned LineNo, bool UseGlobs) { auto it = std::find_if(Sections.begin(), Sections.end(), [&](const Section &s) { -return s.SectionStr == SectionStr && s.LineNo == LineNo; +return s.SectionStr == SectionStr; }); if (it == Sections.end()) { Sections.emplace_back(); auto &sec = Sections.back(); sec.SectionStr = SectionStr; -sec.LineNo = LineNo; it = std::prev(Sections.end()); } if (auto Err = it->SectionMatcher->insert(SectionStr, LineNo, UseGlobs)) { >From 6c30ad8f079fe2fccc8fe194daaa0cf087577710 Mon Sep 17 00:00:00 2001 From: Qinkun Bao Date: Wed, 21 May 2025 01:30:49 + Subject: [PATCH 2/2] Add unit tests. Created using spr 1.3.6 --- .../unittests/Support/SpecialCaseListTest.cpp | 21 --- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/llvm/unittests/Support/SpecialCaseListTest.cpp b/llvm/unittests/Support/SpecialCaseListTest.cpp index d6d5621116d72..16da11e2d7835 100644 --- a/llvm/unittests/Support/SpecialCaseListTest.cpp +++ b/llvm/unittests/Support/SpecialCaseListTest.cpp @@ -216,8 +216,9 @@ TEST_F(SpecialCaseListTest, NoTrigramsInARule) { } TEST_F(SpecialCaseListTest, RepetitiveRule) { - std::unique_ptr SCL = makeSpecialCaseList("fun:*bar*bar*bar*bar*\n" - "fun:bar*\n"); + std::unique_ptr SCL = + makeSpecialCaseList("fun:*bar*bar*bar*bar*\n" + "fun:bar*\n"); EXPECT_TRUE(SCL->inSection("", "fun", "bara")); EXPECT_FALSE(SCL->inSection("", "fun", "abara")); EXPECT_TRUE(SCL->inSection("", "fun", "barbarbarbar")); @@ -226,7 +227,8 @@ TEST_F(SpecialCaseListTest, RepetitiveRule) { } TEST_F(SpecialCaseListTest, SpecialSymbolRule) { - std::unique_ptr SCL = makeSpecialCaseList("src:*c\\+\\+abi*\n"); + std::unique_ptr SCL = + makeSpecialCaseList("src:*c\\+\\+abi*\n"); EXPECT_TRUE(SCL->inSection("", "src", "c++abi")); EXPECT_FALSE(SCL->inSection("", "src", "c\\+\\+abi")); } @@ -242,8 +244,9 @@ TEST_F(SpecialCaseListTest, PopularTrigram) { } TEST_F(SpecialCaseListTest, EscapedSymbols) { - std::unique_ptr SCL = makeSpecialCaseList("src:*c\\+\\+abi*\n" - "src:*helloworld*\n"); + std::unique_ptr SCL = + makeSpecialCaseList("src:*c\\+\\+abi*\n" + "src:*helloworld*\n"); EXPECT_TRUE(SCL->inSection("", "src", "dir/c++abi")); EXPECT_FALSE(SCL->inSection("", "src", "dir/c\\+\\+abi")); EXPECT_FALSE(SCL->inSection("", "src", "c\\+\\+abi")); @@ -315,7 +318,9 @@ TEST_F(SpecialCaseListTest, Version3) { "src:def\n" "[sect2]\n" "src:def\n" - "src:def\n"); + "src:def\n" + "[sect1]\n" + "src:foo*\n"); EXPECT_TRUE(SCL->inSection("sect1", "src", "fooz")); EXPECT_TRUE(SCL->inSection("sect1", "src", "barz")); EXPECT_FALSE(SCL->inSection("sect2", "src", "fooz")); @@ -323,9 +328,9 @@ TEST_F(SpecialCaseListTest, Version3) { EXPECT_TRUE(SCL->inSection("sect2", "src", "def")); EXPECT_TRUE(SCL->inSection("sect1", "src", "def")); - EXPECT_EQ(2u, SCL->inSectionBlame("sect1", "src", "fooz")); EXPECT_EQ(4u, SCL->inSectionBlame("sect1", "src", "barz")); EXPECT_EQ(5u, SCL->inSectionBlame("sect1", "src", "def")); EXPECT_EQ(8u, SCL->inSectionBlame("sect2", "src", "def")); + EXPECT_EQ(10u, SCL
[llvm-branch-commits] [llvm] [NFCI] Avoid adding duplicated SpecialCaseList::Sections. (PR #140821)
https://github.com/qinkunbao updated https://github.com/llvm/llvm-project/pull/140821 >From bb0e2f0bb26a47602978d1ac6f3d73a770075900 Mon Sep 17 00:00:00 2001 From: Qinkun Bao Date: Wed, 21 May 2025 00:28:47 + Subject: [PATCH] Remove lineno. Created using spr 1.3.6 --- llvm/include/llvm/Support/SpecialCaseList.h | 1 - llvm/lib/Support/SpecialCaseList.cpp| 3 +-- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/llvm/include/llvm/Support/SpecialCaseList.h b/llvm/include/llvm/Support/SpecialCaseList.h index baa5c917220e3..fc6dc93651f38 100644 --- a/llvm/include/llvm/Support/SpecialCaseList.h +++ b/llvm/include/llvm/Support/SpecialCaseList.h @@ -138,7 +138,6 @@ class SpecialCaseList { std::unique_ptr SectionMatcher; SectionEntries Entries; std::string SectionStr; -unsigned LineNo; }; std::vector Sections; diff --git a/llvm/lib/Support/SpecialCaseList.cpp b/llvm/lib/Support/SpecialCaseList.cpp index e8dac4680f96f..2d84f7f22cd7b 100644 --- a/llvm/lib/Support/SpecialCaseList.cpp +++ b/llvm/lib/Support/SpecialCaseList.cpp @@ -139,13 +139,12 @@ SpecialCaseList::addSection(StringRef SectionStr, unsigned LineNo, bool UseGlobs) { auto it = std::find_if(Sections.begin(), Sections.end(), [&](const Section &s) { -return s.SectionStr == SectionStr && s.LineNo == LineNo; +return s.SectionStr == SectionStr; }); if (it == Sections.end()) { Sections.emplace_back(); auto &sec = Sections.back(); sec.SectionStr = SectionStr; -sec.LineNo = LineNo; it = std::prev(Sections.end()); } if (auto Err = it->SectionMatcher->insert(SectionStr, LineNo, UseGlobs)) { ___ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
[llvm-branch-commits] [clang] [llvm] [HLSL][RootSignature] Add parsing of flags to RootParam (PR #140152)
@@ -91,6 +99,23 @@ struct RootParam { Register Reg; uint32_t Space = 0; ShaderVisibility Visibility = ShaderVisibility::All; + RootDescriptorFlags Flags; + + void setDefaultFlags() { +assert(Type != ParamType::Sampler && + "Sampler is not a valid type of ParamType"); +switch (Type) { +case ParamType::CBuffer: +case ParamType::SRV: + Flags = RootDescriptorFlags::DataStaticWhileSetAtExecute; + break; +case ParamType::UAV: + Flags = RootDescriptorFlags::DataVolatile; + break; +case ParamType::Sampler: + break; +} + } inbelic wrote: That is correct. Current implementation assume the most permissive version (2) https://github.com/llvm/llvm-project/pull/140152 ___ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
[llvm-branch-commits] [clang-tools-extra] [clang-doc] Extract Info into JSON values (PR #138063)
https://github.com/evelez7 edited https://github.com/llvm/llvm-project/pull/138063 ___ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
[llvm-branch-commits] [clang-tools-extra] [clang-doc] Extract Info into JSON values (PR #138063)
@@ -162,15 +162,271 @@ Error MustacheHTMLGenerator::generateDocs( return Error::success(); } +static json::Value +extractValue(const Location &L, + std::optional RepositoryUrl = std::nullopt) { + Object Obj = Object(); + // TODO: Consider using both Start/End line numbers to improve location report + Obj.insert({"LineNumber", L.StartLineNumber}); + Obj.insert({"Filename", L.Filename}); + + if (!L.IsFileInRootDir || !RepositoryUrl) +return Obj; + SmallString<128> FileURL(*RepositoryUrl); + sys::path::append(FileURL, sys::path::Style::posix, L.Filename); + FileURL += "#" + std::to_string(L.StartLineNumber); + Obj.insert({"FileURL", FileURL}); + + return Obj; +} + +static json::Value extractValue(const Reference &I, +StringRef CurrentDirectory) { + SmallString<64> Path = I.getRelativeFilePath(CurrentDirectory); + sys::path::append(Path, I.getFileBaseName() + ".html"); + sys::path::native(Path, sys::path::Style::posix); + Object Obj = Object(); + Obj.insert({"Link", Path}); + Obj.insert({"Name", I.Name}); + Obj.insert({"QualName", I.QualName}); + Obj.insert({"ID", toHex(toStringRef(I.USR))}); + return Obj; +} + +static json::Value extractValue(const TypedefInfo &I) { + // Not Supported + return nullptr; +} + +static json::Value extractValue(const CommentInfo &I) { + assert((I.Kind == "BlockCommandComment" || I.Kind == "FullComment" || + I.Kind == "ParagraphComment" || I.Kind == "TextComment") && + "Unknown Comment type in CommentInfo."); + + Object Obj = Object(); + json::Value Child = Object(); + + // TextComment has no children, so return it. + if (I.Kind == "TextComment") { +Obj.insert({"TextComment", I.Text}); +return Obj; + } + + // BlockCommandComment needs to generate a Command key. + if (I.Kind == "BlockCommandComment") +Child.getAsObject()->insert({"Command", I.Name}); + + // Use the same handling for everything else. + // Only valid for: + // - BlockCommandComment + // - FullComment + // - ParagraphComment + json::Value ChildArr = Array(); + auto &CARef = *ChildArr.getAsArray(); + CARef.reserve(I.Children.size()); + for (const auto &C : I.Children) +CARef.emplace_back(extractValue(*C)); + Child.getAsObject()->insert({"Children", ChildArr}); + Obj.insert({I.Kind, Child}); + + return Obj; +} + +static void maybeInsertLocation(std::optional Loc, +const ClangDocContext &CDCtx, Object &Obj) { + if (!Loc) +return; + Location L = *Loc; + Obj.insert({"Location", extractValue(L, CDCtx.RepositoryUrl)}); +} + +static void extractDescriptionFromInfo(ArrayRef Descriptions, + json::Object &EnumValObj) { + if (Descriptions.empty()) +return; + json::Value DescArr = Array(); + json::Array &DescARef = *DescArr.getAsArray(); + for (const CommentInfo &Child : Descriptions) +DescARef.emplace_back(extractValue(Child)); + EnumValObj.insert({"EnumValueComments", DescArr}); +} + +static json::Value extractValue(const FunctionInfo &I, StringRef ParentInfoDir, +const ClangDocContext &CDCtx) { + Object Obj = Object(); + Obj.insert({"Name", I.Name}); + Obj.insert({"ID", toHex(toStringRef(I.USR))}); + Obj.insert({"Access", getAccessSpelling(I.Access).str()}); + Obj.insert({"ReturnType", extractValue(I.ReturnType.Type, ParentInfoDir)}); + + json::Value ParamArr = Array(); + json::Array &ParamARef = *ParamArr.getAsArray(); + for (const auto Val : enumerate(I.Params)) { +json::Value V = Object(); +auto &VRef = *V.getAsObject(); +VRef.insert({"Name", Val.value().Name}); +VRef.insert({"Type", Val.value().Type.Name}); +VRef.insert({"End", Val.index() + 1 == I.Params.size()}); +ParamARef.emplace_back(V); + } + Obj.insert({"Params", ParamArr}); + + maybeInsertLocation(I.DefLoc, CDCtx, Obj); + return Obj; +} + +static json::Value extractValue(const EnumInfo &I, +const ClangDocContext &CDCtx) { + Object Obj = Object(); + std::string EnumType = I.Scoped ? "enum class " : "enum "; + EnumType += I.Name; + bool HasComment = std::any_of( + I.Members.begin(), I.Members.end(), + [](const EnumValueInfo &M) { return !M.Description.empty(); }); + Obj.insert({"EnumName", EnumType}); + Obj.insert({"HasComment", HasComment}); + Obj.insert({"ID", toHex(toStringRef(I.USR))}); + json::Value EnumArr = Array(); + json::Array &EnumARef = *EnumArr.getAsArray(); + for (const EnumValueInfo &M : I.Members) { +json::Value EnumValue = Object(); +auto &EnumValObj = *EnumValue.getAsObject(); +EnumValObj.insert({"Name", M.Name}); +if (!M.ValueExpr.empty()) + EnumValObj.insert({"ValueExpr", M.ValueExpr}); +else + EnumValObj.insert({"Value", M.Value}); + +extractDescriptionFromInfo(M.Description, EnumValObj); +EnumARef.emplace_back(EnumValue); + } + Obj.insert({"EnumValues", E
[llvm-branch-commits] [clang-tools-extra] [clang-doc] Extract Info into JSON values (PR #138063)
@@ -162,15 +162,271 @@ Error MustacheHTMLGenerator::generateDocs( return Error::success(); } +static json::Value +extractValue(const Location &L, + std::optional RepositoryUrl = std::nullopt) { + Object Obj = Object(); + // TODO: Consider using both Start/End line numbers to improve location report + Obj.insert({"LineNumber", L.StartLineNumber}); + Obj.insert({"Filename", L.Filename}); + + if (!L.IsFileInRootDir || !RepositoryUrl) +return Obj; + SmallString<128> FileURL(*RepositoryUrl); + sys::path::append(FileURL, sys::path::Style::posix, L.Filename); + FileURL += "#" + std::to_string(L.StartLineNumber); + Obj.insert({"FileURL", FileURL}); + + return Obj; +} + +static json::Value extractValue(const Reference &I, +StringRef CurrentDirectory) { + SmallString<64> Path = I.getRelativeFilePath(CurrentDirectory); + sys::path::append(Path, I.getFileBaseName() + ".html"); + sys::path::native(Path, sys::path::Style::posix); + Object Obj = Object(); + Obj.insert({"Link", Path}); + Obj.insert({"Name", I.Name}); + Obj.insert({"QualName", I.QualName}); + Obj.insert({"ID", toHex(toStringRef(I.USR))}); + return Obj; +} + +static json::Value extractValue(const TypedefInfo &I) { + // Not Supported + return nullptr; +} + +static json::Value extractValue(const CommentInfo &I) { + assert((I.Kind == "BlockCommandComment" || I.Kind == "FullComment" || + I.Kind == "ParagraphComment" || I.Kind == "TextComment") && + "Unknown Comment type in CommentInfo."); + + Object Obj = Object(); + json::Value Child = Object(); + + // TextComment has no children, so return it. + if (I.Kind == "TextComment") { +Obj.insert({"TextComment", I.Text}); +return Obj; + } + + // BlockCommandComment needs to generate a Command key. + if (I.Kind == "BlockCommandComment") +Child.getAsObject()->insert({"Command", I.Name}); + + // Use the same handling for everything else. + // Only valid for: + // - BlockCommandComment + // - FullComment + // - ParagraphComment + json::Value ChildArr = Array(); + auto &CARef = *ChildArr.getAsArray(); + CARef.reserve(I.Children.size()); + for (const auto &C : I.Children) +CARef.emplace_back(extractValue(*C)); + Child.getAsObject()->insert({"Children", ChildArr}); + Obj.insert({I.Kind, Child}); + + return Obj; +} + +static void maybeInsertLocation(std::optional Loc, +const ClangDocContext &CDCtx, Object &Obj) { + if (!Loc) +return; + Location L = *Loc; + Obj.insert({"Location", extractValue(L, CDCtx.RepositoryUrl)}); +} + +static void extractDescriptionFromInfo(ArrayRef Descriptions, + json::Object &EnumValObj) { + if (Descriptions.empty()) +return; + json::Value DescArr = Array(); + json::Array &DescARef = *DescArr.getAsArray(); + for (const CommentInfo &Child : Descriptions) +DescARef.emplace_back(extractValue(Child)); + EnumValObj.insert({"EnumValueComments", DescArr}); +} + +static json::Value extractValue(const FunctionInfo &I, StringRef ParentInfoDir, +const ClangDocContext &CDCtx) { + Object Obj = Object(); + Obj.insert({"Name", I.Name}); + Obj.insert({"ID", toHex(toStringRef(I.USR))}); + Obj.insert({"Access", getAccessSpelling(I.Access).str()}); + Obj.insert({"ReturnType", extractValue(I.ReturnType.Type, ParentInfoDir)}); + + json::Value ParamArr = Array(); + json::Array &ParamARef = *ParamArr.getAsArray(); + for (const auto Val : enumerate(I.Params)) { +json::Value V = Object(); +auto &VRef = *V.getAsObject(); +VRef.insert({"Name", Val.value().Name}); +VRef.insert({"Type", Val.value().Type.Name}); +VRef.insert({"End", Val.index() + 1 == I.Params.size()}); +ParamARef.emplace_back(V); + } + Obj.insert({"Params", ParamArr}); + + maybeInsertLocation(I.DefLoc, CDCtx, Obj); + return Obj; +} + +static json::Value extractValue(const EnumInfo &I, +const ClangDocContext &CDCtx) { + Object Obj = Object(); + std::string EnumType = I.Scoped ? "enum class " : "enum "; + EnumType += I.Name; + bool HasComment = std::any_of( + I.Members.begin(), I.Members.end(), + [](const EnumValueInfo &M) { return !M.Description.empty(); }); + Obj.insert({"EnumName", EnumType}); + Obj.insert({"HasComment", HasComment}); + Obj.insert({"ID", toHex(toStringRef(I.USR))}); + json::Value EnumArr = Array(); + json::Array &EnumARef = *EnumArr.getAsArray(); + for (const EnumValueInfo &M : I.Members) { +json::Value EnumValue = Object(); +auto &EnumValObj = *EnumValue.getAsObject(); +EnumValObj.insert({"Name", M.Name}); +if (!M.ValueExpr.empty()) + EnumValObj.insert({"ValueExpr", M.ValueExpr}); +else + EnumValObj.insert({"Value", M.Value}); + +extractDescriptionFromInfo(M.Description, EnumValObj); +EnumARef.emplace_back(EnumValue); + } + Obj.insert({"EnumValues", E
[llvm-branch-commits] [clang-tools-extra] [clang-doc] Extract Info into JSON values (PR #138063)
https://github.com/evelez7 edited https://github.com/llvm/llvm-project/pull/138063 ___ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
[llvm-branch-commits] [NFCI] Avoid adding duplicated SpecialCaseList::Sections. (PR #140821)
https://github.com/qinkunbao created https://github.com/llvm/llvm-project/pull/140821 https://github.com/llvm/llvm-project/pull/140127 converts SpecialCaseList::Sections from StringMap to vector. However, the previous StringMap ensures that only a new section is created when the SectionStr is different. We should keep the same behavior. ___ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
[llvm-branch-commits] [NFCI] Avoid adding duplicated SpecialCaseList::Sections. (PR #140821)
llvmbot wrote: @llvm/pr-subscribers-llvm-support Author: Qinkun Bao (qinkunbao) Changes https://github.com/llvm/llvm-project/pull/140127 converts SpecialCaseList::Sections from StringMap to vector. However, the previous StringMap ensures that only a new section is created when the SectionStr is different. We should keep the same behavior. --- Full diff: https://github.com/llvm/llvm-project/pull/140821.diff 2 Files Affected: - (modified) llvm/include/llvm/Support/SpecialCaseList.h (+1) - (modified) llvm/lib/Support/SpecialCaseList.cpp (+13-6) ``diff diff --git a/llvm/include/llvm/Support/SpecialCaseList.h b/llvm/include/llvm/Support/SpecialCaseList.h index fc6dc93651f38..baa5c917220e3 100644 --- a/llvm/include/llvm/Support/SpecialCaseList.h +++ b/llvm/include/llvm/Support/SpecialCaseList.h @@ -138,6 +138,7 @@ class SpecialCaseList { std::unique_ptr SectionMatcher; SectionEntries Entries; std::string SectionStr; +unsigned LineNo; }; std::vector Sections; diff --git a/llvm/lib/Support/SpecialCaseList.cpp b/llvm/lib/Support/SpecialCaseList.cpp index 514591e3b..e8dac4680f96f 100644 --- a/llvm/lib/Support/SpecialCaseList.cpp +++ b/llvm/lib/Support/SpecialCaseList.cpp @@ -137,18 +137,25 @@ bool SpecialCaseList::createInternal(const MemoryBuffer *MB, Expected SpecialCaseList::addSection(StringRef SectionStr, unsigned LineNo, bool UseGlobs) { - Sections.emplace_back(); - auto &Section = Sections.back(); - Section.SectionStr = SectionStr; - - if (auto Err = Section.SectionMatcher->insert(SectionStr, LineNo, UseGlobs)) { + auto it = + std::find_if(Sections.begin(), Sections.end(), [&](const Section &s) { +return s.SectionStr == SectionStr && s.LineNo == LineNo; + }); + if (it == Sections.end()) { +Sections.emplace_back(); +auto &sec = Sections.back(); +sec.SectionStr = SectionStr; +sec.LineNo = LineNo; +it = std::prev(Sections.end()); + } + if (auto Err = it->SectionMatcher->insert(SectionStr, LineNo, UseGlobs)) { return createStringError(errc::invalid_argument, "malformed section at line " + Twine(LineNo) + ": '" + SectionStr + "': " + toString(std::move(Err))); } - return &Section; + return &(*it); } bool SpecialCaseList::parse(const MemoryBuffer *MB, std::string &Error) { `` https://github.com/llvm/llvm-project/pull/140821 ___ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
[llvm-branch-commits] [clang-tools-extra] [clang-doc] Extract Info into JSON values (PR #138063)
@@ -162,15 +162,271 @@ Error MustacheHTMLGenerator::generateDocs( return Error::success(); } +static json::Value +extractValue(const Location &L, + std::optional RepositoryUrl = std::nullopt) { + Object Obj = Object(); + // TODO: Consider using both Start/End line numbers to improve location report + Obj.insert({"LineNumber", L.StartLineNumber}); + Obj.insert({"Filename", L.Filename}); + + if (!L.IsFileInRootDir || !RepositoryUrl) +return Obj; + SmallString<128> FileURL(*RepositoryUrl); + sys::path::append(FileURL, sys::path::Style::posix, L.Filename); + FileURL += "#" + std::to_string(L.StartLineNumber); + Obj.insert({"FileURL", FileURL}); + + return Obj; +} + +static json::Value extractValue(const Reference &I, +StringRef CurrentDirectory) { + SmallString<64> Path = I.getRelativeFilePath(CurrentDirectory); + sys::path::append(Path, I.getFileBaseName() + ".html"); + sys::path::native(Path, sys::path::Style::posix); + Object Obj = Object(); + Obj.insert({"Link", Path}); + Obj.insert({"Name", I.Name}); + Obj.insert({"QualName", I.QualName}); + Obj.insert({"ID", toHex(toStringRef(I.USR))}); + return Obj; +} + +static json::Value extractValue(const TypedefInfo &I) { + // Not Supported + return nullptr; +} + +static json::Value extractValue(const CommentInfo &I) { + assert((I.Kind == "BlockCommandComment" || I.Kind == "FullComment" || + I.Kind == "ParagraphComment" || I.Kind == "TextComment") && + "Unknown Comment type in CommentInfo."); + + Object Obj = Object(); + json::Value Child = Object(); + + // TextComment has no children, so return it. + if (I.Kind == "TextComment") { +Obj.insert({"TextComment", I.Text}); +return Obj; + } + + // BlockCommandComment needs to generate a Command key. + if (I.Kind == "BlockCommandComment") +Child.getAsObject()->insert({"Command", I.Name}); + + // Use the same handling for everything else. + // Only valid for: + // - BlockCommandComment + // - FullComment + // - ParagraphComment + json::Value ChildArr = Array(); + auto &CARef = *ChildArr.getAsArray(); + CARef.reserve(I.Children.size()); + for (const auto &C : I.Children) +CARef.emplace_back(extractValue(*C)); + Child.getAsObject()->insert({"Children", ChildArr}); + Obj.insert({I.Kind, Child}); + + return Obj; +} + +static void maybeInsertLocation(std::optional Loc, +const ClangDocContext &CDCtx, Object &Obj) { + if (!Loc) +return; + Location L = *Loc; + Obj.insert({"Location", extractValue(L, CDCtx.RepositoryUrl)}); +} + +static void extractDescriptionFromInfo(ArrayRef Descriptions, + json::Object &EnumValObj) { + if (Descriptions.empty()) +return; + json::Value DescArr = Array(); + json::Array &DescARef = *DescArr.getAsArray(); + for (const CommentInfo &Child : Descriptions) +DescARef.emplace_back(extractValue(Child)); + EnumValObj.insert({"EnumValueComments", DescArr}); +} + +static json::Value extractValue(const FunctionInfo &I, StringRef ParentInfoDir, +const ClangDocContext &CDCtx) { + Object Obj = Object(); + Obj.insert({"Name", I.Name}); + Obj.insert({"ID", toHex(toStringRef(I.USR))}); + Obj.insert({"Access", getAccessSpelling(I.Access).str()}); + Obj.insert({"ReturnType", extractValue(I.ReturnType.Type, ParentInfoDir)}); + + json::Value ParamArr = Array(); + json::Array &ParamARef = *ParamArr.getAsArray(); + for (const auto Val : enumerate(I.Params)) { +json::Value V = Object(); +auto &VRef = *V.getAsObject(); +VRef.insert({"Name", Val.value().Name}); +VRef.insert({"Type", Val.value().Type.Name}); +VRef.insert({"End", Val.index() + 1 == I.Params.size()}); +ParamARef.emplace_back(V); + } + Obj.insert({"Params", ParamArr}); + + maybeInsertLocation(I.DefLoc, CDCtx, Obj); + return Obj; +} + +static json::Value extractValue(const EnumInfo &I, +const ClangDocContext &CDCtx) { + Object Obj = Object(); + std::string EnumType = I.Scoped ? "enum class " : "enum "; + EnumType += I.Name; + bool HasComment = std::any_of( + I.Members.begin(), I.Members.end(), + [](const EnumValueInfo &M) { return !M.Description.empty(); }); + Obj.insert({"EnumName", EnumType}); + Obj.insert({"HasComment", HasComment}); + Obj.insert({"ID", toHex(toStringRef(I.USR))}); + json::Value EnumArr = Array(); + json::Array &EnumARef = *EnumArr.getAsArray(); + for (const EnumValueInfo &M : I.Members) { +json::Value EnumValue = Object(); +auto &EnumValObj = *EnumValue.getAsObject(); +EnumValObj.insert({"Name", M.Name}); +if (!M.ValueExpr.empty()) + EnumValObj.insert({"ValueExpr", M.ValueExpr}); +else + EnumValObj.insert({"Value", M.Value}); + +extractDescriptionFromInfo(M.Description, EnumValObj); +EnumARef.emplace_back(EnumValue); + } + Obj.insert({"EnumValues", E
[llvm-branch-commits] [clang-tools-extra] [clang-doc] Extract Info into JSON values (PR #138063)
@@ -162,15 +162,271 @@ Error MustacheHTMLGenerator::generateDocs( return Error::success(); } +static json::Value +extractValue(const Location &L, + std::optional RepositoryUrl = std::nullopt) { + Object Obj = Object(); + // TODO: Consider using both Start/End line numbers to improve location report + Obj.insert({"LineNumber", L.StartLineNumber}); + Obj.insert({"Filename", L.Filename}); + + if (!L.IsFileInRootDir || !RepositoryUrl) +return Obj; + SmallString<128> FileURL(*RepositoryUrl); + sys::path::append(FileURL, sys::path::Style::posix, L.Filename); + FileURL += "#" + std::to_string(L.StartLineNumber); + Obj.insert({"FileURL", FileURL}); + + return Obj; +} + +static json::Value extractValue(const Reference &I, +StringRef CurrentDirectory) { + SmallString<64> Path = I.getRelativeFilePath(CurrentDirectory); + sys::path::append(Path, I.getFileBaseName() + ".html"); + sys::path::native(Path, sys::path::Style::posix); + Object Obj = Object(); + Obj.insert({"Link", Path}); + Obj.insert({"Name", I.Name}); + Obj.insert({"QualName", I.QualName}); + Obj.insert({"ID", toHex(toStringRef(I.USR))}); + return Obj; +} + +static json::Value extractValue(const TypedefInfo &I) { + // Not Supported + return nullptr; +} + +static json::Value extractValue(const CommentInfo &I) { + assert((I.Kind == "BlockCommandComment" || I.Kind == "FullComment" || + I.Kind == "ParagraphComment" || I.Kind == "TextComment") && + "Unknown Comment type in CommentInfo."); + + Object Obj = Object(); + json::Value Child = Object(); + + // TextComment has no children, so return it. + if (I.Kind == "TextComment") { +Obj.insert({"TextComment", I.Text}); +return Obj; + } + + // BlockCommandComment needs to generate a Command key. + if (I.Kind == "BlockCommandComment") +Child.getAsObject()->insert({"Command", I.Name}); + + // Use the same handling for everything else. + // Only valid for: + // - BlockCommandComment + // - FullComment + // - ParagraphComment + json::Value ChildArr = Array(); + auto &CARef = *ChildArr.getAsArray(); + CARef.reserve(I.Children.size()); + for (const auto &C : I.Children) +CARef.emplace_back(extractValue(*C)); + Child.getAsObject()->insert({"Children", ChildArr}); + Obj.insert({I.Kind, Child}); + + return Obj; +} + +static void maybeInsertLocation(std::optional Loc, +const ClangDocContext &CDCtx, Object &Obj) { + if (!Loc) +return; + Location L = *Loc; + Obj.insert({"Location", extractValue(L, CDCtx.RepositoryUrl)}); +} + +static void extractDescriptionFromInfo(ArrayRef Descriptions, + json::Object &EnumValObj) { + if (Descriptions.empty()) +return; + json::Value DescArr = Array(); + json::Array &DescARef = *DescArr.getAsArray(); + for (const CommentInfo &Child : Descriptions) +DescARef.emplace_back(extractValue(Child)); + EnumValObj.insert({"EnumValueComments", DescArr}); +} + +static json::Value extractValue(const FunctionInfo &I, StringRef ParentInfoDir, +const ClangDocContext &CDCtx) { + Object Obj = Object(); + Obj.insert({"Name", I.Name}); + Obj.insert({"ID", toHex(toStringRef(I.USR))}); + Obj.insert({"Access", getAccessSpelling(I.Access).str()}); + Obj.insert({"ReturnType", extractValue(I.ReturnType.Type, ParentInfoDir)}); + + json::Value ParamArr = Array(); + json::Array &ParamARef = *ParamArr.getAsArray(); + for (const auto Val : enumerate(I.Params)) { +json::Value V = Object(); +auto &VRef = *V.getAsObject(); +VRef.insert({"Name", Val.value().Name}); +VRef.insert({"Type", Val.value().Type.Name}); +VRef.insert({"End", Val.index() + 1 == I.Params.size()}); +ParamARef.emplace_back(V); + } + Obj.insert({"Params", ParamArr}); + + maybeInsertLocation(I.DefLoc, CDCtx, Obj); + return Obj; +} + +static json::Value extractValue(const EnumInfo &I, +const ClangDocContext &CDCtx) { + Object Obj = Object(); + std::string EnumType = I.Scoped ? "enum class " : "enum "; + EnumType += I.Name; + bool HasComment = std::any_of( + I.Members.begin(), I.Members.end(), + [](const EnumValueInfo &M) { return !M.Description.empty(); }); + Obj.insert({"EnumName", EnumType}); + Obj.insert({"HasComment", HasComment}); + Obj.insert({"ID", toHex(toStringRef(I.USR))}); + json::Value EnumArr = Array(); + json::Array &EnumARef = *EnumArr.getAsArray(); + for (const EnumValueInfo &M : I.Members) { +json::Value EnumValue = Object(); +auto &EnumValObj = *EnumValue.getAsObject(); +EnumValObj.insert({"Name", M.Name}); +if (!M.ValueExpr.empty()) + EnumValObj.insert({"ValueExpr", M.ValueExpr}); +else + EnumValObj.insert({"Value", M.Value}); + +extractDescriptionFromInfo(M.Description, EnumValObj); +EnumARef.emplace_back(EnumValue); + } + Obj.insert({"EnumValues", E
[llvm-branch-commits] [clang-tools-extra] [clang-doc] Extract Info into JSON values (PR #138063)
@@ -162,15 +162,271 @@ Error MustacheHTMLGenerator::generateDocs( return Error::success(); } +static json::Value +extractValue(const Location &L, + std::optional RepositoryUrl = std::nullopt) { + Object Obj = Object(); + // TODO: Consider using both Start/End line numbers to improve location report + Obj.insert({"LineNumber", L.StartLineNumber}); + Obj.insert({"Filename", L.Filename}); + + if (!L.IsFileInRootDir || !RepositoryUrl) +return Obj; + SmallString<128> FileURL(*RepositoryUrl); + sys::path::append(FileURL, sys::path::Style::posix, L.Filename); + FileURL += "#" + std::to_string(L.StartLineNumber); + Obj.insert({"FileURL", FileURL}); + + return Obj; +} + +static json::Value extractValue(const Reference &I, +StringRef CurrentDirectory) { + SmallString<64> Path = I.getRelativeFilePath(CurrentDirectory); + sys::path::append(Path, I.getFileBaseName() + ".html"); + sys::path::native(Path, sys::path::Style::posix); + Object Obj = Object(); + Obj.insert({"Link", Path}); + Obj.insert({"Name", I.Name}); + Obj.insert({"QualName", I.QualName}); + Obj.insert({"ID", toHex(toStringRef(I.USR))}); + return Obj; +} + +static json::Value extractValue(const TypedefInfo &I) { + // Not Supported + return nullptr; +} + +static json::Value extractValue(const CommentInfo &I) { + assert((I.Kind == "BlockCommandComment" || I.Kind == "FullComment" || + I.Kind == "ParagraphComment" || I.Kind == "TextComment") && + "Unknown Comment type in CommentInfo."); + + Object Obj = Object(); + json::Value Child = Object(); + + // TextComment has no children, so return it. + if (I.Kind == "TextComment") { +Obj.insert({"TextComment", I.Text}); +return Obj; + } + + // BlockCommandComment needs to generate a Command key. + if (I.Kind == "BlockCommandComment") +Child.getAsObject()->insert({"Command", I.Name}); + + // Use the same handling for everything else. + // Only valid for: + // - BlockCommandComment + // - FullComment + // - ParagraphComment + json::Value ChildArr = Array(); + auto &CARef = *ChildArr.getAsArray(); + CARef.reserve(I.Children.size()); + for (const auto &C : I.Children) +CARef.emplace_back(extractValue(*C)); + Child.getAsObject()->insert({"Children", ChildArr}); + Obj.insert({I.Kind, Child}); + + return Obj; +} + +static void maybeInsertLocation(std::optional Loc, +const ClangDocContext &CDCtx, Object &Obj) { + if (!Loc) +return; + Location L = *Loc; + Obj.insert({"Location", extractValue(L, CDCtx.RepositoryUrl)}); +} + +static void extractDescriptionFromInfo(ArrayRef Descriptions, + json::Object &EnumValObj) { + if (Descriptions.empty()) +return; + json::Value DescArr = Array(); + json::Array &DescARef = *DescArr.getAsArray(); + for (const CommentInfo &Child : Descriptions) +DescARef.emplace_back(extractValue(Child)); + EnumValObj.insert({"EnumValueComments", DescArr}); +} + +static json::Value extractValue(const FunctionInfo &I, StringRef ParentInfoDir, +const ClangDocContext &CDCtx) { + Object Obj = Object(); + Obj.insert({"Name", I.Name}); + Obj.insert({"ID", toHex(toStringRef(I.USR))}); + Obj.insert({"Access", getAccessSpelling(I.Access).str()}); + Obj.insert({"ReturnType", extractValue(I.ReturnType.Type, ParentInfoDir)}); + + json::Value ParamArr = Array(); + json::Array &ParamARef = *ParamArr.getAsArray(); + for (const auto Val : enumerate(I.Params)) { +json::Value V = Object(); +auto &VRef = *V.getAsObject(); +VRef.insert({"Name", Val.value().Name}); +VRef.insert({"Type", Val.value().Type.Name}); +VRef.insert({"End", Val.index() + 1 == I.Params.size()}); +ParamARef.emplace_back(V); + } + Obj.insert({"Params", ParamArr}); + + maybeInsertLocation(I.DefLoc, CDCtx, Obj); + return Obj; +} + +static json::Value extractValue(const EnumInfo &I, +const ClangDocContext &CDCtx) { + Object Obj = Object(); + std::string EnumType = I.Scoped ? "enum class " : "enum "; + EnumType += I.Name; + bool HasComment = std::any_of( + I.Members.begin(), I.Members.end(), + [](const EnumValueInfo &M) { return !M.Description.empty(); }); + Obj.insert({"EnumName", EnumType}); + Obj.insert({"HasComment", HasComment}); + Obj.insert({"ID", toHex(toStringRef(I.USR))}); + json::Value EnumArr = Array(); + json::Array &EnumARef = *EnumArr.getAsArray(); + for (const EnumValueInfo &M : I.Members) { +json::Value EnumValue = Object(); +auto &EnumValObj = *EnumValue.getAsObject(); +EnumValObj.insert({"Name", M.Name}); +if (!M.ValueExpr.empty()) + EnumValObj.insert({"ValueExpr", M.ValueExpr}); +else + EnumValObj.insert({"Value", M.Value}); + +extractDescriptionFromInfo(M.Description, EnumValObj); +EnumARef.emplace_back(EnumValue); + } + Obj.insert({"EnumValues", E
[llvm-branch-commits] [clang-tools-extra] [clang-doc] Extract Info into JSON values (PR #138063)
@@ -162,15 +162,271 @@ Error MustacheHTMLGenerator::generateDocs( return Error::success(); } +static json::Value +extractValue(const Location &L, + std::optional RepositoryUrl = std::nullopt) { + Object Obj = Object(); + // TODO: Consider using both Start/End line numbers to improve location report + Obj.insert({"LineNumber", L.StartLineNumber}); + Obj.insert({"Filename", L.Filename}); + + if (!L.IsFileInRootDir || !RepositoryUrl) +return Obj; + SmallString<128> FileURL(*RepositoryUrl); + sys::path::append(FileURL, sys::path::Style::posix, L.Filename); + FileURL += "#" + std::to_string(L.StartLineNumber); + Obj.insert({"FileURL", FileURL}); + + return Obj; +} + +static json::Value extractValue(const Reference &I, +StringRef CurrentDirectory) { + SmallString<64> Path = I.getRelativeFilePath(CurrentDirectory); + sys::path::append(Path, I.getFileBaseName() + ".html"); + sys::path::native(Path, sys::path::Style::posix); + Object Obj = Object(); + Obj.insert({"Link", Path}); + Obj.insert({"Name", I.Name}); + Obj.insert({"QualName", I.QualName}); + Obj.insert({"ID", toHex(toStringRef(I.USR))}); + return Obj; +} + +static json::Value extractValue(const TypedefInfo &I) { + // Not Supported + return nullptr; +} + +static json::Value extractValue(const CommentInfo &I) { + assert((I.Kind == "BlockCommandComment" || I.Kind == "FullComment" || + I.Kind == "ParagraphComment" || I.Kind == "TextComment") && + "Unknown Comment type in CommentInfo."); + + Object Obj = Object(); + json::Value Child = Object(); + + // TextComment has no children, so return it. + if (I.Kind == "TextComment") { +Obj.insert({"TextComment", I.Text}); +return Obj; + } + + // BlockCommandComment needs to generate a Command key. + if (I.Kind == "BlockCommandComment") +Child.getAsObject()->insert({"Command", I.Name}); + + // Use the same handling for everything else. + // Only valid for: + // - BlockCommandComment + // - FullComment + // - ParagraphComment + json::Value ChildArr = Array(); + auto &CARef = *ChildArr.getAsArray(); + CARef.reserve(I.Children.size()); + for (const auto &C : I.Children) +CARef.emplace_back(extractValue(*C)); + Child.getAsObject()->insert({"Children", ChildArr}); + Obj.insert({I.Kind, Child}); + + return Obj; +} + +static void maybeInsertLocation(std::optional Loc, +const ClangDocContext &CDCtx, Object &Obj) { + if (!Loc) +return; + Location L = *Loc; + Obj.insert({"Location", extractValue(L, CDCtx.RepositoryUrl)}); +} + +static void extractDescriptionFromInfo(ArrayRef Descriptions, + json::Object &EnumValObj) { + if (Descriptions.empty()) +return; + json::Value DescArr = Array(); + json::Array &DescARef = *DescArr.getAsArray(); + for (const CommentInfo &Child : Descriptions) +DescARef.emplace_back(extractValue(Child)); + EnumValObj.insert({"EnumValueComments", DescArr}); +} + +static json::Value extractValue(const FunctionInfo &I, StringRef ParentInfoDir, +const ClangDocContext &CDCtx) { + Object Obj = Object(); + Obj.insert({"Name", I.Name}); + Obj.insert({"ID", toHex(toStringRef(I.USR))}); + Obj.insert({"Access", getAccessSpelling(I.Access).str()}); + Obj.insert({"ReturnType", extractValue(I.ReturnType.Type, ParentInfoDir)}); + + json::Value ParamArr = Array(); + json::Array &ParamARef = *ParamArr.getAsArray(); + for (const auto Val : enumerate(I.Params)) { +json::Value V = Object(); +auto &VRef = *V.getAsObject(); +VRef.insert({"Name", Val.value().Name}); +VRef.insert({"Type", Val.value().Type.Name}); +VRef.insert({"End", Val.index() + 1 == I.Params.size()}); +ParamARef.emplace_back(V); + } + Obj.insert({"Params", ParamArr}); + + maybeInsertLocation(I.DefLoc, CDCtx, Obj); + return Obj; +} + +static json::Value extractValue(const EnumInfo &I, +const ClangDocContext &CDCtx) { + Object Obj = Object(); + std::string EnumType = I.Scoped ? "enum class " : "enum "; + EnumType += I.Name; + bool HasComment = std::any_of( + I.Members.begin(), I.Members.end(), + [](const EnumValueInfo &M) { return !M.Description.empty(); }); + Obj.insert({"EnumName", EnumType}); + Obj.insert({"HasComment", HasComment}); + Obj.insert({"ID", toHex(toStringRef(I.USR))}); + json::Value EnumArr = Array(); + json::Array &EnumARef = *EnumArr.getAsArray(); + for (const EnumValueInfo &M : I.Members) { +json::Value EnumValue = Object(); +auto &EnumValObj = *EnumValue.getAsObject(); +EnumValObj.insert({"Name", M.Name}); +if (!M.ValueExpr.empty()) + EnumValObj.insert({"ValueExpr", M.ValueExpr}); +else + EnumValObj.insert({"Value", M.Value}); + +extractDescriptionFromInfo(M.Description, EnumValObj); +EnumARef.emplace_back(EnumValue); + } + Obj.insert({"EnumValues", E
[llvm-branch-commits] [clang-tools-extra] [clang-doc] Extract Info into JSON values (PR #138063)
https://github.com/evelez7 approved this pull request. Just array naming nits. https://github.com/llvm/llvm-project/pull/138063 ___ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
[llvm-branch-commits] [llvm] [BOLT] Gadget scanner: clarify MCPlusBuilder callbacks interface (PR #136147)
https://github.com/atrosinenko updated https://github.com/llvm/llvm-project/pull/136147 >From 0c885cf21f0b4ec710f1e3c5972112b5b0c4ade9 Mon Sep 17 00:00:00 2001 From: Anatoly Trosinenko Date: Thu, 17 Apr 2025 15:40:05 +0300 Subject: [PATCH 1/2] [BOLT] Gadget scanner: clarify MCPlusBuilder callbacks interface Clarify the semantics of `getAuthenticatedReg` and remove a redundant `isAuthenticationOfReg` method, as combined auth+something instructions (such as `retaa` on AArch64) should be handled carefully, especially when searching for authentication oracles: usually, such instructions cannot be authentication oracles and only some of them actually write an authenticated pointer to a register (such as "ldra x0, [x1]!"). Use `std::optional` returned type instead of plain MCPhysReg and returning `getNoRegister()` as a "not applicable" indication. Document a few existing methods, add information about preconditions. --- bolt/include/bolt/Core/MCPlusBuilder.h| 61 ++- bolt/lib/Passes/PAuthGadgetScanner.cpp| 64 +--- .../Target/AArch64/AArch64MCPlusBuilder.cpp | 76 --- .../AArch64/gs-pauth-debug-output.s | 3 - .../AArch64/gs-pauth-signing-oracles.s| 20 + 5 files changed, 130 insertions(+), 94 deletions(-) diff --git a/bolt/include/bolt/Core/MCPlusBuilder.h b/bolt/include/bolt/Core/MCPlusBuilder.h index 132d58f3f9f79..83ad70ea97076 100644 --- a/bolt/include/bolt/Core/MCPlusBuilder.h +++ b/bolt/include/bolt/Core/MCPlusBuilder.h @@ -562,30 +562,50 @@ class MCPlusBuilder { return {}; } - virtual ErrorOr getAuthenticatedReg(const MCInst &Inst) const { -llvm_unreachable("not implemented"); -return getNoRegister(); - } - - virtual bool isAuthenticationOfReg(const MCInst &Inst, - MCPhysReg AuthenticatedReg) const { + /// Returns the register where an authenticated pointer is written to by Inst, + /// or std::nullopt if not authenticating any register. + /// + /// Sets IsChecked if the instruction always checks authenticated pointer, + /// i.e. it either returns a successfully authenticated pointer or terminates + /// the program abnormally (such as "ldra x0, [x1]!" on AArch64, which crashes + /// on authentication failure even if FEAT_FPAC is not implemented). + virtual std::optional + getWrittenAuthenticatedReg(const MCInst &Inst, bool &IsChecked) const { llvm_unreachable("not implemented"); -return false; +return std::nullopt; } - virtual MCPhysReg getSignedReg(const MCInst &Inst) const { + /// Returns the register signed by Inst, or std::nullopt if not signing any + /// register. + /// + /// The returned register is assumed to be both input and output operand, + /// as it is done on AArch64. + virtual std::optional getSignedReg(const MCInst &Inst) const { llvm_unreachable("not implemented"); -return getNoRegister(); +return std::nullopt; } - virtual ErrorOr getRegUsedAsRetDest(const MCInst &Inst) const { + /// Returns the register used as a return address. Returns std::nullopt if + /// not applicable, such as reading the return address from a system register + /// or from the stack. + /// + /// Sets IsAuthenticatedInternally if the instruction accepts a signed + /// pointer as its operand and authenticates it internally. + /// + /// Should only be called when isReturn(Inst) is true. + virtual std::optional + getRegUsedAsRetDest(const MCInst &Inst, + bool &IsAuthenticatedInternally) const { llvm_unreachable("not implemented"); -return getNoRegister(); +return std::nullopt; } /// Returns the register used as the destination of an indirect branch or call /// instruction. Sets IsAuthenticatedInternally if the instruction accepts /// a signed pointer as its operand and authenticates it internally. + /// + /// Should only be called if isIndirectCall(Inst) or isIndirectBranch(Inst) + /// returns true. virtual MCPhysReg getRegUsedAsIndirectBranchDest(const MCInst &Inst, bool &IsAuthenticatedInternally) const { @@ -602,14 +622,14 @@ class MCPlusBuilder { ///controlled, under the Pointer Authentication threat model. /// /// If the instruction does not write to any register satisfying the above - /// two conditions, NoRegister is returned. + /// two conditions, std::nullopt is returned. /// /// The Pointer Authentication threat model assumes an attacker is able to /// modify any writable memory, but not executable code (due to W^X). - virtual MCPhysReg + virtual std::optional getMaterializedAddressRegForPtrAuth(const MCInst &Inst) const { llvm_unreachable("not implemented"); -return getNoRegister(); +return std::nullopt; } /// Analyzes if this instruction can safely perform address arithmetics @@ -622,10 +642,13 @@ class MCPlusBuilder { /// controlled, provided InReg and exe
[llvm-branch-commits] [llvm] [BOLT] Gadget scanner: detect authentication oracles (PR #135663)
https://github.com/atrosinenko updated https://github.com/llvm/llvm-project/pull/135663 >From def05034a2fb735f281325b40d0448586fe1e715 Mon Sep 17 00:00:00 2001 From: Anatoly Trosinenko Date: Sat, 5 Apr 2025 14:54:01 +0300 Subject: [PATCH 1/5] [BOLT] Gadget scanner: detect authentication oracles Implement the detection of authentication instructions whose results can be inspected by an attacker to know whether authentication succeeded. As the properties of output registers of authentication instructions are inspected, add a second set of analysis-related classes to iterate over the instructions in reverse order. --- bolt/include/bolt/Passes/PAuthGadgetScanner.h | 12 + bolt/lib/Passes/PAuthGadgetScanner.cpp| 543 + .../AArch64/gs-pauth-authentication-oracles.s | 723 ++ .../AArch64/gs-pauth-debug-output.s | 78 ++ 4 files changed, 1356 insertions(+) create mode 100644 bolt/test/binary-analysis/AArch64/gs-pauth-authentication-oracles.s diff --git a/bolt/include/bolt/Passes/PAuthGadgetScanner.h b/bolt/include/bolt/Passes/PAuthGadgetScanner.h index 98a49df862ebd..a3b39fcd5dc02 100644 --- a/bolt/include/bolt/Passes/PAuthGadgetScanner.h +++ b/bolt/include/bolt/Passes/PAuthGadgetScanner.h @@ -284,6 +284,15 @@ class ClobberingInfo : public ExtraInfo { void print(raw_ostream &OS, const MCInstReference Location) const override; }; +class LeakageInfo : public ExtraInfo { + SmallVector LeakingInstrs; + +public: + LeakageInfo(const ArrayRef Instrs) : LeakingInstrs(Instrs) {} + + void print(raw_ostream &OS, const MCInstReference Location) const override; +}; + /// A brief version of a report that can be further augmented with the details. /// /// A half-baked report produced on the first run of the analysis. An extra, @@ -324,6 +333,9 @@ class FunctionAnalysisContext { void findUnsafeUses(SmallVector> &Reports); void augmentUnsafeUseReports(ArrayRef> Reports); + void findUnsafeDefs(SmallVector> &Reports); + void augmentUnsafeDefReports(ArrayRef> Reports); + /// Process the reports which do not have to be augmented, and remove them /// from Reports. void handleSimpleReports(SmallVector> &Reports); diff --git a/bolt/lib/Passes/PAuthGadgetScanner.cpp b/bolt/lib/Passes/PAuthGadgetScanner.cpp index 24ad4844ffb7d..1dd2e3824e385 100644 --- a/bolt/lib/Passes/PAuthGadgetScanner.cpp +++ b/bolt/lib/Passes/PAuthGadgetScanner.cpp @@ -717,6 +717,459 @@ SrcSafetyAnalysis::create(BinaryFunction &BF, RegsToTrackInstsFor); } +/// A state representing which registers are safe to be used as the destination +/// operand of an authentication instruction. +/// +/// Similar to SrcState, it is the analysis that should take register aliasing +/// into account. +/// +/// Depending on the implementation, it may be possible that an authentication +/// instruction returns an invalid pointer on failure instead of terminating +/// the program immediately (assuming the program will crash as soon as that +/// pointer is dereferenced). To prevent brute-forcing the correct signature, +/// it should be impossible for an attacker to test if a pointer is correctly +/// signed - either the program should be terminated on authentication failure +/// or it should be impossible to tell whether authentication succeeded or not. +/// +/// For that reason, a restricted set of operations is allowed on any register +/// containing a value derived from the result of an authentication instruction +/// until that register is either wiped or checked not to contain a result of a +/// failed authentication. +/// +/// Specifically, the safety property for a register is computed by iterating +/// the instructions in backward order: the source register Xn of an instruction +/// Inst is safe if at least one of the following is true: +/// * Inst checks if Xn contains the result of a successful authentication and +/// terminates the program on failure. Note that Inst can either naturally +/// dereference Xn (load, branch, return, etc. instructions) or be the first +/// instruction of an explicit checking sequence. +/// * Inst performs safe address arithmetic AND both source and result +/// registers, as well as any temporary registers, must be safe after +/// execution of Inst (temporaries are not used on AArch64 and thus not +/// currently supported/allowed). +/// See MCPlusBuilder::analyzeAddressArithmeticsForPtrAuth for the details. +/// * Inst fully overwrites Xn with an unrelated value. +struct DstState { + /// The set of registers whose values cannot be inspected by an attacker in + /// a way usable as an authentication oracle. The results of authentication + /// instructions should be written to such registers. + BitVector CannotEscapeUnchecked; + + std::vector> FirstInstLeakingReg; + + /// Construct an empty state. + DstState() {} + + DstState(unsigned NumRegs, unsigned NumRegsToTrack) + : Cannot
[llvm-branch-commits] [llvm] [LoopVectorizer] Bundle partial reductions with different extensions (PR #136997)
https://github.com/SamTebbs33 updated https://github.com/llvm/llvm-project/pull/136997 >From 10c4727074a7f5b4502ad08dc655be8fa5ffa3d2 Mon Sep 17 00:00:00 2001 From: Samuel Tebbs Date: Wed, 23 Apr 2025 13:16:38 +0100 Subject: [PATCH 1/5] [LoopVectorizer] Bundle partial reductions with different extensions This PR adds support for extensions of different signedness to VPMulAccumulateReductionRecipe and allows such partial reductions to be bundled into that class. --- llvm/lib/Transforms/Vectorize/VPlan.h | 42 +- .../lib/Transforms/Vectorize/VPlanRecipes.cpp | 27 ++--- .../Transforms/Vectorize/VPlanTransforms.cpp | 25 - .../partial-reduce-dot-product-mixed.ll | 56 +-- .../LoopVectorize/AArch64/vplan-printing.ll | 29 +- 5 files changed, 99 insertions(+), 80 deletions(-) diff --git a/llvm/lib/Transforms/Vectorize/VPlan.h b/llvm/lib/Transforms/Vectorize/VPlan.h index 20d272e69e6e7..e11f608d068da 100644 --- a/llvm/lib/Transforms/Vectorize/VPlan.h +++ b/llvm/lib/Transforms/Vectorize/VPlan.h @@ -2493,11 +2493,13 @@ class VPExtendedReductionRecipe : public VPReductionRecipe { /// recipe is abstract and needs to be lowered to concrete recipes before /// codegen. The Operands are {ChainOp, VecOp1, VecOp2, [Condition]}. class VPMulAccumulateReductionRecipe : public VPReductionRecipe { - /// Opcode of the extend recipe. - Instruction::CastOps ExtOp; + /// Opcodes of the extend recipes. + Instruction::CastOps ExtOp0; + Instruction::CastOps ExtOp1; - /// Non-neg flag of the extend recipe. - bool IsNonNeg = false; + /// Non-neg flags of the extend recipe. + bool IsNonNeg0 = false; + bool IsNonNeg1 = false; Type *ResultTy; @@ -2512,7 +2514,8 @@ class VPMulAccumulateReductionRecipe : public VPReductionRecipe { MulAcc->getCondOp(), MulAcc->isOrdered(), WrapFlagsTy(MulAcc->hasNoUnsignedWrap(), MulAcc->hasNoSignedWrap()), MulAcc->getDebugLoc()), -ExtOp(MulAcc->getExtOpcode()), IsNonNeg(MulAcc->isNonNeg()), +ExtOp0(MulAcc->getExt0Opcode()), ExtOp1(MulAcc->getExt1Opcode()), +IsNonNeg0(MulAcc->isNonNeg0()), IsNonNeg1(MulAcc->isNonNeg1()), ResultTy(MulAcc->getResultType()), IsPartialReduction(MulAcc->isPartialReduction()) {} @@ -2526,7 +2529,8 @@ class VPMulAccumulateReductionRecipe : public VPReductionRecipe { R->getCondOp(), R->isOrdered(), WrapFlagsTy(Mul->hasNoUnsignedWrap(), Mul->hasNoSignedWrap()), R->getDebugLoc()), -ExtOp(Ext0->getOpcode()), IsNonNeg(Ext0->isNonNeg()), +ExtOp0(Ext0->getOpcode()), ExtOp1(Ext1->getOpcode()), +IsNonNeg0(Ext0->isNonNeg()), IsNonNeg1(Ext1->isNonNeg()), ResultTy(ResultTy), IsPartialReduction(isa(R)) { assert(RecurrenceDescriptor::getOpcode(getRecurrenceKind()) == @@ -2542,7 +2546,8 @@ class VPMulAccumulateReductionRecipe : public VPReductionRecipe { R->getCondOp(), R->isOrdered(), WrapFlagsTy(Mul->hasNoUnsignedWrap(), Mul->hasNoSignedWrap()), R->getDebugLoc()), -ExtOp(Instruction::CastOps::CastOpsEnd) { +ExtOp0(Instruction::CastOps::CastOpsEnd), +ExtOp1(Instruction::CastOps::CastOpsEnd) { assert(RecurrenceDescriptor::getOpcode(getRecurrenceKind()) == Instruction::Add && "The reduction instruction in MulAccumulateReductionRecipe must be " @@ -2586,19 +2591,26 @@ class VPMulAccumulateReductionRecipe : public VPReductionRecipe { VPValue *getVecOp1() const { return getOperand(2); } /// Return if this MulAcc recipe contains extend instructions. - bool isExtended() const { return ExtOp != Instruction::CastOps::CastOpsEnd; } + bool isExtended() const { return ExtOp0 != Instruction::CastOps::CastOpsEnd; } /// Return if the operands of mul instruction come from same extend. - bool isSameExtend() const { return getVecOp0() == getVecOp1(); } + bool isSameExtendVal() const { return getVecOp0() == getVecOp1(); } - /// Return the opcode of the underlying extend. - Instruction::CastOps getExtOpcode() const { return ExtOp; } + /// Return the opcode of the underlying extends. + Instruction::CastOps getExt0Opcode() const { return ExtOp0; } + Instruction::CastOps getExt1Opcode() const { return ExtOp1; } + + /// Return if the first extend's opcode is ZExt. + bool isZExt0() const { return ExtOp0 == Instruction::CastOps::ZExt; } + + /// Return if the second extend's opcode is ZExt. + bool isZExt1() const { return ExtOp1 == Instruction::CastOps::ZExt; } - /// Return if the extend opcode is ZExt. - bool isZExt() const { return ExtOp == Instruction::CastOps::ZExt; } + /// Return the non negative flag of the first ext recipe. + bool isNonNeg0() const { return IsNonNeg0; } - /// Return the non negative flag of the ext recipe. - bool isNonNeg() const { return IsNonNeg; } + /// Return the non negative flag of the second
[llvm-branch-commits] [llvm] [LoopVectorizer] Bundle partial reductions with different extensions (PR #136997)
@@ -2586,22 +2590,21 @@ class VPMulAccumulateReductionRecipe : public VPReductionRecipe { VPValue *getVecOp1() const { return getOperand(2); } /// Return if this MulAcc recipe contains extend instructions. - bool isExtended() const { return ExtOp != Instruction::CastOps::CastOpsEnd; } + bool isExtended() const { +return getVecOp0Info().ExtOp != Instruction::CastOps::CastOpsEnd; SamTebbs33 wrote: That can't happen at the moment, but I think you're right and it's worth considering the other extension as well. Done. https://github.com/llvm/llvm-project/pull/136997 ___ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
[llvm-branch-commits] [llvm] [BOLT] Gadget scanner: refactor issue reporting (PR #135662)
https://github.com/atrosinenko updated https://github.com/llvm/llvm-project/pull/135662 >From 3bfe3b74f31b5af8ee7102207c23ecb0d0bc7a5f Mon Sep 17 00:00:00 2001 From: Anatoly Trosinenko Date: Mon, 14 Apr 2025 15:08:54 +0300 Subject: [PATCH 1/5] [BOLT] Gadget scanner: refactor issue reporting Remove `getAffectedRegisters` and `setOverwritingInstrs` methods from the base `Report` class. Instead, make `Report` always represent the brief version of the report. When an issue is detected on the first run of the analysis, return an optional request for extra details to attach to the report on the second run. --- bolt/include/bolt/Passes/PAuthGadgetScanner.h | 102 ++--- bolt/lib/Passes/PAuthGadgetScanner.cpp| 200 ++ .../AArch64/gs-pauth-debug-output.s | 8 +- 3 files changed, 187 insertions(+), 123 deletions(-) diff --git a/bolt/include/bolt/Passes/PAuthGadgetScanner.h b/bolt/include/bolt/Passes/PAuthGadgetScanner.h index ccfe632889c7a..1cd3f3f6d3233 100644 --- a/bolt/include/bolt/Passes/PAuthGadgetScanner.h +++ b/bolt/include/bolt/Passes/PAuthGadgetScanner.h @@ -217,11 +217,6 @@ struct Report { virtual void generateReport(raw_ostream &OS, const BinaryContext &BC) const = 0; - // The two methods below are called by Analysis::computeDetailedInfo when - // iterating over the reports. - virtual ArrayRef getAffectedRegisters() const { return {}; } - virtual void setOverwritingInstrs(ArrayRef Instrs) {} - void printBasicInfo(raw_ostream &OS, const BinaryContext &BC, StringRef IssueKind) const; }; @@ -229,27 +224,11 @@ struct Report { struct GadgetReport : public Report { // The particular kind of gadget that is detected. const GadgetKind &Kind; - // The set of registers related to this gadget report (possibly empty). - SmallVector AffectedRegisters; - // The instructions that clobber the affected registers. - // There is no one-to-one correspondence with AffectedRegisters: for example, - // the same register can be overwritten by different instructions in different - // preceding basic blocks. - SmallVector OverwritingInstrs; - - GadgetReport(const GadgetKind &Kind, MCInstReference Location, - MCPhysReg AffectedRegister) - : Report(Location), Kind(Kind), AffectedRegisters({AffectedRegister}) {} - - void generateReport(raw_ostream &OS, const BinaryContext &BC) const override; - ArrayRef getAffectedRegisters() const override { -return AffectedRegisters; - } + GadgetReport(const GadgetKind &Kind, MCInstReference Location) + : Report(Location), Kind(Kind) {} - void setOverwritingInstrs(ArrayRef Instrs) override { -OverwritingInstrs.assign(Instrs.begin(), Instrs.end()); - } + void generateReport(raw_ostream &OS, const BinaryContext &BC) const override; }; /// Report with a free-form message attached. @@ -261,8 +240,75 @@ struct GenericReport : public Report { const BinaryContext &BC) const override; }; +/// An information about an issue collected on the slower, detailed, +/// run of an analysis. +class ExtraInfo { +public: + virtual void print(raw_ostream &OS, const MCInstReference Location) const = 0; + + virtual ~ExtraInfo() {} +}; + +class ClobberingInfo : public ExtraInfo { + SmallVector ClobberingInstrs; + +public: + ClobberingInfo(const ArrayRef Instrs) + : ClobberingInstrs(Instrs) {} + + void print(raw_ostream &OS, const MCInstReference Location) const override; +}; + +/// A brief version of a report that can be further augmented with the details. +/// +/// It is common for a particular type of gadget detector to be tied to some +/// specific kind of analysis. If an issue is returned by that detector, it may +/// be further augmented with the detailed info in an analysis-specific way, +/// or just be left as-is (f.e. if a free-form warning was reported). +template struct BriefReport { + BriefReport(std::shared_ptr Issue, + const std::optional RequestedDetails) + : Issue(Issue), RequestedDetails(RequestedDetails) {} + + std::shared_ptr Issue; + std::optional RequestedDetails; +}; + +/// A detailed version of a report. +struct DetailedReport { + DetailedReport(std::shared_ptr Issue, + std::shared_ptr Details) + : Issue(Issue), Details(Details) {} + + std::shared_ptr Issue; + std::shared_ptr Details; +}; + struct FunctionAnalysisResult { - std::vector> Diagnostics; + std::vector Diagnostics; +}; + +/// A helper class storing per-function context to be instantiated by Analysis. +class FunctionAnalysis { + BinaryContext &BC; + BinaryFunction &BF; + MCPlusBuilder::AllocatorIdTy AllocatorId; + FunctionAnalysisResult Result; + + bool PacRetGadgetsOnly; + + void findUnsafeUses(SmallVector> &Reports); + void augmentUnsafeUseReports(const ArrayRef> Reports); + +public: + FunctionAnalysis(BinaryFunction &BF, MCPlusBuilder::AllocatorIdTy AllocatorId, +
[llvm-branch-commits] [llvm] release/20.x: [SDAG] Ensure load is included in output chain of sincos expansion (#140525) (PR #140703)
https://github.com/llvmbot created https://github.com/llvm/llvm-project/pull/140703 Backport c9d6249 Requested by: @MacDue >From 7d2c28923c503b3e6301ccc038529162f4f33913 Mon Sep 17 00:00:00 2001 From: Benjamin Maxwell Date: Tue, 20 May 2025 10:43:50 +0100 Subject: [PATCH] [SDAG] Ensure load is included in output chain of sincos expansion (#140525) The load not being included in the chain meant that it could materialize after a `@llvm.lifetime.end` annotation on the pointer. This could result in miscompiles if the stack slot is reused for another value. Fixes https://github.com/llvm/llvm-project/issues/140491 (cherry picked from commit c9d62491981fe720c1b3255fa2f9ddf744590c65) --- .../lib/CodeGen/SelectionDAG/SelectionDAG.cpp | 9 ++- .../CodeGen/X86/pr140491-sincos-lifetimes.ll | 70 +++ 2 files changed, 76 insertions(+), 3 deletions(-) create mode 100644 llvm/test/CodeGen/X86/pr140491-sincos-lifetimes.ll diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp index b416c0efbbc4f..eecfb41c2d319 100644 --- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp @@ -2660,16 +2660,19 @@ bool SelectionDAG::expandMultipleResultFPLibCall( continue; } MachinePointerInfo PtrInfo; +SDValue LoadResult = +getLoad(Node->getValueType(ResNo), DL, CallChain, ResultPtr, PtrInfo); +SDValue OutChain = LoadResult.getValue(1); + if (StoreSDNode *ST = ResultStores[ResNo]) { // Replace store with the library call. - ReplaceAllUsesOfValueWith(SDValue(ST, 0), CallChain); + ReplaceAllUsesOfValueWith(SDValue(ST, 0), OutChain); PtrInfo = ST->getPointerInfo(); } else { PtrInfo = MachinePointerInfo::getFixedStack( getMachineFunction(), cast(ResultPtr)->getIndex()); } -SDValue LoadResult = -getLoad(Node->getValueType(ResNo), DL, CallChain, ResultPtr, PtrInfo); + Results.push_back(LoadResult); } diff --git a/llvm/test/CodeGen/X86/pr140491-sincos-lifetimes.ll b/llvm/test/CodeGen/X86/pr140491-sincos-lifetimes.ll new file mode 100644 index 0..2ca99bdc4b316 --- /dev/null +++ b/llvm/test/CodeGen/X86/pr140491-sincos-lifetimes.ll @@ -0,0 +1,70 @@ +; RUN: llc < %s | FileCheck %s + +; This test is reduced from https://github.com/llvm/llvm-project/issues/140491. +; It checks that when `@llvm.sincos.f32` is expanded to a call to +; `sincosf(float, float* out_sin, float* out_cos)` and the store of `%cos` to +; `%computed` is folded into the `sincosf` call. The use of `%cos`in the later +; `fneg %cos` -- which expands to a load of `%computed`, will perform the load +; before the `@llvm.lifetime.end.p0(%computed)` to ensure the correct value is +; taken for `%cos`. + +target triple = "x86_64-sie-ps5" + +declare void @use_ptr(ptr readonly) + +define i32 @sincos_stack_slot_with_lifetime(float %in) { +; CHECK-LABEL: sincos_stack_slot_with_lifetime: +; CHECK: # %bb.0: # %entry +; CHECK-NEXT:pushq %rbx +; CHECK-NEXT:.cfi_def_cfa_offset 16 +; CHECK-NEXT:subq $32, %rsp +; CHECK-NEXT:.cfi_def_cfa_offset 48 +; CHECK-NEXT:.cfi_offset %rbx, -16 +; CHECK-NEXT:leaq 12(%rsp), %rdi +; CHECK-NEXT:leaq 8(%rsp), %rbx +; CHECK-NEXT:movq %rbx, %rsi +; CHECK-NEXT:callq sincosf@PLT +; CHECK-NEXT:movss 8(%rsp), %xmm0 # xmm0 = mem[0],zero,zero,zero +; CHECK-NEXT:movaps %xmm0, 16(%rsp) # 16-byte Spill +; CHECK-NEXT:movq %rbx, %rdi +; CHECK-NEXT:callq use_ptr +; CHECK-NEXT:movss 12(%rsp), %xmm0 # xmm0 = mem[0],zero,zero,zero +; CHECK-NEXT:xorps {{\.?LCPI[0-9]+_[0-9]+}}(%rip), %xmm0 +; CHECK-NEXT:movss %xmm0, 8(%rsp) +; CHECK-NEXT:leaq 8(%rsp), %rdi +; CHECK-NEXT:callq use_ptr +; CHECK-NEXT:movaps 16(%rsp), %xmm0 # 16-byte Reload +; CHECK-NEXT:xorps {{\.?LCPI[0-9]+_[0-9]+}}(%rip), %xmm0 +; CHECK-NEXT:movss %xmm0, 8(%rsp) +; CHECK-NEXT:leaq 8(%rsp), %rdi +; CHECK-NEXT:callq use_ptr +; CHECK-NEXT:xorl %eax, %eax +; CHECK-NEXT:addq $32, %rsp +; CHECK-NEXT:.cfi_def_cfa_offset 16 +; CHECK-NEXT:popq %rbx +; CHECK-NEXT:.cfi_def_cfa_offset 8 +; CHECK-NEXT:retq +entry: + %computed = alloca float, align 4 + %computed1 = alloca float, align 4 + %computed3 = alloca float, align 4 + %sincos = tail call { float, float } @llvm.sincos.f32(float %in) + %sin = extractvalue { float, float } %sincos, 0 + %cos = extractvalue { float, float } %sincos, 1 + call void @llvm.lifetime.start.p0(i64 4, ptr nonnull %computed) + store float %cos, ptr %computed, align 4 + call void @use_ptr(ptr nonnull %computed) + call void @llvm.lifetime.end.p0(i64 4, ptr nonnull %computed) + call void @llvm.lifetime.start.p0(i64 4, ptr nonnull %computed1) + %fneg_sin = fneg float %sin + store float %fneg_sin, ptr %computed1, align 4 + call void @use_ptr(ptr nonnull %computed1) + call void @llvm.lifetime.end.p0(i64 4, ptr nonnull %compu
[llvm-branch-commits] [llvm] release/20.x: [SDAG] Ensure load is included in output chain of sincos expansion (#140525) (PR #140703)
https://github.com/llvmbot milestoned https://github.com/llvm/llvm-project/pull/140703 ___ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
[llvm-branch-commits] [llvm] release/20.x: [SDAG] Ensure load is included in output chain of sincos expansion (#140525) (PR #140703)
llvmbot wrote: @MacDue What do you think about merging this PR to the release branch? https://github.com/llvm/llvm-project/pull/140703 ___ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
[llvm-branch-commits] [llvm] [BOLT] Gadget scanner: detect untrusted LR before tail call (PR #137224)
https://github.com/atrosinenko updated https://github.com/llvm/llvm-project/pull/137224 >From 0e859b759886700bcf551459dd9f5c4b22fbad9a Mon Sep 17 00:00:00 2001 From: Anatoly Trosinenko Date: Tue, 22 Apr 2025 21:43:14 +0300 Subject: [PATCH 1/2] [BOLT] Gadget scanner: detect untrusted LR before tail call Implement the detection of tail calls performed with untrusted link register, which violates the assumption made on entry to every function. Unlike other pauth gadgets, this one involves some amount of guessing which branch instructions should be checked as tail calls. --- bolt/lib/Passes/PAuthGadgetScanner.cpp| 94 ++- .../AArch64/gs-pacret-autiasp.s | 31 +- .../AArch64/gs-pauth-debug-output.s | 30 +- .../AArch64/gs-pauth-tail-calls.s | 597 ++ 4 files changed, 706 insertions(+), 46 deletions(-) create mode 100644 bolt/test/binary-analysis/AArch64/gs-pauth-tail-calls.s diff --git a/bolt/lib/Passes/PAuthGadgetScanner.cpp b/bolt/lib/Passes/PAuthGadgetScanner.cpp index 7a5d47a3ff812..dfb71575b2b39 100644 --- a/bolt/lib/Passes/PAuthGadgetScanner.cpp +++ b/bolt/lib/Passes/PAuthGadgetScanner.cpp @@ -701,8 +701,9 @@ class DataflowSrcSafetyAnalysis // // Then, a function can be split into a number of disjoint contiguous sequences // of instructions without labels in between. These sequences can be processed -// the same way basic blocks are processed by data-flow analysis, assuming -// pessimistically that all registers are unsafe at the start of each sequence. +// the same way basic blocks are processed by data-flow analysis, with the same +// pessimistic estimation of the initial state at the start of each sequence +// (except the first instruction of the function). class CFGUnawareSrcSafetyAnalysis : public SrcSafetyAnalysis { BinaryFunction &BF; MCPlusBuilder::AllocatorIdTy AllocId; @@ -713,12 +714,6 @@ class CFGUnawareSrcSafetyAnalysis : public SrcSafetyAnalysis { BC.MIB->removeAnnotation(I.second, StateAnnotationIndex); } - /// Creates a state with all registers marked unsafe (not to be confused - /// with empty state). - SrcState createUnsafeState() const { -return SrcState(NumRegs, RegsToTrackInstsFor.getNumTrackedRegisters()); - } - public: CFGUnawareSrcSafetyAnalysis(BinaryFunction &BF, MCPlusBuilder::AllocatorIdTy AllocId, @@ -729,6 +724,7 @@ class CFGUnawareSrcSafetyAnalysis : public SrcSafetyAnalysis { } void run() override { +const SrcState DefaultState = computePessimisticState(BF); SrcState S = createEntryState(); for (auto &I : BF.instrs()) { MCInst &Inst = I.second; @@ -743,7 +739,7 @@ class CFGUnawareSrcSafetyAnalysis : public SrcSafetyAnalysis { LLVM_DEBUG({ traceInst(BC, "Due to label, resetting the state before", Inst); }); -S = createUnsafeState(); +S = DefaultState; } // Check if we need to remove an old annotation (this is the case if @@ -1288,6 +1284,83 @@ shouldReportReturnGadget(const BinaryContext &BC, const MCInstReference &Inst, return make_gadget_report(RetKind, Inst, *RetReg); } +/// While BOLT already marks some of the branch instructions as tail calls, +/// this function tries to improve the coverage by including less obvious cases +/// when it is possible to do without introducing too many false positives. +static bool shouldAnalyzeTailCallInst(const BinaryContext &BC, + const BinaryFunction &BF, + const MCInstReference &Inst) { + // Some BC.MIB->isXYZ(Inst) methods simply delegate to MCInstrDesc::isXYZ() + // (such as isBranch at the time of writing this comment), some don't (such + // as isCall). For that reason, call MCInstrDesc's methods explicitly when + // it is important. + const MCInstrDesc &Desc = + BC.MII->get(static_cast(Inst).getOpcode()); + // Tail call should be a branch (but not necessarily an indirect one). + if (!Desc.isBranch()) +return false; + + // Always analyze the branches already marked as tail calls by BOLT. + if (BC.MIB->isTailCall(Inst)) +return true; + + // Try to also check the branches marked as "UNKNOWN CONTROL FLOW" - the + // below is a simplified condition from BinaryContext::printInstruction. + bool IsUnknownControlFlow = + BC.MIB->isIndirectBranch(Inst) && !BC.MIB->getJumpTable(Inst); + + if (BF.hasCFG() && IsUnknownControlFlow) +return true; + + return false; +} + +static std::optional> +shouldReportUnsafeTailCall(const BinaryContext &BC, const BinaryFunction &BF, + const MCInstReference &Inst, const SrcState &S) { + static const GadgetKind UntrustedLRKind( + "untrusted link register found before tail call"); + + if (!shouldAnalyzeTailCallInst(BC, BF, Inst)) +return std::nullopt; + + // Not only the set of registers returned by getTrustedLiveInRegs() can be + /
[llvm-branch-commits] [llvm] [BOLT] Gadget scanner: optionally assume auth traps on failure (PR #139778)
https://github.com/atrosinenko updated https://github.com/llvm/llvm-project/pull/139778 >From 4266ffc763c260031b6e6a87c869cbd643c566aa Mon Sep 17 00:00:00 2001 From: Anatoly Trosinenko Date: Tue, 13 May 2025 19:50:41 +0300 Subject: [PATCH] [BOLT] Gadget scanner: optionally assume auth traps on failure On AArch64 it is possible for an auth instruction to either return an invalid address value on failure (without FEAT_FPAC) or generate an error (with FEAT_FPAC). It thus may be possible to never emit explicit pointer checks, if the target CPU is known to support FEAT_FPAC. This commit implements an --auth-traps-on-failure command line option, which essentially makes "safe-to-dereference" and "trusted" register properties identical and disables scanning for authentication oracles completely. --- bolt/lib/Passes/PAuthGadgetScanner.cpp| 112 +++ .../binary-analysis/AArch64/cmdline-args.test | 1 + .../AArch64/gs-pauth-authentication-oracles.s | 6 +- .../binary-analysis/AArch64/gs-pauth-calls.s | 5 +- .../AArch64/gs-pauth-debug-output.s | 177 ++--- .../AArch64/gs-pauth-jump-table.s | 6 +- .../AArch64/gs-pauth-signing-oracles.s| 54 ++--- .../AArch64/gs-pauth-tail-calls.s | 184 +- 8 files changed, 318 insertions(+), 227 deletions(-) diff --git a/bolt/lib/Passes/PAuthGadgetScanner.cpp b/bolt/lib/Passes/PAuthGadgetScanner.cpp index bda971bcd9343..cfe86d32df798 100644 --- a/bolt/lib/Passes/PAuthGadgetScanner.cpp +++ b/bolt/lib/Passes/PAuthGadgetScanner.cpp @@ -14,6 +14,7 @@ #include "bolt/Passes/PAuthGadgetScanner.h" #include "bolt/Core/ParallelUtilities.h" #include "bolt/Passes/DataflowAnalysis.h" +#include "bolt/Utils/CommandLineOpts.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallSet.h" #include "llvm/MC/MCInst.h" @@ -26,6 +27,11 @@ namespace llvm { namespace bolt { namespace PAuthGadgetScanner { +static cl::opt AuthTrapsOnFailure( +"auth-traps-on-failure", +cl::desc("Assume authentication instructions always trap on failure"), +cl::cat(opts::BinaryAnalysisCategory)); + [[maybe_unused]] static void traceInst(const BinaryContext &BC, StringRef Label, const MCInst &MI) { dbgs() << " " << Label << ": "; @@ -365,6 +371,34 @@ class SrcSafetyAnalysis { return Clobbered; } + std::optional getRegMadeTrustedByChecking(const MCInst &Inst, + SrcState Cur) const { +// This functions cannot return multiple registers. This is never the case +// on AArch64. +std::optional RegCheckedByInst = +BC.MIB->getAuthCheckedReg(Inst, /*MayOverwrite=*/false); +if (RegCheckedByInst && Cur.SafeToDerefRegs[*RegCheckedByInst]) + return *RegCheckedByInst; + +auto It = CheckerSequenceInfo.find(&Inst); +if (It == CheckerSequenceInfo.end()) + return std::nullopt; + +MCPhysReg RegCheckedBySequence = It->second.first; +const MCInst *FirstCheckerInst = It->second.second; + +// FirstCheckerInst should belong to the same basic block (see the +// assertion in DataflowSrcSafetyAnalysis::run()), meaning it was +// deterministically processed a few steps before this instruction. +const SrcState &StateBeforeChecker = getStateBefore(*FirstCheckerInst); + +// The sequence checks the register, but it should be authenticated before. +if (!StateBeforeChecker.SafeToDerefRegs[RegCheckedBySequence]) + return std::nullopt; + +return RegCheckedBySequence; + } + // Returns all registers that can be treated as if they are written by an // authentication instruction. SmallVector getRegsMadeSafeToDeref(const MCInst &Point, @@ -387,18 +421,38 @@ class SrcSafetyAnalysis { Regs.push_back(DstAndSrc->first); } +// Make sure explicit checker sequence keeps register safe-to-dereference +// when the register would be clobbered according to the regular rules: +// +//; LR is safe to dereference here +//mov x16, x30 ; start of the sequence, LR is s-t-d right before +//xpaclri ; clobbers LR, LR is not safe anymore +//cmp x30, x16 +//b.eq 1f; end of the sequence: LR is marked as trusted +//brk 0x1234 +// 1: +//; at this point LR would be marked as trusted, +//; but not safe-to-dereference +// +// or even just +// +//; X1 is safe to dereference here +//ldr x0, [x1, #8]! +//; X1 is trusted here, but it was clobbered due to address write-back +if (auto CheckedReg = getRegMadeTrustedByChecking(Point, Cur)) + Regs.push_back(*CheckedReg); + return Regs; } // Returns all registers made trusted by this instruction. SmallVector getRegsMadeTrusted(const MCInst &Point, const SrcState &Cur) const { +assert(!AuthTrapsOnFailure &&
[llvm-branch-commits] [llvm] [BOLT] Introduce helpers to match `MCInst`s one at a time (NFC) (PR #138883)
https://github.com/atrosinenko updated https://github.com/llvm/llvm-project/pull/138883 >From 72d8d2f34bc6e7ab199b9bc62b58391dd7576c2c Mon Sep 17 00:00:00 2001 From: Anatoly Trosinenko Date: Wed, 7 May 2025 16:42:00 +0300 Subject: [PATCH] [BOLT] Introduce helpers to match `MCInst`s one at a time (NFC) Introduce matchInst helper function to capture and/or match the operands of MCInst. Unlike the existing `MCPlusBuilder::MCInstMatcher` machinery, matchInst is intended for the use cases when precise control over the instruction order is required. For example, when validating PtrAuth hardening, all registers are usually considered unsafe after a function call, even though callee-saved registers should preserve their old values *under normal operation*. --- bolt/include/bolt/Core/MCInstUtils.h | 128 ++ .../Target/AArch64/AArch64MCPlusBuilder.cpp | 90 +--- 2 files changed, 162 insertions(+), 56 deletions(-) diff --git a/bolt/include/bolt/Core/MCInstUtils.h b/bolt/include/bolt/Core/MCInstUtils.h index 69bf5e6159b74..50b7d56470c99 100644 --- a/bolt/include/bolt/Core/MCInstUtils.h +++ b/bolt/include/bolt/Core/MCInstUtils.h @@ -162,6 +162,134 @@ static inline raw_ostream &operator<<(raw_ostream &OS, return Ref.print(OS); } +/// Instruction-matching helpers operating on a single instruction at a time. +/// +/// Unlike MCPlusBuilder::MCInstMatcher, this matchInst() function focuses on +/// the cases where a precise control over the instruction order is important: +/// +/// // Bring the short names into the local scope: +/// using namespace MCInstMatcher; +/// // Declare the registers to capture: +/// Reg Xn, Xm; +/// // Capture the 0th and 1st operands, match the 2nd operand against the +/// // just captured Xm register, match the 3rd operand against literal 0: +/// if (!matchInst(MaybeAdd, AArch64::ADDXrs, Xm, Xn, Xm, Imm(0)) +/// return AArch64::NoRegister; +/// // Match the 0th operand against Xm: +/// if (!matchInst(MaybeBr, AArch64::BR, Xm)) +/// return AArch64::NoRegister; +/// // Return the matched register: +/// return Xm.get(); +namespace MCInstMatcher { + +// The base class to match an operand of type T. +// +// The subclasses of OpMatcher are intended to be allocated on the stack and +// to only be used by passing them to matchInst() and by calling their get() +// function, thus the peculiar `mutable` specifiers: to make the calling code +// compact and readable, the templated matchInst() function has to accept both +// long-lived Imm/Reg wrappers declared as local variables (intended to capture +// the first operand's value and match the subsequent operands, whether inside +// a single instruction or across multiple instructions), as well as temporary +// wrappers around literal values to match, f.e. Imm(42) or Reg(AArch64::XZR). +template class OpMatcher { + mutable std::optional Value; + mutable std::optional SavedValue; + + // Remember/restore the last Value - to be called by matchInst. + void remember() const { SavedValue = Value; } + void restore() const { Value = SavedValue; } + + template + friend bool matchInst(const MCInst &, unsigned, const OpMatchers &...); + +protected: + OpMatcher(std::optional ValueToMatch) : Value(ValueToMatch) {} + + bool matchValue(T OpValue) const { +// Check that OpValue does not contradict the existing Value. +bool MatchResult = !Value || *Value == OpValue; +// If MatchResult is false, all matchers will be reset before returning from +// matchInst, including this one, thus no need to assign conditionally. +Value = OpValue; + +return MatchResult; + } + +public: + /// Returns the captured value. + T get() const { +assert(Value.has_value()); +return *Value; + } +}; + +class Reg : public OpMatcher { + bool matches(const MCOperand &Op) const { +if (!Op.isReg()) + return false; + +return matchValue(Op.getReg()); + } + + template + friend bool matchInst(const MCInst &, unsigned, const OpMatchers &...); + +public: + Reg(std::optional RegToMatch = std::nullopt) + : OpMatcher(RegToMatch) {} +}; + +class Imm : public OpMatcher { + bool matches(const MCOperand &Op) const { +if (!Op.isImm()) + return false; + +return matchValue(Op.getImm()); + } + + template + friend bool matchInst(const MCInst &, unsigned, const OpMatchers &...); + +public: + Imm(std::optional ImmToMatch = std::nullopt) + : OpMatcher(ImmToMatch) {} +}; + +/// Tries to match Inst and updates Ops on success. +/// +/// If Inst has the specified Opcode and its operand list prefix matches Ops, +/// this function returns true and updates Ops, otherwise false is returned and +/// values of Ops are kept as before matchInst was called. +/// +/// Please note that while Ops are technically passed by a const reference to +/// make invocations like `matchInst(MI, Opcode, Imm(42))` possible, all their +/// fields are marked mut
[llvm-branch-commits] [llvm] [BOLT] Introduce helpers to match `MCInst`s one at a time (NFC) (PR #138883)
https://github.com/atrosinenko updated https://github.com/llvm/llvm-project/pull/138883 >From 72d8d2f34bc6e7ab199b9bc62b58391dd7576c2c Mon Sep 17 00:00:00 2001 From: Anatoly Trosinenko Date: Wed, 7 May 2025 16:42:00 +0300 Subject: [PATCH] [BOLT] Introduce helpers to match `MCInst`s one at a time (NFC) Introduce matchInst helper function to capture and/or match the operands of MCInst. Unlike the existing `MCPlusBuilder::MCInstMatcher` machinery, matchInst is intended for the use cases when precise control over the instruction order is required. For example, when validating PtrAuth hardening, all registers are usually considered unsafe after a function call, even though callee-saved registers should preserve their old values *under normal operation*. --- bolt/include/bolt/Core/MCInstUtils.h | 128 ++ .../Target/AArch64/AArch64MCPlusBuilder.cpp | 90 +--- 2 files changed, 162 insertions(+), 56 deletions(-) diff --git a/bolt/include/bolt/Core/MCInstUtils.h b/bolt/include/bolt/Core/MCInstUtils.h index 69bf5e6159b74..50b7d56470c99 100644 --- a/bolt/include/bolt/Core/MCInstUtils.h +++ b/bolt/include/bolt/Core/MCInstUtils.h @@ -162,6 +162,134 @@ static inline raw_ostream &operator<<(raw_ostream &OS, return Ref.print(OS); } +/// Instruction-matching helpers operating on a single instruction at a time. +/// +/// Unlike MCPlusBuilder::MCInstMatcher, this matchInst() function focuses on +/// the cases where a precise control over the instruction order is important: +/// +/// // Bring the short names into the local scope: +/// using namespace MCInstMatcher; +/// // Declare the registers to capture: +/// Reg Xn, Xm; +/// // Capture the 0th and 1st operands, match the 2nd operand against the +/// // just captured Xm register, match the 3rd operand against literal 0: +/// if (!matchInst(MaybeAdd, AArch64::ADDXrs, Xm, Xn, Xm, Imm(0)) +/// return AArch64::NoRegister; +/// // Match the 0th operand against Xm: +/// if (!matchInst(MaybeBr, AArch64::BR, Xm)) +/// return AArch64::NoRegister; +/// // Return the matched register: +/// return Xm.get(); +namespace MCInstMatcher { + +// The base class to match an operand of type T. +// +// The subclasses of OpMatcher are intended to be allocated on the stack and +// to only be used by passing them to matchInst() and by calling their get() +// function, thus the peculiar `mutable` specifiers: to make the calling code +// compact and readable, the templated matchInst() function has to accept both +// long-lived Imm/Reg wrappers declared as local variables (intended to capture +// the first operand's value and match the subsequent operands, whether inside +// a single instruction or across multiple instructions), as well as temporary +// wrappers around literal values to match, f.e. Imm(42) or Reg(AArch64::XZR). +template class OpMatcher { + mutable std::optional Value; + mutable std::optional SavedValue; + + // Remember/restore the last Value - to be called by matchInst. + void remember() const { SavedValue = Value; } + void restore() const { Value = SavedValue; } + + template + friend bool matchInst(const MCInst &, unsigned, const OpMatchers &...); + +protected: + OpMatcher(std::optional ValueToMatch) : Value(ValueToMatch) {} + + bool matchValue(T OpValue) const { +// Check that OpValue does not contradict the existing Value. +bool MatchResult = !Value || *Value == OpValue; +// If MatchResult is false, all matchers will be reset before returning from +// matchInst, including this one, thus no need to assign conditionally. +Value = OpValue; + +return MatchResult; + } + +public: + /// Returns the captured value. + T get() const { +assert(Value.has_value()); +return *Value; + } +}; + +class Reg : public OpMatcher { + bool matches(const MCOperand &Op) const { +if (!Op.isReg()) + return false; + +return matchValue(Op.getReg()); + } + + template + friend bool matchInst(const MCInst &, unsigned, const OpMatchers &...); + +public: + Reg(std::optional RegToMatch = std::nullopt) + : OpMatcher(RegToMatch) {} +}; + +class Imm : public OpMatcher { + bool matches(const MCOperand &Op) const { +if (!Op.isImm()) + return false; + +return matchValue(Op.getImm()); + } + + template + friend bool matchInst(const MCInst &, unsigned, const OpMatchers &...); + +public: + Imm(std::optional ImmToMatch = std::nullopt) + : OpMatcher(ImmToMatch) {} +}; + +/// Tries to match Inst and updates Ops on success. +/// +/// If Inst has the specified Opcode and its operand list prefix matches Ops, +/// this function returns true and updates Ops, otherwise false is returned and +/// values of Ops are kept as before matchInst was called. +/// +/// Please note that while Ops are technically passed by a const reference to +/// make invocations like `matchInst(MI, Opcode, Imm(42))` possible, all their +/// fields are marked mut
[llvm-branch-commits] [llvm] [BOLT] Gadget scanner: prevent false positives due to jump tables (PR #138884)
https://github.com/atrosinenko updated https://github.com/llvm/llvm-project/pull/138884 >From 5c33526a475b60c4e4333457d6cdd8ea59e2cb64 Mon Sep 17 00:00:00 2001 From: Anatoly Trosinenko Date: Tue, 6 May 2025 11:31:03 +0300 Subject: [PATCH] [BOLT] Gadget scanner: prevent false positives due to jump tables As part of PAuth hardening, AArch64 LLVM backend can use a special BR_JumpTable pseudo (enabled by -faarch64-jump-table-hardening Clang option) which is expanded in the AsmPrinter into a contiguous sequence without unsafe instructions in the middle. This commit adds another target-specific callback to MCPlusBuilder to make it possible to inhibit false positives for known-safe jump table dispatch sequences. Without special handling, the branch instruction is likely to be reported as a non-protected call (as its destination is not produced by an auth instruction, PC-relative address materialization, etc.) and possibly as a tail call being performed with unsafe link register (as the detection whether the branch instruction is a tail call is an heuristic). For now, only the specific instruction sequence used by the AArch64 LLVM backend is matched. --- bolt/include/bolt/Core/MCInstUtils.h | 9 + bolt/include/bolt/Core/MCPlusBuilder.h| 14 + bolt/lib/Core/MCInstUtils.cpp | 20 + bolt/lib/Passes/PAuthGadgetScanner.cpp| 10 + .../Target/AArch64/AArch64MCPlusBuilder.cpp | 73 ++ .../AArch64/gs-pauth-jump-table.s | 703 ++ 6 files changed, 829 insertions(+) create mode 100644 bolt/test/binary-analysis/AArch64/gs-pauth-jump-table.s diff --git a/bolt/include/bolt/Core/MCInstUtils.h b/bolt/include/bolt/Core/MCInstUtils.h index 50b7d56470c99..33d36cccbcfff 100644 --- a/bolt/include/bolt/Core/MCInstUtils.h +++ b/bolt/include/bolt/Core/MCInstUtils.h @@ -154,6 +154,15 @@ class MCInstReference { return nullptr; } + /// Returns the only preceding instruction, or std::nullopt if multiple or no + /// predecessors are possible. + /// + /// If CFG information is available, basic block boundary can be crossed, + /// provided there is exactly one predecessor. If CFG is not available, the + /// preceding instruction in the offset order is returned, unless this is the + /// first instruction of the function. + std::optional getSinglePredecessor(); + raw_ostream &print(raw_ostream &OS) const; }; diff --git a/bolt/include/bolt/Core/MCPlusBuilder.h b/bolt/include/bolt/Core/MCPlusBuilder.h index 87de6754017db..eb93d7de7fee9 100644 --- a/bolt/include/bolt/Core/MCPlusBuilder.h +++ b/bolt/include/bolt/Core/MCPlusBuilder.h @@ -14,6 +14,7 @@ #ifndef BOLT_CORE_MCPLUSBUILDER_H #define BOLT_CORE_MCPLUSBUILDER_H +#include "bolt/Core/MCInstUtils.h" #include "bolt/Core/MCPlus.h" #include "bolt/Core/Relocation.h" #include "llvm/ADT/ArrayRef.h" @@ -699,6 +700,19 @@ class MCPlusBuilder { return std::nullopt; } + /// Tests if BranchInst corresponds to an instruction sequence which is known + /// to be a safe dispatch via jump table. + /// + /// The target can decide which instruction sequences to consider "safe" from + /// the Pointer Authentication point of view, such as any jump table dispatch + /// sequence without function calls inside, any sequence which is contiguous, + /// or only some specific well-known sequences. + virtual bool + isSafeJumpTableBranchForPtrAuth(MCInstReference BranchInst) const { +llvm_unreachable("not implemented"); +return false; + } + virtual bool isTerminator(const MCInst &Inst) const; virtual bool isNoop(const MCInst &Inst) const { diff --git a/bolt/lib/Core/MCInstUtils.cpp b/bolt/lib/Core/MCInstUtils.cpp index 40f6edd59135c..b7c6d898988af 100644 --- a/bolt/lib/Core/MCInstUtils.cpp +++ b/bolt/lib/Core/MCInstUtils.cpp @@ -55,3 +55,23 @@ raw_ostream &MCInstReference::print(raw_ostream &OS) const { OS << ">"; return OS; } + +std::optional MCInstReference::getSinglePredecessor() { + if (const RefInBB *Ref = tryGetRefInBB()) { +if (Ref->It != Ref->BB->begin()) + return MCInstReference(Ref->BB, &*std::prev(Ref->It)); + +if (Ref->BB->pred_size() != 1) + return std::nullopt; + +BinaryBasicBlock *PredBB = *Ref->BB->pred_begin(); +assert(!PredBB->empty() && "Empty basic blocks are not supported yet"); +return MCInstReference(PredBB, &*PredBB->rbegin()); + } + + const RefInBF &Ref = getRefInBF(); + if (Ref.It == Ref.BF->instrs().begin()) +return std::nullopt; + + return MCInstReference(Ref.BF, std::prev(Ref.It)); +} diff --git a/bolt/lib/Passes/PAuthGadgetScanner.cpp b/bolt/lib/Passes/PAuthGadgetScanner.cpp index 5e08ae3fbf767..bda971bcd9343 100644 --- a/bolt/lib/Passes/PAuthGadgetScanner.cpp +++ b/bolt/lib/Passes/PAuthGadgetScanner.cpp @@ -1328,6 +1328,11 @@ shouldReportUnsafeTailCall(const BinaryContext &BC, const BinaryFunction &BF, return std::nullopt; } + if (BC.MIB->isSafeJumpTableBranchForPtrAuth(Inst)) { +LL
[llvm-branch-commits] [llvm] [BOLT] Gadget scanner: detect untrusted LR before tail call (PR #137224)
https://github.com/atrosinenko updated https://github.com/llvm/llvm-project/pull/137224 >From 0e859b759886700bcf551459dd9f5c4b22fbad9a Mon Sep 17 00:00:00 2001 From: Anatoly Trosinenko Date: Tue, 22 Apr 2025 21:43:14 +0300 Subject: [PATCH 1/2] [BOLT] Gadget scanner: detect untrusted LR before tail call Implement the detection of tail calls performed with untrusted link register, which violates the assumption made on entry to every function. Unlike other pauth gadgets, this one involves some amount of guessing which branch instructions should be checked as tail calls. --- bolt/lib/Passes/PAuthGadgetScanner.cpp| 94 ++- .../AArch64/gs-pacret-autiasp.s | 31 +- .../AArch64/gs-pauth-debug-output.s | 30 +- .../AArch64/gs-pauth-tail-calls.s | 597 ++ 4 files changed, 706 insertions(+), 46 deletions(-) create mode 100644 bolt/test/binary-analysis/AArch64/gs-pauth-tail-calls.s diff --git a/bolt/lib/Passes/PAuthGadgetScanner.cpp b/bolt/lib/Passes/PAuthGadgetScanner.cpp index 7a5d47a3ff812..dfb71575b2b39 100644 --- a/bolt/lib/Passes/PAuthGadgetScanner.cpp +++ b/bolt/lib/Passes/PAuthGadgetScanner.cpp @@ -701,8 +701,9 @@ class DataflowSrcSafetyAnalysis // // Then, a function can be split into a number of disjoint contiguous sequences // of instructions without labels in between. These sequences can be processed -// the same way basic blocks are processed by data-flow analysis, assuming -// pessimistically that all registers are unsafe at the start of each sequence. +// the same way basic blocks are processed by data-flow analysis, with the same +// pessimistic estimation of the initial state at the start of each sequence +// (except the first instruction of the function). class CFGUnawareSrcSafetyAnalysis : public SrcSafetyAnalysis { BinaryFunction &BF; MCPlusBuilder::AllocatorIdTy AllocId; @@ -713,12 +714,6 @@ class CFGUnawareSrcSafetyAnalysis : public SrcSafetyAnalysis { BC.MIB->removeAnnotation(I.second, StateAnnotationIndex); } - /// Creates a state with all registers marked unsafe (not to be confused - /// with empty state). - SrcState createUnsafeState() const { -return SrcState(NumRegs, RegsToTrackInstsFor.getNumTrackedRegisters()); - } - public: CFGUnawareSrcSafetyAnalysis(BinaryFunction &BF, MCPlusBuilder::AllocatorIdTy AllocId, @@ -729,6 +724,7 @@ class CFGUnawareSrcSafetyAnalysis : public SrcSafetyAnalysis { } void run() override { +const SrcState DefaultState = computePessimisticState(BF); SrcState S = createEntryState(); for (auto &I : BF.instrs()) { MCInst &Inst = I.second; @@ -743,7 +739,7 @@ class CFGUnawareSrcSafetyAnalysis : public SrcSafetyAnalysis { LLVM_DEBUG({ traceInst(BC, "Due to label, resetting the state before", Inst); }); -S = createUnsafeState(); +S = DefaultState; } // Check if we need to remove an old annotation (this is the case if @@ -1288,6 +1284,83 @@ shouldReportReturnGadget(const BinaryContext &BC, const MCInstReference &Inst, return make_gadget_report(RetKind, Inst, *RetReg); } +/// While BOLT already marks some of the branch instructions as tail calls, +/// this function tries to improve the coverage by including less obvious cases +/// when it is possible to do without introducing too many false positives. +static bool shouldAnalyzeTailCallInst(const BinaryContext &BC, + const BinaryFunction &BF, + const MCInstReference &Inst) { + // Some BC.MIB->isXYZ(Inst) methods simply delegate to MCInstrDesc::isXYZ() + // (such as isBranch at the time of writing this comment), some don't (such + // as isCall). For that reason, call MCInstrDesc's methods explicitly when + // it is important. + const MCInstrDesc &Desc = + BC.MII->get(static_cast(Inst).getOpcode()); + // Tail call should be a branch (but not necessarily an indirect one). + if (!Desc.isBranch()) +return false; + + // Always analyze the branches already marked as tail calls by BOLT. + if (BC.MIB->isTailCall(Inst)) +return true; + + // Try to also check the branches marked as "UNKNOWN CONTROL FLOW" - the + // below is a simplified condition from BinaryContext::printInstruction. + bool IsUnknownControlFlow = + BC.MIB->isIndirectBranch(Inst) && !BC.MIB->getJumpTable(Inst); + + if (BF.hasCFG() && IsUnknownControlFlow) +return true; + + return false; +} + +static std::optional> +shouldReportUnsafeTailCall(const BinaryContext &BC, const BinaryFunction &BF, + const MCInstReference &Inst, const SrcState &S) { + static const GadgetKind UntrustedLRKind( + "untrusted link register found before tail call"); + + if (!shouldAnalyzeTailCallInst(BC, BF, Inst)) +return std::nullopt; + + // Not only the set of registers returned by getTrustedLiveInRegs() can be + /
[llvm-branch-commits] [llvm] [BOLT] Gadget scanner: clarify MCPlusBuilder callbacks interface (PR #136147)
https://github.com/atrosinenko updated https://github.com/llvm/llvm-project/pull/136147 >From 0c885cf21f0b4ec710f1e3c5972112b5b0c4ade9 Mon Sep 17 00:00:00 2001 From: Anatoly Trosinenko Date: Thu, 17 Apr 2025 15:40:05 +0300 Subject: [PATCH 1/2] [BOLT] Gadget scanner: clarify MCPlusBuilder callbacks interface Clarify the semantics of `getAuthenticatedReg` and remove a redundant `isAuthenticationOfReg` method, as combined auth+something instructions (such as `retaa` on AArch64) should be handled carefully, especially when searching for authentication oracles: usually, such instructions cannot be authentication oracles and only some of them actually write an authenticated pointer to a register (such as "ldra x0, [x1]!"). Use `std::optional` returned type instead of plain MCPhysReg and returning `getNoRegister()` as a "not applicable" indication. Document a few existing methods, add information about preconditions. --- bolt/include/bolt/Core/MCPlusBuilder.h| 61 ++- bolt/lib/Passes/PAuthGadgetScanner.cpp| 64 +--- .../Target/AArch64/AArch64MCPlusBuilder.cpp | 76 --- .../AArch64/gs-pauth-debug-output.s | 3 - .../AArch64/gs-pauth-signing-oracles.s| 20 + 5 files changed, 130 insertions(+), 94 deletions(-) diff --git a/bolt/include/bolt/Core/MCPlusBuilder.h b/bolt/include/bolt/Core/MCPlusBuilder.h index 132d58f3f9f79..83ad70ea97076 100644 --- a/bolt/include/bolt/Core/MCPlusBuilder.h +++ b/bolt/include/bolt/Core/MCPlusBuilder.h @@ -562,30 +562,50 @@ class MCPlusBuilder { return {}; } - virtual ErrorOr getAuthenticatedReg(const MCInst &Inst) const { -llvm_unreachable("not implemented"); -return getNoRegister(); - } - - virtual bool isAuthenticationOfReg(const MCInst &Inst, - MCPhysReg AuthenticatedReg) const { + /// Returns the register where an authenticated pointer is written to by Inst, + /// or std::nullopt if not authenticating any register. + /// + /// Sets IsChecked if the instruction always checks authenticated pointer, + /// i.e. it either returns a successfully authenticated pointer or terminates + /// the program abnormally (such as "ldra x0, [x1]!" on AArch64, which crashes + /// on authentication failure even if FEAT_FPAC is not implemented). + virtual std::optional + getWrittenAuthenticatedReg(const MCInst &Inst, bool &IsChecked) const { llvm_unreachable("not implemented"); -return false; +return std::nullopt; } - virtual MCPhysReg getSignedReg(const MCInst &Inst) const { + /// Returns the register signed by Inst, or std::nullopt if not signing any + /// register. + /// + /// The returned register is assumed to be both input and output operand, + /// as it is done on AArch64. + virtual std::optional getSignedReg(const MCInst &Inst) const { llvm_unreachable("not implemented"); -return getNoRegister(); +return std::nullopt; } - virtual ErrorOr getRegUsedAsRetDest(const MCInst &Inst) const { + /// Returns the register used as a return address. Returns std::nullopt if + /// not applicable, such as reading the return address from a system register + /// or from the stack. + /// + /// Sets IsAuthenticatedInternally if the instruction accepts a signed + /// pointer as its operand and authenticates it internally. + /// + /// Should only be called when isReturn(Inst) is true. + virtual std::optional + getRegUsedAsRetDest(const MCInst &Inst, + bool &IsAuthenticatedInternally) const { llvm_unreachable("not implemented"); -return getNoRegister(); +return std::nullopt; } /// Returns the register used as the destination of an indirect branch or call /// instruction. Sets IsAuthenticatedInternally if the instruction accepts /// a signed pointer as its operand and authenticates it internally. + /// + /// Should only be called if isIndirectCall(Inst) or isIndirectBranch(Inst) + /// returns true. virtual MCPhysReg getRegUsedAsIndirectBranchDest(const MCInst &Inst, bool &IsAuthenticatedInternally) const { @@ -602,14 +622,14 @@ class MCPlusBuilder { ///controlled, under the Pointer Authentication threat model. /// /// If the instruction does not write to any register satisfying the above - /// two conditions, NoRegister is returned. + /// two conditions, std::nullopt is returned. /// /// The Pointer Authentication threat model assumes an attacker is able to /// modify any writable memory, but not executable code (due to W^X). - virtual MCPhysReg + virtual std::optional getMaterializedAddressRegForPtrAuth(const MCInst &Inst) const { llvm_unreachable("not implemented"); -return getNoRegister(); +return std::nullopt; } /// Analyzes if this instruction can safely perform address arithmetics @@ -622,10 +642,13 @@ class MCPlusBuilder { /// controlled, provided InReg and exe
[llvm-branch-commits] [llvm] [BOLT] Gadget scanner: optionally assume auth traps on failure (PR #139778)
https://github.com/atrosinenko updated https://github.com/llvm/llvm-project/pull/139778 >From 4266ffc763c260031b6e6a87c869cbd643c566aa Mon Sep 17 00:00:00 2001 From: Anatoly Trosinenko Date: Tue, 13 May 2025 19:50:41 +0300 Subject: [PATCH] [BOLT] Gadget scanner: optionally assume auth traps on failure On AArch64 it is possible for an auth instruction to either return an invalid address value on failure (without FEAT_FPAC) or generate an error (with FEAT_FPAC). It thus may be possible to never emit explicit pointer checks, if the target CPU is known to support FEAT_FPAC. This commit implements an --auth-traps-on-failure command line option, which essentially makes "safe-to-dereference" and "trusted" register properties identical and disables scanning for authentication oracles completely. --- bolt/lib/Passes/PAuthGadgetScanner.cpp| 112 +++ .../binary-analysis/AArch64/cmdline-args.test | 1 + .../AArch64/gs-pauth-authentication-oracles.s | 6 +- .../binary-analysis/AArch64/gs-pauth-calls.s | 5 +- .../AArch64/gs-pauth-debug-output.s | 177 ++--- .../AArch64/gs-pauth-jump-table.s | 6 +- .../AArch64/gs-pauth-signing-oracles.s| 54 ++--- .../AArch64/gs-pauth-tail-calls.s | 184 +- 8 files changed, 318 insertions(+), 227 deletions(-) diff --git a/bolt/lib/Passes/PAuthGadgetScanner.cpp b/bolt/lib/Passes/PAuthGadgetScanner.cpp index bda971bcd9343..cfe86d32df798 100644 --- a/bolt/lib/Passes/PAuthGadgetScanner.cpp +++ b/bolt/lib/Passes/PAuthGadgetScanner.cpp @@ -14,6 +14,7 @@ #include "bolt/Passes/PAuthGadgetScanner.h" #include "bolt/Core/ParallelUtilities.h" #include "bolt/Passes/DataflowAnalysis.h" +#include "bolt/Utils/CommandLineOpts.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallSet.h" #include "llvm/MC/MCInst.h" @@ -26,6 +27,11 @@ namespace llvm { namespace bolt { namespace PAuthGadgetScanner { +static cl::opt AuthTrapsOnFailure( +"auth-traps-on-failure", +cl::desc("Assume authentication instructions always trap on failure"), +cl::cat(opts::BinaryAnalysisCategory)); + [[maybe_unused]] static void traceInst(const BinaryContext &BC, StringRef Label, const MCInst &MI) { dbgs() << " " << Label << ": "; @@ -365,6 +371,34 @@ class SrcSafetyAnalysis { return Clobbered; } + std::optional getRegMadeTrustedByChecking(const MCInst &Inst, + SrcState Cur) const { +// This functions cannot return multiple registers. This is never the case +// on AArch64. +std::optional RegCheckedByInst = +BC.MIB->getAuthCheckedReg(Inst, /*MayOverwrite=*/false); +if (RegCheckedByInst && Cur.SafeToDerefRegs[*RegCheckedByInst]) + return *RegCheckedByInst; + +auto It = CheckerSequenceInfo.find(&Inst); +if (It == CheckerSequenceInfo.end()) + return std::nullopt; + +MCPhysReg RegCheckedBySequence = It->second.first; +const MCInst *FirstCheckerInst = It->second.second; + +// FirstCheckerInst should belong to the same basic block (see the +// assertion in DataflowSrcSafetyAnalysis::run()), meaning it was +// deterministically processed a few steps before this instruction. +const SrcState &StateBeforeChecker = getStateBefore(*FirstCheckerInst); + +// The sequence checks the register, but it should be authenticated before. +if (!StateBeforeChecker.SafeToDerefRegs[RegCheckedBySequence]) + return std::nullopt; + +return RegCheckedBySequence; + } + // Returns all registers that can be treated as if they are written by an // authentication instruction. SmallVector getRegsMadeSafeToDeref(const MCInst &Point, @@ -387,18 +421,38 @@ class SrcSafetyAnalysis { Regs.push_back(DstAndSrc->first); } +// Make sure explicit checker sequence keeps register safe-to-dereference +// when the register would be clobbered according to the regular rules: +// +//; LR is safe to dereference here +//mov x16, x30 ; start of the sequence, LR is s-t-d right before +//xpaclri ; clobbers LR, LR is not safe anymore +//cmp x30, x16 +//b.eq 1f; end of the sequence: LR is marked as trusted +//brk 0x1234 +// 1: +//; at this point LR would be marked as trusted, +//; but not safe-to-dereference +// +// or even just +// +//; X1 is safe to dereference here +//ldr x0, [x1, #8]! +//; X1 is trusted here, but it was clobbered due to address write-back +if (auto CheckedReg = getRegMadeTrustedByChecking(Point, Cur)) + Regs.push_back(*CheckedReg); + return Regs; } // Returns all registers made trusted by this instruction. SmallVector getRegsMadeTrusted(const MCInst &Point, const SrcState &Cur) const { +assert(!AuthTrapsOnFailure &&
[llvm-branch-commits] [llvm] [BOLT] Factor out MCInstReference from gadget scanner (NFC) (PR #138655)
https://github.com/atrosinenko updated https://github.com/llvm/llvm-project/pull/138655 >From 69b6e028492a787934a4d5a4f23644988cb35af9 Mon Sep 17 00:00:00 2001 From: Anatoly Trosinenko Date: Mon, 28 Apr 2025 18:35:48 +0300 Subject: [PATCH] [BOLT] Factor out MCInstReference from gadget scanner (NFC) Move MCInstReference representing a constant reference to an instruction inside a parent entity - either inside a basic block (which has a reference to its parent function) or directly to the function (when CFG information is not available). --- bolt/include/bolt/Core/MCInstUtils.h | 168 + bolt/include/bolt/Passes/PAuthGadgetScanner.h | 178 +- bolt/lib/Core/CMakeLists.txt | 1 + bolt/lib/Core/MCInstUtils.cpp | 57 ++ bolt/lib/Passes/PAuthGadgetScanner.cpp| 102 +- 5 files changed, 269 insertions(+), 237 deletions(-) create mode 100644 bolt/include/bolt/Core/MCInstUtils.h create mode 100644 bolt/lib/Core/MCInstUtils.cpp diff --git a/bolt/include/bolt/Core/MCInstUtils.h b/bolt/include/bolt/Core/MCInstUtils.h new file mode 100644 index 0..69bf5e6159b74 --- /dev/null +++ b/bolt/include/bolt/Core/MCInstUtils.h @@ -0,0 +1,168 @@ +//===- bolt/Core/MCInstUtils.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 +// +//===--===// + +#ifndef BOLT_CORE_MCINSTUTILS_H +#define BOLT_CORE_MCINSTUTILS_H + +#include "bolt/Core/BinaryBasicBlock.h" + +#include +#include +#include + +namespace llvm { +namespace bolt { + +class BinaryFunction; + +/// MCInstReference represents a reference to a constant MCInst as stored either +/// in a BinaryFunction (i.e. before a CFG is created), or in a BinaryBasicBlock +/// (after a CFG is created). +class MCInstReference { + using nocfg_const_iterator = std::map::const_iterator; + + // Two cases are possible: + // * functions with CFG reconstructed - a function stores a collection of + // basic blocks, each basic block stores a contiguous vector of MCInst + // * functions without CFG - there are no basic blocks created, + // the instructions are directly stored in std::map in BinaryFunction + // + // In both cases, the direct parent of MCInst is stored together with an + // iterator pointing to the instruction. + + // Helper struct: CFG is available, the direct parent is a basic block, + // iterator's type is `MCInst *`. + struct RefInBB { +RefInBB(const BinaryBasicBlock *BB, const MCInst *Inst) +: BB(BB), It(Inst) {} +RefInBB(const RefInBB &Other) = default; +RefInBB &operator=(const RefInBB &Other) = default; + +const BinaryBasicBlock *BB; +BinaryBasicBlock::const_iterator It; + +bool operator<(const RefInBB &Other) const { + return std::tie(BB, It) < std::tie(Other.BB, Other.It); +} + +bool operator==(const RefInBB &Other) const { + return BB == Other.BB && It == Other.It; +} + }; + + // Helper struct: CFG is *not* available, the direct parent is a function, + // iterator's type is std::map::iterator (the mapped value + // is an instruction's offset). + struct RefInBF { +RefInBF(const BinaryFunction *BF, nocfg_const_iterator It) +: BF(BF), It(It) {} +RefInBF(const RefInBF &Other) = default; +RefInBF &operator=(const RefInBF &Other) = default; + +const BinaryFunction *BF; +nocfg_const_iterator It; + +bool operator<(const RefInBF &Other) const { + return std::tie(BF, It->first) < std::tie(Other.BF, Other.It->first); +} + +bool operator==(const RefInBF &Other) const { + return BF == Other.BF && It->first == Other.It->first; +} + }; + + std::variant Reference; + + // Utility methods to be used like this: + // + // if (auto *Ref = tryGetRefInBB()) + // return Ref->doSomething(...); + // return getRefInBF().doSomethingElse(...); + const RefInBB *tryGetRefInBB() const { +assert(std::get_if(&Reference) || + std::get_if(&Reference)); +return std::get_if(&Reference); + } + const RefInBF &getRefInBF() const { +assert(std::get_if(&Reference)); +return *std::get_if(&Reference); + } + +public: + /// Constructs an empty reference. + MCInstReference() : Reference(RefInBB(nullptr, nullptr)) {} + /// Constructs a reference to the instruction inside the basic block. + MCInstReference(const BinaryBasicBlock *BB, const MCInst *Inst) + : Reference(RefInBB(BB, Inst)) { +assert(BB && Inst && "Neither BB nor Inst should be nullptr"); + } + /// Constructs a reference to the instruction inside the basic block. + MCInstReference(const BinaryBasicBlock *BB, unsigned Index) + : Reference(RefInBB(BB, &BB->getInstructionAtIndex(I
[llvm-branch-commits] [llvm] [BOLT] Gadget scanner: do not crash on debug-printing CFI instructions (PR #136151)
https://github.com/atrosinenko updated https://github.com/llvm/llvm-project/pull/136151 >From f81401c81dfcb247cc8b5c08b9594bf78ef0af1e Mon Sep 17 00:00:00 2001 From: Anatoly Trosinenko Date: Tue, 15 Apr 2025 21:47:18 +0300 Subject: [PATCH] [BOLT] Gadget scanner: do not crash on debug-printing CFI instructions Some instruction-printing code used under LLVM_DEBUG does not handle CFI instructions well. While CFI instructions seem to be harmless for the correctness of the analysis results, they do not convey any useful information to the analysis either, so skip them early. --- bolt/lib/Passes/PAuthGadgetScanner.cpp| 16 ++ .../AArch64/gs-pauth-debug-output.s | 32 +++ 2 files changed, 48 insertions(+) diff --git a/bolt/lib/Passes/PAuthGadgetScanner.cpp b/bolt/lib/Passes/PAuthGadgetScanner.cpp index c18829bc313a8..cd7c077a6412e 100644 --- a/bolt/lib/Passes/PAuthGadgetScanner.cpp +++ b/bolt/lib/Passes/PAuthGadgetScanner.cpp @@ -431,6 +431,9 @@ class SrcSafetyAnalysis { } SrcState computeNext(const MCInst &Point, const SrcState &Cur) { +if (BC.MIB->isCFI(Point)) + return Cur; + SrcStatePrinter P(BC); LLVM_DEBUG({ dbgs() << " SrcSafetyAnalysis::ComputeNext("; @@ -674,6 +677,8 @@ class CFGUnawareSrcSafetyAnalysis : public SrcSafetyAnalysis { SrcState S = createEntryState(); for (auto &I : BF.instrs()) { MCInst &Inst = I.second; + if (BC.MIB->isCFI(Inst)) +continue; // If there is a label before this instruction, it is possible that it // can be jumped-to, thus conservatively resetting S. As an exception, @@ -959,6 +964,9 @@ class DstSafetyAnalysis { } DstState computeNext(const MCInst &Point, const DstState &Cur) { +if (BC.MIB->isCFI(Point)) + return Cur; + DstStatePrinter P(BC); LLVM_DEBUG({ dbgs() << " DstSafetyAnalysis::ComputeNext("; @@ -1135,6 +1143,8 @@ class CFGUnawareDstSafetyAnalysis : public DstSafetyAnalysis { DstState S = createUnsafeState(); for (auto &I : llvm::reverse(BF.instrs())) { MCInst &Inst = I.second; + if (BC.MIB->isCFI(Inst)) +continue; // If Inst can change the control flow, we cannot be sure that the next // instruction (to be executed in analyzed program) is the one processed @@ -1333,6 +1343,9 @@ void FunctionAnalysisContext::findUnsafeUses( }); iterateOverInstrs(BF, [&](MCInstReference Inst) { +if (BC.MIB->isCFI(Inst)) + return; + const SrcState &S = Analysis->getStateBefore(Inst); // If non-empty state was never propagated from the entry basic block @@ -1396,6 +1409,9 @@ void FunctionAnalysisContext::findUnsafeDefs( }); iterateOverInstrs(BF, [&](MCInstReference Inst) { +if (BC.MIB->isCFI(Inst)) + return; + const DstState &S = Analysis->getStateAfter(Inst); if (auto Report = shouldReportAuthOracle(BC, Inst, S)) diff --git a/bolt/test/binary-analysis/AArch64/gs-pauth-debug-output.s b/bolt/test/binary-analysis/AArch64/gs-pauth-debug-output.s index 61aa84377b88e..5aec945621987 100644 --- a/bolt/test/binary-analysis/AArch64/gs-pauth-debug-output.s +++ b/bolt/test/binary-analysis/AArch64/gs-pauth-debug-output.s @@ -329,6 +329,38 @@ auth_oracle: // PAUTH-EMPTY: // PAUTH-NEXT: Attaching leakage info to: : autia x0, x1 # DataflowDstSafetyAnalysis: dst-state +// Gadget scanner should not crash on CFI instructions, including when debug-printing them. +// Note that the particular debug output is not checked, but BOLT should be +// compiled with assertions enabled to support -debug-only argument. + +.globl cfi_inst_df +.type cfi_inst_df,@function +cfi_inst_df: +.cfi_startproc +sub sp, sp, #16 +.cfi_def_cfa_offset 16 +add sp, sp, #16 +.cfi_def_cfa_offset 0 +ret +.size cfi_inst_df, .-cfi_inst_df +.cfi_endproc + +.globl cfi_inst_nocfg +.type cfi_inst_nocfg,@function +cfi_inst_nocfg: +.cfi_startproc +sub sp, sp, #16 +.cfi_def_cfa_offset 16 + +adr x0, 1f +br x0 +1: +add sp, sp, #16 +.cfi_def_cfa_offset 0 +ret +.size cfi_inst_nocfg, .-cfi_inst_nocfg +.cfi_endproc + // CHECK-LABEL:Analyzing function main, AllocatorId = 1 .globl main .type main,@function ___ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
[llvm-branch-commits] [llvm] [BOLT] Gadget scanner: account for BRK when searching for auth oracles (PR #137975)
https://github.com/atrosinenko updated https://github.com/llvm/llvm-project/pull/137975 >From 47a6ece20ffb8cedf3c86650886e73d8bdc463d7 Mon Sep 17 00:00:00 2001 From: Anatoly Trosinenko Date: Wed, 30 Apr 2025 16:08:10 +0300 Subject: [PATCH] [BOLT] Gadget scanner: account for BRK when searching for auth oracles An authenticated pointer can be explicitly checked by the compiler via a sequence of instructions that executes BRK on failure. It is important to recognize such BRK instruction as checking every register (as it is expected to immediately trigger an abnormal program termination) to prevent false positive reports about authentication oracles: autia x2, x3 autia x0, x1 ; neither x0 nor x2 are checked at this point eor x16, x0, x0, lsl #1 tbz x16, #62, on_success ; marks x0 as checked ; end of BB: for x2 to be checked here, it must be checked in both ; successor basic blocks on_failure: brk 0xc470 on_success: ; x2 is checked ldr x1, [x2] ; marks x2 as checked --- bolt/include/bolt/Core/MCPlusBuilder.h| 14 ++ bolt/lib/Passes/PAuthGadgetScanner.cpp| 13 +- .../Target/AArch64/AArch64MCPlusBuilder.cpp | 24 -- .../AArch64/gs-pauth-address-checks.s | 44 +-- .../AArch64/gs-pauth-authentication-oracles.s | 9 ++-- .../AArch64/gs-pauth-signing-oracles.s| 6 +-- 6 files changed, 75 insertions(+), 35 deletions(-) diff --git a/bolt/include/bolt/Core/MCPlusBuilder.h b/bolt/include/bolt/Core/MCPlusBuilder.h index 6d3aa4f5f0feb..87de6754017db 100644 --- a/bolt/include/bolt/Core/MCPlusBuilder.h +++ b/bolt/include/bolt/Core/MCPlusBuilder.h @@ -706,6 +706,20 @@ class MCPlusBuilder { return false; } + /// Returns true if Inst is a trap instruction. + /// + /// Tests if Inst is an instruction that immediately causes an abnormal + /// program termination, for example when a security violation is detected + /// by a compiler-inserted check. + /// + /// @note An implementation of this method should likely return false for + /// calls to library functions like abort(), as it is possible that the + /// execution state is partially attacker-controlled at this point. + virtual bool isTrap(const MCInst &Inst) const { +llvm_unreachable("not implemented"); +return false; + } + virtual bool isBreakpoint(const MCInst &Inst) const { llvm_unreachable("not implemented"); return false; diff --git a/bolt/lib/Passes/PAuthGadgetScanner.cpp b/bolt/lib/Passes/PAuthGadgetScanner.cpp index dfb71575b2b39..835ee26aaf08a 100644 --- a/bolt/lib/Passes/PAuthGadgetScanner.cpp +++ b/bolt/lib/Passes/PAuthGadgetScanner.cpp @@ -1028,6 +1028,15 @@ class DstSafetyAnalysis { dbgs() << ")\n"; }); +// If this instruction terminates the program immediately, no +// authentication oracles are possible past this point. +if (BC.MIB->isTrap(Point)) { + LLVM_DEBUG({ traceInst(BC, "Trap instruction found", Point); }); + DstState Next(NumRegs, RegsToTrackInstsFor.getNumTrackedRegisters()); + Next.CannotEscapeUnchecked.set(); + return Next; +} + // If this instruction is reachable by the analysis, a non-empty state will // be propagated to it sooner or later. Until then, skip computeNext(). if (Cur.empty()) { @@ -1133,8 +1142,8 @@ class DataflowDstSafetyAnalysis // // A basic block without any successors, on the other hand, can be // pessimistically initialized to everything-is-unsafe: this will naturally -// handle both return and tail call instructions and is harmless for -// internal indirect branch instructions (such as computed gotos). +// handle return, trap and tail call instructions. At the same time, it is +// harmless for internal indirect branch instructions, like computed gotos. if (BB.succ_empty()) return createUnsafeState(); diff --git a/bolt/lib/Target/AArch64/AArch64MCPlusBuilder.cpp b/bolt/lib/Target/AArch64/AArch64MCPlusBuilder.cpp index f3c29e6ee43b9..4d11c5b206eab 100644 --- a/bolt/lib/Target/AArch64/AArch64MCPlusBuilder.cpp +++ b/bolt/lib/Target/AArch64/AArch64MCPlusBuilder.cpp @@ -386,10 +386,9 @@ class AArch64MCPlusBuilder : public MCPlusBuilder { // the list of successors of this basic block as appropriate. // Any of the above code sequences assume the fall-through basic block -// is a dead-end BRK instruction (any immediate operand is accepted). +// is a dead-end trap instruction. const BinaryBasicBlock *BreakBB = BB.getFallthrough(); -if (!BreakBB || BreakBB->empty() || -BreakBB->front().getOpcode() != AArch64::BRK) +if (!BreakBB || BreakBB->empty() || !isTrap(BreakBB->front())) return std::nullopt; // Iterate over the instructions of BB in reverse order, matching opcodes @@ -1745,6 +1744,25 @@ class AArch64MCPlusBuilder : public MCPlusBuilder { Inst.addOperand(MCOperand::createImm(0)); }
[llvm-branch-commits] [llvm] release/20.x: [SDAG] Ensure load is included in output chain of sincos expansion (#140525) (PR #140703)
llvmbot wrote: @llvm/pr-subscribers-backend-x86 Author: None (llvmbot) Changes Backport c9d6249 Requested by: @MacDue --- Full diff: https://github.com/llvm/llvm-project/pull/140703.diff 2 Files Affected: - (modified) llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp (+6-3) - (added) llvm/test/CodeGen/X86/pr140491-sincos-lifetimes.ll (+70) ``diff diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp index b416c0efbbc4f..eecfb41c2d319 100644 --- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp @@ -2660,16 +2660,19 @@ bool SelectionDAG::expandMultipleResultFPLibCall( continue; } MachinePointerInfo PtrInfo; +SDValue LoadResult = +getLoad(Node->getValueType(ResNo), DL, CallChain, ResultPtr, PtrInfo); +SDValue OutChain = LoadResult.getValue(1); + if (StoreSDNode *ST = ResultStores[ResNo]) { // Replace store with the library call. - ReplaceAllUsesOfValueWith(SDValue(ST, 0), CallChain); + ReplaceAllUsesOfValueWith(SDValue(ST, 0), OutChain); PtrInfo = ST->getPointerInfo(); } else { PtrInfo = MachinePointerInfo::getFixedStack( getMachineFunction(), cast(ResultPtr)->getIndex()); } -SDValue LoadResult = -getLoad(Node->getValueType(ResNo), DL, CallChain, ResultPtr, PtrInfo); + Results.push_back(LoadResult); } diff --git a/llvm/test/CodeGen/X86/pr140491-sincos-lifetimes.ll b/llvm/test/CodeGen/X86/pr140491-sincos-lifetimes.ll new file mode 100644 index 0..2ca99bdc4b316 --- /dev/null +++ b/llvm/test/CodeGen/X86/pr140491-sincos-lifetimes.ll @@ -0,0 +1,70 @@ +; RUN: llc < %s | FileCheck %s + +; This test is reduced from https://github.com/llvm/llvm-project/issues/140491. +; It checks that when `@llvm.sincos.f32` is expanded to a call to +; `sincosf(float, float* out_sin, float* out_cos)` and the store of `%cos` to +; `%computed` is folded into the `sincosf` call. The use of `%cos`in the later +; `fneg %cos` -- which expands to a load of `%computed`, will perform the load +; before the `@llvm.lifetime.end.p0(%computed)` to ensure the correct value is +; taken for `%cos`. + +target triple = "x86_64-sie-ps5" + +declare void @use_ptr(ptr readonly) + +define i32 @sincos_stack_slot_with_lifetime(float %in) { +; CHECK-LABEL: sincos_stack_slot_with_lifetime: +; CHECK: # %bb.0: # %entry +; CHECK-NEXT:pushq %rbx +; CHECK-NEXT:.cfi_def_cfa_offset 16 +; CHECK-NEXT:subq $32, %rsp +; CHECK-NEXT:.cfi_def_cfa_offset 48 +; CHECK-NEXT:.cfi_offset %rbx, -16 +; CHECK-NEXT:leaq 12(%rsp), %rdi +; CHECK-NEXT:leaq 8(%rsp), %rbx +; CHECK-NEXT:movq %rbx, %rsi +; CHECK-NEXT:callq sincosf@PLT +; CHECK-NEXT:movss 8(%rsp), %xmm0 # xmm0 = mem[0],zero,zero,zero +; CHECK-NEXT:movaps %xmm0, 16(%rsp) # 16-byte Spill +; CHECK-NEXT:movq %rbx, %rdi +; CHECK-NEXT:callq use_ptr +; CHECK-NEXT:movss 12(%rsp), %xmm0 # xmm0 = mem[0],zero,zero,zero +; CHECK-NEXT:xorps {{\.?LCPI[0-9]+_[0-9]+}}(%rip), %xmm0 +; CHECK-NEXT:movss %xmm0, 8(%rsp) +; CHECK-NEXT:leaq 8(%rsp), %rdi +; CHECK-NEXT:callq use_ptr +; CHECK-NEXT:movaps 16(%rsp), %xmm0 # 16-byte Reload +; CHECK-NEXT:xorps {{\.?LCPI[0-9]+_[0-9]+}}(%rip), %xmm0 +; CHECK-NEXT:movss %xmm0, 8(%rsp) +; CHECK-NEXT:leaq 8(%rsp), %rdi +; CHECK-NEXT:callq use_ptr +; CHECK-NEXT:xorl %eax, %eax +; CHECK-NEXT:addq $32, %rsp +; CHECK-NEXT:.cfi_def_cfa_offset 16 +; CHECK-NEXT:popq %rbx +; CHECK-NEXT:.cfi_def_cfa_offset 8 +; CHECK-NEXT:retq +entry: + %computed = alloca float, align 4 + %computed1 = alloca float, align 4 + %computed3 = alloca float, align 4 + %sincos = tail call { float, float } @llvm.sincos.f32(float %in) + %sin = extractvalue { float, float } %sincos, 0 + %cos = extractvalue { float, float } %sincos, 1 + call void @llvm.lifetime.start.p0(i64 4, ptr nonnull %computed) + store float %cos, ptr %computed, align 4 + call void @use_ptr(ptr nonnull %computed) + call void @llvm.lifetime.end.p0(i64 4, ptr nonnull %computed) + call void @llvm.lifetime.start.p0(i64 4, ptr nonnull %computed1) + %fneg_sin = fneg float %sin + store float %fneg_sin, ptr %computed1, align 4 + call void @use_ptr(ptr nonnull %computed1) + call void @llvm.lifetime.end.p0(i64 4, ptr nonnull %computed1) + call void @llvm.lifetime.start.p0(i64 4, ptr nonnull %computed3) + %fneg_cos = fneg float %cos + store float %fneg_cos, ptr %computed3, align 4 + call void @use_ptr(ptr nonnull %computed3) + call void @llvm.lifetime.end.p0(i64 4, ptr nonnull %computed3) + ret i32 0 +} + `` https://github.com/llvm/llvm-project/pull/140703 ___ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
[llvm-branch-commits] [llvm] [BOLT] Gadget scanner: use more appropriate types (NFC) (PR #135661)
https://github.com/atrosinenko updated https://github.com/llvm/llvm-project/pull/135661 >From 3ee58131f5d224a71e2ee32075009fde17772856 Mon Sep 17 00:00:00 2001 From: Anatoly Trosinenko Date: Mon, 14 Apr 2025 14:35:56 +0300 Subject: [PATCH 1/2] [BOLT] Gadget scanner: use more appropriate types (NFC) * use more flexible `const ArrayRef` and `StringRef` types instead of `const std::vector &` and `const std::string &`, correspondingly, for function arguments * return plain `const SrcState &` instead of `ErrorOr` from `SrcSafetyAnalysis::getStateBefore`, as absent state is not handled gracefully by any caller --- bolt/include/bolt/Passes/PAuthGadgetScanner.h | 8 +--- bolt/lib/Passes/PAuthGadgetScanner.cpp| 39 --- 2 files changed, 19 insertions(+), 28 deletions(-) diff --git a/bolt/include/bolt/Passes/PAuthGadgetScanner.h b/bolt/include/bolt/Passes/PAuthGadgetScanner.h index 75a8d26c64537..451299327e3b2 100644 --- a/bolt/include/bolt/Passes/PAuthGadgetScanner.h +++ b/bolt/include/bolt/Passes/PAuthGadgetScanner.h @@ -12,7 +12,6 @@ #include "bolt/Core/BinaryContext.h" #include "bolt/Core/BinaryFunction.h" #include "bolt/Passes/BinaryPasses.h" -#include "llvm/ADT/SmallSet.h" #include "llvm/Support/raw_ostream.h" #include @@ -197,9 +196,6 @@ raw_ostream &operator<<(raw_ostream &OS, const MCInstReference &); namespace PAuthGadgetScanner { -class SrcSafetyAnalysis; -struct SrcState; - /// Description of a gadget kind that can be detected. Intended to be /// statically allocated to be attached to reports by reference. class GadgetKind { @@ -208,7 +204,7 @@ class GadgetKind { public: GadgetKind(const char *Description) : Description(Description) {} - const StringRef getDescription() const { return Description; } + StringRef getDescription() const { return Description; } }; /// Base report located at some instruction, without any additional information. @@ -259,7 +255,7 @@ struct GadgetReport : public Report { /// Report with a free-form message attached. struct GenericReport : public Report { std::string Text; - GenericReport(MCInstReference Location, const std::string &Text) + GenericReport(MCInstReference Location, StringRef Text) : Report(Location), Text(Text) {} virtual void generateReport(raw_ostream &OS, const BinaryContext &BC) const override; diff --git a/bolt/lib/Passes/PAuthGadgetScanner.cpp b/bolt/lib/Passes/PAuthGadgetScanner.cpp index 12eb9c66130b9..3d723456b6ffd 100644 --- a/bolt/lib/Passes/PAuthGadgetScanner.cpp +++ b/bolt/lib/Passes/PAuthGadgetScanner.cpp @@ -91,14 +91,14 @@ class TrackedRegisters { const std::vector Registers; std::vector RegToIndexMapping; - static size_t getMappingSize(const std::vector &RegsToTrack) { + static size_t getMappingSize(const ArrayRef RegsToTrack) { if (RegsToTrack.empty()) return 0; return 1 + *llvm::max_element(RegsToTrack); } public: - TrackedRegisters(const std::vector &RegsToTrack) + TrackedRegisters(const ArrayRef RegsToTrack) : Registers(RegsToTrack), RegToIndexMapping(getMappingSize(RegsToTrack), NoIndex) { for (unsigned I = 0; I < RegsToTrack.size(); ++I) @@ -234,7 +234,7 @@ struct SrcState { static void printLastInsts( raw_ostream &OS, -const std::vector> &LastInstWritingReg) { +const ArrayRef> LastInstWritingReg) { OS << "Insts: "; for (unsigned I = 0; I < LastInstWritingReg.size(); ++I) { auto &Set = LastInstWritingReg[I]; @@ -295,7 +295,7 @@ void SrcStatePrinter::print(raw_ostream &OS, const SrcState &S) const { class SrcSafetyAnalysis { public: SrcSafetyAnalysis(BinaryFunction &BF, -const std::vector &RegsToTrackInstsFor) +const ArrayRef RegsToTrackInstsFor) : BC(BF.getBinaryContext()), NumRegs(BC.MRI->getNumRegs()), RegsToTrackInstsFor(RegsToTrackInstsFor) {} @@ -303,11 +303,10 @@ class SrcSafetyAnalysis { static std::shared_ptr create(BinaryFunction &BF, MCPlusBuilder::AllocatorIdTy AllocId, - const std::vector &RegsToTrackInstsFor); + const ArrayRef RegsToTrackInstsFor); virtual void run() = 0; - virtual ErrorOr - getStateBefore(const MCInst &Inst) const = 0; + virtual const SrcState &getStateBefore(const MCInst &Inst) const = 0; protected: BinaryContext &BC; @@ -347,7 +346,7 @@ class SrcSafetyAnalysis { } BitVector getClobberedRegs(const MCInst &Point) const { -BitVector Clobbered(NumRegs, false); +BitVector Clobbered(NumRegs); // Assume a call can clobber all registers, including callee-saved // registers. There's a good chance that callee-saved registers will be // saved on the stack at some point during execution of the callee. @@ -409,8 +408,7 @@ class SrcSafetyAnalysis { // FirstCheckerInst should belong to the same basic block (see the // assertion in DataflowSrcSafetyAnalysis::run()), meaning it was
[llvm-branch-commits] [llvm] [BOLT] Gadget scanner: improve handling of unreachable basic blocks (PR #136183)
https://github.com/atrosinenko updated https://github.com/llvm/llvm-project/pull/136183 >From fc2106d33c0f703a6138f481dea8fa43f02da37a Mon Sep 17 00:00:00 2001 From: Anatoly Trosinenko Date: Thu, 17 Apr 2025 20:51:16 +0300 Subject: [PATCH 1/3] [BOLT] Gadget scanner: improve handling of unreachable basic blocks Instead of refusing to analyze an instruction completely, when it is unreachable according to the CFG reconstructed by BOLT, pessimistically assume all registers to be unsafe at the start of basic blocks without any predecessors. Nevertheless, unreachable basic blocks found in optimized code likely means imprecise CFG reconstruction, thus report a warning once per basic block without predecessors. --- bolt/lib/Passes/PAuthGadgetScanner.cpp| 46 ++- .../AArch64/gs-pacret-autiasp.s | 7 ++- .../binary-analysis/AArch64/gs-pauth-calls.s | 57 +++ 3 files changed, 95 insertions(+), 15 deletions(-) diff --git a/bolt/lib/Passes/PAuthGadgetScanner.cpp b/bolt/lib/Passes/PAuthGadgetScanner.cpp index cd7c077a6412e..3cee579ef2a15 100644 --- a/bolt/lib/Passes/PAuthGadgetScanner.cpp +++ b/bolt/lib/Passes/PAuthGadgetScanner.cpp @@ -343,6 +343,12 @@ class SrcSafetyAnalysis { return S; } + /// Creates a state with all registers marked unsafe (not to be confused + /// with empty state). + SrcState createUnsafeState() const { +return SrcState(NumRegs, RegsToTrackInstsFor.getNumTrackedRegisters()); + } + BitVector getClobberedRegs(const MCInst &Point) const { BitVector Clobbered(NumRegs); // Assume a call can clobber all registers, including callee-saved @@ -585,6 +591,13 @@ class DataflowSrcSafetyAnalysis if (BB.isEntryPoint()) return createEntryState(); +// If a basic block without any predecessors is found in an optimized code, +// this likely means that some CFG edges were not detected. Pessimistically +// assume all registers to be unsafe before this basic block and warn about +// this fact in FunctionAnalysis::findUnsafeUses(). +if (BB.pred_empty()) + return createUnsafeState(); + return SrcState(); } @@ -658,12 +671,6 @@ class CFGUnawareSrcSafetyAnalysis : public SrcSafetyAnalysis { BC.MIB->removeAnnotation(I.second, StateAnnotationIndex); } - /// Creates a state with all registers marked unsafe (not to be confused - /// with empty state). - SrcState createUnsafeState() const { -return SrcState(NumRegs, RegsToTrackInstsFor.getNumTrackedRegisters()); - } - public: CFGUnawareSrcSafetyAnalysis(BinaryFunction &BF, MCPlusBuilder::AllocatorIdTy AllocId, @@ -1342,19 +1349,30 @@ void FunctionAnalysisContext::findUnsafeUses( BF.dump(); }); + if (BF.hasCFG()) { +// Warn on basic blocks being unreachable according to BOLT, as this +// likely means CFG is imprecise. +for (BinaryBasicBlock &BB : BF) { + if (!BB.pred_empty() || BB.isEntryPoint()) +continue; + // Arbitrarily attach the report to the first instruction of BB. + MCInst *InstToReport = BB.getFirstNonPseudoInstr(); + if (!InstToReport) +continue; // BB has no real instructions + + Reports.push_back( + make_generic_report(MCInstReference::get(InstToReport, BF), + "Warning: no predecessor basic blocks detected " + "(possibly incomplete CFG)")); +} + } + iterateOverInstrs(BF, [&](MCInstReference Inst) { if (BC.MIB->isCFI(Inst)) return; const SrcState &S = Analysis->getStateBefore(Inst); - -// If non-empty state was never propagated from the entry basic block -// to Inst, assume it to be unreachable and report a warning. -if (S.empty()) { - Reports.push_back( - make_generic_report(Inst, "Warning: unreachable instruction found")); - return; -} +assert(!S.empty() && "Instruction has no associated state"); if (auto Report = shouldReportReturnGadget(BC, Inst, S)) Reports.push_back(*Report); diff --git a/bolt/test/binary-analysis/AArch64/gs-pacret-autiasp.s b/bolt/test/binary-analysis/AArch64/gs-pacret-autiasp.s index 284f0bea607a5..6559ba336e8de 100644 --- a/bolt/test/binary-analysis/AArch64/gs-pacret-autiasp.s +++ b/bolt/test/binary-analysis/AArch64/gs-pacret-autiasp.s @@ -215,12 +215,17 @@ f_callclobbered_calleesaved: .globl f_unreachable_instruction .type f_unreachable_instruction,@function f_unreachable_instruction: -// CHECK-LABEL: GS-PAUTH: Warning: unreachable instruction found in function f_unreachable_instruction, basic block {{[0-9a-zA-Z.]+}}, at address +// CHECK-LABEL: GS-PAUTH: Warning: no predecessor basic blocks detected (possibly incomplete CFG) in function f_unreachable_instruction, basic block {{[0-9a-zA-Z.]+}}, at address // CHECK-NEXT:The instruction is {{[0-9a-f]+}}: add x0, x1, x2 // CHECK-NOT: instructi
[llvm-branch-commits] [llvm] [BOLT] Gadget scanner: prevent false positives due to jump tables (PR #138884)
https://github.com/atrosinenko updated https://github.com/llvm/llvm-project/pull/138884 >From 5c33526a475b60c4e4333457d6cdd8ea59e2cb64 Mon Sep 17 00:00:00 2001 From: Anatoly Trosinenko Date: Tue, 6 May 2025 11:31:03 +0300 Subject: [PATCH] [BOLT] Gadget scanner: prevent false positives due to jump tables As part of PAuth hardening, AArch64 LLVM backend can use a special BR_JumpTable pseudo (enabled by -faarch64-jump-table-hardening Clang option) which is expanded in the AsmPrinter into a contiguous sequence without unsafe instructions in the middle. This commit adds another target-specific callback to MCPlusBuilder to make it possible to inhibit false positives for known-safe jump table dispatch sequences. Without special handling, the branch instruction is likely to be reported as a non-protected call (as its destination is not produced by an auth instruction, PC-relative address materialization, etc.) and possibly as a tail call being performed with unsafe link register (as the detection whether the branch instruction is a tail call is an heuristic). For now, only the specific instruction sequence used by the AArch64 LLVM backend is matched. --- bolt/include/bolt/Core/MCInstUtils.h | 9 + bolt/include/bolt/Core/MCPlusBuilder.h| 14 + bolt/lib/Core/MCInstUtils.cpp | 20 + bolt/lib/Passes/PAuthGadgetScanner.cpp| 10 + .../Target/AArch64/AArch64MCPlusBuilder.cpp | 73 ++ .../AArch64/gs-pauth-jump-table.s | 703 ++ 6 files changed, 829 insertions(+) create mode 100644 bolt/test/binary-analysis/AArch64/gs-pauth-jump-table.s diff --git a/bolt/include/bolt/Core/MCInstUtils.h b/bolt/include/bolt/Core/MCInstUtils.h index 50b7d56470c99..33d36cccbcfff 100644 --- a/bolt/include/bolt/Core/MCInstUtils.h +++ b/bolt/include/bolt/Core/MCInstUtils.h @@ -154,6 +154,15 @@ class MCInstReference { return nullptr; } + /// Returns the only preceding instruction, or std::nullopt if multiple or no + /// predecessors are possible. + /// + /// If CFG information is available, basic block boundary can be crossed, + /// provided there is exactly one predecessor. If CFG is not available, the + /// preceding instruction in the offset order is returned, unless this is the + /// first instruction of the function. + std::optional getSinglePredecessor(); + raw_ostream &print(raw_ostream &OS) const; }; diff --git a/bolt/include/bolt/Core/MCPlusBuilder.h b/bolt/include/bolt/Core/MCPlusBuilder.h index 87de6754017db..eb93d7de7fee9 100644 --- a/bolt/include/bolt/Core/MCPlusBuilder.h +++ b/bolt/include/bolt/Core/MCPlusBuilder.h @@ -14,6 +14,7 @@ #ifndef BOLT_CORE_MCPLUSBUILDER_H #define BOLT_CORE_MCPLUSBUILDER_H +#include "bolt/Core/MCInstUtils.h" #include "bolt/Core/MCPlus.h" #include "bolt/Core/Relocation.h" #include "llvm/ADT/ArrayRef.h" @@ -699,6 +700,19 @@ class MCPlusBuilder { return std::nullopt; } + /// Tests if BranchInst corresponds to an instruction sequence which is known + /// to be a safe dispatch via jump table. + /// + /// The target can decide which instruction sequences to consider "safe" from + /// the Pointer Authentication point of view, such as any jump table dispatch + /// sequence without function calls inside, any sequence which is contiguous, + /// or only some specific well-known sequences. + virtual bool + isSafeJumpTableBranchForPtrAuth(MCInstReference BranchInst) const { +llvm_unreachable("not implemented"); +return false; + } + virtual bool isTerminator(const MCInst &Inst) const; virtual bool isNoop(const MCInst &Inst) const { diff --git a/bolt/lib/Core/MCInstUtils.cpp b/bolt/lib/Core/MCInstUtils.cpp index 40f6edd59135c..b7c6d898988af 100644 --- a/bolt/lib/Core/MCInstUtils.cpp +++ b/bolt/lib/Core/MCInstUtils.cpp @@ -55,3 +55,23 @@ raw_ostream &MCInstReference::print(raw_ostream &OS) const { OS << ">"; return OS; } + +std::optional MCInstReference::getSinglePredecessor() { + if (const RefInBB *Ref = tryGetRefInBB()) { +if (Ref->It != Ref->BB->begin()) + return MCInstReference(Ref->BB, &*std::prev(Ref->It)); + +if (Ref->BB->pred_size() != 1) + return std::nullopt; + +BinaryBasicBlock *PredBB = *Ref->BB->pred_begin(); +assert(!PredBB->empty() && "Empty basic blocks are not supported yet"); +return MCInstReference(PredBB, &*PredBB->rbegin()); + } + + const RefInBF &Ref = getRefInBF(); + if (Ref.It == Ref.BF->instrs().begin()) +return std::nullopt; + + return MCInstReference(Ref.BF, std::prev(Ref.It)); +} diff --git a/bolt/lib/Passes/PAuthGadgetScanner.cpp b/bolt/lib/Passes/PAuthGadgetScanner.cpp index 5e08ae3fbf767..bda971bcd9343 100644 --- a/bolt/lib/Passes/PAuthGadgetScanner.cpp +++ b/bolt/lib/Passes/PAuthGadgetScanner.cpp @@ -1328,6 +1328,11 @@ shouldReportUnsafeTailCall(const BinaryContext &BC, const BinaryFunction &BF, return std::nullopt; } + if (BC.MIB->isSafeJumpTableBranchForPtrAuth(Inst)) { +LL
[llvm-branch-commits] [llvm] [BOLT] Gadget scanner: improve handling of unreachable basic blocks (PR #136183)
https://github.com/atrosinenko updated https://github.com/llvm/llvm-project/pull/136183 >From fc2106d33c0f703a6138f481dea8fa43f02da37a Mon Sep 17 00:00:00 2001 From: Anatoly Trosinenko Date: Thu, 17 Apr 2025 20:51:16 +0300 Subject: [PATCH 1/3] [BOLT] Gadget scanner: improve handling of unreachable basic blocks Instead of refusing to analyze an instruction completely, when it is unreachable according to the CFG reconstructed by BOLT, pessimistically assume all registers to be unsafe at the start of basic blocks without any predecessors. Nevertheless, unreachable basic blocks found in optimized code likely means imprecise CFG reconstruction, thus report a warning once per basic block without predecessors. --- bolt/lib/Passes/PAuthGadgetScanner.cpp| 46 ++- .../AArch64/gs-pacret-autiasp.s | 7 ++- .../binary-analysis/AArch64/gs-pauth-calls.s | 57 +++ 3 files changed, 95 insertions(+), 15 deletions(-) diff --git a/bolt/lib/Passes/PAuthGadgetScanner.cpp b/bolt/lib/Passes/PAuthGadgetScanner.cpp index cd7c077a6412e..3cee579ef2a15 100644 --- a/bolt/lib/Passes/PAuthGadgetScanner.cpp +++ b/bolt/lib/Passes/PAuthGadgetScanner.cpp @@ -343,6 +343,12 @@ class SrcSafetyAnalysis { return S; } + /// Creates a state with all registers marked unsafe (not to be confused + /// with empty state). + SrcState createUnsafeState() const { +return SrcState(NumRegs, RegsToTrackInstsFor.getNumTrackedRegisters()); + } + BitVector getClobberedRegs(const MCInst &Point) const { BitVector Clobbered(NumRegs); // Assume a call can clobber all registers, including callee-saved @@ -585,6 +591,13 @@ class DataflowSrcSafetyAnalysis if (BB.isEntryPoint()) return createEntryState(); +// If a basic block without any predecessors is found in an optimized code, +// this likely means that some CFG edges were not detected. Pessimistically +// assume all registers to be unsafe before this basic block and warn about +// this fact in FunctionAnalysis::findUnsafeUses(). +if (BB.pred_empty()) + return createUnsafeState(); + return SrcState(); } @@ -658,12 +671,6 @@ class CFGUnawareSrcSafetyAnalysis : public SrcSafetyAnalysis { BC.MIB->removeAnnotation(I.second, StateAnnotationIndex); } - /// Creates a state with all registers marked unsafe (not to be confused - /// with empty state). - SrcState createUnsafeState() const { -return SrcState(NumRegs, RegsToTrackInstsFor.getNumTrackedRegisters()); - } - public: CFGUnawareSrcSafetyAnalysis(BinaryFunction &BF, MCPlusBuilder::AllocatorIdTy AllocId, @@ -1342,19 +1349,30 @@ void FunctionAnalysisContext::findUnsafeUses( BF.dump(); }); + if (BF.hasCFG()) { +// Warn on basic blocks being unreachable according to BOLT, as this +// likely means CFG is imprecise. +for (BinaryBasicBlock &BB : BF) { + if (!BB.pred_empty() || BB.isEntryPoint()) +continue; + // Arbitrarily attach the report to the first instruction of BB. + MCInst *InstToReport = BB.getFirstNonPseudoInstr(); + if (!InstToReport) +continue; // BB has no real instructions + + Reports.push_back( + make_generic_report(MCInstReference::get(InstToReport, BF), + "Warning: no predecessor basic blocks detected " + "(possibly incomplete CFG)")); +} + } + iterateOverInstrs(BF, [&](MCInstReference Inst) { if (BC.MIB->isCFI(Inst)) return; const SrcState &S = Analysis->getStateBefore(Inst); - -// If non-empty state was never propagated from the entry basic block -// to Inst, assume it to be unreachable and report a warning. -if (S.empty()) { - Reports.push_back( - make_generic_report(Inst, "Warning: unreachable instruction found")); - return; -} +assert(!S.empty() && "Instruction has no associated state"); if (auto Report = shouldReportReturnGadget(BC, Inst, S)) Reports.push_back(*Report); diff --git a/bolt/test/binary-analysis/AArch64/gs-pacret-autiasp.s b/bolt/test/binary-analysis/AArch64/gs-pacret-autiasp.s index 284f0bea607a5..6559ba336e8de 100644 --- a/bolt/test/binary-analysis/AArch64/gs-pacret-autiasp.s +++ b/bolt/test/binary-analysis/AArch64/gs-pacret-autiasp.s @@ -215,12 +215,17 @@ f_callclobbered_calleesaved: .globl f_unreachable_instruction .type f_unreachable_instruction,@function f_unreachable_instruction: -// CHECK-LABEL: GS-PAUTH: Warning: unreachable instruction found in function f_unreachable_instruction, basic block {{[0-9a-zA-Z.]+}}, at address +// CHECK-LABEL: GS-PAUTH: Warning: no predecessor basic blocks detected (possibly incomplete CFG) in function f_unreachable_instruction, basic block {{[0-9a-zA-Z.]+}}, at address // CHECK-NEXT:The instruction is {{[0-9a-f]+}}: add x0, x1, x2 // CHECK-NOT: instructi
[llvm-branch-commits] [llvm] release/20.x: [SDAG] Ensure load is included in output chain of sincos expansion (#140525) (PR #140703)
MacDue wrote: Not sure why the bot is asking me (I think it's fine, but I requested the backport). cc @arsenm, @RKSimon https://github.com/llvm/llvm-project/pull/140703 ___ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
[llvm-branch-commits] [clang] [llvm] [HLSL][RootSignature] Add parsing infastructure for StaticSampler (PR #140180)
@@ -223,6 +223,34 @@ TEST_F(ParseHLSLRootSignatureTest, ValidParseDTClausesTest) { ASSERT_TRUE(Consumer->isSatisfied()); } +TEST_F(ParseHLSLRootSignatureTest, ValidParseStaticSamplerTest) { inbelic wrote: I opted-out to reduce redundancy, as there is already testing for the errors that are encountered, albeit from a different code path. https://github.com/llvm/llvm-project/pull/140180 ___ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
[llvm-branch-commits] [llvm] [LoopVectorizer] Bundle partial reductions inside VPMulAccumulateReductionRecipe (PR #136173)
SamTebbs33 wrote: Ping :) https://github.com/llvm/llvm-project/pull/136173 ___ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
[llvm-branch-commits] [llvm] AMDGPU: Remove redundant operand folding checks (PR #140587)
https://github.com/Sisyph approved this pull request. Your logic makes sense to me. Handing cases uniformly is good. https://github.com/llvm/llvm-project/pull/140587 ___ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
[llvm-branch-commits] [llvm] [llvm][EmbedBitcodePass] Prevent modifying the module with ThinLTO (PR #139999)
ilovepi wrote: @nikic I spent some time over the weekend looking at this, I think if we want to avoid cloning the module, we'd need to record enough metadata to recover the old state. For the Vtable I think that's not too much data, but IDK about the rest. That said, that whole approach seems awfully invasive to the pass and is likely to be both buggy and brittle. I just don't see a good way for us to roll back those changes to the VTable otherwise... Should we just land this for now, and try to come up with something better/different as a follow up? https://github.com/llvm/llvm-project/pull/13 ___ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
[llvm-branch-commits] [llvm] [DirectX] Use resource names when generating DXIL metadata (PR #140635)
https://github.com/hekota updated https://github.com/llvm/llvm-project/pull/140635 >From 0dc74a35fe2c774d8d3de847fe4272363b38ff85 Mon Sep 17 00:00:00 2001 From: Helena Kotas Date: Mon, 19 May 2025 15:18:09 -0700 Subject: [PATCH 1/2] [DirectX] Use resource names in DXIL metadata pass --- llvm/lib/Analysis/DXILResource.cpp| 2 +- .../DirectX/Metadata/cbuffer_metadata.ll | 38 ++--- .../DirectX/Metadata/resource-symbols.ll | 11 +- .../CodeGen/DirectX/Metadata/srv_metadata.ll | 105 +++--- .../CodeGen/DirectX/Metadata/uav_metadata.ll | 133 +- 5 files changed, 150 insertions(+), 139 deletions(-) diff --git a/llvm/lib/Analysis/DXILResource.cpp b/llvm/lib/Analysis/DXILResource.cpp index 8cc9316dfb667..f642603306713 100644 --- a/llvm/lib/Analysis/DXILResource.cpp +++ b/llvm/lib/Analysis/DXILResource.cpp @@ -559,7 +559,7 @@ MDTuple *ResourceInfo::getAsMetadata(Module &M, MDVals.push_back(getIntMD(Binding.RecordID)); assert(Symbol && "Cannot yet create useful resource metadata without symbol"); MDVals.push_back(ValueAsMetadata::get(Symbol)); - MDVals.push_back(MDString::get(Ctx, Symbol->getName())); + MDVals.push_back(MDString::get(Ctx, Name)); MDVals.push_back(getIntMD(Binding.Space)); MDVals.push_back(getIntMD(Binding.LowerBound)); MDVals.push_back(getIntMD(Binding.Size)); diff --git a/llvm/test/CodeGen/DirectX/Metadata/cbuffer_metadata.ll b/llvm/test/CodeGen/DirectX/Metadata/cbuffer_metadata.ll index 8eda78c1d1c42..7f878c9be63f2 100644 --- a/llvm/test/CodeGen/DirectX/Metadata/cbuffer_metadata.ll +++ b/llvm/test/CodeGen/DirectX/Metadata/cbuffer_metadata.ll @@ -7,20 +7,23 @@ target triple = "dxil-pc-shadermodel6.6-compute" %__cblayout_CB1 = type <{ float, i32, double, <2 x i32> }> @CB1.cb = global target("dx.CBuffer", target("dx.Layout", %__cblayout_CB1, 24, 0, 4, 8, 16)) poison +@CB1.str = private unnamed_addr constant [4 x i8] c"CB1\00", align 1 %__cblayout_CB2 = type <{ float, double, float, half, i16, i64, i32 }> @CB2.cb = global target("dx.CBuffer", target("dx.Layout", %__cblayout_CB2, 36, 0, 8, 16, 20, 22, 24, 32)) poison +@CB2.str = private unnamed_addr constant [4 x i8] c"CB2\00", align 1 -%__cblayout_CB3 = type <{ double, <3 x float>, float, <3 x double>, half, <2 x double>, float, <3 x half>, <3 x half> }> -@CB3.cb = global target("dx.CBuffer", target("dx.Layout", %__cblayout_CB3, 96, 0, 16, 28, 32, 56, 64, 80, 84, 90)) poison +%__cblayout_MyConstants = type <{ double, <3 x float>, float, <3 x double>, half, <2 x double>, float, <3 x half>, <3 x half> }> +@MyConstants.cb = global target("dx.CBuffer", target("dx.Layout", %__cblayout_MyConstants, 96, 0, 16, 28, 32, 56, 64, 80, 84, 90)) poison +@MyConstants.str = private unnamed_addr constant [12 x i8] c"MyConstants\00", align 1 ; PRINT:; Resource Bindings: ; PRINT-NEXT:; ; PRINT-NEXT:; Name Type Format Dim ID HLSL Bind Count ; PRINT-NEXT:; -- -- --- --- --- -- -- -; PRINT-NEXT:; cbuffer NA NA CB0cb0 1 -; PRINT-NEXT:; cbuffer NA NA CB1cb1 1 -; PRINT-NEXT:; cbuffer NA NA CB2cb5,space15 1 +; PRINT-NEXT:; CB1 cbuffer NA NA CB0cb0 1 +; PRINT-NEXT:; CB2 cbuffer NA NA CB1cb1 1 +; PRINT-NEXT:; MyConstants cbuffer NA NA CB2cb5,space15 1 define void @test() #0 { @@ -31,9 +34,7 @@ define void @test() #0 { ; int2 d; ; } %CB1.cb_h = call target("dx.CBuffer", target("dx.Layout", %__cblayout_CB1, 24, 0, 4, 8, 16)) -@llvm.dx.resource.handlefrombinding(i32 0, i32 0, i32 1, i32 0, i1 false, ptr null) - store target("dx.CBuffer", target("dx.Layout", %__cblayout_CB1, 24, 0, 4, 8, 16)) %CB1.cb_h, ptr @CB1.cb, align 4 - +@llvm.dx.resource.handlefrombinding(i32 0, i32 0, i32 1, i32 0, i1 false, ptr @CB1.str) ; cbuffer CB2 : register(b0) { ; float a; ; double b; @@ -45,9 +46,7 @@ define void @test() #0 { ;} %CB2.cb_h = call target("dx.CBuffer", target("dx.Layout", %__cblayout_CB2, 36, 0, 8, 16, 20, 22, 24, 32)) -@llvm.dx.resource.handlefrombinding(i32 0, i32 1, i32 1, i32 0, i1 false, ptr null) - store target("dx.CBuffer", target("dx.Layout", %__cblayout_CB2, 36, 0, 8, 16, 20, 22, 24, 32)) %CB2.cb_h, ptr @CB2.cb, align 4 - +@llvm.dx.resource.handlefrombinding(i32 0, i32 1, i32 1, i32 0, i1 false, ptr @CB2.str) ; cbuffer CB3 : register(b5) { ; double B0; ; float3 B1; @@ -59,19 +58,22 @@ define void @test() #0 { ; half3 B7; ; half3 B8; ; } - %CB3.cb_h = call target("d
[llvm-branch-commits] [clang-tools-extra] [clang-doc] Extract Info into JSON values (PR #138063)
@@ -162,15 +162,264 @@ Error MustacheHTMLGenerator::generateDocs( return Error::success(); } +static json::Value +extractValue(const Location &L, + std::optional RepositoryUrl = std::nullopt) { + Object Obj = Object(); + // Should there be Start/End line numbers? + Obj.insert({"LineNumber", L.StartLineNumber}); + Obj.insert({"Filename", L.Filename}); + + if (!L.IsFileInRootDir || !RepositoryUrl) +return Obj; + SmallString<128> FileURL(*RepositoryUrl); + sys::path::append(FileURL, sys::path::Style::posix, L.Filename); + FileURL += "#" + std::to_string(L.StartLineNumber); + Obj.insert({"FileURL", FileURL}); + + return Obj; +} + +static json::Value extractValue(const Reference &I, +StringRef CurrentDirectory) { + SmallString<64> Path = I.getRelativeFilePath(CurrentDirectory); + sys::path::append(Path, I.getFileBaseName() + ".html"); + sys::path::native(Path, sys::path::Style::posix); + Object Obj = Object(); + Obj.insert({"Link", Path}); + Obj.insert({"Name", I.Name}); + Obj.insert({"QualName", I.QualName}); + Obj.insert({"ID", toHex(toStringRef(I.USR))}); + return Obj; +} + +static json::Value extractValue(const TypedefInfo &I) { + // Not Supported + return nullptr; +} + +static json::Value extractValue(const CommentInfo &I) { + assert((I.Kind == "BlockCommandComment" || I.Kind == "FullComment" || + I.Kind == "ParagraphComment" || I.Kind == "TextComment") && + "Unknown Comment type in CommentInfo."); + + Object Obj = Object(); + json::Value Child = Object(); + + // TextComment has no children, so return it. + if (I.Kind == "TextComment") { +Obj.insert({"TextComment", I.Text}); +return Obj; + } + + // BlockCommandComment needs to generate a Command key. + if (I.Kind == "BlockCommandComment") +Child.getAsObject()->insert({"Command", I.Name}); + + // Use the same handling for everything else. + // Only valid for: + // - BlockCommandComment + // - FullComment + // - ParagraphComment + json::Value ChildArr = Array(); + auto &CARef = *ChildArr.getAsArray(); + CARef.reserve(I.Children.size()); + for (const auto &C : I.Children) +CARef.emplace_back(extractValue(*C)); + Child.getAsObject()->insert({"Children", ChildArr}); + Obj.insert({I.Kind, Child}); + + return Obj; +} + +static void maybeInsertLocation(std::optional Loc, +const ClangDocContext &CDCtx, Object &Obj) { + if (!Loc) +return; + Location L = *Loc; + Obj.insert({"Location", extractValue(L, CDCtx.RepositoryUrl)}); +} + +static void extractDescriptionFromInfo(ArrayRef Descriptions, + json::Object &EnumValObj) { + if (Descriptions.empty()) +return; + json::Value ArrDesc = Array(); + json::Array &ADescRef = *ArrDesc.getAsArray(); + for (const CommentInfo &Child : Descriptions) +ADescRef.emplace_back(extractValue(Child)); + EnumValObj.insert({"EnumValueComments", ArrDesc}); +} + +static json::Value extractValue(const FunctionInfo &I, StringRef ParentInfoDir, +const ClangDocContext &CDCtx) { + Object Obj = Object(); + Obj.insert({"Name", I.Name}); + Obj.insert({"ID", toHex(toStringRef(I.USR))}); + Obj.insert({"Access", getAccessSpelling(I.Access).str()}); + Obj.insert({"ReturnType", extractValue(I.ReturnType.Type, ParentInfoDir)}); + + json::Value ParamArr = Array(); + for (const auto Val : enumerate(I.Params)) { +json::Value V = Object(); +auto &VRef = *V.getAsObject(); +VRef.insert({"Name", Val.value().Name}); +VRef.insert({"Type", Val.value().Type.Name}); +VRef.insert({"End", Val.index() + 1 == I.Params.size()}); +ParamArr.getAsArray()->emplace_back(V); + } + Obj.insert({"Params", ParamArr}); + + maybeInsertLocation(I.DefLoc, CDCtx, Obj); + return Obj; +} + +static json::Value extractValue(const EnumInfo &I, +const ClangDocContext &CDCtx) { + Object Obj = Object(); + std::string EnumType = I.Scoped ? "enum class " : "enum "; + EnumType += I.Name; + bool HasComment = std::any_of( + I.Members.begin(), I.Members.end(), + [](const EnumValueInfo &M) { return !M.Description.empty(); }); + Obj.insert({"EnumName", EnumType}); + Obj.insert({"HasComment", HasComment}); + Obj.insert({"ID", toHex(toStringRef(I.USR))}); + json::Value Arr = Array(); + json::Array &ARef = *Arr.getAsArray(); + for (const EnumValueInfo &M : I.Members) { +json::Value EnumValue = Object(); +auto &EnumValObj = *EnumValue.getAsObject(); +EnumValObj.insert({"Name", M.Name}); +if (!M.ValueExpr.empty()) + EnumValObj.insert({"ValueExpr", M.ValueExpr}); +else + EnumValObj.insert({"Value", M.Value}); + +extractDescriptionFromInfo(M.Description, EnumValObj); +ARef.emplace_back(EnumValue); + } + Obj.insert({"EnumValues", Arr}); + + extractDescriptionFromInfo(I.Description, Obj); + maybeInsertLocation(I.DefLoc,
[llvm-branch-commits] [clang-tools-extra] [clang-doc] Track if a type is a template or builtin (PR #138067)
https://github.com/ilovepi updated https://github.com/llvm/llvm-project/pull/138067 >From a3420f9d5d6bb9af11a587a69a273e88e14f4fd5 Mon Sep 17 00:00:00 2001 From: Paul Kirth Date: Wed, 30 Apr 2025 14:20:40 -0700 Subject: [PATCH] [clang-doc] Track if a type is a template or builtin Originally part of #133161. This patch adds preliminary tracking for of TypeInfo, by tracking if the type is a builtin or template. The new functionality is not yet exercised. Co-authored-by: Peter Chou --- clang-tools-extra/clang-doc/Representation.h | 3 +++ clang-tools-extra/clang-doc/Serialize.cpp| 17 - 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/clang-tools-extra/clang-doc/Representation.h b/clang-tools-extra/clang-doc/Representation.h index 1673be496b7b2..a3a6217f76bbd 100644 --- a/clang-tools-extra/clang-doc/Representation.h +++ b/clang-tools-extra/clang-doc/Representation.h @@ -164,6 +164,9 @@ struct TypeInfo { bool operator==(const TypeInfo &Other) const { return Type == Other.Type; } Reference Type; // Referenced type in this info. + + bool IsTemplate = false; + bool IsBuiltIn = false; }; // Represents one template parameter. diff --git a/clang-tools-extra/clang-doc/Serialize.cpp b/clang-tools-extra/clang-doc/Serialize.cpp index 0a59724a0d75a..9b2fa36194b63 100644 --- a/clang-tools-extra/clang-doc/Serialize.cpp +++ b/clang-tools-extra/clang-doc/Serialize.cpp @@ -405,9 +405,12 @@ static RecordDecl *getRecordDeclForType(const QualType &T) { static TypeInfo getTypeInfoForType(const QualType &T, const PrintingPolicy &Policy) { const TagDecl *TD = getTagDeclForType(T); - if (!TD) -return TypeInfo(Reference(SymbolID(), T.getAsString(Policy))); - + if (!TD) { +TypeInfo TI = TypeInfo(Reference(SymbolID(), T.getAsString(Policy))); +TI.IsBuiltIn = T->isBuiltinType(); +TI.IsTemplate = T->isTemplateTypeParmType(); +return TI; + } InfoType IT; if (isa(TD)) { IT = InfoType::IT_enum; @@ -416,8 +419,12 @@ static TypeInfo getTypeInfoForType(const QualType &T, } else { IT = InfoType::IT_default; } - return TypeInfo(Reference(getUSRForDecl(TD), TD->getNameAsString(), IT, -T.getAsString(Policy), getInfoRelativePath(TD))); + Reference R = Reference(getUSRForDecl(TD), TD->getNameAsString(), IT, + T.getAsString(Policy), getInfoRelativePath(TD)); + TypeInfo TI = TypeInfo(R); + TI.IsBuiltIn = T->isBuiltinType(); + TI.IsTemplate = T->isTemplateTypeParmType(); + return TI; } static bool isPublic(const clang::AccessSpecifier AS, ___ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
[llvm-branch-commits] [clang-tools-extra] [clang-doc] Update serializer for improved template handling (PR #138065)
https://github.com/ilovepi updated https://github.com/llvm/llvm-project/pull/138065 >From 6d69f6400edaf79895938c1db3ad2e7e96a284e2 Mon Sep 17 00:00:00 2001 From: Paul Kirth Date: Tue, 29 Apr 2025 18:31:54 -0700 Subject: [PATCH] [clang-doc] Update serializer for improved template handling This patch updates Serialize.cpp to serialize more data about C++ templates, which are supported by the new mustache HTML template. Split from #133161. Co-authored-by: Peter Chou --- clang-tools-extra/clang-doc/Representation.h | 3 + clang-tools-extra/clang-doc/Serialize.cpp| 203 ++- 2 files changed, 198 insertions(+), 8 deletions(-) diff --git a/clang-tools-extra/clang-doc/Representation.h b/clang-tools-extra/clang-doc/Representation.h index a2e01719eb59e..1673be496b7b2 100644 --- a/clang-tools-extra/clang-doc/Representation.h +++ b/clang-tools-extra/clang-doc/Representation.h @@ -363,6 +363,9 @@ struct FunctionInfo : public SymbolInfo { // specializations. SmallString<16> FullName; + // Function Prototype + SmallString<256> Prototype; + // When present, this function is a template or specialization. std::optional Template; }; diff --git a/clang-tools-extra/clang-doc/Serialize.cpp b/clang-tools-extra/clang-doc/Serialize.cpp index 18db427b5239e..0a59724a0d75a 100644 --- a/clang-tools-extra/clang-doc/Serialize.cpp +++ b/clang-tools-extra/clang-doc/Serialize.cpp @@ -8,10 +8,10 @@ #include "Serialize.h" #include "BitcodeWriter.h" +#include "clang/AST/Attr.h" #include "clang/AST/Comment.h" #include "clang/Index/USRGeneration.h" #include "clang/Lex/Lexer.h" -#include "llvm/ADT/Hashing.h" #include "llvm/ADT/StringExtras.h" #include "llvm/Support/SHA1.h" @@ -35,6 +35,169 @@ static void populateMemberTypeInfo(RecordInfo &I, AccessSpecifier &Access, const DeclaratorDecl *D, bool IsStatic = false); +static void getTemplateParameters(const TemplateParameterList *TemplateParams, + llvm::raw_ostream &Stream) { + Stream << "template <"; + + for (unsigned i = 0; i < TemplateParams->size(); ++i) { +if (i > 0) + Stream << ", "; + +const NamedDecl *Param = TemplateParams->getParam(i); +if (const auto *TTP = llvm::dyn_cast(Param)) { + if (TTP->wasDeclaredWithTypename()) +Stream << "typename"; + else +Stream << "class"; + if (TTP->isParameterPack()) +Stream << "..."; + Stream << " " << TTP->getNameAsString(); +} else if (const auto *NTTP = + llvm::dyn_cast(Param)) { + NTTP->getType().print(Stream, NTTP->getASTContext().getPrintingPolicy()); + if (NTTP->isParameterPack()) +Stream << "..."; + Stream << " " << NTTP->getNameAsString(); +} else if (const auto *TTPD = + llvm::dyn_cast(Param)) { + Stream << "template <"; + getTemplateParameters(TTPD->getTemplateParameters(), Stream); + Stream << "> class " << TTPD->getNameAsString(); +} + } + + Stream << "> "; +} + +// Extract the full function prototype from a FunctionDecl including +// Full Decl +static llvm::SmallString<256> +getFunctionPrototype(const FunctionDecl *FuncDecl) { + llvm::SmallString<256> Result; + llvm::raw_svector_ostream Stream(Result); + const ASTContext &Ctx = FuncDecl->getASTContext(); + const auto *Method = llvm::dyn_cast(FuncDecl); + // If it's a templated function, handle the template parameters + if (const auto *TmplDecl = FuncDecl->getDescribedTemplate()) +getTemplateParameters(TmplDecl->getTemplateParameters(), Stream); + + // If it's a virtual method + if (Method && Method->isVirtual()) +Stream << "virtual "; + + // Print return type + FuncDecl->getReturnType().print(Stream, Ctx.getPrintingPolicy()); + + // Print function name + Stream << " " << FuncDecl->getNameAsString() << "("; + + // Print parameter list with types, names, and default values + for (unsigned I = 0; I < FuncDecl->getNumParams(); ++I) { +if (I > 0) + Stream << ", "; +const ParmVarDecl *ParamDecl = FuncDecl->getParamDecl(I); +QualType ParamType = ParamDecl->getType(); +ParamType.print(Stream, Ctx.getPrintingPolicy()); + +// Print parameter name if it has one +if (!ParamDecl->getName().empty()) + Stream << " " << ParamDecl->getNameAsString(); + +// Print default argument if it exists +if (ParamDecl->hasDefaultArg()) { + const Expr *DefaultArg = ParamDecl->getDefaultArg(); + if (DefaultArg) { +Stream << " = "; +DefaultArg->printPretty(Stream, nullptr, Ctx.getPrintingPolicy()); + } +} + } + + // If it is a variadic function, add '...' + if (FuncDecl->isVariadic()) { +if (FuncDecl->getNumParams() > 0) + Stream << ", "; +Stream << "..."; + } + + Stream << ")"; + + // If it's a const method, add 'const' qualifier + if (Method) { +if (Method->size_overridden_methods()) +
[llvm-branch-commits] [clang-tools-extra] [clang-doc] Update clang-doc tool to enable mustache templates (PR #138066)
https://github.com/ilovepi updated https://github.com/llvm/llvm-project/pull/138066 >From 5475c902afd46daf5f0d4e092919dd9b9dc9b37c Mon Sep 17 00:00:00 2001 From: Paul Kirth Date: Tue, 29 Apr 2025 18:08:03 -0700 Subject: [PATCH] [clang-doc] Update clang-doc tool to enable mustache templates This patch adds a command line option and enables the Mustache template HTML backend. This allows users to use the new, more flexible templates over the old and cumbersome HTML output. Split from #133161. Co-authored-by: Peter Chou --- .../clang-doc/tool/ClangDocMain.cpp | 80 +-- .../clang-doc/basic-project.mustache.test | 481 ++ 2 files changed, 531 insertions(+), 30 deletions(-) create mode 100644 clang-tools-extra/test/clang-doc/basic-project.mustache.test diff --git a/clang-tools-extra/clang-doc/tool/ClangDocMain.cpp b/clang-tools-extra/clang-doc/tool/ClangDocMain.cpp index 8e8f7053a8f87..41fbe87a713d9 100644 --- a/clang-tools-extra/clang-doc/tool/ClangDocMain.cpp +++ b/clang-tools-extra/clang-doc/tool/ClangDocMain.cpp @@ -18,20 +18,14 @@ //===--===// #include "BitcodeReader.h" -#include "BitcodeWriter.h" #include "ClangDoc.h" #include "Generators.h" #include "Representation.h" -#include "clang/AST/AST.h" -#include "clang/AST/Decl.h" -#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "support/Utils.h" #include "clang/ASTMatchers/ASTMatchersInternal.h" -#include "clang/Driver/Options.h" -#include "clang/Frontend/FrontendActions.h" #include "clang/Tooling/AllTUsExecution.h" #include "clang/Tooling/CommonOptionsParser.h" #include "clang/Tooling/Execution.h" -#include "clang/Tooling/Tooling.h" #include "llvm/ADT/APFloat.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/Error.h" @@ -110,22 +104,19 @@ static llvm::cl::opt RepositoryCodeLinePrefix( llvm::cl::desc("Prefix of line code for repository."), llvm::cl::cat(ClangDocCategory)); -enum OutputFormatTy { - md, - yaml, - html, -}; - -static llvm::cl::opt -FormatEnum("format", llvm::cl::desc("Format for outputted docs."), - llvm::cl::values(clEnumValN(OutputFormatTy::yaml, "yaml", - "Documentation in YAML format."), -clEnumValN(OutputFormatTy::md, "md", - "Documentation in MD format."), -clEnumValN(OutputFormatTy::html, "html", - "Documentation in HTML format.")), - llvm::cl::init(OutputFormatTy::yaml), - llvm::cl::cat(ClangDocCategory)); +enum OutputFormatTy { md, yaml, html, mustache }; + +static llvm::cl::opt FormatEnum( +"format", llvm::cl::desc("Format for outputted docs."), +llvm::cl::values(clEnumValN(OutputFormatTy::yaml, "yaml", +"Documentation in YAML format."), + clEnumValN(OutputFormatTy::md, "md", +"Documentation in MD format."), + clEnumValN(OutputFormatTy::html, "html", +"Documentation in HTML format."), + clEnumValN(OutputFormatTy::mustache, "mustache", +"Documentation in mustache HTML format")), +llvm::cl::init(OutputFormatTy::yaml), llvm::cl::cat(ClangDocCategory)); static std::string getFormatString() { switch (FormatEnum) { @@ -135,6 +126,8 @@ static std::string getFormatString() { return "md"; case OutputFormatTy::html: return "html"; + case OutputFormatTy::mustache: +return "mustache"; } llvm_unreachable("Unknown OutputFormatTy"); } @@ -178,13 +171,9 @@ static llvm::Error getDefaultAssetFiles(const char *Argv0, llvm::SmallString<128> AssetsPath; AssetsPath = llvm::sys::path::parent_path(NativeClangDocPath); llvm::sys::path::append(AssetsPath, "..", "share", "clang-doc"); - llvm::SmallString<128> DefaultStylesheet; - llvm::sys::path::native(AssetsPath, DefaultStylesheet); - llvm::sys::path::append(DefaultStylesheet, - "clang-doc-default-stylesheet.css"); - llvm::SmallString<128> IndexJS; - llvm::sys::path::native(AssetsPath, IndexJS); - llvm::sys::path::append(IndexJS, "index.js"); + llvm::SmallString<128> DefaultStylesheet = + appendPathNative(AssetsPath, "clang-doc-default-stylesheet.css"); + llvm::SmallString<128> IndexJS = appendPathNative(AssetsPath, "index.js"); if (!llvm::sys::fs::is_regular_file(IndexJS)) return llvm::createStringError(llvm::inconvertibleErrorCode(), @@ -215,6 +204,30 @@ static llvm::Error getHtmlAssetFiles(const char *Argv0, return getDefaultAssetFiles(Argv0, CDCtx); } +static llvm::Error getMustacheHtmlFiles(const char *Argv0, +clang::doc::ClangDocContext &CDCtx) { + bool IsDir = llvm::sys::fs::is_direct
[llvm-branch-commits] [clang-tools-extra] [clang-doc] Update serializer for improved template handling (PR #138065)
https://github.com/ilovepi updated https://github.com/llvm/llvm-project/pull/138065 >From 6d69f6400edaf79895938c1db3ad2e7e96a284e2 Mon Sep 17 00:00:00 2001 From: Paul Kirth Date: Tue, 29 Apr 2025 18:31:54 -0700 Subject: [PATCH] [clang-doc] Update serializer for improved template handling This patch updates Serialize.cpp to serialize more data about C++ templates, which are supported by the new mustache HTML template. Split from #133161. Co-authored-by: Peter Chou --- clang-tools-extra/clang-doc/Representation.h | 3 + clang-tools-extra/clang-doc/Serialize.cpp| 203 ++- 2 files changed, 198 insertions(+), 8 deletions(-) diff --git a/clang-tools-extra/clang-doc/Representation.h b/clang-tools-extra/clang-doc/Representation.h index a2e01719eb59e..1673be496b7b2 100644 --- a/clang-tools-extra/clang-doc/Representation.h +++ b/clang-tools-extra/clang-doc/Representation.h @@ -363,6 +363,9 @@ struct FunctionInfo : public SymbolInfo { // specializations. SmallString<16> FullName; + // Function Prototype + SmallString<256> Prototype; + // When present, this function is a template or specialization. std::optional Template; }; diff --git a/clang-tools-extra/clang-doc/Serialize.cpp b/clang-tools-extra/clang-doc/Serialize.cpp index 18db427b5239e..0a59724a0d75a 100644 --- a/clang-tools-extra/clang-doc/Serialize.cpp +++ b/clang-tools-extra/clang-doc/Serialize.cpp @@ -8,10 +8,10 @@ #include "Serialize.h" #include "BitcodeWriter.h" +#include "clang/AST/Attr.h" #include "clang/AST/Comment.h" #include "clang/Index/USRGeneration.h" #include "clang/Lex/Lexer.h" -#include "llvm/ADT/Hashing.h" #include "llvm/ADT/StringExtras.h" #include "llvm/Support/SHA1.h" @@ -35,6 +35,169 @@ static void populateMemberTypeInfo(RecordInfo &I, AccessSpecifier &Access, const DeclaratorDecl *D, bool IsStatic = false); +static void getTemplateParameters(const TemplateParameterList *TemplateParams, + llvm::raw_ostream &Stream) { + Stream << "template <"; + + for (unsigned i = 0; i < TemplateParams->size(); ++i) { +if (i > 0) + Stream << ", "; + +const NamedDecl *Param = TemplateParams->getParam(i); +if (const auto *TTP = llvm::dyn_cast(Param)) { + if (TTP->wasDeclaredWithTypename()) +Stream << "typename"; + else +Stream << "class"; + if (TTP->isParameterPack()) +Stream << "..."; + Stream << " " << TTP->getNameAsString(); +} else if (const auto *NTTP = + llvm::dyn_cast(Param)) { + NTTP->getType().print(Stream, NTTP->getASTContext().getPrintingPolicy()); + if (NTTP->isParameterPack()) +Stream << "..."; + Stream << " " << NTTP->getNameAsString(); +} else if (const auto *TTPD = + llvm::dyn_cast(Param)) { + Stream << "template <"; + getTemplateParameters(TTPD->getTemplateParameters(), Stream); + Stream << "> class " << TTPD->getNameAsString(); +} + } + + Stream << "> "; +} + +// Extract the full function prototype from a FunctionDecl including +// Full Decl +static llvm::SmallString<256> +getFunctionPrototype(const FunctionDecl *FuncDecl) { + llvm::SmallString<256> Result; + llvm::raw_svector_ostream Stream(Result); + const ASTContext &Ctx = FuncDecl->getASTContext(); + const auto *Method = llvm::dyn_cast(FuncDecl); + // If it's a templated function, handle the template parameters + if (const auto *TmplDecl = FuncDecl->getDescribedTemplate()) +getTemplateParameters(TmplDecl->getTemplateParameters(), Stream); + + // If it's a virtual method + if (Method && Method->isVirtual()) +Stream << "virtual "; + + // Print return type + FuncDecl->getReturnType().print(Stream, Ctx.getPrintingPolicy()); + + // Print function name + Stream << " " << FuncDecl->getNameAsString() << "("; + + // Print parameter list with types, names, and default values + for (unsigned I = 0; I < FuncDecl->getNumParams(); ++I) { +if (I > 0) + Stream << ", "; +const ParmVarDecl *ParamDecl = FuncDecl->getParamDecl(I); +QualType ParamType = ParamDecl->getType(); +ParamType.print(Stream, Ctx.getPrintingPolicy()); + +// Print parameter name if it has one +if (!ParamDecl->getName().empty()) + Stream << " " << ParamDecl->getNameAsString(); + +// Print default argument if it exists +if (ParamDecl->hasDefaultArg()) { + const Expr *DefaultArg = ParamDecl->getDefaultArg(); + if (DefaultArg) { +Stream << " = "; +DefaultArg->printPretty(Stream, nullptr, Ctx.getPrintingPolicy()); + } +} + } + + // If it is a variadic function, add '...' + if (FuncDecl->isVariadic()) { +if (FuncDecl->getNumParams() > 0) + Stream << ", "; +Stream << "..."; + } + + Stream << ")"; + + // If it's a const method, add 'const' qualifier + if (Method) { +if (Method->size_overridden_methods()) +
[llvm-branch-commits] [clang-tools-extra] [clang-doc] Update clang-doc tool to enable mustache templates (PR #138066)
https://github.com/ilovepi updated https://github.com/llvm/llvm-project/pull/138066 >From 5475c902afd46daf5f0d4e092919dd9b9dc9b37c Mon Sep 17 00:00:00 2001 From: Paul Kirth Date: Tue, 29 Apr 2025 18:08:03 -0700 Subject: [PATCH] [clang-doc] Update clang-doc tool to enable mustache templates This patch adds a command line option and enables the Mustache template HTML backend. This allows users to use the new, more flexible templates over the old and cumbersome HTML output. Split from #133161. Co-authored-by: Peter Chou --- .../clang-doc/tool/ClangDocMain.cpp | 80 +-- .../clang-doc/basic-project.mustache.test | 481 ++ 2 files changed, 531 insertions(+), 30 deletions(-) create mode 100644 clang-tools-extra/test/clang-doc/basic-project.mustache.test diff --git a/clang-tools-extra/clang-doc/tool/ClangDocMain.cpp b/clang-tools-extra/clang-doc/tool/ClangDocMain.cpp index 8e8f7053a8f87..41fbe87a713d9 100644 --- a/clang-tools-extra/clang-doc/tool/ClangDocMain.cpp +++ b/clang-tools-extra/clang-doc/tool/ClangDocMain.cpp @@ -18,20 +18,14 @@ //===--===// #include "BitcodeReader.h" -#include "BitcodeWriter.h" #include "ClangDoc.h" #include "Generators.h" #include "Representation.h" -#include "clang/AST/AST.h" -#include "clang/AST/Decl.h" -#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "support/Utils.h" #include "clang/ASTMatchers/ASTMatchersInternal.h" -#include "clang/Driver/Options.h" -#include "clang/Frontend/FrontendActions.h" #include "clang/Tooling/AllTUsExecution.h" #include "clang/Tooling/CommonOptionsParser.h" #include "clang/Tooling/Execution.h" -#include "clang/Tooling/Tooling.h" #include "llvm/ADT/APFloat.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/Error.h" @@ -110,22 +104,19 @@ static llvm::cl::opt RepositoryCodeLinePrefix( llvm::cl::desc("Prefix of line code for repository."), llvm::cl::cat(ClangDocCategory)); -enum OutputFormatTy { - md, - yaml, - html, -}; - -static llvm::cl::opt -FormatEnum("format", llvm::cl::desc("Format for outputted docs."), - llvm::cl::values(clEnumValN(OutputFormatTy::yaml, "yaml", - "Documentation in YAML format."), -clEnumValN(OutputFormatTy::md, "md", - "Documentation in MD format."), -clEnumValN(OutputFormatTy::html, "html", - "Documentation in HTML format.")), - llvm::cl::init(OutputFormatTy::yaml), - llvm::cl::cat(ClangDocCategory)); +enum OutputFormatTy { md, yaml, html, mustache }; + +static llvm::cl::opt FormatEnum( +"format", llvm::cl::desc("Format for outputted docs."), +llvm::cl::values(clEnumValN(OutputFormatTy::yaml, "yaml", +"Documentation in YAML format."), + clEnumValN(OutputFormatTy::md, "md", +"Documentation in MD format."), + clEnumValN(OutputFormatTy::html, "html", +"Documentation in HTML format."), + clEnumValN(OutputFormatTy::mustache, "mustache", +"Documentation in mustache HTML format")), +llvm::cl::init(OutputFormatTy::yaml), llvm::cl::cat(ClangDocCategory)); static std::string getFormatString() { switch (FormatEnum) { @@ -135,6 +126,8 @@ static std::string getFormatString() { return "md"; case OutputFormatTy::html: return "html"; + case OutputFormatTy::mustache: +return "mustache"; } llvm_unreachable("Unknown OutputFormatTy"); } @@ -178,13 +171,9 @@ static llvm::Error getDefaultAssetFiles(const char *Argv0, llvm::SmallString<128> AssetsPath; AssetsPath = llvm::sys::path::parent_path(NativeClangDocPath); llvm::sys::path::append(AssetsPath, "..", "share", "clang-doc"); - llvm::SmallString<128> DefaultStylesheet; - llvm::sys::path::native(AssetsPath, DefaultStylesheet); - llvm::sys::path::append(DefaultStylesheet, - "clang-doc-default-stylesheet.css"); - llvm::SmallString<128> IndexJS; - llvm::sys::path::native(AssetsPath, IndexJS); - llvm::sys::path::append(IndexJS, "index.js"); + llvm::SmallString<128> DefaultStylesheet = + appendPathNative(AssetsPath, "clang-doc-default-stylesheet.css"); + llvm::SmallString<128> IndexJS = appendPathNative(AssetsPath, "index.js"); if (!llvm::sys::fs::is_regular_file(IndexJS)) return llvm::createStringError(llvm::inconvertibleErrorCode(), @@ -215,6 +204,30 @@ static llvm::Error getHtmlAssetFiles(const char *Argv0, return getDefaultAssetFiles(Argv0, CDCtx); } +static llvm::Error getMustacheHtmlFiles(const char *Argv0, +clang::doc::ClangDocContext &CDCtx) { + bool IsDir = llvm::sys::fs::is_direct
[llvm-branch-commits] [clang-tools-extra] [clang-doc] Extract Info into JSON values (PR #138063)
https://github.com/ilovepi updated https://github.com/llvm/llvm-project/pull/138063 >From c97f7afb7152967300392c368921900f08416625 Mon Sep 17 00:00:00 2001 From: Paul Kirth Date: Wed, 30 Apr 2025 08:11:39 -0700 Subject: [PATCH] [clang-doc] Extract Info into JSON values Split from #133161. This patch provides the implementation of a number of extractValue overloads used with the different types of Info. The new helper functions extract the relevant information from the different *Infos and inserts them into the correct fields of the JSON values that will be used with the specific Mustache templates, which will land separately. Co-authored-by: Peter Chou --- .../clang-doc/HTMLMustacheGenerator.cpp | 256 ++ 1 file changed, 256 insertions(+) diff --git a/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp b/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp index 2d397d9b75c7a..e0221a0bb90f1 100644 --- a/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp +++ b/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp @@ -162,15 +162,271 @@ Error MustacheHTMLGenerator::generateDocs( return Error::success(); } +static json::Value +extractValue(const Location &L, + std::optional RepositoryUrl = std::nullopt) { + Object Obj = Object(); + // TODO: Consider using both Start/End line numbers to improve location report + Obj.insert({"LineNumber", L.StartLineNumber}); + Obj.insert({"Filename", L.Filename}); + + if (!L.IsFileInRootDir || !RepositoryUrl) +return Obj; + SmallString<128> FileURL(*RepositoryUrl); + sys::path::append(FileURL, sys::path::Style::posix, L.Filename); + FileURL += "#" + std::to_string(L.StartLineNumber); + Obj.insert({"FileURL", FileURL}); + + return Obj; +} + +static json::Value extractValue(const Reference &I, +StringRef CurrentDirectory) { + SmallString<64> Path = I.getRelativeFilePath(CurrentDirectory); + sys::path::append(Path, I.getFileBaseName() + ".html"); + sys::path::native(Path, sys::path::Style::posix); + Object Obj = Object(); + Obj.insert({"Link", Path}); + Obj.insert({"Name", I.Name}); + Obj.insert({"QualName", I.QualName}); + Obj.insert({"ID", toHex(toStringRef(I.USR))}); + return Obj; +} + +static json::Value extractValue(const TypedefInfo &I) { + // Not Supported + return nullptr; +} + +static json::Value extractValue(const CommentInfo &I) { + assert((I.Kind == "BlockCommandComment" || I.Kind == "FullComment" || + I.Kind == "ParagraphComment" || I.Kind == "TextComment") && + "Unknown Comment type in CommentInfo."); + + Object Obj = Object(); + json::Value Child = Object(); + + // TextComment has no children, so return it. + if (I.Kind == "TextComment") { +Obj.insert({"TextComment", I.Text}); +return Obj; + } + + // BlockCommandComment needs to generate a Command key. + if (I.Kind == "BlockCommandComment") +Child.getAsObject()->insert({"Command", I.Name}); + + // Use the same handling for everything else. + // Only valid for: + // - BlockCommandComment + // - FullComment + // - ParagraphComment + json::Value ChildArr = Array(); + auto &CARef = *ChildArr.getAsArray(); + CARef.reserve(I.Children.size()); + for (const auto &C : I.Children) +CARef.emplace_back(extractValue(*C)); + Child.getAsObject()->insert({"Children", ChildArr}); + Obj.insert({I.Kind, Child}); + + return Obj; +} + +static void maybeInsertLocation(std::optional Loc, +const ClangDocContext &CDCtx, Object &Obj) { + if (!Loc) +return; + Location L = *Loc; + Obj.insert({"Location", extractValue(L, CDCtx.RepositoryUrl)}); +} + +static void extractDescriptionFromInfo(ArrayRef Descriptions, + json::Object &EnumValObj) { + if (Descriptions.empty()) +return; + json::Value DescArr = Array(); + json::Array &DescARef = *DescArr.getAsArray(); + for (const CommentInfo &Child : Descriptions) +DescARef.emplace_back(extractValue(Child)); + EnumValObj.insert({"EnumValueComments", DescArr}); +} + +static json::Value extractValue(const FunctionInfo &I, StringRef ParentInfoDir, +const ClangDocContext &CDCtx) { + Object Obj = Object(); + Obj.insert({"Name", I.Name}); + Obj.insert({"ID", toHex(toStringRef(I.USR))}); + Obj.insert({"Access", getAccessSpelling(I.Access).str()}); + Obj.insert({"ReturnType", extractValue(I.ReturnType.Type, ParentInfoDir)}); + + json::Value ParamArr = Array(); + json::Array &ParamARef = *ParamArr.getAsArray(); + for (const auto Val : enumerate(I.Params)) { +json::Value V = Object(); +auto &VRef = *V.getAsObject(); +VRef.insert({"Name", Val.value().Name}); +VRef.insert({"Type", Val.value().Type.Name}); +VRef.insert({"End", Val.index() + 1 == I.Params.size()}); +ParamARef.emplace_back(V); + } + Obj.insert({"Params", ParamArr}); + + maybeInsertLocation(I.DefLoc, CDCtx, Obj); + return Obj; +}
[llvm-branch-commits] [clang-tools-extra] [clang-doc] Track if a type is a template or builtin (PR #138067)
https://github.com/ilovepi updated https://github.com/llvm/llvm-project/pull/138067 >From a3420f9d5d6bb9af11a587a69a273e88e14f4fd5 Mon Sep 17 00:00:00 2001 From: Paul Kirth Date: Wed, 30 Apr 2025 14:20:40 -0700 Subject: [PATCH] [clang-doc] Track if a type is a template or builtin Originally part of #133161. This patch adds preliminary tracking for of TypeInfo, by tracking if the type is a builtin or template. The new functionality is not yet exercised. Co-authored-by: Peter Chou --- clang-tools-extra/clang-doc/Representation.h | 3 +++ clang-tools-extra/clang-doc/Serialize.cpp| 17 - 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/clang-tools-extra/clang-doc/Representation.h b/clang-tools-extra/clang-doc/Representation.h index 1673be496b7b2..a3a6217f76bbd 100644 --- a/clang-tools-extra/clang-doc/Representation.h +++ b/clang-tools-extra/clang-doc/Representation.h @@ -164,6 +164,9 @@ struct TypeInfo { bool operator==(const TypeInfo &Other) const { return Type == Other.Type; } Reference Type; // Referenced type in this info. + + bool IsTemplate = false; + bool IsBuiltIn = false; }; // Represents one template parameter. diff --git a/clang-tools-extra/clang-doc/Serialize.cpp b/clang-tools-extra/clang-doc/Serialize.cpp index 0a59724a0d75a..9b2fa36194b63 100644 --- a/clang-tools-extra/clang-doc/Serialize.cpp +++ b/clang-tools-extra/clang-doc/Serialize.cpp @@ -405,9 +405,12 @@ static RecordDecl *getRecordDeclForType(const QualType &T) { static TypeInfo getTypeInfoForType(const QualType &T, const PrintingPolicy &Policy) { const TagDecl *TD = getTagDeclForType(T); - if (!TD) -return TypeInfo(Reference(SymbolID(), T.getAsString(Policy))); - + if (!TD) { +TypeInfo TI = TypeInfo(Reference(SymbolID(), T.getAsString(Policy))); +TI.IsBuiltIn = T->isBuiltinType(); +TI.IsTemplate = T->isTemplateTypeParmType(); +return TI; + } InfoType IT; if (isa(TD)) { IT = InfoType::IT_enum; @@ -416,8 +419,12 @@ static TypeInfo getTypeInfoForType(const QualType &T, } else { IT = InfoType::IT_default; } - return TypeInfo(Reference(getUSRForDecl(TD), TD->getNameAsString(), IT, -T.getAsString(Policy), getInfoRelativePath(TD))); + Reference R = Reference(getUSRForDecl(TD), TD->getNameAsString(), IT, + T.getAsString(Policy), getInfoRelativePath(TD)); + TypeInfo TI = TypeInfo(R); + TI.IsBuiltIn = T->isBuiltinType(); + TI.IsTemplate = T->isTemplateTypeParmType(); + return TI; } static bool isPublic(const clang::AccessSpecifier AS, ___ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
[llvm-branch-commits] [clang-tools-extra] [clang-doc] Implement setupTemplateValue for HTMLMustacheGenerator (PR #138064)
https://github.com/ilovepi updated https://github.com/llvm/llvm-project/pull/138064 >From 603c1b69a3897e74b72c2f763448024ac69830d9 Mon Sep 17 00:00:00 2001 From: Paul Kirth Date: Wed, 30 Apr 2025 08:13:46 -0700 Subject: [PATCH] [clang-doc] Implement setupTemplateValue for HTMLMustacheGenerator This patch implements the business logic for setupTemplateValue, which was split from #133161. The implementation configures the relative path relationships between the various HTML components, and prepares them prior to their use in the generator. Co-authored-by: Peter Chou --- .../clang-doc/HTMLMustacheGenerator.cpp | 27 +- .../clang-doc/HTMLMustacheGeneratorTest.cpp | 416 +- 2 files changed, 434 insertions(+), 9 deletions(-) diff --git a/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp b/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp index e0221a0bb90f1..07260a676a24b 100644 --- a/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp +++ b/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp @@ -398,7 +398,7 @@ static json::Value extractValue(const RecordInfo &I, maybeInsertLocation(I.DefLoc, CDCtx, RecordValue); - StringRef BasePath = I.getRelativeFilePath(""); + SmallString<64> BasePath = I.getRelativeFilePath(""); extractScopeChildren(I.Children, RecordValue, BasePath, CDCtx); json::Value PublicMembers = Array(); json::Array &PubMemberRef = *PublicMembers.getAsArray(); @@ -432,8 +432,28 @@ static json::Value extractValue(const RecordInfo &I, static Error setupTemplateValue(const ClangDocContext &CDCtx, json::Value &V, Info *I) { - return createStringError(inconvertibleErrorCode(), - "setupTemplateValue is unimplemented"); + V.getAsObject()->insert({"ProjectName", CDCtx.ProjectName}); + json::Value StylesheetArr = Array(); + auto InfoPath = I->getRelativeFilePath(""); + SmallString<128> RelativePath = computeRelativePath("", InfoPath); + sys::path::native(RelativePath, sys::path::Style::posix); + for (const auto &FilePath : CDCtx.UserStylesheets) { +SmallString<128> StylesheetPath = RelativePath; +sys::path::append(StylesheetPath, sys::path::Style::posix, + sys::path::filename(FilePath)); +StylesheetArr.getAsArray()->emplace_back(StylesheetPath); + } + V.getAsObject()->insert({"Stylesheets", StylesheetArr}); + + json::Value ScriptArr = Array(); + for (auto Script : CDCtx.JsScripts) { +SmallString<128> JsPath = RelativePath; +sys::path::append(JsPath, sys::path::Style::posix, + sys::path::filename(Script)); +ScriptArr.getAsArray()->emplace_back(JsPath); + } + V.getAsObject()->insert({"Scripts", ScriptArr}); + return Error::success(); } Error MustacheHTMLGenerator::generateDocForInfo(Info *I, raw_ostream &OS, @@ -444,6 +464,7 @@ Error MustacheHTMLGenerator::generateDocForInfo(Info *I, raw_ostream &OS, extractValue(*static_cast(I), CDCtx); if (auto Err = setupTemplateValue(CDCtx, V, I)) return Err; +assert(NamespaceTemplate && "NamespaceTemplate is nullptr."); NamespaceTemplate->render(V, OS); break; } diff --git a/clang-tools-extra/unittests/clang-doc/HTMLMustacheGeneratorTest.cpp b/clang-tools-extra/unittests/clang-doc/HTMLMustacheGeneratorTest.cpp index 70491f0754b3d..9a6969f789a65 100644 --- a/clang-tools-extra/unittests/clang-doc/HTMLMustacheGeneratorTest.cpp +++ b/clang-tools-extra/unittests/clang-doc/HTMLMustacheGeneratorTest.cpp @@ -20,10 +20,10 @@ using namespace llvm; using namespace testing; +using namespace clang; using namespace clang::doc; -static const std::string ClangDocVersion = -clang::getClangToolFullVersion("clang-doc"); +static const std::string ClangDocVersion = getClangToolFullVersion("clang-doc"); static std::unique_ptr getHTMLMustacheGenerator() { auto G = findGeneratorByName("mustache"); @@ -114,12 +114,416 @@ TEST(HTMLMustacheGeneratorTest, generateDocsForInfo) { I.Children.Records.emplace_back(EmptySID, "ChildStruct", InfoType::IT_record, "Namespace::ChildStruct", "Namespace"); I.Children.Functions.emplace_back(); - I.Children.Functions.back().Access = clang::AccessSpecifier::AS_none; + I.Children.Functions.back().Access = AccessSpecifier::AS_none; I.Children.Functions.back().Name = "OneFunction"; I.Children.Enums.emplace_back(); - EXPECT_THAT_ERROR(G->generateDocForInfo(&I, Actual, CDCtx), Failed()); + unittest::TempDir RootTestDirectory("generateDocForInfoTest", + /*Unique=*/true); + CDCtx.OutDirectory = RootTestDirectory.path(); + + getMustacheHtmlFiles(CLANG_DOC_TEST_ASSET_DIR, CDCtx); + + // FIXME: This is a terrible hack, since we can't initialize the templates + // directly. We'll need to update the interfaces so that we can call + // SetupTemplateFiles() from outsize of HTMLMustacheGenerator.cpp + EXPECT_THAT_ERROR(
[llvm-branch-commits] [clang-tools-extra] [clang-doc] Implement setupTemplateValue for HTMLMustacheGenerator (PR #138064)
https://github.com/ilovepi updated https://github.com/llvm/llvm-project/pull/138064 >From 603c1b69a3897e74b72c2f763448024ac69830d9 Mon Sep 17 00:00:00 2001 From: Paul Kirth Date: Wed, 30 Apr 2025 08:13:46 -0700 Subject: [PATCH] [clang-doc] Implement setupTemplateValue for HTMLMustacheGenerator This patch implements the business logic for setupTemplateValue, which was split from #133161. The implementation configures the relative path relationships between the various HTML components, and prepares them prior to their use in the generator. Co-authored-by: Peter Chou --- .../clang-doc/HTMLMustacheGenerator.cpp | 27 +- .../clang-doc/HTMLMustacheGeneratorTest.cpp | 416 +- 2 files changed, 434 insertions(+), 9 deletions(-) diff --git a/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp b/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp index e0221a0bb90f1..07260a676a24b 100644 --- a/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp +++ b/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp @@ -398,7 +398,7 @@ static json::Value extractValue(const RecordInfo &I, maybeInsertLocation(I.DefLoc, CDCtx, RecordValue); - StringRef BasePath = I.getRelativeFilePath(""); + SmallString<64> BasePath = I.getRelativeFilePath(""); extractScopeChildren(I.Children, RecordValue, BasePath, CDCtx); json::Value PublicMembers = Array(); json::Array &PubMemberRef = *PublicMembers.getAsArray(); @@ -432,8 +432,28 @@ static json::Value extractValue(const RecordInfo &I, static Error setupTemplateValue(const ClangDocContext &CDCtx, json::Value &V, Info *I) { - return createStringError(inconvertibleErrorCode(), - "setupTemplateValue is unimplemented"); + V.getAsObject()->insert({"ProjectName", CDCtx.ProjectName}); + json::Value StylesheetArr = Array(); + auto InfoPath = I->getRelativeFilePath(""); + SmallString<128> RelativePath = computeRelativePath("", InfoPath); + sys::path::native(RelativePath, sys::path::Style::posix); + for (const auto &FilePath : CDCtx.UserStylesheets) { +SmallString<128> StylesheetPath = RelativePath; +sys::path::append(StylesheetPath, sys::path::Style::posix, + sys::path::filename(FilePath)); +StylesheetArr.getAsArray()->emplace_back(StylesheetPath); + } + V.getAsObject()->insert({"Stylesheets", StylesheetArr}); + + json::Value ScriptArr = Array(); + for (auto Script : CDCtx.JsScripts) { +SmallString<128> JsPath = RelativePath; +sys::path::append(JsPath, sys::path::Style::posix, + sys::path::filename(Script)); +ScriptArr.getAsArray()->emplace_back(JsPath); + } + V.getAsObject()->insert({"Scripts", ScriptArr}); + return Error::success(); } Error MustacheHTMLGenerator::generateDocForInfo(Info *I, raw_ostream &OS, @@ -444,6 +464,7 @@ Error MustacheHTMLGenerator::generateDocForInfo(Info *I, raw_ostream &OS, extractValue(*static_cast(I), CDCtx); if (auto Err = setupTemplateValue(CDCtx, V, I)) return Err; +assert(NamespaceTemplate && "NamespaceTemplate is nullptr."); NamespaceTemplate->render(V, OS); break; } diff --git a/clang-tools-extra/unittests/clang-doc/HTMLMustacheGeneratorTest.cpp b/clang-tools-extra/unittests/clang-doc/HTMLMustacheGeneratorTest.cpp index 70491f0754b3d..9a6969f789a65 100644 --- a/clang-tools-extra/unittests/clang-doc/HTMLMustacheGeneratorTest.cpp +++ b/clang-tools-extra/unittests/clang-doc/HTMLMustacheGeneratorTest.cpp @@ -20,10 +20,10 @@ using namespace llvm; using namespace testing; +using namespace clang; using namespace clang::doc; -static const std::string ClangDocVersion = -clang::getClangToolFullVersion("clang-doc"); +static const std::string ClangDocVersion = getClangToolFullVersion("clang-doc"); static std::unique_ptr getHTMLMustacheGenerator() { auto G = findGeneratorByName("mustache"); @@ -114,12 +114,416 @@ TEST(HTMLMustacheGeneratorTest, generateDocsForInfo) { I.Children.Records.emplace_back(EmptySID, "ChildStruct", InfoType::IT_record, "Namespace::ChildStruct", "Namespace"); I.Children.Functions.emplace_back(); - I.Children.Functions.back().Access = clang::AccessSpecifier::AS_none; + I.Children.Functions.back().Access = AccessSpecifier::AS_none; I.Children.Functions.back().Name = "OneFunction"; I.Children.Enums.emplace_back(); - EXPECT_THAT_ERROR(G->generateDocForInfo(&I, Actual, CDCtx), Failed()); + unittest::TempDir RootTestDirectory("generateDocForInfoTest", + /*Unique=*/true); + CDCtx.OutDirectory = RootTestDirectory.path(); + + getMustacheHtmlFiles(CLANG_DOC_TEST_ASSET_DIR, CDCtx); + + // FIXME: This is a terrible hack, since we can't initialize the templates + // directly. We'll need to update the interfaces so that we can call + // SetupTemplateFiles() from outsize of HTMLMustacheGenerator.cpp + EXPECT_THAT_ERROR(
[llvm-branch-commits] Fix the tests that have multiple same entry. (PR #140751)
https://github.com/qinkunbao closed https://github.com/llvm/llvm-project/pull/140751 ___ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
[llvm-branch-commits] Fix the tests that have multiple same entry. (PR #140751)
llvmbot wrote: @llvm/pr-subscribers-llvm-support Author: Qinkun Bao (qinkunbao) Changes --- Full diff: https://github.com/llvm/llvm-project/pull/140751.diff 1 Files Affected: - (modified) llvm/lib/Support/SpecialCaseList.cpp (+5) ``diff diff --git a/llvm/lib/Support/SpecialCaseList.cpp b/llvm/lib/Support/SpecialCaseList.cpp index dddf84cbb1ced..514591e3b 100644 --- a/llvm/lib/Support/SpecialCaseList.cpp +++ b/llvm/lib/Support/SpecialCaseList.cpp @@ -63,6 +63,11 @@ Error SpecialCaseList::Matcher::insert(StringRef Pattern, unsigned LineNumber, .moveInto(Pair.first)) return Err; Pair.second = LineNumber; + } else { +// We should update the new line number if an entry with the same pattern +// repeats. +auto &Pair = It->getValue(); +Pair.second = LineNumber; } return Error::success(); } `` https://github.com/llvm/llvm-project/pull/140751 ___ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
[llvm-branch-commits] [clang] [llvm] [HLSL][RootSignature] Add parsing infastructure for StaticSampler (PR #140180)
@@ -606,6 +644,30 @@ RootSignatureParser::parseDescriptorTableClauseParams(TokenKind RegType) { return Params; } +std::optional +RootSignatureParser::parseStaticSamplerParams() { + assert(CurToken.TokKind == TokenKind::pu_l_paren && + "Expects to only be invoked starting at given token"); + + ParsedStaticSamplerParams Params; + do { +// `s` POS_INT +if (tryConsumeExpectedToken(TokenKind::sReg)) { + if (Params.Reg.has_value()) { +getDiags().Report(CurToken.TokLoc, diag::err_hlsl_rootsig_repeat_param) +<< CurToken.TokKind; +return std::nullopt; + } + auto Reg = parseRegister(); + if (!Reg.has_value()) inbelic wrote: This case would occur, for instance, when the register number would overflow an u32. Something like `s4294967296`. It will have already reported the error (as part of `handleUIntLiteral` invoked by `parseRegister`) at this point and we are just propagating the error up. https://github.com/llvm/llvm-project/pull/140180 ___ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
[llvm-branch-commits] Fix the tests that have multiple same entry. (PR #140751)
https://github.com/qinkunbao created https://github.com/llvm/llvm-project/pull/140751 None ___ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
[llvm-branch-commits] [mlir] 8986694 - Revert "[mlir][affine] allow iter args as valid dims (#139069)"
Author: Oleksandr "Alex" Zinenko Date: 2025-05-20T23:33:42+02:00 New Revision: 898669409278a7ae2000cc3cda2a6a7495d0f3af URL: https://github.com/llvm/llvm-project/commit/898669409278a7ae2000cc3cda2a6a7495d0f3af DIFF: https://github.com/llvm/llvm-project/commit/898669409278a7ae2000cc3cda2a6a7495d0f3af.diff LOG: Revert "[mlir][affine] allow iter args as valid dims (#139069)" This reverts commit 5f9fd475a03363db6da069b6ded1c503833a695c. Added: Modified: mlir/lib/Dialect/Affine/IR/AffineOps.cpp mlir/test/Dialect/Affine/raise-memref.mlir Removed: diff --git a/mlir/lib/Dialect/Affine/IR/AffineOps.cpp b/mlir/lib/Dialect/Affine/IR/AffineOps.cpp index 4ba24c66e3b82..2364f8957992d 100644 --- a/mlir/lib/Dialect/Affine/IR/AffineOps.cpp +++ b/mlir/lib/Dialect/Affine/IR/AffineOps.cpp @@ -294,10 +294,12 @@ bool mlir::affine::isValidDim(Value value) { return isValidDim(value, getAffineScope(defOp)); // This value has to be a block argument for an op that has the - // `AffineScope` trait or for an affine.for or affine.parallel. + // `AffineScope` trait or an induction var of an affine.for or + // affine.parallel. + if (isAffineInductionVar(value)) +return true; auto *parentOp = llvm::cast(value).getOwner()->getParentOp(); - return parentOp && (parentOp->hasTrait() || - isa(parentOp)); + return parentOp && parentOp->hasTrait(); } // Value can be used as a dimension id iff it meets one of the following @@ -318,10 +320,9 @@ bool mlir::affine::isValidDim(Value value, Region *region) { auto *op = value.getDefiningOp(); if (!op) { -// This value has to be a block argument for an affine.for or an +// This value has to be an induction var for an affine.for or an // affine.parallel. -auto *parentOp = llvm::cast(value).getOwner()->getParentOp(); -return isa(parentOp); +return isAffineInductionVar(value); } // Affine apply operation is ok if all of its operands are ok. diff --git a/mlir/test/Dialect/Affine/raise-memref.mlir b/mlir/test/Dialect/Affine/raise-memref.mlir index 98c54e3998b73..8dc24d99db3e2 100644 --- a/mlir/test/Dialect/Affine/raise-memref.mlir +++ b/mlir/test/Dialect/Affine/raise-memref.mlir @@ -112,7 +112,7 @@ func.func @symbols(%N : index) { // CHECK: %[[lhs5:.*]] = arith.addf %[[lhs]], %[[lhs4]] // CHECK: %[[lhs6:.*]] = arith.addi %[[a4]], %[[cst1]] // CHECK: affine.store %[[lhs5]], %{{.*}}[%[[a1]], symbol(%arg0) + 1] : -// CHECK: affine.store %[[lhs5]], %{{.*}}[%[[a1]], %arg4 + 1] : +// CHECK: memref.store %[[lhs5]], %{{.*}}[%[[a1]], %[[lhs6]]] : // CHECK: %[[lhs7:.*]] = "ab.v" // CHECK: memref.store %[[lhs5]], %{{.*}}[%[[a1]], %[[lhs7]]] : // CHECK: affine.yield %[[lhs6]] ___ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
[llvm-branch-commits] [mlir] [mlir] Unique property constraints where possible (PR #140849)
llvmbot wrote: @llvm/pr-subscribers-mlir-core Author: Krzysztof Drewniak (krzysz00) Changes Now that `Property` is a `PropConstraint`, hook it up to the same constraint-uniquing machinery that other types of constraints use. This will primarily save on code size for types, like enums, that have inherent constraints which are shared across many operations. --- Patch is 23.43 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/140849.diff 6 Files Affected: - (modified) mlir/include/mlir/TableGen/CodeGenHelpers.h (+21) - (modified) mlir/include/mlir/TableGen/Property.h (+15-9) - (modified) mlir/lib/TableGen/CodeGenHelpers.cpp (+64) - (modified) mlir/lib/TableGen/Property.cpp (+11-7) - (modified) mlir/test/mlir-tblgen/op-properties-predicates.td (+45-16) - (modified) mlir/tools/mlir-tblgen/OpDefinitionsGen.cpp (+45-29) ``diff diff --git a/mlir/include/mlir/TableGen/CodeGenHelpers.h b/mlir/include/mlir/TableGen/CodeGenHelpers.h index 465240907a3de..cf14f65b93ed2 100644 --- a/mlir/include/mlir/TableGen/CodeGenHelpers.h +++ b/mlir/include/mlir/TableGen/CodeGenHelpers.h @@ -153,6 +153,23 @@ class StaticVerifierFunctionEmitter { std::optional getAttrConstraintFn(const Constraint &constraint) const; + /// Get the name of the static function used for the given property + /// constraint. These functions are in the form: + /// + /// LogicalResult(Operation *op, T property, StringRef propName); + /// + /// where T is the interface type specified in the constraint. + /// If a uniqued constraint was not found, this function returns std::nullopt. + /// The uniqued constraints cannot be used in the context of an OpAdaptor. + /// + /// Pattern constraints have the form: + /// + /// LogicalResult(PatternRewriter &rewriter, Operation *op, T property, + /// StringRef failureStr); + /// + std::optional + getPropConstraintFn(const Constraint &constraint) const; + /// Get the name of the static function used for the given successor /// constraint. These functions are in the form: /// @@ -175,6 +192,8 @@ class StaticVerifierFunctionEmitter { void emitTypeConstraints(); /// Emit static attribute constraint functions. void emitAttrConstraints(); + /// Emit static property constraint functions. + void emitPropConstraints(); /// Emit static successor constraint functions. void emitSuccessorConstraints(); /// Emit static region constraint functions. @@ -212,6 +231,8 @@ class StaticVerifierFunctionEmitter { ConstraintMap typeConstraints; /// The set of attribute constraints used in the current file. ConstraintMap attrConstraints; + /// The set of property constraints used in the current file. + ConstraintMap propConstraints; /// The set of successor constraints used in the current file. ConstraintMap successorConstraints; /// The set of region constraints used in the current file. diff --git a/mlir/include/mlir/TableGen/Property.h b/mlir/include/mlir/TableGen/Property.h index 386159191ef1f..6af96f077efe5 100644 --- a/mlir/include/mlir/TableGen/Property.h +++ b/mlir/include/mlir/TableGen/Property.h @@ -29,14 +29,26 @@ class Dialect; class Type; class Pred; +// Wrapper class providing helper methods for accesing property constraint +// values. +class PropConstraint : public Constraint { + using Constraint::Constraint; + +public: + static bool classof(const Constraint *c) { return c->getKind() == CK_Prop; } + + StringRef getInterfaceType() const; +}; + // Wrapper class providing helper methods for accessing MLIR Property defined // in TableGen. This class should closely reflect what is defined as class // `Property` in TableGen. -class Property { +class Property : public PropConstraint { public: - explicit Property(const llvm::Record *record); + explicit Property(const llvm::Record *def); explicit Property(const llvm::DefInit *init); - Property(StringRef summary, StringRef description, StringRef storageType, + Property(const llvm::Record *maybeDef, StringRef summary, + StringRef description, StringRef storageType, StringRef interfaceType, StringRef convertFromStorageCall, StringRef assignToStorageCall, StringRef convertToAttributeCall, StringRef convertFromAttributeCall, StringRef parserCall, @@ -131,13 +143,7 @@ class Property { // property constraints, this function is added for future-proofing) Property getBaseProperty() const; - // Returns the TableGen definition this Property was constructed from. - const llvm::Record &getDef() const { return *def; } - private: - // The TableGen definition of this constraint. - const llvm::Record *def; - // Elements describing a Property, in general fetched from the record. StringRef summary; StringRef description; diff --git a/mlir/lib/TableGen/CodeGenHelpers.cpp b/mlir/lib/TableGen/CodeGenHelpers.cpp index f4031be24dfdb..4ce6ab1dbfce5 100644 --- a/m
[llvm-branch-commits] [mlir] [mlir] Unique property constraints where possible (PR #140849)
krzysz00 wrote: PR stack: Lives on top of #140848 https://github.com/llvm/llvm-project/pull/140849 ___ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
[llvm-branch-commits] [mlir] [mlir] Unique property constraints where possible (PR #140849)
llvmbot wrote: @llvm/pr-subscribers-mlir Author: Krzysztof Drewniak (krzysz00) Changes Now that `Property` is a `PropConstraint`, hook it up to the same constraint-uniquing machinery that other types of constraints use. This will primarily save on code size for types, like enums, that have inherent constraints which are shared across many operations. --- Patch is 23.43 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/140849.diff 6 Files Affected: - (modified) mlir/include/mlir/TableGen/CodeGenHelpers.h (+21) - (modified) mlir/include/mlir/TableGen/Property.h (+15-9) - (modified) mlir/lib/TableGen/CodeGenHelpers.cpp (+64) - (modified) mlir/lib/TableGen/Property.cpp (+11-7) - (modified) mlir/test/mlir-tblgen/op-properties-predicates.td (+45-16) - (modified) mlir/tools/mlir-tblgen/OpDefinitionsGen.cpp (+45-29) ``diff diff --git a/mlir/include/mlir/TableGen/CodeGenHelpers.h b/mlir/include/mlir/TableGen/CodeGenHelpers.h index 465240907a3de..cf14f65b93ed2 100644 --- a/mlir/include/mlir/TableGen/CodeGenHelpers.h +++ b/mlir/include/mlir/TableGen/CodeGenHelpers.h @@ -153,6 +153,23 @@ class StaticVerifierFunctionEmitter { std::optional getAttrConstraintFn(const Constraint &constraint) const; + /// Get the name of the static function used for the given property + /// constraint. These functions are in the form: + /// + /// LogicalResult(Operation *op, T property, StringRef propName); + /// + /// where T is the interface type specified in the constraint. + /// If a uniqued constraint was not found, this function returns std::nullopt. + /// The uniqued constraints cannot be used in the context of an OpAdaptor. + /// + /// Pattern constraints have the form: + /// + /// LogicalResult(PatternRewriter &rewriter, Operation *op, T property, + /// StringRef failureStr); + /// + std::optional + getPropConstraintFn(const Constraint &constraint) const; + /// Get the name of the static function used for the given successor /// constraint. These functions are in the form: /// @@ -175,6 +192,8 @@ class StaticVerifierFunctionEmitter { void emitTypeConstraints(); /// Emit static attribute constraint functions. void emitAttrConstraints(); + /// Emit static property constraint functions. + void emitPropConstraints(); /// Emit static successor constraint functions. void emitSuccessorConstraints(); /// Emit static region constraint functions. @@ -212,6 +231,8 @@ class StaticVerifierFunctionEmitter { ConstraintMap typeConstraints; /// The set of attribute constraints used in the current file. ConstraintMap attrConstraints; + /// The set of property constraints used in the current file. + ConstraintMap propConstraints; /// The set of successor constraints used in the current file. ConstraintMap successorConstraints; /// The set of region constraints used in the current file. diff --git a/mlir/include/mlir/TableGen/Property.h b/mlir/include/mlir/TableGen/Property.h index 386159191ef1f..6af96f077efe5 100644 --- a/mlir/include/mlir/TableGen/Property.h +++ b/mlir/include/mlir/TableGen/Property.h @@ -29,14 +29,26 @@ class Dialect; class Type; class Pred; +// Wrapper class providing helper methods for accesing property constraint +// values. +class PropConstraint : public Constraint { + using Constraint::Constraint; + +public: + static bool classof(const Constraint *c) { return c->getKind() == CK_Prop; } + + StringRef getInterfaceType() const; +}; + // Wrapper class providing helper methods for accessing MLIR Property defined // in TableGen. This class should closely reflect what is defined as class // `Property` in TableGen. -class Property { +class Property : public PropConstraint { public: - explicit Property(const llvm::Record *record); + explicit Property(const llvm::Record *def); explicit Property(const llvm::DefInit *init); - Property(StringRef summary, StringRef description, StringRef storageType, + Property(const llvm::Record *maybeDef, StringRef summary, + StringRef description, StringRef storageType, StringRef interfaceType, StringRef convertFromStorageCall, StringRef assignToStorageCall, StringRef convertToAttributeCall, StringRef convertFromAttributeCall, StringRef parserCall, @@ -131,13 +143,7 @@ class Property { // property constraints, this function is added for future-proofing) Property getBaseProperty() const; - // Returns the TableGen definition this Property was constructed from. - const llvm::Record &getDef() const { return *def; } - private: - // The TableGen definition of this constraint. - const llvm::Record *def; - // Elements describing a Property, in general fetched from the record. StringRef summary; StringRef description; diff --git a/mlir/lib/TableGen/CodeGenHelpers.cpp b/mlir/lib/TableGen/CodeGenHelpers.cpp index f4031be24dfdb..4ce6ab1dbfce5 100644 --- a/mlir/l