[llvm-branch-commits] [mlir] [MLIR] Add apply_patterns.vector.arm_sve.lower_contraction TD Op (PR #140572)

2025-05-20 Thread Oleksandr Alex Zinenko via llvm-branch-commits


@@ -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)

2025-05-20 Thread Oleksandr Alex Zinenko via llvm-branch-commits

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)

2025-05-20 Thread Simon Pilgrim via llvm-branch-commits

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)

2025-05-20 Thread Matt Arsenault via llvm-branch-commits

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)

2025-05-20 Thread Anatoly Trosinenko via llvm-branch-commits

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)

2025-05-20 Thread Jacob Bramley via llvm-branch-commits

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)

2025-05-20 Thread Anatoly Trosinenko via llvm-branch-commits

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)

2025-05-20 Thread via llvm-branch-commits


@@ -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)

2025-05-20 Thread Finn Plummer via llvm-branch-commits


@@ -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)

2025-05-20 Thread Finn Plummer via llvm-branch-commits


@@ -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)

2025-05-20 Thread Finn Plummer via llvm-branch-commits


@@ -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)

2025-05-20 Thread Finn Plummer via llvm-branch-commits


@@ -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)

2025-05-20 Thread Finn Plummer via llvm-branch-commits


@@ -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)

2025-05-20 Thread via llvm-branch-commits

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)

2025-05-20 Thread via llvm-branch-commits


@@ -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)

2025-05-20 Thread via llvm-branch-commits

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)

2025-05-20 Thread Finn Plummer via llvm-branch-commits


@@ -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)

2025-05-20 Thread Qinkun Bao via llvm-branch-commits

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)

2025-05-20 Thread Qinkun Bao via llvm-branch-commits

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)

2025-05-20 Thread Finn Plummer via llvm-branch-commits


@@ -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)

2025-05-20 Thread Erick Velez via llvm-branch-commits

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)

2025-05-20 Thread Erick Velez via llvm-branch-commits


@@ -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)

2025-05-20 Thread Erick Velez via llvm-branch-commits


@@ -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)

2025-05-20 Thread Erick Velez via llvm-branch-commits

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)

2025-05-20 Thread Qinkun Bao via llvm-branch-commits

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)

2025-05-20 Thread via llvm-branch-commits

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)

2025-05-20 Thread Erick Velez via llvm-branch-commits


@@ -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)

2025-05-20 Thread Erick Velez via llvm-branch-commits


@@ -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)

2025-05-20 Thread Erick Velez via llvm-branch-commits


@@ -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)

2025-05-20 Thread Erick Velez via llvm-branch-commits


@@ -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)

2025-05-20 Thread Erick Velez via llvm-branch-commits

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)

2025-05-20 Thread Anatoly Trosinenko via llvm-branch-commits

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)

2025-05-20 Thread Anatoly Trosinenko via llvm-branch-commits

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)

2025-05-20 Thread Sam Tebbs via llvm-branch-commits

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)

2025-05-20 Thread Sam Tebbs via llvm-branch-commits


@@ -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)

2025-05-20 Thread Anatoly Trosinenko via llvm-branch-commits

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)

2025-05-20 Thread via llvm-branch-commits

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)

2025-05-20 Thread via llvm-branch-commits

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)

2025-05-20 Thread via llvm-branch-commits

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)

2025-05-20 Thread Anatoly Trosinenko via llvm-branch-commits

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)

2025-05-20 Thread Anatoly Trosinenko via llvm-branch-commits

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)

2025-05-20 Thread Anatoly Trosinenko via llvm-branch-commits

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)

2025-05-20 Thread Anatoly Trosinenko via llvm-branch-commits

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)

2025-05-20 Thread Anatoly Trosinenko via llvm-branch-commits

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)

2025-05-20 Thread Anatoly Trosinenko via llvm-branch-commits

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)

2025-05-20 Thread Anatoly Trosinenko via llvm-branch-commits

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)

2025-05-20 Thread Anatoly Trosinenko via llvm-branch-commits

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)

2025-05-20 Thread Anatoly Trosinenko via llvm-branch-commits

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)

2025-05-20 Thread Anatoly Trosinenko via llvm-branch-commits

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)

2025-05-20 Thread Anatoly Trosinenko via llvm-branch-commits

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)

2025-05-20 Thread via llvm-branch-commits

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)

2025-05-20 Thread Anatoly Trosinenko via llvm-branch-commits

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)

2025-05-20 Thread Anatoly Trosinenko via llvm-branch-commits

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)

2025-05-20 Thread Anatoly Trosinenko via llvm-branch-commits

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)

2025-05-20 Thread Anatoly Trosinenko via llvm-branch-commits

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)

2025-05-20 Thread Benjamin Maxwell via llvm-branch-commits

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)

2025-05-20 Thread Finn Plummer via llvm-branch-commits


@@ -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)

2025-05-20 Thread Sam Tebbs via llvm-branch-commits

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)

2025-05-20 Thread Joe Nash via llvm-branch-commits

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)

2025-05-20 Thread Paul Kirth via llvm-branch-commits

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)

2025-05-20 Thread Helena Kotas via llvm-branch-commits

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)

2025-05-20 Thread Paul Kirth via llvm-branch-commits


@@ -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)

2025-05-20 Thread Paul Kirth via llvm-branch-commits

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)

2025-05-20 Thread Paul Kirth via llvm-branch-commits

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)

2025-05-20 Thread Paul Kirth via llvm-branch-commits

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)

2025-05-20 Thread Paul Kirth via llvm-branch-commits

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)

2025-05-20 Thread Paul Kirth via llvm-branch-commits

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)

2025-05-20 Thread Paul Kirth via llvm-branch-commits

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)

2025-05-20 Thread Paul Kirth via llvm-branch-commits

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)

2025-05-20 Thread Paul Kirth via llvm-branch-commits

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)

2025-05-20 Thread Paul Kirth via llvm-branch-commits

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)

2025-05-20 Thread Qinkun Bao via llvm-branch-commits

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)

2025-05-20 Thread via llvm-branch-commits

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)

2025-05-20 Thread Finn Plummer via llvm-branch-commits


@@ -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)

2025-05-20 Thread Qinkun Bao via llvm-branch-commits

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)"

2025-05-20 Thread via llvm-branch-commits

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)

2025-05-20 Thread via llvm-branch-commits

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)

2025-05-20 Thread Krzysztof Drewniak via llvm-branch-commits

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)

2025-05-20 Thread via llvm-branch-commits

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