[llvm] [WIP][NFC] Start to hide details of DIExpression (PR #161716)
Scott Linder via llvm-commits
llvm-commits at lists.llvm.org
Thu Oct 2 11:22:07 PDT 2025
https://github.com/slinder1 created https://github.com/llvm/llvm-project/pull/161716
This is a WIP review to facilitate discussing https://discourse.llvm.org/t/rfc-for-diexpression-editing/88459
The first commit is just some refactoring to prepare for the change. The core of the proposal is in commit [Add DebugInfoExprs](https://github.com/llvm/llvm-project/commit/4175db72b48d5c0f40d469880c3aa86c4dea9c2d), and is followed by multiple commits adding example use.
The original commit message for the squashed version was:
Add a DIOp::Op type that fits in 2 qwords to replace the use of
uint64_ts in the interface.
Pull out nearly every DIExpression method which has a relationship to
the actual expression representation into either DIExprRef (an immutable
reference without ownership, similar to an ArrayRef) or DIExprBuf (a
mutable "builder" type).
TODO: A lot of docs, some remaining methods from DIExpression, and many
of the uses in the codebase
Rather than a std::variant this uses a bespoke X-macro based approach
I tried a few approaches which still had std::variant at the core, but
they all have drawbacks.
I also considered reviving the "IntrusiveVariant" generic type from
https://reviews.llvm.org/D98477 but ultimately I just went with the
approach @epilk suggested, basing the core implementation of visit on the
macros that are already useful for defining the alternative types
themselves.
I did not match the std::variant API particularly faithfully, but
if you squint it does map somewhat closely:
* Rather than free functions, everything is a member. A member visit is
already part of newer standards, although I have currently only
implemented a sort of "helper" over it that I called visitOverload,
which just removes the need to nest another call to e.g.
llvm::makeVisitor. Adding a standards-faithful visit member isn't
quite possible without future language features, but as the type is
canonically used by-value it seems fine to just add one (TODO). Adding
a N-ary free-function visit is probably not needed for this type.
* Rather than has_alternative, a regretably verbose identifier that I
assume was chosen to avoid collisions, we just have `has`.
* The getIf member is changed to be value-based and returns an optional.
Misc notes:
* DIExprBuf is double-buffered, added one example of using this
in FastISel to amortize allocations over many expression updates.
Unsure on this one, need to also consider how large the SmallVectors
themselves are and whether this is an acceptable tradeoff to e.g. the
size of what will hold the buf.
* DIExprBuf eagerly copies the expression into buffer on
construction/assignment, could also lazily work from the original
expression for the first operation.
>From 61fa59e8f3914875bee229355fcff5920a2f5325 Mon Sep 17 00:00:00 2001
From: Scott Linder <Scott.Linder at amd.com>
Date: Wed, 1 Oct 2025 21:52:38 +0000
Subject: [PATCH 1/4] Refactor and add DebugInfoCommon
---
llvm/include/llvm/IR/DebugInfoCommon.h | 124 ++++++++++++++++++
llvm/include/llvm/IR/DebugInfoMetadata.h | 102 ++------------
.../CodeGen/AsmPrinter/DwarfCompileUnit.cpp | 9 +-
.../CodeGen/AsmPrinter/DwarfExpression.cpp | 4 +-
llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp | 3 +-
llvm/lib/IR/AsmWriter.cpp | 5 +-
llvm/lib/IR/CMakeLists.txt | 1 +
llvm/lib/IR/DIExpressionOptimizer.cpp | 19 ++-
llvm/lib/IR/DebugInfoCommon.cpp | 57 ++++++++
llvm/lib/IR/DebugInfoMetadata.cpp | 39 +-----
.../Transforms/Scalar/LoopStrengthReduce.cpp | 12 +-
11 files changed, 219 insertions(+), 156 deletions(-)
create mode 100644 llvm/include/llvm/IR/DebugInfoCommon.h
create mode 100644 llvm/lib/IR/DebugInfoCommon.cpp
diff --git a/llvm/include/llvm/IR/DebugInfoCommon.h b/llvm/include/llvm/IR/DebugInfoCommon.h
new file mode 100644
index 0000000000000..ed0ce32b0ac8b
--- /dev/null
+++ b/llvm/include/llvm/IR/DebugInfoCommon.h
@@ -0,0 +1,124 @@
+//===- DebugInfoCommon.h - Shared Debug Info Types --------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines small types common to multiple DebugInfo translation units
+// while transitioning to DebugInfoExprs.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_IR_DEBUGINFOCOMMON_H
+#define LLVM_IR_DEBUGINFOCOMMON_H
+
+#include <llvm/ADT/ArrayRef.h>
+#include <llvm/ADT/SmallVector.h>
+#include <llvm/Support/Compiler.h>
+
+#include <cstdint>
+
+namespace llvm {
+
+enum class SignedOrUnsignedConstant { SignedConstant, UnsignedConstant };
+
+/// A lightweight wrapper around an expression operand.
+class ExprOperand {
+ const uint64_t *Op = nullptr;
+
+public:
+ ExprOperand() = default;
+ explicit ExprOperand(const uint64_t *Op) : Op(Op) {}
+
+ const uint64_t *get() const { return Op; }
+
+ /// Get the operand code.
+ uint64_t getOp() const { return *Op; }
+
+ /// Get an argument to the operand.
+ ///
+ /// Never returns the operand itself.
+ uint64_t getArg(unsigned I) const { return Op[I + 1]; }
+
+ unsigned getNumArgs() const { return getSize() - 1; }
+
+ /// Return the size of the operand.
+ ///
+ /// Return the number of elements in the operand (1 + args).
+ LLVM_ABI unsigned getSize() const;
+
+ /// Append the elements of this operand to \p V.
+ void appendToVector(SmallVectorImpl<uint64_t> &V) const {
+ V.append(get(), get() + getSize());
+ }
+};
+
+/// An iterator for expression operands.
+class expr_op_iterator { // NOLINT(readability-identifier-naming)
+ ExprOperand Op;
+
+public:
+ using iterator_category = std::input_iterator_tag;
+ using value_type = ExprOperand;
+ using difference_type = std::ptrdiff_t;
+ using pointer = value_type *;
+ using reference = value_type &;
+
+ using element_iterator = ArrayRef<uint64_t>::iterator;
+
+ expr_op_iterator() = default;
+ explicit expr_op_iterator(element_iterator I) : Op(I) {}
+
+ element_iterator getBase() const { return Op.get(); }
+ const ExprOperand &operator*() const { return Op; }
+ const ExprOperand *operator->() const { return &Op; }
+
+ expr_op_iterator &operator++() {
+ increment();
+ return *this;
+ }
+ expr_op_iterator operator++(int) {
+ expr_op_iterator T(*this);
+ increment();
+ return T;
+ }
+
+ /// Get the next iterator.
+ ///
+ /// \a std::next() doesn't work because this is technically an
+ /// input_iterator, but it's a perfectly valid operation. This is an
+ /// accessor to provide the same functionality.
+ expr_op_iterator getNext() const { return ++expr_op_iterator(*this); }
+
+ bool operator==(const expr_op_iterator &X) const {
+ return getBase() == X.getBase();
+ }
+ bool operator!=(const expr_op_iterator &X) const {
+ return getBase() != X.getBase();
+ }
+
+private:
+ void increment() { Op = ExprOperand(getBase() + Op.getSize()); }
+};
+
+// NOLINTNEXTLINE(readability-identifier-naming)
+static inline expr_op_iterator expr_op_begin(ArrayRef<uint64_t> Elements) {
+ return expr_op_iterator(Elements.begin());
+}
+// NOLINTNEXTLINE(readability-identifier-naming)
+static inline expr_op_iterator expr_op_end(ArrayRef<uint64_t> Elements) {
+ return expr_op_iterator(Elements.end());
+}
+// NOLINTNEXTLINE(readability-identifier-naming)
+static inline iterator_range<expr_op_iterator>
+expr_ops(ArrayRef<uint64_t> Elements) {
+ return {expr_op_begin(Elements), expr_op_end(Elements)};
+}
+
+void appendOffsetImpl(SmallVectorImpl<uint64_t> &Ops, int64_t Offset);
+
+} // end namespace llvm
+
+#endif // LLVM_IR_DEBUGINFOCOMMON_H
diff --git a/llvm/include/llvm/IR/DebugInfoMetadata.h b/llvm/include/llvm/IR/DebugInfoMetadata.h
index 6652e303a6648..1dbfd1585008e 100644
--- a/llvm/include/llvm/IR/DebugInfoMetadata.h
+++ b/llvm/include/llvm/IR/DebugInfoMetadata.h
@@ -22,6 +22,7 @@
#include "llvm/ADT/iterator_range.h"
#include "llvm/IR/Constants.h"
#include "llvm/IR/DbgVariableFragmentInfo.h"
+#include "llvm/IR/DebugInfoCommon.h"
#include "llvm/IR/Metadata.h"
#include "llvm/IR/PseudoProbe.h"
#include "llvm/Support/Casting.h"
@@ -3330,7 +3331,6 @@ class DIExpression : public MDNode {
return Elements[I];
}
- enum SignedOrUnsignedConstant { SignedConstant, UnsignedConstant };
/// Determine whether this represents a constant value, if so
// return it's sign information.
LLVM_ABI std::optional<SignedOrUnsignedConstant> isConstant() const;
@@ -3350,86 +3350,6 @@ class DIExpression : public MDNode {
element_iterator elements_begin() const { return getElements().begin(); }
element_iterator elements_end() const { return getElements().end(); }
- /// A lightweight wrapper around an expression operand.
- ///
- /// TODO: Store arguments directly and change \a DIExpression to store a
- /// range of these.
- class ExprOperand {
- const uint64_t *Op = nullptr;
-
- public:
- ExprOperand() = default;
- explicit ExprOperand(const uint64_t *Op) : Op(Op) {}
-
- const uint64_t *get() const { return Op; }
-
- /// Get the operand code.
- uint64_t getOp() const { return *Op; }
-
- /// Get an argument to the operand.
- ///
- /// Never returns the operand itself.
- uint64_t getArg(unsigned I) const { return Op[I + 1]; }
-
- unsigned getNumArgs() const { return getSize() - 1; }
-
- /// Return the size of the operand.
- ///
- /// Return the number of elements in the operand (1 + args).
- LLVM_ABI unsigned getSize() const;
-
- /// Append the elements of this operand to \p V.
- void appendToVector(SmallVectorImpl<uint64_t> &V) const {
- V.append(get(), get() + getSize());
- }
- };
-
- /// An iterator for expression operands.
- class expr_op_iterator {
- ExprOperand Op;
-
- public:
- using iterator_category = std::input_iterator_tag;
- using value_type = ExprOperand;
- using difference_type = std::ptrdiff_t;
- using pointer = value_type *;
- using reference = value_type &;
-
- expr_op_iterator() = default;
- explicit expr_op_iterator(element_iterator I) : Op(I) {}
-
- element_iterator getBase() const { return Op.get(); }
- const ExprOperand &operator*() const { return Op; }
- const ExprOperand *operator->() const { return &Op; }
-
- expr_op_iterator &operator++() {
- increment();
- return *this;
- }
- expr_op_iterator operator++(int) {
- expr_op_iterator T(*this);
- increment();
- return T;
- }
-
- /// Get the next iterator.
- ///
- /// \a std::next() doesn't work because this is technically an
- /// input_iterator, but it's a perfectly valid operation. This is an
- /// accessor to provide the same functionality.
- expr_op_iterator getNext() const { return ++expr_op_iterator(*this); }
-
- bool operator==(const expr_op_iterator &X) const {
- return getBase() == X.getBase();
- }
- bool operator!=(const expr_op_iterator &X) const {
- return getBase() != X.getBase();
- }
-
- private:
- void increment() { Op = ExprOperand(getBase() + Op.getSize()); }
- };
-
/// Visit the elements via ExprOperand wrappers.
///
/// These range iterators visit elements through \a ExprOperand wrappers.
@@ -3779,7 +3699,7 @@ template <> struct DenseMapInfo<DIExpression::FragmentInfo> {
/// Holds a DIExpression and keeps track of how many operands have been consumed
/// so far.
class DIExpressionCursor {
- DIExpression::expr_op_iterator Start, End;
+ expr_op_iterator Start, End;
public:
DIExpressionCursor(const DIExpression *Expr) {
@@ -3797,7 +3717,7 @@ class DIExpressionCursor {
DIExpressionCursor(const DIExpressionCursor &) = default;
/// Consume one operation.
- std::optional<DIExpression::ExprOperand> take() {
+ std::optional<ExprOperand> take() {
if (Start == End)
return std::nullopt;
return *(Start++);
@@ -3807,14 +3727,14 @@ class DIExpressionCursor {
void consume(unsigned N) { std::advance(Start, N); }
/// Return the current operation.
- std::optional<DIExpression::ExprOperand> peek() const {
+ std::optional<ExprOperand> peek() const {
if (Start == End)
return std::nullopt;
return *(Start);
}
/// Return the next operation.
- std::optional<DIExpression::ExprOperand> peekNext() const {
+ std::optional<ExprOperand> peekNext() const {
if (Start == End)
return std::nullopt;
@@ -3825,10 +3745,10 @@ class DIExpressionCursor {
return *Next;
}
- std::optional<DIExpression::ExprOperand> peekNextN(unsigned N) const {
+ std::optional<ExprOperand> peekNextN(unsigned N) const {
if (Start == End)
return std::nullopt;
- DIExpression::expr_op_iterator Nth = Start;
+ expr_op_iterator Nth = Start;
for (unsigned I = 0; I < N; I++) {
Nth = Nth.getNext();
if (Nth == End)
@@ -3838,15 +3758,15 @@ class DIExpressionCursor {
}
void assignNewExpr(ArrayRef<uint64_t> Expr) {
- this->Start = DIExpression::expr_op_iterator(Expr.begin());
- this->End = DIExpression::expr_op_iterator(Expr.end());
+ this->Start = expr_op_iterator(Expr.begin());
+ this->End = expr_op_iterator(Expr.end());
}
/// Determine whether there are any operations left in this expression.
operator bool() const { return Start != End; }
- DIExpression::expr_op_iterator begin() const { return Start; }
- DIExpression::expr_op_iterator end() const { return End; }
+ expr_op_iterator begin() const { return Start; }
+ expr_op_iterator end() const { return End; }
/// Retrieve the fragment information, if any.
std::optional<DIExpression::FragmentInfo> getFragmentInfo() const {
diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp b/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp
index 518121e200190..a439d51929bfc 100644
--- a/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp
+++ b/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp
@@ -257,11 +257,10 @@ void DwarfCompileUnit::addLocationAttribute(
// DW_AT_const_value(X).
if (GlobalExprs.size() == 1 && Expr && Expr->isConstant()) {
addToAccelTable = true;
- addConstantValue(
- *VariableDIE,
- DIExpression::SignedOrUnsignedConstant::UnsignedConstant ==
- *Expr->isConstant(),
- Expr->getElement(1));
+ addConstantValue(*VariableDIE,
+ SignedOrUnsignedConstant::UnsignedConstant ==
+ *Expr->isConstant(),
+ Expr->getElement(1));
break;
}
diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfExpression.cpp b/llvm/lib/CodeGen/AsmPrinter/DwarfExpression.cpp
index 1703b27d350f3..3c9d660ecf5e7 100644
--- a/llvm/lib/CodeGen/AsmPrinter/DwarfExpression.cpp
+++ b/llvm/lib/CodeGen/AsmPrinter/DwarfExpression.cpp
@@ -337,7 +337,7 @@ bool DwarfExpression::addMachineRegExpression(const TargetRegisterInfo &TRI,
// Don't emit locations that cannot be expressed without DW_OP_stack_value.
if (DwarfVersion < 4)
- if (any_of(ExprCursor, [](DIExpression::ExprOperand Op) -> bool {
+ if (any_of(ExprCursor, [](ExprOperand Op) -> bool {
return Op.getOp() == dwarf::DW_OP_stack_value;
})) {
DwarfRegs.clear();
@@ -511,7 +511,7 @@ bool DwarfExpression::addExpression(
// and not any other parts of the following DWARF expression.
assert(!IsEmittingEntryValue && "Can't emit entry value around expression");
- std::optional<DIExpression::ExprOperand> PrevConvertOp;
+ std::optional<ExprOperand> PrevConvertOp;
while (ExprCursor) {
auto Op = ExprCursor.take();
diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp b/llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp
index 62fb5eb011cf2..861bfb0368d08 100644
--- a/llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp
+++ b/llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp
@@ -1669,8 +1669,7 @@ void DwarfUnit::constructGenericSubrangeDIE(DIE &Buffer,
addDIEEntry(DwGenericSubrange, Attr, *VarDIE);
} else if (auto *BE = dyn_cast_if_present<DIExpression *>(Bound)) {
if (BE->isConstant() &&
- DIExpression::SignedOrUnsignedConstant::SignedConstant ==
- *BE->isConstant()) {
+ SignedOrUnsignedConstant::SignedConstant == *BE->isConstant()) {
if (Attr != dwarf::DW_AT_lower_bound || DefaultLowerBound == -1 ||
static_cast<int64_t>(BE->getElement(1)) != DefaultLowerBound)
addSInt(DwGenericSubrange, Attr, dwarf::DW_FORM_sdata,
diff --git a/llvm/lib/IR/AsmWriter.cpp b/llvm/lib/IR/AsmWriter.cpp
index 1a518305cffbe..a83422f9611ff 100644
--- a/llvm/lib/IR/AsmWriter.cpp
+++ b/llvm/lib/IR/AsmWriter.cpp
@@ -2190,8 +2190,7 @@ static void writeDIGenericSubrange(raw_ostream &Out, const DIGenericSubrange *N,
if (!BE)
return std::nullopt;
if (BE->isConstant() &&
- DIExpression::SignedOrUnsignedConstant::SignedConstant ==
- *BE->isConstant()) {
+ SignedOrUnsignedConstant::SignedConstant == *BE->isConstant()) {
return static_cast<int64_t>(BE->getElement(1));
}
return std::nullopt;
@@ -2640,7 +2639,7 @@ static void writeDIExpression(raw_ostream &Out, const DIExpression *N,
Out << "!DIExpression(";
FieldSeparator FS;
if (N->isValid()) {
- for (const DIExpression::ExprOperand &Op : N->expr_ops()) {
+ for (const ExprOperand &Op : N->expr_ops()) {
auto OpStr = dwarf::OperationEncodingString(Op.getOp());
assert(!OpStr.empty() && "Expected valid opcode");
diff --git a/llvm/lib/IR/CMakeLists.txt b/llvm/lib/IR/CMakeLists.txt
index 10572ff708bd3..f13f39e8df60c 100644
--- a/llvm/lib/IR/CMakeLists.txt
+++ b/llvm/lib/IR/CMakeLists.txt
@@ -18,6 +18,7 @@ add_llvm_component_library(LLVMCore
DIBuilder.cpp
DataLayout.cpp
DebugInfo.cpp
+ DebugInfoCommon.cpp
DebugInfoMetadata.cpp
DIExpressionOptimizer.cpp
DebugProgramInstruction.cpp
diff --git a/llvm/lib/IR/DIExpressionOptimizer.cpp b/llvm/lib/IR/DIExpressionOptimizer.cpp
index be9e13a34235a..bf8cf79d9ccd4 100644
--- a/llvm/lib/IR/DIExpressionOptimizer.cpp
+++ b/llvm/lib/IR/DIExpressionOptimizer.cpp
@@ -7,7 +7,7 @@
//===----------------------------------------------------------------------===//
//
// This file implements functions to constant fold DIExpressions. Which were
-// declared in DIExpressionOptimizer.h
+// declared in DebugInfoMetadata.h
//
//===----------------------------------------------------------------------===//
@@ -17,7 +17,7 @@
using namespace llvm;
/// Returns true if the Op is a DW_OP_constu.
-static std::optional<uint64_t> isConstantVal(DIExpression::ExprOperand Op) {
+static std::optional<uint64_t> isConstantVal(ExprOperand Op) {
if (Op.getOp() == dwarf::DW_OP_constu)
return Op.getArg(0);
return std::nullopt;
@@ -96,7 +96,7 @@ static bool operationsAreFoldableAndCommutative(dwarf::LocationAtom Operator1,
/// Consume one operator and its operand(s).
static void consumeOneOperator(DIExpressionCursor &Cursor, uint64_t &Loc,
- const DIExpression::ExprOperand &Op) {
+ const ExprOperand &Op) {
Cursor.consume(1);
Loc = Loc + Op.getSize();
}
@@ -190,8 +190,7 @@ optimizeDwarfOperations(ArrayRef<uint64_t> WorkingOps) {
/// {DW_OP_constu, 0, DW_OP_[plus, minus, shl, shr]} -> {}
/// {DW_OP_constu, 1, DW_OP_[mul, div]} -> {}
-static bool tryFoldNoOpMath(uint64_t Const1,
- ArrayRef<DIExpression::ExprOperand> Ops,
+static bool tryFoldNoOpMath(uint64_t Const1, ArrayRef<ExprOperand> Ops,
uint64_t &Loc, DIExpressionCursor &Cursor,
SmallVectorImpl<uint64_t> &WorkingOps) {
@@ -206,8 +205,7 @@ static bool tryFoldNoOpMath(uint64_t Const1,
/// {DW_OP_constu, Const1, DW_OP_constu, Const2, DW_OP_[plus,
/// minus, mul, div, shl, shr] -> {DW_OP_constu, Const1 [+, -, *, /, <<, >>]
/// Const2}
-static bool tryFoldConstants(uint64_t Const1,
- ArrayRef<DIExpression::ExprOperand> Ops,
+static bool tryFoldConstants(uint64_t Const1, ArrayRef<ExprOperand> Ops,
uint64_t &Loc, DIExpressionCursor &Cursor,
SmallVectorImpl<uint64_t> &WorkingOps) {
@@ -231,8 +229,7 @@ static bool tryFoldConstants(uint64_t Const1,
/// {DW_OP_constu, Const1, DW_OP_[plus, mul], DW_OP_constu, Const2,
/// DW_OP_[plus, mul]} -> {DW_OP_constu, Const1 [+, *] Const2, DW_OP_[plus,
/// mul]}
-static bool tryFoldCommutativeMath(uint64_t Const1,
- ArrayRef<DIExpression::ExprOperand> Ops,
+static bool tryFoldCommutativeMath(uint64_t Const1, ArrayRef<ExprOperand> Ops,
uint64_t &Loc, DIExpressionCursor &Cursor,
SmallVectorImpl<uint64_t> &WorkingOps) {
@@ -260,7 +257,7 @@ static bool tryFoldCommutativeMath(uint64_t Const1,
/// {DW_OP_constu, Const1 [+, *] Const2, DW_OP_[plus, mul], DW_OP_LLVM_arg,
/// Arg1, DW_OP_[plus, mul]}
static bool tryFoldCommutativeMathWithArgInBetween(
- uint64_t Const1, ArrayRef<DIExpression::ExprOperand> Ops, uint64_t &Loc,
+ uint64_t Const1, ArrayRef<ExprOperand> Ops, uint64_t &Loc,
DIExpressionCursor &Cursor, SmallVectorImpl<uint64_t> &WorkingOps) {
auto Const2 = isConstantVal(Ops[4]);
@@ -291,7 +288,7 @@ DIExpression *DIExpression::foldConstantMath() {
uint64_t Loc = 0;
SmallVector<uint64_t> ResultOps = canonicalizeDwarfOperations(WorkingOps);
DIExpressionCursor Cursor(ResultOps);
- SmallVector<DIExpression::ExprOperand, 8> Ops;
+ SmallVector<ExprOperand, 8> Ops;
// Iterate over all Operations in a DIExpression to match the smallest pattern
// that can be folded.
diff --git a/llvm/lib/IR/DebugInfoCommon.cpp b/llvm/lib/IR/DebugInfoCommon.cpp
new file mode 100644
index 0000000000000..621f343f8ba61
--- /dev/null
+++ b/llvm/lib/IR/DebugInfoCommon.cpp
@@ -0,0 +1,57 @@
+//===- DebugInfoCommon.cpp - Shared Debug Info Types ------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements DebugInfoCommon types.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/IR/DebugInfoCommon.h"
+#include "llvm/BinaryFormat/Dwarf.h"
+
+using namespace llvm;
+
+unsigned ExprOperand::getSize() const {
+ uint64_t Op = getOp();
+
+ if (Op >= dwarf::DW_OP_breg0 && Op <= dwarf::DW_OP_breg31)
+ return 2;
+
+ switch (Op) {
+ case dwarf::DW_OP_LLVM_convert:
+ case dwarf::DW_OP_LLVM_fragment:
+ case dwarf::DW_OP_LLVM_extract_bits_sext:
+ case dwarf::DW_OP_LLVM_extract_bits_zext:
+ case dwarf::DW_OP_bregx:
+ return 3;
+ case dwarf::DW_OP_constu:
+ case dwarf::DW_OP_consts:
+ case dwarf::DW_OP_deref_size:
+ case dwarf::DW_OP_plus_uconst:
+ case dwarf::DW_OP_LLVM_tag_offset:
+ case dwarf::DW_OP_LLVM_entry_value:
+ case dwarf::DW_OP_LLVM_arg:
+ case dwarf::DW_OP_regx:
+ return 2;
+ default:
+ return 1;
+ }
+}
+
+void llvm::appendOffsetImpl(SmallVectorImpl<uint64_t> &Ops, int64_t Offset) {
+ if (Offset > 0) {
+ Ops.push_back(dwarf::DW_OP_plus_uconst);
+ Ops.push_back(Offset);
+ } else if (Offset < 0) {
+ Ops.push_back(dwarf::DW_OP_constu);
+ // Avoid UB when encountering LLONG_MIN, because in 2's complement
+ // abs(LLONG_MIN) is LLONG_MAX+1.
+ uint64_t AbsMinusOne = -(Offset + 1);
+ Ops.push_back(AbsMinusOne + 1);
+ Ops.push_back(dwarf::DW_OP_minus);
+ }
+}
diff --git a/llvm/lib/IR/DebugInfoMetadata.cpp b/llvm/lib/IR/DebugInfoMetadata.cpp
index 1ededb9e7b3e2..4a9543488e675 100644
--- a/llvm/lib/IR/DebugInfoMetadata.cpp
+++ b/llvm/lib/IR/DebugInfoMetadata.cpp
@@ -1662,33 +1662,6 @@ DIAssignID *DIAssignID::getImpl(LLVMContext &Context, StorageType Storage,
return storeImpl(new (0u, Storage) DIAssignID(Context, Storage), Storage);
}
-unsigned DIExpression::ExprOperand::getSize() const {
- uint64_t Op = getOp();
-
- if (Op >= dwarf::DW_OP_breg0 && Op <= dwarf::DW_OP_breg31)
- return 2;
-
- switch (Op) {
- case dwarf::DW_OP_LLVM_convert:
- case dwarf::DW_OP_LLVM_fragment:
- case dwarf::DW_OP_LLVM_extract_bits_sext:
- case dwarf::DW_OP_LLVM_extract_bits_zext:
- case dwarf::DW_OP_bregx:
- return 3;
- case dwarf::DW_OP_constu:
- case dwarf::DW_OP_consts:
- case dwarf::DW_OP_deref_size:
- case dwarf::DW_OP_plus_uconst:
- case dwarf::DW_OP_LLVM_tag_offset:
- case dwarf::DW_OP_LLVM_entry_value:
- case dwarf::DW_OP_LLVM_arg:
- case dwarf::DW_OP_regx:
- return 2;
- default:
- return 1;
- }
-}
-
bool DIExpression::isValid() const {
for (auto I = expr_op_begin(), E = expr_op_end(); I != E; ++I) {
// Check that there's space for the operand.
@@ -2476,14 +2449,12 @@ uint64_t DIExpression::getNumLocationOperands() const {
return Result;
}
-std::optional<DIExpression::SignedOrUnsignedConstant>
-DIExpression::isConstant() const {
-
+std::optional<SignedOrUnsignedConstant> DIExpression::isConstant() const {
// Recognize signed and unsigned constants.
- // An signed constants can be represented as DW_OP_consts C DW_OP_stack_value
- // (DW_OP_LLVM_fragment of Len).
- // An unsigned constant can be represented as
- // DW_OP_constu C DW_OP_stack_value (DW_OP_LLVM_fragment of Len).
+ // An signed constants can be represented as DW_OP_consts C
+ // DW_OP_stack_value (DW_OP_LLVM_fragment of Len). An unsigned constant can
+ // be represented as DW_OP_constu C DW_OP_stack_value (DW_OP_LLVM_fragment
+ // of Len).
if ((getNumElements() != 2 && getNumElements() != 3 &&
getNumElements() != 6) ||
diff --git a/llvm/lib/Transforms/Scalar/LoopStrengthReduce.cpp b/llvm/lib/Transforms/Scalar/LoopStrengthReduce.cpp
index 1a279b6198182..a513e211160bb 100644
--- a/llvm/lib/Transforms/Scalar/LoopStrengthReduce.cpp
+++ b/llvm/lib/Transforms/Scalar/LoopStrengthReduce.cpp
@@ -6340,12 +6340,10 @@ void LoopStrengthReduce::getAnalysisUsage(AnalysisUsage &AU) const {
namespace {
/// Enables more convenient iteration over a DWARF expression vector.
-static iterator_range<llvm::DIExpression::expr_op_iterator>
+static iterator_range<expr_op_iterator>
ToDwarfOpIter(SmallVectorImpl<uint64_t> &Expr) {
- llvm::DIExpression::expr_op_iterator Begin =
- llvm::DIExpression::expr_op_iterator(Expr.begin());
- llvm::DIExpression::expr_op_iterator End =
- llvm::DIExpression::expr_op_iterator(Expr.end());
+ expr_op_iterator Begin = expr_op_iterator(Expr.begin());
+ expr_op_iterator End = expr_op_iterator(Expr.end());
return {Begin, End};
}
@@ -6401,9 +6399,7 @@ struct SCEVDbgValueBuilder {
// Iterating the expression as DWARF ops is convenient when updating
// DWARF_OP_LLVM_args.
- iterator_range<llvm::DIExpression::expr_op_iterator> expr_ops() {
- return ToDwarfOpIter(Expr);
- }
+ iterator_range<expr_op_iterator> expr_ops() { return ToDwarfOpIter(Expr); }
/// Several SCEV types are sequences of the same arithmetic operator applied
/// to constants and values that may be extended or truncated.
>From 4175db72b48d5c0f40d469880c3aa86c4dea9c2d Mon Sep 17 00:00:00 2001
From: Scott Linder <Scott.Linder at amd.com>
Date: Wed, 1 Oct 2025 21:54:51 +0000
Subject: [PATCH 2/4] Add DebugInfoExprs
---
llvm/include/llvm/IR/DIOps.def | 407 ++++++++++++
llvm/include/llvm/IR/DebugInfoExprs.h | 375 +++++++++++
llvm/lib/IR/CMakeLists.txt | 1 +
llvm/lib/IR/DebugInfoExprs.cpp | 879 ++++++++++++++++++++++++++
4 files changed, 1662 insertions(+)
create mode 100644 llvm/include/llvm/IR/DIOps.def
create mode 100644 llvm/include/llvm/IR/DebugInfoExprs.h
create mode 100644 llvm/lib/IR/DebugInfoExprs.cpp
diff --git a/llvm/include/llvm/IR/DIOps.def b/llvm/include/llvm/IR/DIOps.def
new file mode 100644
index 0000000000000..ca0614e20502d
--- /dev/null
+++ b/llvm/include/llvm/IR/DIOps.def
@@ -0,0 +1,407 @@
+//===- llvm/IR/DIOps.def - DIExpression Op definitions ----------*- 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
+//
+//===----------------------------------------------------------------------===//
+//
+// Macros for running through all typed DIExpression operations.
+//
+//===----------------------------------------------------------------------===//
+
+#if !(defined HANDLE_OP || defined HANDLE_OP0 || defined HANDLE_OP1 || \
+ defined HANDLE_OP2)
+#error "Missing macro definition of HANDLE_OP*"
+#endif
+
+#if defined HANDLE_OP && \
+ (defined HANDLE_OP0 || defined HANDLE_OP1 || defined HANDLE_OP2)
+#error "HANDLE_OP cannot be defined together with HANDLE_OP{0,1,2}"
+#endif
+
+// TODO: Update docs to reflect ENCODING param
+
+/// If defined, HANDLE_OP is invoked for each DIExpr operation.
+///
+/// It is invoked with one argument, which is the identifier for the name of
+/// the operation.
+///
+/// If defined, none of HANDLE_OP{0,1,2} may be defined.
+#ifndef HANDLE_OP
+#define HANDLE_OP(NAME, ENCODING)
+#endif
+
+/// If defined, HANDLE_OP0 is invoked once for each DIExpr operation which has
+/// exactly zero arguments.
+///
+/// It is invoked with one argument, which is the identifier for the name of
+/// the operation.
+#ifndef HANDLE_OP0
+#define HANDLE_OP0(NAME, ENCODING) HANDLE_OP(NAME, ENCODING)
+#endif
+
+/// If defined, HANDLE_OP1 is invoked once for each DIExpr operation which has
+/// exactly one argument.
+///
+/// It is invoked with three arguments:
+///
+/// 1. The identifier for the name of the operation.
+/// (2, 3). The type and identifier of the first argument to the operation.
+#ifndef HANDLE_OP1
+#define HANDLE_OP1(NAME, ENCODING, ...) HANDLE_OP(NAME, ENCODING)
+#endif
+
+/// If defined, HANDLE_OP2 is invoked once for each DIExpr operation which has
+/// exactly two arguments.
+///
+/// It is invoked with five arguments:
+///
+/// 1. The identifier for the name of the operation.
+/// (2, 3). The type and identifier of the first argument to the operation.
+/// (4, 5). The type and identifier of the second argument to the operation.
+#ifndef HANDLE_OP2
+#define HANDLE_OP2(NAME, ENCODING, ...) HANDLE_OP(NAME, ENCODING)
+#endif
+
+/// If defined, SEPARATOR is invoked between each invocation of the HANDLE_OP*
+/// macros.
+#ifndef SEPARATOR
+#define SEPARATOR
+#endif
+
+HANDLE_OP1(Addr, ::llvm::dwarf::DW_OP_addr, uint64_t, Value)
+SEPARATOR
+HANDLE_OP0(Deref, ::llvm::dwarf::DW_OP_deref)
+SEPARATOR
+HANDLE_OP1(Const1U, ::llvm::dwarf::DW_OP_const1u, uint8_t, Value)
+SEPARATOR
+HANDLE_OP1(Const1S, ::llvm::dwarf::DW_OP_const1s, int8_t, Value)
+SEPARATOR
+HANDLE_OP1(Const2U, ::llvm::dwarf::DW_OP_const2u, uint16_t, Value)
+SEPARATOR
+HANDLE_OP1(Const2S, ::llvm::dwarf::DW_OP_const2s, int16_t, Value)
+SEPARATOR
+HANDLE_OP1(Const4U, ::llvm::dwarf::DW_OP_const4u, uint32_t, Value)
+SEPARATOR
+HANDLE_OP1(Const4S, ::llvm::dwarf::DW_OP_const4s, int32_t, Value)
+SEPARATOR
+HANDLE_OP1(Const8U, ::llvm::dwarf::DW_OP_const8u, uint64_t, Value)
+SEPARATOR
+HANDLE_OP1(Const8S, ::llvm::dwarf::DW_OP_const8s, int64_t, Value)
+SEPARATOR
+HANDLE_OP1(ConstU, ::llvm::dwarf::DW_OP_constu, uint64_t, Value)
+SEPARATOR
+HANDLE_OP1(ConstS, ::llvm::dwarf::DW_OP_consts, int64_t, Value)
+SEPARATOR
+HANDLE_OP0(Dup, ::llvm::dwarf::DW_OP_dup)
+SEPARATOR
+HANDLE_OP0(Drop, ::llvm::dwarf::DW_OP_drop)
+SEPARATOR
+HANDLE_OP0(Over, ::llvm::dwarf::DW_OP_over)
+SEPARATOR
+HANDLE_OP1(Pick, ::llvm::dwarf::DW_OP_pick, uint64_t, Index)
+SEPARATOR
+HANDLE_OP0(Swap, ::llvm::dwarf::DW_OP_swap)
+SEPARATOR
+HANDLE_OP0(Rot, ::llvm::dwarf::DW_OP_rot)
+SEPARATOR
+HANDLE_OP0(XDeref, ::llvm::dwarf::DW_OP_xderef)
+SEPARATOR
+HANDLE_OP0(Abs, ::llvm::dwarf::DW_OP_abs)
+SEPARATOR
+HANDLE_OP0(And, ::llvm::dwarf::DW_OP_and)
+SEPARATOR
+HANDLE_OP0(Div, ::llvm::dwarf::DW_OP_div)
+SEPARATOR
+HANDLE_OP0(Minus, ::llvm::dwarf::DW_OP_minus)
+SEPARATOR
+HANDLE_OP0(Mod, ::llvm::dwarf::DW_OP_mod)
+SEPARATOR
+HANDLE_OP0(Mul, ::llvm::dwarf::DW_OP_mul)
+SEPARATOR
+HANDLE_OP0(Neg, ::llvm::dwarf::DW_OP_neg)
+SEPARATOR
+HANDLE_OP0(Not, ::llvm::dwarf::DW_OP_not)
+SEPARATOR
+HANDLE_OP0(Or, ::llvm::dwarf::DW_OP_or)
+SEPARATOR
+HANDLE_OP0(Plus, ::llvm::dwarf::DW_OP_plus)
+SEPARATOR
+HANDLE_OP1(PlusUConst, ::llvm::dwarf::DW_OP_plus_uconst, uint64_t, Value)
+SEPARATOR
+HANDLE_OP0(Shl, ::llvm::dwarf::DW_OP_shl)
+SEPARATOR
+HANDLE_OP0(Shr, ::llvm::dwarf::DW_OP_shr)
+SEPARATOR
+HANDLE_OP0(Shra, ::llvm::dwarf::DW_OP_shra)
+SEPARATOR
+HANDLE_OP0(Xor, ::llvm::dwarf::DW_OP_xor)
+SEPARATOR
+HANDLE_OP0(Bra, ::llvm::dwarf::DW_OP_bra)
+SEPARATOR
+HANDLE_OP0(Eq, ::llvm::dwarf::DW_OP_eq)
+SEPARATOR
+HANDLE_OP0(Ge, ::llvm::dwarf::DW_OP_ge)
+SEPARATOR
+HANDLE_OP0(Gt, ::llvm::dwarf::DW_OP_gt)
+SEPARATOR
+HANDLE_OP0(Le, ::llvm::dwarf::DW_OP_le)
+SEPARATOR
+HANDLE_OP0(Lt, ::llvm::dwarf::DW_OP_lt)
+SEPARATOR
+HANDLE_OP0(Ne, ::llvm::dwarf::DW_OP_ne)
+SEPARATOR
+HANDLE_OP1(Skip, ::llvm::dwarf::DW_OP_skip, uint16_t, Bytes)
+SEPARATOR
+HANDLE_OP0(Lit0, ::llvm::dwarf::DW_OP_lit0)
+SEPARATOR
+HANDLE_OP0(Lit1, ::llvm::dwarf::DW_OP_lit1)
+SEPARATOR
+HANDLE_OP0(Lit2, ::llvm::dwarf::DW_OP_lit2)
+SEPARATOR
+HANDLE_OP0(Lit3, ::llvm::dwarf::DW_OP_lit3)
+SEPARATOR
+HANDLE_OP0(Lit4, ::llvm::dwarf::DW_OP_lit4)
+SEPARATOR
+HANDLE_OP0(Lit5, ::llvm::dwarf::DW_OP_lit5)
+SEPARATOR
+HANDLE_OP0(Lit6, ::llvm::dwarf::DW_OP_lit6)
+SEPARATOR
+HANDLE_OP0(Lit7, ::llvm::dwarf::DW_OP_lit7)
+SEPARATOR
+HANDLE_OP0(Lit8, ::llvm::dwarf::DW_OP_lit8)
+SEPARATOR
+HANDLE_OP0(Lit9, ::llvm::dwarf::DW_OP_lit9)
+SEPARATOR
+HANDLE_OP0(Lit10, ::llvm::dwarf::DW_OP_lit10)
+SEPARATOR
+HANDLE_OP0(Lit11, ::llvm::dwarf::DW_OP_lit11)
+SEPARATOR
+HANDLE_OP0(Lit12, ::llvm::dwarf::DW_OP_lit12)
+SEPARATOR
+HANDLE_OP0(Lit13, ::llvm::dwarf::DW_OP_lit13)
+SEPARATOR
+HANDLE_OP0(Lit14, ::llvm::dwarf::DW_OP_lit14)
+SEPARATOR
+HANDLE_OP0(Lit15, ::llvm::dwarf::DW_OP_lit15)
+SEPARATOR
+HANDLE_OP0(Lit16, ::llvm::dwarf::DW_OP_lit16)
+SEPARATOR
+HANDLE_OP0(Lit17, ::llvm::dwarf::DW_OP_lit17)
+SEPARATOR
+HANDLE_OP0(Lit18, ::llvm::dwarf::DW_OP_lit18)
+SEPARATOR
+HANDLE_OP0(Lit19, ::llvm::dwarf::DW_OP_lit19)
+SEPARATOR
+HANDLE_OP0(Lit20, ::llvm::dwarf::DW_OP_lit20)
+SEPARATOR
+HANDLE_OP0(Lit21, ::llvm::dwarf::DW_OP_lit21)
+SEPARATOR
+HANDLE_OP0(Lit22, ::llvm::dwarf::DW_OP_lit22)
+SEPARATOR
+HANDLE_OP0(Lit23, ::llvm::dwarf::DW_OP_lit23)
+SEPARATOR
+HANDLE_OP0(Lit24, ::llvm::dwarf::DW_OP_lit24)
+SEPARATOR
+HANDLE_OP0(Lit25, ::llvm::dwarf::DW_OP_lit25)
+SEPARATOR
+HANDLE_OP0(Lit26, ::llvm::dwarf::DW_OP_lit26)
+SEPARATOR
+HANDLE_OP0(Lit27, ::llvm::dwarf::DW_OP_lit27)
+SEPARATOR
+HANDLE_OP0(Lit28, ::llvm::dwarf::DW_OP_lit28)
+SEPARATOR
+HANDLE_OP0(Lit29, ::llvm::dwarf::DW_OP_lit29)
+SEPARATOR
+HANDLE_OP0(Lit30, ::llvm::dwarf::DW_OP_lit30)
+SEPARATOR
+HANDLE_OP0(Lit31, ::llvm::dwarf::DW_OP_lit31)
+SEPARATOR
+HANDLE_OP0(Reg0, ::llvm::dwarf::DW_OP_reg0)
+SEPARATOR
+HANDLE_OP0(Reg1, ::llvm::dwarf::DW_OP_reg1)
+SEPARATOR
+HANDLE_OP0(Reg2, ::llvm::dwarf::DW_OP_reg2)
+SEPARATOR
+HANDLE_OP0(Reg3, ::llvm::dwarf::DW_OP_reg3)
+SEPARATOR
+HANDLE_OP0(Reg4, ::llvm::dwarf::DW_OP_reg4)
+SEPARATOR
+HANDLE_OP0(Reg5, ::llvm::dwarf::DW_OP_reg5)
+SEPARATOR
+HANDLE_OP0(Reg6, ::llvm::dwarf::DW_OP_reg6)
+SEPARATOR
+HANDLE_OP0(Reg7, ::llvm::dwarf::DW_OP_reg7)
+SEPARATOR
+HANDLE_OP0(Reg8, ::llvm::dwarf::DW_OP_reg8)
+SEPARATOR
+HANDLE_OP0(Reg9, ::llvm::dwarf::DW_OP_reg9)
+SEPARATOR
+HANDLE_OP0(Reg10, ::llvm::dwarf::DW_OP_reg10)
+SEPARATOR
+HANDLE_OP0(Reg11, ::llvm::dwarf::DW_OP_reg11)
+SEPARATOR
+HANDLE_OP0(Reg12, ::llvm::dwarf::DW_OP_reg12)
+SEPARATOR
+HANDLE_OP0(Reg13, ::llvm::dwarf::DW_OP_reg13)
+SEPARATOR
+HANDLE_OP0(Reg14, ::llvm::dwarf::DW_OP_reg14)
+SEPARATOR
+HANDLE_OP0(Reg15, ::llvm::dwarf::DW_OP_reg15)
+SEPARATOR
+HANDLE_OP0(Reg16, ::llvm::dwarf::DW_OP_reg16)
+SEPARATOR
+HANDLE_OP0(Reg17, ::llvm::dwarf::DW_OP_reg17)
+SEPARATOR
+HANDLE_OP0(Reg18, ::llvm::dwarf::DW_OP_reg18)
+SEPARATOR
+HANDLE_OP0(Reg19, ::llvm::dwarf::DW_OP_reg19)
+SEPARATOR
+HANDLE_OP0(Reg20, ::llvm::dwarf::DW_OP_reg20)
+SEPARATOR
+HANDLE_OP0(Reg21, ::llvm::dwarf::DW_OP_reg21)
+SEPARATOR
+HANDLE_OP0(Reg22, ::llvm::dwarf::DW_OP_reg22)
+SEPARATOR
+HANDLE_OP0(Reg23, ::llvm::dwarf::DW_OP_reg23)
+SEPARATOR
+HANDLE_OP0(Reg24, ::llvm::dwarf::DW_OP_reg24)
+SEPARATOR
+HANDLE_OP0(Reg25, ::llvm::dwarf::DW_OP_reg25)
+SEPARATOR
+HANDLE_OP0(Reg26, ::llvm::dwarf::DW_OP_reg26)
+SEPARATOR
+HANDLE_OP0(Reg27, ::llvm::dwarf::DW_OP_reg27)
+SEPARATOR
+HANDLE_OP0(Reg28, ::llvm::dwarf::DW_OP_reg28)
+SEPARATOR
+HANDLE_OP0(Reg29, ::llvm::dwarf::DW_OP_reg29)
+SEPARATOR
+HANDLE_OP0(Reg30, ::llvm::dwarf::DW_OP_reg30)
+SEPARATOR
+HANDLE_OP0(Reg31, ::llvm::dwarf::DW_OP_reg31)
+SEPARATOR
+HANDLE_OP1(BReg0, ::llvm::dwarf::DW_OP_breg0, uint64_t, Offset)
+SEPARATOR
+HANDLE_OP1(BReg1, ::llvm::dwarf::DW_OP_breg1, uint64_t, Offset)
+SEPARATOR
+HANDLE_OP1(BReg2, ::llvm::dwarf::DW_OP_breg2, uint64_t, Offset)
+SEPARATOR
+HANDLE_OP1(BReg3, ::llvm::dwarf::DW_OP_breg3, uint64_t, Offset)
+SEPARATOR
+HANDLE_OP1(BReg4, ::llvm::dwarf::DW_OP_breg4, uint64_t, Offset)
+SEPARATOR
+HANDLE_OP1(BReg5, ::llvm::dwarf::DW_OP_breg5, uint64_t, Offset)
+SEPARATOR
+HANDLE_OP1(BReg6, ::llvm::dwarf::DW_OP_breg6, uint64_t, Offset)
+SEPARATOR
+HANDLE_OP1(BReg7, ::llvm::dwarf::DW_OP_breg7, uint64_t, Offset)
+SEPARATOR
+HANDLE_OP1(BReg8, ::llvm::dwarf::DW_OP_breg8, uint64_t, Offset)
+SEPARATOR
+HANDLE_OP1(BReg9, ::llvm::dwarf::DW_OP_breg9, uint64_t, Offset)
+SEPARATOR
+HANDLE_OP1(BReg10, ::llvm::dwarf::DW_OP_breg10, uint64_t, Offset)
+SEPARATOR
+HANDLE_OP1(BReg11, ::llvm::dwarf::DW_OP_breg11, uint64_t, Offset)
+SEPARATOR
+HANDLE_OP1(BReg12, ::llvm::dwarf::DW_OP_breg12, uint64_t, Offset)
+SEPARATOR
+HANDLE_OP1(BReg13, ::llvm::dwarf::DW_OP_breg13, uint64_t, Offset)
+SEPARATOR
+HANDLE_OP1(BReg14, ::llvm::dwarf::DW_OP_breg14, uint64_t, Offset)
+SEPARATOR
+HANDLE_OP1(BReg15, ::llvm::dwarf::DW_OP_breg15, uint64_t, Offset)
+SEPARATOR
+HANDLE_OP1(BReg16, ::llvm::dwarf::DW_OP_breg16, uint64_t, Offset)
+SEPARATOR
+HANDLE_OP1(BReg17, ::llvm::dwarf::DW_OP_breg17, uint64_t, Offset)
+SEPARATOR
+HANDLE_OP1(BReg18, ::llvm::dwarf::DW_OP_breg18, uint64_t, Offset)
+SEPARATOR
+HANDLE_OP1(BReg19, ::llvm::dwarf::DW_OP_breg19, uint64_t, Offset)
+SEPARATOR
+HANDLE_OP1(BReg20, ::llvm::dwarf::DW_OP_breg20, uint64_t, Offset)
+SEPARATOR
+HANDLE_OP1(BReg21, ::llvm::dwarf::DW_OP_breg21, uint64_t, Offset)
+SEPARATOR
+HANDLE_OP1(BReg22, ::llvm::dwarf::DW_OP_breg22, uint64_t, Offset)
+SEPARATOR
+HANDLE_OP1(BReg23, ::llvm::dwarf::DW_OP_breg23, uint64_t, Offset)
+SEPARATOR
+HANDLE_OP1(BReg24, ::llvm::dwarf::DW_OP_breg24, uint64_t, Offset)
+SEPARATOR
+HANDLE_OP1(BReg25, ::llvm::dwarf::DW_OP_breg25, uint64_t, Offset)
+SEPARATOR
+HANDLE_OP1(BReg26, ::llvm::dwarf::DW_OP_breg26, uint64_t, Offset)
+SEPARATOR
+HANDLE_OP1(BReg27, ::llvm::dwarf::DW_OP_breg27, uint64_t, Offset)
+SEPARATOR
+HANDLE_OP1(BReg28, ::llvm::dwarf::DW_OP_breg28, uint64_t, Offset)
+SEPARATOR
+HANDLE_OP1(BReg29, ::llvm::dwarf::DW_OP_breg29, uint64_t, Offset)
+SEPARATOR
+HANDLE_OP1(BReg30, ::llvm::dwarf::DW_OP_breg30, uint64_t, Offset)
+SEPARATOR
+HANDLE_OP1(BReg31, ::llvm::dwarf::DW_OP_breg31, uint64_t, Offset)
+SEPARATOR
+HANDLE_OP1(RegX, ::llvm::dwarf::DW_OP_regx, uint32_t, Register)
+SEPARATOR
+HANDLE_OP1(FBReg, ::llvm::dwarf::DW_OP_fbreg, uint64_t, Offset)
+SEPARATOR
+HANDLE_OP2(BRegX, ::llvm::dwarf::DW_OP_bregx, uint32_t, Register, uint32_t, Offset)
+SEPARATOR
+HANDLE_OP1(Piece, ::llvm::dwarf::DW_OP_piece, uint64_t, SizeInBytes)
+SEPARATOR
+HANDLE_OP1(DerefSize, ::llvm::dwarf::DW_OP_deref_size, uint8_t, SizeInBytes)
+SEPARATOR
+HANDLE_OP1(XDerefSize, ::llvm::dwarf::DW_OP_xderef_size, uint8_t, SizeInBytes)
+SEPARATOR
+HANDLE_OP0(Nop, ::llvm::dwarf::DW_OP_nop)
+SEPARATOR
+HANDLE_OP0(PushObjectAddress, ::llvm::dwarf::DW_OP_push_object_address)
+SEPARATOR
+HANDLE_OP1(Call2, ::llvm::dwarf::DW_OP_call2, uint16_t, DIEOffset)
+SEPARATOR
+HANDLE_OP1(Call4, ::llvm::dwarf::DW_OP_call4, uint32_t, DIEOffset)
+SEPARATOR
+HANDLE_OP1(CallRef, ::llvm::dwarf::DW_OP_call_ref, uint64_t, DIEOffset)
+SEPARATOR
+HANDLE_OP0(FormTLSAddress, ::llvm::dwarf::DW_OP_form_tls_address)
+SEPARATOR
+HANDLE_OP0(CallFrameCFA, ::llvm::dwarf::DW_OP_call_frame_cfa)
+SEPARATOR
+HANDLE_OP2(BitPiece, ::llvm::dwarf::DW_OP_bit_piece, uint32_t, SizeInBits, uint32_t, OffsetInBits)
+SEPARATOR
+HANDLE_OP0(StackValue, ::llvm::dwarf::DW_OP_stack_value)
+SEPARATOR
+HANDLE_OP1(AddrX, ::llvm::dwarf::DW_OP_addrx, uint64_t, Index)
+SEPARATOR
+HANDLE_OP1(ConstX, ::llvm::dwarf::DW_OP_constx, uint64_t, Index)
+SEPARATOR
+HANDLE_OP1(Convert, ::llvm::dwarf::DW_OP_convert, uint64_t, DIEOffset)
+SEPARATOR
+HANDLE_OP1(Reinterpret, ::llvm::dwarf::DW_OP_reinterpret, uint64_t, DIEOffset)
+SEPARATOR
+HANDLE_OP2(LLVMFragment, ::llvm::dwarf::DW_OP_LLVM_fragment, uint32_t, OffsetInBits, uint32_t, SizeInBits)
+SEPARATOR
+HANDLE_OP2(LLVMConvert, ::llvm::dwarf::DW_OP_LLVM_convert, uint32_t, SizeInBits, uint8_t, Encoding)
+SEPARATOR
+HANDLE_OP1(LLVMTagOffset, ::llvm::dwarf::DW_OP_LLVM_tag_offset, uint64_t, Tag)
+SEPARATOR
+HANDLE_OP1(LLVMEntryValue, ::llvm::dwarf::DW_OP_LLVM_entry_value, uint64_t, Ops)
+SEPARATOR
+HANDLE_OP0(LLVMImplicitPointer, ::llvm::dwarf::DW_OP_LLVM_implicit_pointer)
+SEPARATOR
+HANDLE_OP1(LLVMArg, ::llvm::dwarf::DW_OP_LLVM_arg, uint64_t, Index)
+SEPARATOR
+HANDLE_OP2(LLVMExtractBitsSExt, ::llvm::dwarf::DW_OP_LLVM_extract_bits_sext, uint32_t, OffsetInBits, uint32_t, SizeInBits)
+SEPARATOR
+HANDLE_OP2(LLVMExtractBitsZExt, ::llvm::dwarf::DW_OP_LLVM_extract_bits_zext, uint32_t, OffsetInBits, uint32_t, SizeInBits)
+
+#undef SEPARATOR
+#undef HANDLE_OP2
+#undef HANDLE_OP1
+#undef HANDLE_OP0
+#undef HANDLE_OP
diff --git a/llvm/include/llvm/IR/DebugInfoExprs.h b/llvm/include/llvm/IR/DebugInfoExprs.h
new file mode 100644
index 0000000000000..61aff9f65648d
--- /dev/null
+++ b/llvm/include/llvm/IR/DebugInfoExprs.h
@@ -0,0 +1,375 @@
+//===- DebugInfoExprs.h - Debug Info Expression Manipulation ----*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines types for working with DIExpression in a type-safe way.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_IR_DEBUGINFOEXPRS_H
+#define LLVM_IR_DEBUGINFOEXPRS_H
+
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/BinaryFormat/Dwarf.h"
+#include "llvm/IR/DbgVariableFragmentInfo.h"
+#include "llvm/IR/DebugInfoCommon.h"
+#include <cassert>
+#include <climits>
+#include <cstdint>
+#include <optional>
+
+namespace llvm {
+
+class ConstantInt;
+class DIExpression;
+class DIVariable;
+class LLVMContext;
+
+namespace impl {
+class DIExpr;
+} // namespace impl
+class DIExprRef;
+class DIExprBuf;
+
+namespace DIOp {
+
+namespace impl {
+enum class TagT : uint8_t {
+ LLVMEscape = 0,
+#define HANDLE_OP(NAME, ENCODING) NAME
+#define SEPARATOR ,
+#include "llvm/IR/DIOps.def"
+};
+struct CommonInitialSequence {
+ TagT Tag;
+ constexpr CommonInitialSequence(TagT Tag) : Tag(Tag) {}
+};
+} // namespace impl
+
+// Below are the concrete alternative types that a DIOp::Op encapsulates.
+
+class LLVMEscape {
+ impl::CommonInitialSequence CIS = impl::TagT::LLVMEscape;
+ uint32_t Size;
+ const uint64_t *Data;
+
+public:
+ constexpr explicit LLVMEscape(const uint64_t *Data, uint32_t Size)
+ : Size(Size), Data(Data) {}
+ explicit LLVMEscape(ArrayRef<uint64_t> A)
+ : Size(static_cast<uint32_t>(A.size())), Data(A.data()) {}
+ constexpr bool operator==(const LLVMEscape &O) const {
+ return Size == O.Size && Data == O.Data;
+ }
+ static constexpr StringRef getAsmName() { return "DIOpLLVMEscape"; }
+ void toUIntVec(SmallVectorImpl<uint64_t> &Out) const;
+ ArrayRef<uint64_t> getData() const { return {Data, Size}; }
+};
+
+#define HANDLE_OP0(NAME, ENCODING) \
+ class NAME { \
+ impl::CommonInitialSequence CIS = impl::TagT::NAME; \
+ \
+ public: \
+ explicit constexpr NAME() {} \
+ bool operator==(const NAME &O) const; \
+ void toUIntVec(SmallVectorImpl<uint64_t> &Out) const; \
+ static StringRef getAsmName(); \
+ static uint64_t getDwarfEncoding(); \
+ };
+#define HANDLE_OP1(NAME, ENCODING, TYPE1, NAME1) \
+ class NAME { \
+ impl::CommonInitialSequence CIS = impl::TagT::NAME; \
+ TYPE1 NAME1; \
+ \
+ public: \
+ explicit constexpr NAME(TYPE1 NAME1) : NAME1(NAME1) {} \
+ bool operator==(const NAME &O) const; \
+ void toUIntVec(SmallVectorImpl<uint64_t> &Out) const; \
+ TYPE1 get##NAME1() const; \
+ void set##NAME1(TYPE1 NAME1); \
+ static StringRef getAsmName(); \
+ static uint64_t getDwarfEncoding(); \
+ };
+#define HANDLE_OP2(NAME, ENCODING, TYPE1, NAME1, TYPE2, NAME2) \
+ class NAME { \
+ impl::CommonInitialSequence CIS = impl::TagT::NAME; \
+ TYPE1 NAME1; \
+ TYPE2 NAME2; \
+ \
+ public: \
+ explicit constexpr NAME(TYPE1 NAME1, TYPE2 NAME2) \
+ : NAME1(NAME1), NAME2(NAME2) {} \
+ bool operator==(const NAME &O) const; \
+ void toUIntVec(SmallVectorImpl<uint64_t> &Out) const; \
+ TYPE1 get##NAME1() const; \
+ void set##NAME1(TYPE1 NAME1); \
+ TYPE2 get##NAME2() const; \
+ void set##NAME2(TYPE2 NAME2); \
+ static StringRef getAsmName(); \
+ static uint64_t getDwarfEncoding(); \
+ };
+#include "llvm/IR/DIOps.def"
+
+class Op {
+ union {
+ impl::CommonInitialSequence CIS;
+#define HANDLE_OP(NAME, ENCODING) DIOp::NAME NAME;
+ HANDLE_OP(LLVMEscape, void)
+#include "llvm/IR/DIOps.def"
+ };
+
+public:
+#define HANDLE_OP(NAME, ENCODING) \
+ constexpr Op(DIOp::NAME V) : NAME(V) {}
+ HANDLE_OP(LLVMEscape, void)
+#include "llvm/IR/DIOps.def"
+
+ template <typename T> bool holds() const;
+ template <typename... T> bool holdsOneOf() const {
+ return (holds<T>() || ...);
+ }
+ template <typename T> T get() const;
+ template <typename T> std::optional<T> getIf() const;
+
+ template <typename... CallableTs>
+ decltype(auto) visitOverload(CallableTs &&...Callables) const;
+ template <typename R, typename... CallableTs>
+ R visitOverload(CallableTs &&...Callables) const;
+ void toUIntVec(SmallVectorImpl<uint64_t> &Out) const;
+};
+#define HANDLE_OP(NAME, ENCODING) \
+ template <> bool Op::holds<DIOp::NAME>() const; \
+ template <> DIOp::NAME Op::get<DIOp::NAME>() const; \
+ template <> std::optional<DIOp::NAME> Op::getIf<DIOp::NAME>() const;
+#include "llvm/IR/DIOps.def"
+
+template <typename... CallableTs>
+inline decltype(auto) Op::visitOverload(CallableTs &&...Callables) const {
+ auto Visitor = makeVisitor(std::forward<CallableTs>(Callables)...);
+ switch (CIS.Tag) {
+#define HANDLE_OP(NAME, ENCODING) \
+ case impl::TagT::NAME: \
+ return Visitor(get<DIOp::NAME>());
+ HANDLE_OP(LLVMEscape, void)
+#include "llvm/IR/DIOps.def"
+ }
+ llvm_unreachable("DIOp::visitOverload does not handle all tags");
+}
+template <typename R, typename... CallableTs>
+inline R Op::visitOverload(CallableTs &&...Callables) const {
+ auto Visitor = makeVisitor(std::forward<CallableTs>(Callables)...);
+ switch (CIS.Tag) {
+#define HANDLE_OP(NAME, ENCODING) \
+ case impl::TagT::NAME: \
+ return Visitor(get<DIOp::NAME>());
+ HANDLE_OP(LLVMEscape, void)
+#include "llvm/IR/DIOps.def"
+ }
+ llvm_unreachable("DIOp::visitOverload does not handle all tags");
+}
+
+class FromUIntIterator
+ : public iterator_facade_base<FromUIntIterator, std::forward_iterator_tag,
+ DIOp::Op, std::ptrdiff_t, DIOp::Op,
+ DIOp::Op> {
+ friend class llvm::impl::DIExpr;
+ // Each iterator knows the End so we can transparently yield an LLVMEscape
+ // of all remaining ops if we encounter an invalid expression. We don't
+ // keep a lot of these iterators around, so the doubling in size shouldn't
+ // be significant. If it ever becomes an issue, we could explore using
+ // the "sentinel" support from RangeTS when/if that becomes available to
+ // make the actual end() an empty struct.
+ const uint64_t *I = nullptr;
+ const uint64_t *End = nullptr;
+
+ uint32_t getRemainingSize() const;
+ uint32_t getCurrentOpSize() const;
+
+ struct ArrowProxy {
+ Op O;
+ Op *operator->() { return &O; }
+ };
+
+public:
+ static iterator_range<FromUIntIterator> makeRange(ArrayRef<uint64_t> From) {
+ return {FromUIntIterator(From.begin(), From.end()),
+ FromUIntIterator(From.end(), From.end())};
+ }
+ FromUIntIterator(const uint64_t *Op, const uint64_t *End) : I(Op), End(End) {}
+ FromUIntIterator(const FromUIntIterator &R) : I(R.I), End(R.End) {}
+ FromUIntIterator &operator=(const FromUIntIterator &R) {
+ I = R.I;
+ End = R.End;
+ return *this;
+ }
+ bool operator==(const FromUIntIterator &R) const {
+ return I == R.I && End == R.End;
+ }
+ Op operator*() const;
+ ArrowProxy operator->() const { return ArrowProxy{**this}; }
+ FromUIntIterator &operator++() {
+ I += getCurrentOpSize();
+ return *this;
+ }
+ FromUIntIterator operator++(int) {
+ auto O = *this;
+ I += getCurrentOpSize();
+ return O;
+ }
+};
+
+} // namespace DIOp
+
+class DIExprRef {
+ friend class impl::DIExpr;
+ friend class DIExprBuf;
+ iterator_range<DIOp::FromUIntIterator> Ops;
+
+ explicit DIExprRef(iterator_range<DIOp::FromUIntIterator> Ops) : Ops(Ops) {};
+ explicit DIExprRef(DIOp::FromUIntIterator Begin, DIOp::FromUIntIterator End)
+ : Ops(make_range(Begin, End)) {};
+
+ std::optional<DIOp::Op> maybeAdvance(DIOp::FromUIntIterator &I) const {
+ if (I != Ops.end())
+ return *I++;
+ return std::nullopt;
+ };
+ std::optional<DIExprRef> getSingleLocationExprRef() const;
+
+public:
+ explicit DIExprRef(const DIExpression *From);
+
+ using ExtOps = std::array<DIOp::Op, 2>;
+
+ /// Returns the ops for a zero- or sign-extension in a DIExpression.
+ static ExtOps getExtOps(unsigned FromSize, unsigned ToSize, bool Signed);
+
+ std::optional<DbgVariableFragmentInfo> getFragmentInfo() const;
+ bool isValid() const;
+ bool isSingleLocationExpression() const;
+ bool startsWithDeref() const;
+ bool isDeref() const;
+ bool isImplicit() const;
+ bool isComplex() const;
+ bool isEntryValue() const;
+ std::optional<SignedOrUnsignedConstant> isConstant() const;
+ uint64_t getNumLocationOperands() const;
+ std::optional<uint64_t> getActiveBits(DIVariable *Var) const;
+ std::optional<int64_t> extractIfOffset() const;
+ std::optional<std::pair<int64_t, DIExprRef>> extractLeadingOffset() const;
+ bool hasAllLocationOps(unsigned N) const;
+ void toUIntVec(SmallVectorImpl<uint64_t> &Out) const;
+ SmallVector<uint64_t> toUIntVec() const;
+};
+
+class DIExprBuf {
+ friend class impl::DIExpr;
+ LLVMContext *Ctx = nullptr;
+ SmallVector<uint64_t, 0> Elements;
+
+ // Nearly all operations require double-buffering, so we bake it in.
+ // This allows us to re-use a small number of allocations for the
+ // processing of many expressions, even where each expression may require
+ // multiple operations.
+ //
+ // Each method has as an implicit post-condition that the backing buffer
+ // NewElements is empty, and so on entry it can be used without being cleared.
+ SmallVector<uint64_t, 0> NewElements;
+ // Helper for failure paths in fallible methods to clear backing buffer.
+ bool drop() {
+ NewElements.clear();
+ return false;
+ }
+ // Helper for success paths to swap buffers and clear new backing buffer.
+ DIExprBuf &swap() {
+ std::swap(Elements, NewElements);
+ drop();
+ return *this;
+ }
+
+ iterator_range<DIOp::FromUIntIterator> ops() {
+ return DIOp::FromUIntIterator::makeRange(Elements);
+ }
+
+ DIExprBuf &prependOpcodesFinalize(bool StackValue, bool EntryValue);
+
+public:
+ DIExprBuf() = default;
+ explicit DIExprBuf(LLVMContext *Ctx);
+ explicit DIExprBuf(const DIExpression *From);
+
+ /// Clear the buffer and assign From into it as-if it were being newly
+ /// constructed. Useful where many expressions are manipulated to amortize
+ /// allocation costs.
+ DIExprBuf &assign(const DIExpression *From);
+ DIExprBuf &assign(LLVMContext *Ctx, DIExprRef From);
+
+ DIExprRef asRef() const {
+ return DIExprRef(DIOp::FromUIntIterator::makeRange(Elements));
+ }
+
+ /*
+ static DIExprBuf canonicalize(LLVMContext *Ctx, DIExprRef From,
+ bool IsIndirect);
+ static DIExprBuf canonicalize(const DIExpression *From, bool IsIndirect);
+ */
+
+ DIExprBuf &appendRaw(iterator_range<const DIOp::Op *> NewOps);
+ DIExprBuf &appendRaw(std::initializer_list<DIOp::Op> NewOps);
+
+ DIExprBuf &clear();
+ DIExprBuf &convertToUndefExpression();
+ DIExprBuf &convertToVariadicExpressionUnchecked();
+ DIExprBuf &convertToVariadicExpression();
+ bool convertToNonVariadicExpression();
+ DIExprBuf &prepend(uint8_t Flags, int64_t Offset = 0);
+
+ DIExprBuf &append(iterator_range<const DIOp::Op *> NewOps);
+ DIExprBuf &append(std::initializer_list<DIOp::Op> NewOps);
+ DIExprBuf &append(DIExprRef NewOps);
+ DIExprBuf &append(iterator_range<DIOp::FromUIntIterator> NewOps);
+
+ DIExprBuf &prependOpcodes(iterator_range<const DIOp::Op *> NewOps,
+ bool StackValue = false, bool EntryValue = false);
+ DIExprBuf &prependOpcodes(std::initializer_list<DIOp::Op> NewOps,
+ bool StackValue = false, bool EntryValue = false);
+ DIExprBuf &prependOpcodes(DIExprRef NewOps, bool StackValue = false,
+ bool EntryValue = false);
+ DIExprBuf &prependOpcodes(iterator_range<DIOp::FromUIntIterator> NewOps,
+ bool StackValue = false, bool EntryValue = false);
+
+ DIExprBuf &appendOpsToArg(iterator_range<const DIOp::Op *> NewOps,
+ unsigned ArgIndex, bool StackValue = false);
+ DIExprBuf &appendOpsToArg(std::initializer_list<DIOp::Op> NewOps,
+ unsigned ArgIndex, bool StackValue = false);
+ DIExprBuf &appendOpsToArg(DIExprRef NewOps, unsigned ArgIndex,
+ bool StackValue = false);
+ DIExprBuf &appendOpsToArg(iterator_range<DIOp::FromUIntIterator> NewOps,
+ unsigned ArgIndex, bool StackValue = false);
+
+ DIExprBuf &appendConstant(SignedOrUnsignedConstant SignedOrUnsigned,
+ uint64_t Value);
+ DIExprBuf &appendOffset(int64_t Offset);
+
+ DIExprBuf &replaceArg(uint64_t OldArgIndex, uint64_t NewArgIndex);
+ bool createFragmentExpression(unsigned OffsetInBits, unsigned SizeInBits);
+
+ const ConstantInt *constantFold(const ConstantInt *CI);
+
+ DIExprBuf &foldConstantMath();
+
+ void toUIntVec(SmallVectorImpl<uint64_t> &Out) const;
+ SmallVector<uint64_t> toUIntVec() const;
+
+ DIExpression *toExpr() const;
+};
+
+} // namespace llvm
+
+#endif // LLVM_IR_DEBUGINFOEXPRS_H
diff --git a/llvm/lib/IR/CMakeLists.txt b/llvm/lib/IR/CMakeLists.txt
index f13f39e8df60c..32efb0cd8e62f 100644
--- a/llvm/lib/IR/CMakeLists.txt
+++ b/llvm/lib/IR/CMakeLists.txt
@@ -19,6 +19,7 @@ add_llvm_component_library(LLVMCore
DataLayout.cpp
DebugInfo.cpp
DebugInfoCommon.cpp
+ DebugInfoExprs.cpp
DebugInfoMetadata.cpp
DIExpressionOptimizer.cpp
DebugProgramInstruction.cpp
diff --git a/llvm/lib/IR/DebugInfoExprs.cpp b/llvm/lib/IR/DebugInfoExprs.cpp
new file mode 100644
index 0000000000000..c8120f25f3642
--- /dev/null
+++ b/llvm/lib/IR/DebugInfoExprs.cpp
@@ -0,0 +1,879 @@
+//===- DebugInfoExprs.h - Debug Info Expression Manipulation ----*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements DebugInfoExprs.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/IR/DebugInfoExprs.h"
+#include "llvm/BinaryFormat/Dwarf.h"
+#include "llvm/IR/DebugInfoMetadata.h"
+#include "llvm/IR/Function.h"
+#include "llvm/IR/Type.h"
+#include "llvm/IR/Value.h"
+
+#include <optional>
+
+using namespace llvm;
+
+namespace llvm {
+
+namespace DIOp {
+
+#define HANDLE_OP0(NAME, ENCODING) \
+ bool NAME::operator==(const NAME &O) const { return true; } \
+ void NAME::toUIntVec(SmallVectorImpl<uint64_t> &Out) const { \
+ Out.append({ENCODING}); \
+ }
+#define HANDLE_OP1(NAME, ENCODING, TYPE1, NAME1) \
+ bool NAME::operator==(const NAME &O) const { return NAME1 == O.NAME1; } \
+ void NAME::toUIntVec(SmallVectorImpl<uint64_t> &Out) const { \
+ Out.append({ENCODING, static_cast<uint64_t>(NAME1)}); \
+ } \
+ TYPE1 NAME::get##NAME1() const { return NAME1; } \
+ void NAME::set##NAME1(TYPE1 NAME1) { this->NAME1 = NAME1; }
+#define HANDLE_OP2(NAME, ENCODING, TYPE1, NAME1, TYPE2, NAME2) \
+ bool NAME::operator==(const NAME &O) const { \
+ return NAME1 == O.NAME1 && NAME2 == O.NAME2; \
+ } \
+ void NAME::toUIntVec(SmallVectorImpl<uint64_t> &Out) const { \
+ Out.append({ENCODING, static_cast<uint64_t>(NAME1), \
+ static_cast<uint64_t>(NAME2)}); \
+ } \
+ TYPE1 NAME::get##NAME1() const { return NAME1; } \
+ void NAME::set##NAME1(TYPE1 NAME1) { this->NAME1 = NAME1; } \
+ TYPE2 NAME::get##NAME2() const { return NAME2; } \
+ void NAME::set##NAME2(TYPE2 NAME2) { this->NAME2 = NAME2; }
+#include "llvm/IR/DIOps.def"
+
+#define HANDLE_OP(NAME, ENCODING) \
+ StringRef NAME::getAsmName() { return "DIOp" #NAME; } \
+ uint64_t NAME::getDwarfEncoding() { return ENCODING; }
+#include "llvm/IR/DIOps.def"
+
+#define HANDLE_OP(NAME, ENCODING) \
+ template <> bool Op::holds<DIOp::NAME>() const { \
+ return CIS.Tag == impl::TagT::NAME; \
+ } \
+ template <> DIOp::NAME Op::get<DIOp::NAME>() const { \
+ assert(holds<DIOp::NAME>()); \
+ return NAME; \
+ } \
+ template <> std::optional<DIOp::NAME> Op::getIf<DIOp::NAME>() const { \
+ return (holds<DIOp::NAME>()) ? std::optional<DIOp::NAME>(NAME) \
+ : std::nullopt; \
+ }
+HANDLE_OP(LLVMEscape, void)
+#include "llvm/IR/DIOps.def"
+
+// The standard_layout is required to make use of the "Common Initial Sequence"
+// rule, so that we can pack the discriminator "tag" into each alternative type.
+//
+// The trivially_destructible is a necessary (although perhaps not quite
+// sufficient?) condition for the way we use a union, and it also enables
+// cheaper versions of e.g. SmallVector methods.
+//
+// Finally the size check is a sanity check to make sure we don't accidentally
+// balloon the struct. We are relying on the no-more-than-two-qword size to
+// sneak into purely register passing on e.g. Itanium x86_64, and any increase
+// also has a multiplicative effect on the size of buffers of Ops.
+#define ASSERT_REQUIREMENTS(NAME) \
+ static_assert(std::is_standard_layout<NAME>::value); \
+ static_assert(std::is_trivially_destructible<NAME>::value); \
+ static_assert(sizeof(NAME) <= 16);
+#define HANDLE_OP(NAME, ENCODING) ASSERT_REQUIREMENTS(NAME)
+HANDLE_OP(LLVMEscape, void)
+#include "llvm/IR/DIOps.def"
+ASSERT_REQUIREMENTS(Op)
+#undef ASSERT_REQUIREMENTS
+
+} // end namespace DIOp
+
+uint32_t DIOp::FromUIntIterator::getRemainingSize() const {
+ assert(End > I);
+ assert(End - I < std::numeric_limits<uint32_t>::max());
+ return static_cast<uint32_t>(End - I);
+}
+
+uint32_t DIOp::FromUIntIterator::getCurrentOpSize() const {
+ const uint32_t RemainingSize = getRemainingSize();
+ uint32_t Ret;
+ switch (*I) {
+#define HANDLE_OP0(NAME, ENCODING) \
+ case ENCODING: \
+ Ret = 1u; \
+ break;
+#define HANDLE_OP1(NAME, ENCODING, ...) \
+ case ENCODING: \
+ Ret = 2u; \
+ break;
+#define HANDLE_OP2(NAME, ENCODING, ...) \
+ case ENCODING: \
+ Ret = 3u; \
+ break;
+#include "llvm/IR/DIOps.def"
+ default:
+ Ret = RemainingSize;
+ }
+ if (Ret > RemainingSize)
+ return RemainingSize;
+ return Ret;
+}
+
+DIOp::Op DIOp::FromUIntIterator::operator*() const {
+ const uint32_t RemainingSize = getRemainingSize();
+ switch (*I) {
+#define HANDLE_OP0(NAME, ENCODING) \
+ case ENCODING: \
+ return DIOp::NAME();
+#define HANDLE_OP1(NAME, ENCODING, ...) \
+ case ENCODING: \
+ if (RemainingSize < 2) \
+ break; \
+ return DIOp::NAME(I[1]);
+#define HANDLE_OP2(NAME, ENCODING, ...) \
+ case ENCODING: \
+ if (RemainingSize < 3) \
+ break; \
+ return DIOp::NAME(I[1], I[2]);
+#include "llvm/IR/DIOps.def"
+ }
+ return LLVMEscape(I, RemainingSize);
+}
+
+void DIOp::LLVMEscape::toUIntVec(SmallVectorImpl<uint64_t> &Out) const {
+ auto Data = getData();
+ Out.append(Data.begin(), Data.end());
+}
+
+void DIOp::Op::toUIntVec(SmallVectorImpl<uint64_t> &Out) const {
+ visitOverload([&](auto Op) { Op.toUIntVec(Out); });
+}
+
+DIExprRef::DIExprRef(const DIExpression *From)
+ : Ops(DIOp::FromUIntIterator::makeRange(From->getElements())) {}
+
+DIExprRef::ExtOps DIExprRef::getExtOps(unsigned FromSize, unsigned ToSize,
+ bool Signed) {
+ dwarf::TypeKind TK = Signed ? dwarf::DW_ATE_signed : dwarf::DW_ATE_unsigned;
+ return {DIOp::LLVMConvert(FromSize, TK), DIOp::LLVMConvert(ToSize, TK)};
+}
+
+std::optional<DbgVariableFragmentInfo> DIExprRef::getFragmentInfo() const {
+ auto I =
+ find_if(Ops, [](DIOp::Op Op) { return Op.holds<DIOp::LLVMFragment>(); });
+ if (I == Ops.end())
+ return std::nullopt;
+ assert(std::next(I) == Ops.end());
+ auto Fragment = I->get<DIOp::LLVMFragment>();
+ return DbgVariableFragmentInfo{Fragment.getSizeInBits(),
+ Fragment.getOffsetInBits()};
+}
+
+bool DIExprRef::isValid() const {
+ for (auto I = Ops.begin(), E = Ops.end(); I != E; ++I) {
+ auto IsValid = I->visitOverload(
+ [=](DIOp::LLVMFragment Fragment) { return std::next(I) == Ops.end(); },
+ [=](DIOp::StackValue StackValue) {
+ // Must be the last one or followed by a
+ // DW_OP_LLVM_fragment.
+ if (std::next(I) == Ops.end())
+ return true;
+ if (std::next(I)->holds<DIOp::LLVMFragment>())
+ return true;
+ return false;
+ },
+ [=](DIOp::Swap Swap) { return std::next(Ops.begin()) != Ops.end(); },
+ [=](DIOp::LLVMEntryValue EntryValue) {
+ // An entry value operator must appear at the
+ // beginning or immediately following `DW_OP_LLVM_arg
+ // 0`, and the number of operations it cover can
+ // currently only be 1, because we support only entry
+ // values of a simple register location. One reason
+ // for this is that we currently can't calculate the
+ // size of the resulting DWARF block for other
+ // expressions.
+ auto J = Ops.begin();
+ if (auto Arg = J->getIf<DIOp::LLVMArg>())
+ if (Arg->getIndex() == 0)
+ ++J;
+ return I == J && EntryValue.getOps() == 1;
+ },
+ [=](DIOp::LLVMEscape Op) { return false; },
+ [=](auto Op) { return true; });
+ if (!IsValid)
+ return false;
+ }
+ return true;
+}
+
+bool DIExprRef::isSingleLocationExpression() const {
+ if (!isValid())
+ return false;
+
+ if (Ops.empty())
+ return true;
+
+ auto I = Ops.begin();
+ if (auto Arg = I->getIf<DIOp::LLVMArg>()) {
+ if (Arg->getIndex() != 0u)
+ return false;
+ ++I;
+ }
+
+ return !std::any_of(I, Ops.end(),
+ [](DIOp::Op Op) { return Op.holds<DIOp::LLVMArg>(); });
+}
+
+std::optional<DIExprRef> DIExprRef::getSingleLocationExprRef() const {
+ // Check for `isValid` covered by `isSingleLocationExpression`.
+ if (!isSingleLocationExpression())
+ return std::nullopt;
+
+ if (Ops.empty())
+ return *this;
+
+ if (Ops.begin()->holds<DIOp::LLVMArg>())
+ return DIExprRef{std::next(Ops.begin()), Ops.end()};
+ return *this;
+}
+
+bool DIExprRef::startsWithDeref() const {
+ if (auto SingleLocRef = getSingleLocationExprRef())
+ return !SingleLocRef->Ops.empty() &&
+ SingleLocRef->Ops.begin()->holds<DIOp::Deref>();
+ return false;
+}
+
+bool DIExprRef::isDeref() const {
+ if (auto SingleLocRef = getSingleLocationExprRef())
+ return !SingleLocRef->Ops.empty() &&
+ std::next(SingleLocRef->Ops.begin()) == SingleLocRef->Ops.end() &&
+ SingleLocRef->Ops.begin()->holds<DIOp::Deref>();
+ return false;
+}
+
+bool DIExprRef::isImplicit() const {
+ return isValid() && find_if(Ops, [](DIOp::Op Op) {
+ return Op.holds<DIOp::StackValue>();
+ }) != Ops.end();
+}
+
+bool DIExprRef::isComplex() const {
+ // If there are any elements other than fragment or tag_offset, then some
+ // kind of complex computation occurs.
+ return isValid() && find_if_not(Ops, [](DIOp::Op Op) {
+ return Op.holds<DIOp::LLVMTagOffset>() ||
+ Op.holds<DIOp::LLVMFragment>() ||
+ Op.holds<DIOp::LLVMArg>();
+ }) != Ops.end();
+}
+
+bool DIExprRef::isEntryValue() const {
+ if (auto SingleLocRef = getSingleLocationExprRef())
+ return !SingleLocRef->Ops.empty() &&
+ SingleLocRef->Ops.begin()->holds<DIOp::LLVMEntryValue>();
+ return false;
+}
+
+std::optional<SignedOrUnsignedConstant> DIExprRef::isConstant() const {
+ // Recognize signed and unsigned constants.
+ // An signed constants can be represented as:
+ // DIOp::ConstS(N) (DIOp::StackValue (DIOp::Fragment(O, S)))
+ // An unsigned constant can be represented as:
+ // DIOp::ConstU(N) (DIOp::StackValue (DIOp::Fragment(O, S)))
+
+ auto I = Ops.begin();
+ auto Op0 = maybeAdvance(I);
+ auto Op1 = maybeAdvance(I);
+ auto Op2 = maybeAdvance(I);
+
+ if (I != Ops.end())
+ return std::nullopt;
+
+ if (!Op0 || (!Op0->holds<DIOp::ConstU>() && !Op0->holds<DIOp::ConstS>()))
+ return std::nullopt;
+
+ if (Op1 && !Op1->holds<DIOp::StackValue>())
+ return std::nullopt;
+
+ if (Op2 && !Op2->holds<DIOp::LLVMFragment>())
+ return std::nullopt;
+
+ return Op0->holds<DIOp::ConstU>() ? SignedOrUnsignedConstant::UnsignedConstant
+ : SignedOrUnsignedConstant::SignedConstant;
+}
+
+uint64_t DIExprRef::getNumLocationOperands() const {
+ uint64_t Result = 0;
+ for (auto Op : Ops)
+ if (auto Arg = Op.getIf<DIOp::LLVMArg>())
+ Result = std::max(Result, Arg->getIndex() + 1);
+ /*FIXME:
+ assert(hasAllLocationOps(Result) &&
+ "Expression is missing one or more location operands.");
+ */
+ return Result;
+}
+
+std::optional<uint64_t> DIExprRef::getActiveBits(DIVariable *Var) const {
+ std::optional<uint64_t> InitialActiveBits = Var->getSizeInBits();
+ std::optional<uint64_t> ActiveBits = InitialActiveBits;
+ auto UpdateActiveBits = [&](uint64_t NewActiveBits) {
+ // Narrow the active bits
+ if (ActiveBits)
+ ActiveBits = std::min(*ActiveBits, NewActiveBits);
+ else
+ ActiveBits = NewActiveBits;
+ };
+ auto HandleExtOp = [&](DIBasicType::Signedness OpSignedness,
+ uint64_t OpActiveBits) {
+ // We can't handle an extract whose sign doesn't match that of the
+ // variable.
+ std::optional<DIBasicType::Signedness> VarSignedness = Var->getSignedness();
+ if (VarSignedness && *VarSignedness == OpSignedness)
+ UpdateActiveBits(OpActiveBits);
+ else
+ ActiveBits = InitialActiveBits;
+ };
+ for (auto Op : Ops)
+ Op.visitOverload(
+ [&](DIOp::LLVMExtractBitsZExt ZExt) {
+ HandleExtOp(DIBasicType::Signedness::Unsigned, ZExt.getSizeInBits());
+ },
+ [&](DIOp::LLVMExtractBitsSExt SExt) {
+ HandleExtOp(DIBasicType::Signedness::Signed, SExt.getSizeInBits());
+ },
+ [&](DIOp::LLVMFragment Fragment) {
+ UpdateActiveBits(Fragment.getSizeInBits());
+ },
+ [&](auto Op) {
+ // We assume the worst case for anything we don't currently
+ // handle and revert to the initial active bits.
+ ActiveBits = InitialActiveBits;
+ });
+ return ActiveBits;
+}
+
+std::optional<int64_t> DIExprRef::extractIfOffset() const {
+ auto SLExprRefOpt = getSingleLocationExprRef();
+ if (!SLExprRefOpt)
+ return std::nullopt;
+ auto SLExprRef = *SLExprRefOpt;
+
+ auto I = SLExprRef.Ops.begin();
+ auto Op0 = maybeAdvance(I);
+ auto Op1 = maybeAdvance(I);
+ if (I != SLExprRef.Ops.end())
+ return std::nullopt;
+
+ if (Op1) {
+ if (auto ConstU = Op0->getIf<DIOp::ConstU>()) {
+ if (Op1->holds<DIOp::Plus>())
+ return ConstU->getValue();
+ if (Op1->holds<DIOp::Minus>())
+ return -ConstU->getValue();
+ }
+ return std::nullopt;
+ }
+
+ if (Op0) {
+ if (auto PlusUConst = Op0->getIf<DIOp::PlusUConst>())
+ return PlusUConst->getValue();
+ return std::nullopt;
+ }
+
+ return 0;
+}
+
+std::optional<std::pair<int64_t, DIExprRef>>
+DIExprRef::extractLeadingOffset() const {
+ int64_t OffsetInBytes = 0u;
+
+ auto SLExprRefOpt = getSingleLocationExprRef();
+ if (!SLExprRefOpt)
+ return std::nullopt;
+ auto SLExprRef = *SLExprRefOpt;
+
+ auto I = SLExprRef.Ops.begin();
+ while (I != SLExprRef.Ops.end()) {
+ if (I->holdsOneOf<DIOp::Deref, DIOp::DerefSize, DIOp::LLVMFragment,
+ DIOp::LLVMExtractBitsZExt, DIOp::LLVMExtractBitsSExt>())
+ break;
+ if (auto P = I->getIf<DIOp::PlusUConst>()) {
+ OffsetInBytes += P->getValue();
+ } else if (auto C = I->getIf<DIOp::ConstU>()) {
+ uint64_t Value = C->getValue();
+ auto J = maybeAdvance(I);
+ if (!J)
+ return std::nullopt;
+ if (J->holds<DIOp::Plus>())
+ OffsetInBytes += Value;
+ else if (J->holds<DIOp::Minus>())
+ OffsetInBytes -= Value;
+ else
+ return std::nullopt;
+ } else {
+ // Not a const plus/minus operation or deref.
+ return std::nullopt;
+ }
+ ++I;
+ }
+ return std::make_pair(OffsetInBytes, DIExprRef{I, SLExprRef.Ops.end()});
+}
+
+bool DIExprRef::hasAllLocationOps(unsigned N) const {
+ SmallDenseSet<uint64_t> SeenIndexes;
+ for (auto Op : Ops)
+ if (auto Arg = Op.getIf<DIOp::LLVMArg>())
+ SeenIndexes.insert(Arg->getIndex());
+ for (uint64_t Index = 0; Index < N; ++Index)
+ if (!SeenIndexes.contains(Index))
+ return false;
+ return true;
+}
+
+void DIExprRef::toUIntVec(SmallVectorImpl<uint64_t> &Out) const {
+ for (auto Op : Ops)
+ Op.toUIntVec(Out);
+}
+SmallVector<uint64_t> DIExprRef::toUIntVec() const {
+ SmallVector<uint64_t> Ops;
+ toUIntVec(Ops);
+ return Ops;
+}
+
+namespace impl {
+class DIExpr {
+public:
+ template <typename OpsT>
+ static void appendRaw(SmallVectorImpl<uint64_t> &Buf, OpsT Ops) {
+ for (auto Op : Ops)
+ Op.toUIntVec(Buf);
+ }
+ template <typename OpsT>
+ static void assignRaw(SmallVectorImpl<uint64_t> &Buf, OpsT Ops) {
+ Buf.clear();
+ appendRaw(Buf, Ops);
+ }
+ template <typename OpsT>
+ static DIExprBuf &append(DIExprBuf &DIBuf, OpsT Ops) {
+ auto ExistingOps = DIOp::FromUIntIterator::makeRange(DIBuf.Elements);
+ auto InsertPoint = find_if(ExistingOps, [](DIOp::Op Op) {
+ return Op.holds<DIOp::StackValue>() || Op.holds<DIOp::LLVMFragment>();
+ });
+ DIBuf.NewElements.append(ExistingOps.begin().I, InsertPoint.I);
+ appendRaw(DIBuf.NewElements, Ops);
+ DIBuf.NewElements.append(InsertPoint.I, ExistingOps.end().I);
+ return DIBuf.swap();
+ }
+ template <typename OpsT>
+ static DIExprBuf &prependOpcodes(DIExprBuf &DIBuf, OpsT Ops, bool StackValue,
+ bool EntryValue) {
+ appendRaw(DIBuf.NewElements, Ops);
+ return DIBuf.prependOpcodesFinalize(StackValue, EntryValue);
+ }
+ template <typename OpsT>
+ static DIExprBuf &appendOpsToArg(DIExprBuf &DIBuf, OpsT Ops,
+ unsigned ArgIndex, bool StackValue = false) {
+ // Handle non-variadic intrinsics by prepending the opcodes.
+ if (!any_of(expr_ops(DIBuf.Elements),
+ [](auto Op) { return Op.getOp() == dwarf::DW_OP_LLVM_arg; })) {
+ assert(ArgIndex == 0 &&
+ "Location Index must be 0 for a non-variadic expression.");
+ appendRaw(DIBuf.NewElements, Ops);
+ return DIBuf.prependOpcodesFinalize(StackValue, false);
+ }
+
+ for (auto Op : expr_ops(DIBuf.Elements)) {
+ // A DW_OP_stack_value comes at the end, but before a DW_OP_LLVM_fragment.
+ if (StackValue) {
+ if (Op.getOp() == dwarf::DW_OP_stack_value)
+ StackValue = false;
+ else if (Op.getOp() == dwarf::DW_OP_LLVM_fragment) {
+ DIBuf.NewElements.push_back(dwarf::DW_OP_stack_value);
+ StackValue = false;
+ }
+ }
+ Op.appendToVector(DIBuf.NewElements);
+ if (Op.getOp() == dwarf::DW_OP_LLVM_arg && Op.getArg(0) == ArgIndex)
+ for (auto Op : Ops)
+ Op.toUIntVec(DIBuf.NewElements);
+ }
+ if (StackValue)
+ DIBuf.NewElements.push_back(dwarf::DW_OP_stack_value);
+
+ return DIBuf.swap();
+ }
+};
+template <>
+void DIExpr::appendRaw<DIExprRef>(SmallVectorImpl<uint64_t> &Buf,
+ DIExprRef Ops) {
+ Buf.append(Ops.Ops.begin().I, Ops.Ops.end().I);
+}
+template <>
+void DIExpr::appendRaw<const DIExpression *>(SmallVectorImpl<uint64_t> &Buf,
+ const DIExpression *Ops) {
+ Buf.append(Ops->elements_begin(), Ops->elements_end());
+}
+} // end namespace impl
+} // end namespace llvm
+
+DIExprBuf &DIExprBuf::prependOpcodesFinalize(bool StackValue, bool EntryValue) {
+ if (EntryValue) {
+ NewElements.push_back(dwarf::DW_OP_LLVM_entry_value);
+ // Use a block size of 1 for the target register operand. The
+ // DWARF backend currently cannot emit entry values with a block
+ // size > 1.
+ NewElements.push_back(1);
+ }
+ // If there are no ops to prepend, do not even add the DW_OP_stack_value.
+ if (NewElements.empty())
+ StackValue = false;
+ for (auto Op : expr_ops(Elements)) {
+ // A DW_OP_stack_value comes at the end, but before a DW_OP_LLVM_fragment.
+ if (StackValue) {
+ if (Op.getOp() == dwarf::DW_OP_stack_value)
+ StackValue = false;
+ else if (Op.getOp() == dwarf::DW_OP_LLVM_fragment) {
+ NewElements.push_back(dwarf::DW_OP_stack_value);
+ StackValue = false;
+ }
+ }
+ Op.appendToVector(NewElements);
+ }
+ if (StackValue)
+ NewElements.push_back(dwarf::DW_OP_stack_value);
+ return swap();
+}
+
+DIExprBuf::DIExprBuf(LLVMContext *Ctx) : Ctx(Ctx) {}
+DIExprBuf::DIExprBuf(const DIExpression *From)
+ : Ctx(&From->getContext()), Elements(From->getElements()) {}
+
+DIExprBuf &DIExprBuf::assign(const DIExpression *From) {
+ Ctx = &From->getContext();
+ impl::DIExpr::assignRaw(Elements, From);
+ return *this;
+}
+
+DIExprBuf &DIExprBuf::assign(LLVMContext *Ctx, DIExprRef From) {
+ this->Ctx = Ctx;
+ impl::DIExpr::assignRaw(Elements, From);
+ return *this;
+}
+
+/*
+DIExprBuf DIExprBuf::canonicalize(LLVMContext *Ctx, DIExprRef From,
+ bool IsIndirect) {
+bool NeedsDeref = IsIndirect;
+DIExprBuf Result(Ctx);
+
+if (!any_of(From.Ops, [](DIOp::Op Op) { return Op.holds<DIOp::LLVMArg>(); }))
+Result.Ops.emplace_back(DIOp::LLVMArg(0u));
+for (auto I = From.Ops.begin(), E = From.Ops.end(); I != E; ++I) {
+if (NeedsDeref &&
+ (I->holds<DIOp::StackValue>() || I->holds<DIOp::LLVMFragment>())) {
+ Result.Ops.emplace_back(DIOp::Deref());
+ NeedsDeref = false;
+}
+Result.Ops.emplace_back(*I);
+}
+if (NeedsDeref)
+Result.Ops.emplace_back(DIOp::Deref());
+return Result;
+}
+
+DIExprBuf DIExprBuf::canonicalize(const DIExpression *From, bool IsIndirect) {
+return DIExprBuf::canonicalize(&From->getContext(), From->asRef(),
+ IsIndirect);
+}
+*/
+
+DIExprBuf &DIExprBuf::appendRaw(std::initializer_list<DIOp::Op> NewOps) {
+ impl::DIExpr::appendRaw(Elements, NewOps);
+ return *this;
+}
+DIExprBuf &DIExprBuf::appendRaw(iterator_range<const DIOp::Op *> NewOps) {
+ impl::DIExpr::appendRaw(Elements, NewOps);
+ return *this;
+}
+
+DIExprBuf &DIExprBuf::clear() {
+ Elements.clear();
+ return *this;
+}
+
+DIExprBuf &DIExprBuf::convertToUndefExpression() {
+ if (auto FragmentInfo = asRef().getFragmentInfo())
+ NewElements.append({dwarf::DW_OP_LLVM_fragment, FragmentInfo->OffsetInBits,
+ FragmentInfo->SizeInBits});
+ return swap();
+}
+
+DIExprBuf &DIExprBuf::convertToVariadicExpressionUnchecked() {
+ NewElements.reserve(Elements.size() + 2);
+ NewElements.append({dwarf::DW_OP_LLVM_arg, 0});
+ NewElements.append(Elements);
+ return swap();
+}
+
+DIExprBuf &DIExprBuf::convertToVariadicExpression() {
+ if (any_of(expr_ops(Elements), [](auto ExprOp) {
+ return ExprOp.getOp() == dwarf::DW_OP_LLVM_arg;
+ }))
+ return *this;
+ return convertToVariadicExpressionUnchecked();
+}
+
+bool DIExprBuf::convertToNonVariadicExpression() {
+ auto SingleLocRefOpt = asRef().getSingleLocationExprRef();
+ if (!SingleLocRefOpt)
+ return false;
+ SingleLocRefOpt->toUIntVec(NewElements);
+ swap();
+ return true;
+}
+
+DIExprBuf &DIExprBuf::prepend(uint8_t Flags, int64_t Offset) {
+ if (Flags & DIExpression::DerefBefore)
+ NewElements.push_back(dwarf::DW_OP_deref);
+ appendOffsetImpl(NewElements, Offset);
+ if (Flags & DIExpression::DerefAfter)
+ NewElements.push_back(dwarf::DW_OP_deref);
+ bool StackValue = Flags & DIExpression::StackValue;
+ bool EntryValue = Flags & DIExpression::EntryValue;
+ return prependOpcodesFinalize(StackValue, EntryValue);
+}
+
+DIExprBuf &DIExprBuf::append(iterator_range<const DIOp::Op *> NewOps) {
+ return impl::DIExpr::append(*this, NewOps);
+}
+DIExprBuf &DIExprBuf::append(std::initializer_list<DIOp::Op> NewOps) {
+ return impl::DIExpr::append(*this, NewOps);
+}
+DIExprBuf &DIExprBuf::append(DIExprRef NewOps) {
+ return impl::DIExpr::append(*this, NewOps);
+}
+DIExprBuf &DIExprBuf::append(iterator_range<DIOp::FromUIntIterator> NewOps) {
+ return impl::DIExpr::append(*this, NewOps);
+}
+
+DIExprBuf &DIExprBuf::prependOpcodes(iterator_range<const DIOp::Op *> NewOps,
+ bool StackValue, bool EntryValue) {
+ return impl::DIExpr::prependOpcodes(*this, NewOps, StackValue, EntryValue);
+}
+DIExprBuf &DIExprBuf::prependOpcodes(std::initializer_list<DIOp::Op> NewOps,
+ bool StackValue, bool EntryValue) {
+ return impl::DIExpr::prependOpcodes(*this, NewOps, StackValue, EntryValue);
+}
+DIExprBuf &DIExprBuf::prependOpcodes(DIExprRef NewOps, bool StackValue,
+ bool EntryValue) {
+ return impl::DIExpr::prependOpcodes(*this, NewOps, StackValue, EntryValue);
+}
+DIExprBuf &
+DIExprBuf::prependOpcodes(iterator_range<DIOp::FromUIntIterator> NewOps,
+ bool StackValue, bool EntryValue) {
+ return impl::DIExpr::prependOpcodes(*this, NewOps, StackValue, EntryValue);
+}
+
+DIExprBuf &DIExprBuf::appendOpsToArg(iterator_range<const DIOp::Op *> NewOps,
+ unsigned ArgIndex, bool StackValue) {
+ return impl::DIExpr::appendOpsToArg(*this, NewOps, ArgIndex, StackValue);
+}
+DIExprBuf &DIExprBuf::appendOpsToArg(std::initializer_list<DIOp::Op> NewOps,
+ unsigned ArgIndex, bool StackValue) {
+ return impl::DIExpr::appendOpsToArg(*this, NewOps, ArgIndex, StackValue);
+}
+DIExprBuf &DIExprBuf::appendOpsToArg(DIExprRef NewOps, unsigned ArgIndex,
+ bool StackValue) {
+ return impl::DIExpr::appendOpsToArg(*this, NewOps.Ops, ArgIndex, StackValue);
+}
+DIExprBuf &
+DIExprBuf::appendOpsToArg(iterator_range<DIOp::FromUIntIterator> NewOps,
+ unsigned ArgIndex, bool StackValue) {
+ return impl::DIExpr::appendOpsToArg(*this, NewOps, ArgIndex, StackValue);
+}
+
+DIExprBuf &DIExprBuf::appendConstant(SignedOrUnsignedConstant SignedOrUnsigned,
+ uint64_t Val) {
+ uint64_t Opcode = SignedOrUnsigned == SignedOrUnsignedConstant::SignedConstant
+ ? dwarf::DW_OP_consts
+ : dwarf::DW_OP_constu;
+ Elements.append({Opcode, Val});
+ return *this;
+}
+
+DIExprBuf &DIExprBuf::appendOffset(int64_t Offset) {
+ appendOffsetImpl(Elements, Offset);
+ return *this;
+}
+
+DIExprBuf &DIExprBuf::replaceArg(uint64_t OldArgIndex, uint64_t NewArgIndex) {
+ for (auto Op : expr_ops(Elements)) {
+ if (Op.getOp() != dwarf::DW_OP_LLVM_arg || Op.getArg(0) < OldArgIndex) {
+ Op.appendToVector(NewElements);
+ continue;
+ }
+ NewElements.push_back(dwarf::DW_OP_LLVM_arg);
+ uint64_t ArgIndex =
+ Op.getArg(0) == OldArgIndex ? NewArgIndex : Op.getArg(0);
+ // OldArg has been deleted from the Op list, so decrement all indices
+ // greater than it.
+ if (ArgIndex > OldArgIndex)
+ --ArgIndex;
+ NewElements.push_back(ArgIndex);
+ }
+ return swap();
+}
+
+bool DIExprBuf::createFragmentExpression(unsigned OffsetInBits,
+ unsigned SizeInBits) {
+ // Track whether it's safe to split the value at the top of the DWARF stack,
+ // assuming that it'll be used as an implicit location value.
+ bool CanSplitValue = true;
+ // Track whether we need to add a fragment expression to the end of Expr.
+ bool EmitFragment = true;
+ // Copy over the expression, but leave off any trailing DW_OP_LLVM_fragment.
+ for (auto Op : expr_ops(Elements)) {
+ switch (Op.getOp()) {
+ default:
+ break;
+ case dwarf::DW_OP_shr:
+ case dwarf::DW_OP_shra:
+ case dwarf::DW_OP_shl:
+ case dwarf::DW_OP_plus:
+ case dwarf::DW_OP_plus_uconst:
+ case dwarf::DW_OP_minus:
+ // We can't safely split arithmetic or shift operations into multiple
+ // fragments because we can't express carry-over between fragments.
+ //
+ // FIXME: We *could* preserve the lowest fragment of a constant offset
+ // operation if the offset fits into SizeInBits.
+ CanSplitValue = false;
+ break;
+ case dwarf::DW_OP_deref:
+ case dwarf::DW_OP_deref_size:
+ case dwarf::DW_OP_deref_type:
+ case dwarf::DW_OP_xderef:
+ case dwarf::DW_OP_xderef_size:
+ case dwarf::DW_OP_xderef_type:
+ // Preceeding arithmetic operations have been applied to compute an
+ // address. It's okay to split the value loaded from that address.
+ CanSplitValue = true;
+ break;
+ case dwarf::DW_OP_stack_value:
+ // Bail if this expression computes a value that cannot be split.
+ if (!CanSplitValue)
+ return drop();
+ break;
+ case dwarf::DW_OP_LLVM_fragment: {
+ // If we've decided we don't need a fragment then give up if we see that
+ // there's already a fragment expression.
+ // FIXME: We could probably do better here
+ if (!EmitFragment)
+ return drop();
+ // Make the new offset point into the existing fragment.
+ uint64_t FragmentOffsetInBits = Op.getArg(0);
+ uint64_t FragmentSizeInBits = Op.getArg(1);
+ (void)FragmentSizeInBits;
+ assert((OffsetInBits + SizeInBits <= FragmentSizeInBits) &&
+ "new fragment outside of original fragment");
+ OffsetInBits += FragmentOffsetInBits;
+ continue;
+ }
+ case dwarf::DW_OP_LLVM_extract_bits_zext:
+ case dwarf::DW_OP_LLVM_extract_bits_sext: {
+ // If we're extracting bits from inside of the fragment that we're
+ // creating then we don't have a fragment after all, and just need to
+ // adjust the offset that we're extracting from.
+ uint64_t ExtractOffsetInBits = Op.getArg(0);
+ uint64_t ExtractSizeInBits = Op.getArg(1);
+ if (ExtractOffsetInBits >= OffsetInBits &&
+ ExtractOffsetInBits + ExtractSizeInBits <=
+ OffsetInBits + SizeInBits) {
+ NewElements.push_back(Op.getOp());
+ NewElements.push_back(ExtractOffsetInBits - OffsetInBits);
+ NewElements.push_back(ExtractSizeInBits);
+ EmitFragment = false;
+ continue;
+ }
+ // If the extracted bits aren't fully contained within the fragment then
+ // give up.
+ // FIXME: We could probably do better here
+ return drop();
+ }
+ }
+ Op.appendToVector(NewElements);
+ }
+ if (EmitFragment) {
+ NewElements.push_back(dwarf::DW_OP_LLVM_fragment);
+ NewElements.push_back(OffsetInBits);
+ NewElements.push_back(SizeInBits);
+ }
+ swap();
+ return true;
+}
+
+const ConstantInt *DIExprBuf::constantFold(const ConstantInt *CI) {
+ assert(Ctx && "constantFold called on DIExprBuf without LLVMContext");
+ // Copy the APInt so we can modify it.
+ APInt NewInt = CI->getValue();
+ const auto Ops = ops();
+ const auto Begin = Ops.begin();
+ const auto End = Ops.end();
+ // Attempt to fold Op into NewInt. Returns true if successful, else false.
+ auto MaybeFold = [](APInt &NewInt, DIOp::Op Op) {
+ return Op.visitOverload(
+ [&](DIOp::LLVMConvert Convert) {
+ if (Convert.getEncoding() == dwarf::DW_ATE_signed) {
+ NewInt = NewInt.sextOrTrunc(Convert.getSizeInBits());
+ } else {
+ assert(Convert.getEncoding() == dwarf::DW_ATE_unsigned &&
+ "Unexpected DIOp::LLVMConvert encoding");
+ NewInt = NewInt.zextOrTrunc(Convert.getSizeInBits());
+ }
+ return true;
+ },
+ [=](auto) { return false; });
+ };
+ // We only fold prefixes of the expression, so we stop on the first
+ // thing we cannot fold
+ auto I = Begin;
+ for (; I != End; ++I)
+ if (!MaybeFold(NewInt, *I))
+ break;
+ // Short-circuit if we folded nothing
+ if (I == Begin) {
+ drop();
+ return CI;
+ }
+ // Otherwise use the remainder of the expression
+ impl::DIExpr::appendRaw(NewElements, make_range(I, End));
+ swap();
+ return ConstantInt::get(*Ctx, NewInt);
+}
+
+DIExprBuf &DIExprBuf::foldConstantMath() { return swap(); }
+
+void DIExprBuf::toUIntVec(SmallVectorImpl<uint64_t> &Out) const {
+ asRef().toUIntVec(Out);
+}
+SmallVector<uint64_t> DIExprBuf::toUIntVec() const {
+ return asRef().toUIntVec();
+}
+
+DIExpression *DIExprBuf::toExpr() const {
+ assert(Ctx && "toExpr called on DIExprBuf without LLVMContext");
+ auto *Expr = DIExpression::get(*Ctx, Elements);
+ /*
+ assert(Expr->isValid() &&
+ "toExpr called on DIExprBuf with invalid expression");
+ */
+ return Expr;
+}
>From 77bac0f1a106db4abdbfbe22145cda52c1b695d4 Mon Sep 17 00:00:00 2001
From: Scott Linder <Scott.Linder at amd.com>
Date: Wed, 1 Oct 2025 21:57:15 +0000
Subject: [PATCH 3/4] Add options to use DebugInfoExprs impls in
DebugInfoMetadata
---
llvm/lib/IR/DebugInfoMetadata.cpp | 110 +++++++++++++++++++++++++++---
1 file changed, 99 insertions(+), 11 deletions(-)
diff --git a/llvm/lib/IR/DebugInfoMetadata.cpp b/llvm/lib/IR/DebugInfoMetadata.cpp
index 4a9543488e675..b6197a87362ab 100644
--- a/llvm/lib/IR/DebugInfoMetadata.cpp
+++ b/llvm/lib/IR/DebugInfoMetadata.cpp
@@ -16,6 +16,7 @@
#include "llvm/ADT/SetVector.h"
#include "llvm/ADT/StringSwitch.h"
#include "llvm/BinaryFormat/Dwarf.h"
+#include "llvm/IR/DebugInfoExprs.h"
#include "llvm/IR/DebugProgramInstruction.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/IntrinsicInst.h"
@@ -42,6 +43,32 @@ LLVM_ABI cl::opt<bool> PickMergedSourceLocations(
cl::desc("Preserve line and column number when merging locations."));
} // namespace llvm
+enum class DIExprsFlags {
+ Ref,
+ Buf,
+};
+
+// Enable DIOp-based implementation of many DIExpression methods.
+static cl::bits<DIExprsFlags>
+ DIExprsMode("diexprs-mode", cl::Hidden, cl::CommaSeparated,
+ cl::desc("Enable DIExprs impls of many DIExpression methods."),
+ cl::values(clEnumValN(DIExprsFlags::Ref, "ref",
+ "enable DIExprRef-based impls"),
+ clEnumValN(DIExprsFlags::Buf, "buf",
+ "enable DIExprBuf-based impls")));
+
+#if 1
+static inline bool isDIRefEnabled() {
+ return DIExprsMode.isSet(DIExprsFlags::Ref);
+}
+static inline bool isDIBufEnabled() {
+ return DIExprsMode.isSet(DIExprsFlags::Buf);
+}
+#else
+static inline bool isDIRefEnabled() { return true; }
+static inline bool isDIBufEnabled() { return true; }
+#endif
+
uint32_t DIType::getAlignInBits() const {
return (getTag() == dwarf::DW_TAG_LLVM_ptrauth_type ? 0 : SubclassData32);
}
@@ -1636,6 +1663,8 @@ DIExpression *DIExpression::getImpl(LLVMContext &Context,
DEFINE_GETIMPL_STORE_NO_OPS(DIExpression, (Elements));
}
bool DIExpression::isEntryValue() const {
+ if (isDIRefEnabled())
+ return DIExprRef(this).isEntryValue();
if (auto singleLocElts = getSingleLocationExpressionElements()) {
return singleLocElts->size() > 0 &&
(*singleLocElts)[0] == dwarf::DW_OP_LLVM_entry_value;
@@ -1643,12 +1672,16 @@ bool DIExpression::isEntryValue() const {
return false;
}
bool DIExpression::startsWithDeref() const {
+ if (isDIRefEnabled())
+ return DIExprRef(this).startsWithDeref();
if (auto singleLocElts = getSingleLocationExpressionElements())
return singleLocElts->size() > 0 &&
(*singleLocElts)[0] == dwarf::DW_OP_deref;
return false;
}
bool DIExpression::isDeref() const {
+ if (isDIRefEnabled())
+ return DIExprRef(this).isDeref();
if (auto singleLocElts = getSingleLocationExpressionElements())
return singleLocElts->size() == 1 &&
(*singleLocElts)[0] == dwarf::DW_OP_deref;
@@ -1663,6 +1696,8 @@ DIAssignID *DIAssignID::getImpl(LLVMContext &Context, StorageType Storage,
}
bool DIExpression::isValid() const {
+ if (isDIRefEnabled())
+ return DIExprRef(this).isValid();
for (auto I = expr_op_begin(), E = expr_op_end(); I != E; ++I) {
// Check that there's space for the operand.
if (I->get() + I->getSize() > E->get())
@@ -1755,6 +1790,8 @@ bool DIExpression::isValid() const {
}
bool DIExpression::isImplicit() const {
+ if (isDIRefEnabled())
+ return DIExprRef(this).isImplicit();
if (!isValid())
return false;
@@ -1774,6 +1811,8 @@ bool DIExpression::isImplicit() const {
}
bool DIExpression::isComplex() const {
+ if (isDIRefEnabled())
+ return DIExprRef(this).isComplex();
if (!isValid())
return false;
@@ -1797,6 +1836,8 @@ bool DIExpression::isComplex() const {
}
bool DIExpression::isSingleLocationExpression() const {
+ if (isDIRefEnabled())
+ return DIExprRef(this).isSingleLocationExpression();
if (!isValid())
return false;
@@ -1835,6 +1876,8 @@ DIExpression::getSingleLocationExpressionElements() const {
const DIExpression *
DIExpression::convertToUndefExpression(const DIExpression *Expr) {
+ if (isDIBufEnabled())
+ return DIExprBuf(Expr).convertToUndefExpression().toExpr();
SmallVector<uint64_t, 3> UndefOps;
if (auto FragmentInfo = Expr->getFragmentInfo()) {
UndefOps.append({dwarf::DW_OP_LLVM_fragment, FragmentInfo->OffsetInBits,
@@ -1845,6 +1888,8 @@ DIExpression::convertToUndefExpression(const DIExpression *Expr) {
const DIExpression *
DIExpression::convertToVariadicExpression(const DIExpression *Expr) {
+ if (isDIBufEnabled())
+ return DIExprBuf(Expr).convertToVariadicExpression().toExpr();
if (any_of(Expr->expr_ops(), [](auto ExprOp) {
return ExprOp.getOp() == dwarf::DW_OP_LLVM_arg;
}))
@@ -1858,6 +1903,12 @@ DIExpression::convertToVariadicExpression(const DIExpression *Expr) {
std::optional<const DIExpression *>
DIExpression::convertToNonVariadicExpression(const DIExpression *Expr) {
+ if (isDIBufEnabled()) {
+ DIExprBuf DIBuf(Expr);
+ if (DIBuf.convertToNonVariadicExpression())
+ return DIBuf.toExpr();
+ return std::nullopt;
+ }
if (!Expr)
return std::nullopt;
@@ -1920,6 +1971,8 @@ DIExpression::getFragmentInfo(expr_op_iterator Start, expr_op_iterator End) {
}
std::optional<uint64_t> DIExpression::getActiveBits(DIVariable *Var) {
+ if (isDIRefEnabled())
+ return DIExprRef(this).getActiveBits(Var);
std::optional<uint64_t> InitialActiveBits = Var->getSizeInBits();
std::optional<uint64_t> ActiveBits = InitialActiveBits;
for (auto Op : expr_ops()) {
@@ -1956,20 +2009,17 @@ std::optional<uint64_t> DIExpression::getActiveBits(DIVariable *Var) {
void DIExpression::appendOffset(SmallVectorImpl<uint64_t> &Ops,
int64_t Offset) {
- if (Offset > 0) {
- Ops.push_back(dwarf::DW_OP_plus_uconst);
- Ops.push_back(Offset);
- } else if (Offset < 0) {
- Ops.push_back(dwarf::DW_OP_constu);
- // Avoid UB when encountering LLONG_MIN, because in 2's complement
- // abs(LLONG_MIN) is LLONG_MAX+1.
- uint64_t AbsMinusOne = -(Offset+1);
- Ops.push_back(AbsMinusOne + 1);
- Ops.push_back(dwarf::DW_OP_minus);
- }
+ appendOffsetImpl(Ops, Offset);
}
bool DIExpression::extractIfOffset(int64_t &Offset) const {
+ if (isDIRefEnabled()) {
+ if (auto OffsetOp = DIExprRef(this).extractIfOffset()) {
+ Offset = *OffsetOp;
+ return true;
+ }
+ return false;
+ }
auto SingleLocEltsOpt = getSingleLocationExpressionElements();
if (!SingleLocEltsOpt)
return false;
@@ -2040,6 +2090,8 @@ bool DIExpression::extractLeadingOffset(
}
bool DIExpression::hasAllLocationOps(unsigned N) const {
+ if (isDIRefEnabled())
+ return DIExprRef(this).hasAllLocationOps(N);
SmallDenseSet<uint64_t, 4> SeenOps;
for (auto ExprOp : expr_ops())
if (ExprOp.getOp() == dwarf::DW_OP_LLVM_arg)
@@ -2077,6 +2129,8 @@ const DIExpression *DIExpression::extractAddressClass(const DIExpression *Expr,
DIExpression *DIExpression::prepend(const DIExpression *Expr, uint8_t Flags,
int64_t Offset) {
+ if (isDIBufEnabled())
+ return DIExprBuf(Expr).prepend(Flags, Offset).toExpr();
SmallVector<uint64_t, 8> Ops;
if (Flags & DIExpression::DerefBefore)
Ops.push_back(dwarf::DW_OP_deref);
@@ -2095,6 +2149,13 @@ DIExpression *DIExpression::appendOpsToArg(const DIExpression *Expr,
ArrayRef<uint64_t> Ops,
unsigned ArgNo, bool StackValue) {
assert(Expr && "Can't add ops to this expression");
+ if (isDIBufEnabled()) {
+ DIExprBuf DIBuf(Expr);
+ return DIBuf
+ .appendOpsToArg(DIOp::FromUIntIterator::makeRange(Ops), ArgNo,
+ StackValue)
+ .toExpr();
+ }
// Handle non-variadic intrinsics by prepending the opcodes.
if (!any_of(Expr->expr_ops(),
@@ -2152,6 +2213,13 @@ DIExpression *DIExpression::prependOpcodes(const DIExpression *Expr,
SmallVectorImpl<uint64_t> &Ops,
bool StackValue, bool EntryValue) {
assert(Expr && "Can't prepend ops to this expression");
+ if (isDIBufEnabled()) {
+ DIExprBuf DIBuf(Expr);
+ return DIBuf
+ .prependOpcodes(DIOp::FromUIntIterator::makeRange(Ops), StackValue,
+ EntryValue)
+ .toExpr();
+ }
if (EntryValue) {
Ops.push_back(dwarf::DW_OP_LLVM_entry_value);
@@ -2184,6 +2252,12 @@ DIExpression *DIExpression::prependOpcodes(const DIExpression *Expr,
DIExpression *DIExpression::append(const DIExpression *Expr,
ArrayRef<uint64_t> Ops) {
assert(Expr && !Ops.empty() && "Can't append ops to this expression");
+ if (isDIBufEnabled()) {
+ DIExprBuf DIBuf(Expr);
+ return DIBuf.append(DIOp::FromUIntIterator::makeRange(Ops))
+ .toExpr()
+ ->foldConstantMath();
+ }
// Copy Expr's current op list.
SmallVector<uint64_t, 16> NewOps;
@@ -2241,6 +2315,12 @@ DIExpression *DIExpression::appendToStack(const DIExpression *Expr,
std::optional<DIExpression *> DIExpression::createFragmentExpression(
const DIExpression *Expr, unsigned OffsetInBits, unsigned SizeInBits) {
+ if (isDIBufEnabled()) {
+ DIExprBuf DIBuf(Expr);
+ if (DIBuf.createFragmentExpression(OffsetInBits, SizeInBits))
+ return DIBuf.toExpr();
+ return std::nullopt;
+ }
SmallVector<uint64_t, 8> Ops;
// Track whether it's safe to split the value at the top of the DWARF stack,
// assuming that it'll be used as an implicit location value.
@@ -2402,6 +2482,12 @@ bool DIExpression::calculateFragmentIntersect(
std::pair<DIExpression *, const ConstantInt *>
DIExpression::constantFold(const ConstantInt *CI) {
+ if (isDIBufEnabled()) {
+ DIExprBuf DIBuf(this);
+ auto *NewInt = DIBuf.constantFold(CI);
+ return {DIBuf.toExpr(), NewInt};
+ }
+
// Copy the APInt so we can modify it.
APInt NewInt = CI->getValue();
SmallVector<uint64_t, 8> Ops;
@@ -2440,6 +2526,8 @@ DIExpression::constantFold(const ConstantInt *CI) {
}
uint64_t DIExpression::getNumLocationOperands() const {
+ if (isDIRefEnabled())
+ return DIExprRef(this).getNumLocationOperands();
uint64_t Result = 0;
for (auto ExprOp : expr_ops())
if (ExprOp.getOp() == dwarf::DW_OP_LLVM_arg)
>From 2dee797b6d08aa65256d4f9e0210d549fdb21627 Mon Sep 17 00:00:00 2001
From: Scott Linder <Scott.Linder at amd.com>
Date: Wed, 1 Oct 2025 22:00:53 +0000
Subject: [PATCH 4/4] Use DebugInfoExprs in SalvageDebugInfo and a few other
places
---
llvm/include/llvm/CodeGen/FastISel.h | 3 +
llvm/include/llvm/Transforms/Utils/Local.h | 5 +-
llvm/lib/CodeGen/MachineInstr.cpp | 13 +-
llvm/lib/CodeGen/SelectionDAG/FastISel.cpp | 5 +
.../SelectionDAG/SelectionDAGBuilder.cpp | 17 +-
llvm/lib/Transforms/Coroutines/CoroFrame.cpp | 26 ++-
llvm/lib/Transforms/Scalar/SROA.cpp | 33 +--
llvm/lib/Transforms/Utils/Local.cpp | 217 ++++++++++--------
8 files changed, 178 insertions(+), 141 deletions(-)
diff --git a/llvm/include/llvm/CodeGen/FastISel.h b/llvm/include/llvm/CodeGen/FastISel.h
index b9d6694a935f6..17f959990c1f4 100644
--- a/llvm/include/llvm/CodeGen/FastISel.h
+++ b/llvm/include/llvm/CodeGen/FastISel.h
@@ -23,6 +23,8 @@
#include "llvm/CodeGenTypes/MachineValueType.h"
#include "llvm/IR/Attributes.h"
#include "llvm/IR/CallingConv.h"
+#include "llvm/IR/DebugInfoExprs.h"
+#include "llvm/IR/DebugInfoMetadata.h"
#include "llvm/IR/DebugLoc.h"
#include "llvm/IR/DerivedTypes.h"
#include "llvm/IR/InstrTypes.h"
@@ -213,6 +215,7 @@ class FastISel {
const TargetRegisterInfo &TRI;
const TargetLibraryInfo *LibInfo;
bool SkipTargetIndependentISel;
+ DIExprBuf DIBuf;
/// The position of the last instruction for materializing constants
/// for use in the current block. It resets to EmitStartPt when it makes sense
diff --git a/llvm/include/llvm/Transforms/Utils/Local.h b/llvm/include/llvm/Transforms/Utils/Local.h
index 3f5f4278a2766..222913cbc2a56 100644
--- a/llvm/include/llvm/Transforms/Utils/Local.h
+++ b/llvm/include/llvm/Transforms/Utils/Local.h
@@ -37,6 +37,7 @@ class BranchInst;
class CallBase;
class CallInst;
class DIBuilder;
+class DIExprBuf;
class DomTreeUpdater;
class Function;
class Instruction;
@@ -349,8 +350,8 @@ salvageDebugInfoForDbgValues(Instruction &I,
/// Ops = llvm::dwarf::DW_OP_LLVM_arg0 llvm::dwarf::DW_OP_add
/// AdditionalValues = %b
LLVM_ABI Value *
-salvageDebugInfoImpl(Instruction &I, uint64_t CurrentLocOps,
- SmallVectorImpl<uint64_t> &Ops,
+salvageDebugInfoImpl(Instruction &I, uint64_t &CurrentLocOps,
+ DIExprBuf &OpsToAppend,
SmallVectorImpl<Value *> &AdditionalValues);
/// Point debug users of \p From to \p To or salvage them. Use this function
diff --git a/llvm/lib/CodeGen/MachineInstr.cpp b/llvm/lib/CodeGen/MachineInstr.cpp
index 8ad9245a47684..66cd9987715d6 100644
--- a/llvm/lib/CodeGen/MachineInstr.cpp
+++ b/llvm/lib/CodeGen/MachineInstr.cpp
@@ -36,6 +36,7 @@
#include "llvm/CodeGen/TargetSubtargetInfo.h"
#include "llvm/CodeGenTypes/LowLevelType.h"
#include "llvm/IR/Constants.h"
+#include "llvm/IR/DebugInfoExprs.h"
#include "llvm/IR/DebugInfoMetadata.h"
#include "llvm/IR/DebugLoc.h"
#include "llvm/IR/Function.h"
@@ -2426,20 +2427,18 @@ static const DIExpression *computeExprForSpill(
"Expected inlined-at fields to agree");
const DIExpression *Expr = MI.getDebugExpression();
+ DIExprBuf DIBuf(Expr);
if (MI.isIndirectDebugValue()) {
assert(MI.getDebugOffset().getImm() == 0 &&
"DBG_VALUE with nonzero offset");
- Expr = DIExpression::prepend(Expr, DIExpression::DerefBefore);
+ DIBuf.prepend(DIExpression::DerefBefore);
} else if (MI.isDebugValueList()) {
// We will replace the spilled register with a frame index, so
// immediately deref all references to the spilled register.
- std::array<uint64_t, 1> Ops{{dwarf::DW_OP_deref}};
- for (const MachineOperand *Op : SpilledOperands) {
- unsigned OpIdx = MI.getDebugOperandIndex(Op);
- Expr = DIExpression::appendOpsToArg(Expr, Ops, OpIdx);
- }
+ for (const MachineOperand *Op : SpilledOperands)
+ DIBuf.appendOpsToArg({DIOp::Deref()}, MI.getDebugOperandIndex(Op));
}
- return Expr;
+ return DIBuf.toExpr();
}
static const DIExpression *computeExprForSpill(const MachineInstr &MI,
Register SpillReg) {
diff --git a/llvm/lib/CodeGen/SelectionDAG/FastISel.cpp b/llvm/lib/CodeGen/SelectionDAG/FastISel.cpp
index 851d445f75fa8..685de04b3d196 100644
--- a/llvm/lib/CodeGen/SelectionDAG/FastISel.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/FastISel.cpp
@@ -1350,9 +1350,14 @@ bool FastISel::lowerDbgDeclare(const Value *Address, DIExpression *Expr,
// If using instruction referencing, produce this as a DBG_INSTR_REF,
// to be later patched up by finalizeDebugInstrRefs. Tack a deref onto
// the expression, we don't have an "indirect" flag in DBG_INSTR_REF.
+ /*
SmallVector<uint64_t, 3> Ops(
{dwarf::DW_OP_LLVM_arg, 0, dwarf::DW_OP_deref});
auto *NewExpr = DIExpression::prependOpcodes(Expr, Ops);
+ */
+ auto *NewExpr = DIBuf.assign(Expr)
+ .prependOpcodes({DIOp::LLVMArg(0), DIOp::Deref()})
+ .toExpr();
BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, DL,
TII.get(TargetOpcode::DBG_INSTR_REF), /*IsIndirect*/ false, *Op,
Var, NewExpr);
diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
index b5201a311c591..0adb607201ef4 100644
--- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
@@ -64,6 +64,7 @@
#include "llvm/IR/Constants.h"
#include "llvm/IR/DataLayout.h"
#include "llvm/IR/DebugInfo.h"
+#include "llvm/IR/DebugInfoExprs.h"
#include "llvm/IR/DebugInfoMetadata.h"
#include "llvm/IR/DerivedTypes.h"
#include "llvm/IR/DiagnosticInfo.h"
@@ -1530,17 +1531,20 @@ void SelectionDAGBuilder::salvageUnresolvedDbgValue(const Value *V,
if (handleDebugValue(V, Var, Expr, DL, SDOrder, /*IsVariadic=*/false))
return;
+ DIExprBuf DIBuf(Expr);
+ DIExprBuf OpsToAppend;
+ uint64_t CurrentLocOps = Expr->getNumLocationOperands();
+
// Attempt to salvage back through as many instructions as possible. Bail if
// a non-instruction is seen, such as a constant expression or global
// variable. FIXME: Further work could recover those too.
while (isa<Instruction>(V)) {
const Instruction &VAsInst = *cast<const Instruction>(V);
// Temporary "0", awaiting real implementation.
- SmallVector<uint64_t, 16> Ops;
+ OpsToAppend.clear();
SmallVector<Value *, 4> AdditionalValues;
- V = salvageDebugInfoImpl(const_cast<Instruction &>(VAsInst),
- Expr->getNumLocationOperands(), Ops,
- AdditionalValues);
+ V = salvageDebugInfoImpl(const_cast<Instruction &>(VAsInst), CurrentLocOps,
+ OpsToAppend, AdditionalValues);
// If we cannot salvage any further, and haven't yet found a suitable debug
// expression, bail out.
if (!V)
@@ -1553,11 +1557,12 @@ void SelectionDAGBuilder::salvageUnresolvedDbgValue(const Value *V,
break;
// New value and expr now represent this debuginfo.
- Expr = DIExpression::appendOpsToArg(Expr, Ops, 0, StackValue);
+ DIBuf.appendOpsToArg(OpsToAppend.asRef(), 0, StackValue);
// Some kind of simplification occurred: check whether the operand of the
// salvaged debug expression can be encoded in this DAG.
- if (handleDebugValue(V, Var, Expr, DL, SDOrder, /*IsVariadic=*/false)) {
+ if (handleDebugValue(V, Var, DIBuf.toExpr(), DL, SDOrder,
+ /*IsVariadic=*/false)) {
LLVM_DEBUG(
dbgs() << "Salvaged debug location info for:\n " << *Var << "\n"
<< *OrigV << "\nBy stripping back to:\n " << *V << "\n");
diff --git a/llvm/lib/Transforms/Coroutines/CoroFrame.cpp b/llvm/lib/Transforms/Coroutines/CoroFrame.cpp
index 0accb225122be..d92ff3f900d53 100644
--- a/llvm/lib/Transforms/Coroutines/CoroFrame.cpp
+++ b/llvm/lib/Transforms/Coroutines/CoroFrame.cpp
@@ -21,6 +21,7 @@
#include "llvm/Analysis/StackLifetime.h"
#include "llvm/IR/DIBuilder.h"
#include "llvm/IR/DebugInfo.h"
+#include "llvm/IR/DebugInfoExprs.h"
#include "llvm/IR/Dominators.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/InstIterator.h"
@@ -1845,6 +1846,9 @@ salvageDebugInfoImpl(SmallDenseMap<Argument *, AllocaInst *, 4> &ArgToAllocaMap,
++InsertPt;
Builder.SetInsertPoint(&F->getEntryBlock(), InsertPt);
+ DIExprBuf DIBuf(Expr);
+ DIExprBuf OpsToAppend;
+ uint64_t CurrentLocOps = Expr->getNumLocationOperands();
while (auto *Inst = dyn_cast_or_null<Instruction>(Storage)) {
if (auto *LdInst = dyn_cast<LoadInst>(Inst)) {
Storage = LdInst->getPointerOperand();
@@ -1855,22 +1859,21 @@ salvageDebugInfoImpl(SmallDenseMap<Argument *, AllocaInst *, 4> &ArgToAllocaMap,
// last direct load from an alloca is necessary. This condition
// effectively drops the *last* DW_OP_deref in the expression.
if (!SkipOutermostLoad)
- Expr = DIExpression::prepend(Expr, DIExpression::DerefBefore);
+ DIBuf.prepend(DIExpression::DerefBefore);
} else if (auto *StInst = dyn_cast<StoreInst>(Inst)) {
Storage = StInst->getValueOperand();
} else {
- SmallVector<uint64_t, 16> Ops;
SmallVector<Value *, 0> AdditionalValues;
- Value *Op = llvm::salvageDebugInfoImpl(
- *Inst, Expr ? Expr->getNumLocationOperands() : 0, Ops,
- AdditionalValues);
+ OpsToAppend.clear();
+ Value *Op = llvm::salvageDebugInfoImpl(*Inst, CurrentLocOps, OpsToAppend,
+ AdditionalValues);
if (!Op || !AdditionalValues.empty()) {
// If salvaging failed or salvaging produced more than one location
// operand, give up.
break;
}
Storage = Op;
- Expr = DIExpression::appendOpsToArg(Expr, Ops, 0, /*StackValue*/ false);
+ DIBuf.appendOpsToArg(OpsToAppend.asRef(), 0, /*StackValue*/ false);
}
SkipOutermostLoad = false;
}
@@ -1884,9 +1887,9 @@ salvageDebugInfoImpl(SmallDenseMap<Argument *, AllocaInst *, 4> &ArgToAllocaMap,
// Swift async arguments are described by an entry value of the ABI-defined
// register containing the coroutine context.
// Entry values in variadic expressions are not supported.
- if (IsSwiftAsyncArg && UseEntryValue && !Expr->isEntryValue() &&
- Expr->isSingleLocationExpression())
- Expr = DIExpression::prepend(Expr, DIExpression::EntryValue);
+ if (IsSwiftAsyncArg && UseEntryValue && !DIBuf.asRef().isEntryValue() &&
+ DIBuf.asRef().isSingleLocationExpression())
+ DIBuf.prepend(DIExpression::EntryValue);
// If the coroutine frame is an Argument, store it in an alloca to improve
// its availability (e.g. registers may be clobbered).
@@ -1907,9 +1910,10 @@ salvageDebugInfoImpl(SmallDenseMap<Argument *, AllocaInst *, 4> &ArgToAllocaMap,
// expression, we need to add a DW_OP_deref at the *start* of the
// expression to first load the contents of the alloca before
// adjusting it with the expression.
- Expr = DIExpression::prepend(Expr, DIExpression::DerefBefore);
+ DIBuf.prepend(DIExpression::DerefBefore);
}
-
+ Expr = DIBuf.toExpr();
+ // FIXME:
Expr = Expr->foldConstantMath();
return {{*Storage, *Expr}};
}
diff --git a/llvm/lib/Transforms/Scalar/SROA.cpp b/llvm/lib/Transforms/Scalar/SROA.cpp
index 45d3d493a9e68..d4f26ab7e6c69 100644
--- a/llvm/lib/Transforms/Scalar/SROA.cpp
+++ b/llvm/lib/Transforms/Scalar/SROA.cpp
@@ -52,6 +52,7 @@
#include "llvm/IR/DIBuilder.h"
#include "llvm/IR/DataLayout.h"
#include "llvm/IR/DebugInfo.h"
+#include "llvm/IR/DebugInfoExprs.h"
#include "llvm/IR/DebugInfoMetadata.h"
#include "llvm/IR/DerivedTypes.h"
#include "llvm/IR/Dominators.h"
@@ -234,6 +235,8 @@ class SROA {
static std::optional<RewriteableMemOps>
isSafeSelectToSpeculate(SelectInst &SI, bool PreserveCFG);
+ DIExprBuf DIBuf;
+
public:
SROA(LLVMContext *C, DomTreeUpdater *DTU, AssumptionCache *AC,
SROAOptions PreserveCFG_)
@@ -403,19 +406,20 @@ static void migrateDebugInfo(AllocaInst *OldAlloca, bool IsSplit,
// relative fragment too?
NewFragment.OffsetInBits -= CurrentFragment->OffsetInBits;
}
+ DIExprBuf Buf(Expr);
// Add the new fragment info to the existing expression if possible.
- if (auto E = DIExpression::createFragmentExpression(
- Expr, NewFragment.OffsetInBits, NewFragment.SizeInBits)) {
- Expr = *E;
- } else {
+ if (!Buf.createFragmentExpression(NewFragment.OffsetInBits,
+ NewFragment.SizeInBits)) {
// Otherwise, add the new fragment info to an empty expression and
// discard the value component of this dbg.assign as the value cannot
// be computed with the new fragment.
- Expr = *DIExpression::createFragmentExpression(
- DIExpression::get(Expr->getContext(), {}),
- NewFragment.OffsetInBits, NewFragment.SizeInBits);
+ Buf.clear();
+ auto Success = Buf.createFragmentExpression(NewFragment.OffsetInBits,
+ NewFragment.SizeInBits);
+ assert(Success);
SetKillLocation = true;
}
+ Expr = Buf.toExpr();
}
}
@@ -5622,19 +5626,19 @@ bool SROA::splitAlloca(AllocaInst &AI, AllocaSlices &AS) {
return;
const Value *DbgPtr = DbgVariable->getAddress();
+ const DIExpression *AddressExpr = getAddressExpression(DbgVariable);
DIExpression::FragmentInfo VarFrag =
DbgVariable->getFragmentOrEntireVariable();
// Get the address expression constant offset if one exists and the ops
// that come after it.
- int64_t CurrentExprOffsetInBytes = 0;
- SmallVector<uint64_t> PostOffsetOps;
- if (!getAddressExpression(DbgVariable)
- ->extractLeadingOffset(CurrentExprOffsetInBytes, PostOffsetOps))
+ auto Res = DIExprRef(AddressExpr).extractLeadingOffset();
+ if (!Res)
return; // Couldn't interpret this DIExpression - drop the var.
+ auto [CurrentExprOffsetInBytes, PostOffsetOps] = *Res;
// Offset defined by a DW_OP_LLVM_extract_bits_[sz]ext.
int64_t ExtractOffsetInBits = 0;
- for (auto Op : getAddressExpression(DbgVariable)->expr_ops()) {
+ for (auto Op : AddressExpr->expr_ops()) {
if (Op.getOp() == dwarf::DW_OP_LLVM_extract_bits_zext ||
Op.getOp() == dwarf::DW_OP_LLVM_extract_bits_sext) {
ExtractOffsetInBits = Op.getArg(0);
@@ -5685,11 +5689,12 @@ bool SROA::splitAlloca(AllocaInst &AI, AllocaSlices &AS) {
// {Offset(OffestFromNewAllocaInBits), PostOffsetOps, NewDbgFragment}
// Add NewDbgFragment later, because dbg.assigns don't want it in the
// address expression but the value expression instead.
- DIExpression *NewExpr = DIExpression::get(AI.getContext(), PostOffsetOps);
+ DIBuf.assign(&DbgVariable->getContext(), PostOffsetOps);
if (OffestFromNewAllocaInBits > 0) {
int64_t OffsetInBytes = (OffestFromNewAllocaInBits + 7) / 8;
- NewExpr = DIExpression::prepend(NewExpr, /*flags=*/0, OffsetInBytes);
+ DIBuf.prepend(/*flags=*/0, OffsetInBytes);
}
+ DIExpression *NewExpr = DIBuf.toExpr();
// Remove any existing intrinsics on the new alloca describing
// the variable fragment.
diff --git a/llvm/lib/Transforms/Utils/Local.cpp b/llvm/lib/Transforms/Utils/Local.cpp
index 123881e276584..62d3e2e220fb5 100644
--- a/llvm/lib/Transforms/Utils/Local.cpp
+++ b/llvm/lib/Transforms/Utils/Local.cpp
@@ -42,6 +42,7 @@
#include "llvm/IR/DIBuilder.h"
#include "llvm/IR/DataLayout.h"
#include "llvm/IR/DebugInfo.h"
+#include "llvm/IR/DebugInfoExprs.h"
#include "llvm/IR/DebugInfoMetadata.h"
#include "llvm/IR/DebugLoc.h"
#include "llvm/IR/DerivedTypes.h"
@@ -2011,15 +2012,17 @@ template <typename T> static void salvageDbgAssignAddress(T *Assign) {
// The address component of a dbg.assign cannot be variadic.
uint64_t CurrentLocOps = 0;
SmallVector<Value *, 4> AdditionalValues;
- SmallVector<uint64_t, 16> Ops;
- Value *NewV = salvageDebugInfoImpl(*I, CurrentLocOps, Ops, AdditionalValues);
+ DIExprBuf OpsToAppend;
+ Value *NewV =
+ salvageDebugInfoImpl(*I, CurrentLocOps, OpsToAppend, AdditionalValues);
// Check if the salvage failed.
if (!NewV)
return;
- DIExpression *SalvagedExpr = DIExpression::appendOpsToArg(
- Assign->getAddressExpression(), Ops, 0, /*StackValue=*/false);
+ DIExprBuf DIBuf(Assign->getAddressExpression());
+ DIExpression *SalvagedExpr =
+ DIBuf.appendOpsToArg(OpsToAppend.asRef(), 0, false).toExpr();
assert(!SalvagedExpr->getFragmentInfo().has_value() &&
"address-expression shouldn't have fragment info");
@@ -2043,6 +2046,9 @@ void llvm::salvageDebugInfoForDbgValues(Instruction &I,
const unsigned MaxExpressionSize = 128;
bool Salvaged = false;
+ DIExprBuf DIBuf;
+ DIExprBuf OpsToAppend;
+
for (auto *DVR : DPUsers) {
if (DVR->isDbgAssign()) {
if (DVR->getAddress() == &I) {
@@ -2068,17 +2074,20 @@ void llvm::salvageDebugInfoForDbgValues(Instruction &I,
// values added; thus we call salvageDebugInfoImpl for each 'I' instance in
// DVRLocation.
Value *Op0 = nullptr;
- DIExpression *SalvagedExpr = DVR->getExpression();
+ DIExpression *OriginalExpr = DVR->getExpression();
+ if (!OriginalExpr)
+ break;
+ DIBuf.assign(OriginalExpr);
auto LocItr = find(DVRLocation, &I);
- while (SalvagedExpr && LocItr != DVRLocation.end()) {
- SmallVector<uint64_t, 16> Ops;
+ while (LocItr != DVRLocation.end()) {
unsigned LocNo = std::distance(DVRLocation.begin(), LocItr);
- uint64_t CurrentLocOps = SalvagedExpr->getNumLocationOperands();
- Op0 = salvageDebugInfoImpl(I, CurrentLocOps, Ops, AdditionalValues);
+ uint64_t CurrentLocOps = DIBuf.asRef().getNumLocationOperands();
+ OpsToAppend.clear();
+ Op0 =
+ salvageDebugInfoImpl(I, CurrentLocOps, OpsToAppend, AdditionalValues);
if (!Op0)
break;
- SalvagedExpr =
- DIExpression::appendOpsToArg(SalvagedExpr, Ops, LocNo, StackValue);
+ DIBuf.appendOpsToArg(OpsToAppend.asRef(), LocNo, StackValue);
LocItr = std::find(++LocItr, DVRLocation.end(), &I);
}
// salvageDebugInfoImpl should fail on examining the first element of
@@ -2086,6 +2095,7 @@ void llvm::salvageDebugInfoForDbgValues(Instruction &I,
if (!Op0)
break;
+ DIExpression *SalvagedExpr = DIBuf.toExpr();
SalvagedExpr = SalvagedExpr->foldConstantMath();
DVR->replaceVariableLocationOp(&I, Op0);
bool IsValidSalvageExpr =
@@ -2115,9 +2125,38 @@ void llvm::salvageDebugInfoForDbgValues(Instruction &I,
DVR->setKillLocation();
}
+Value *getSalvageOpsForCast(CastInst *CI, const DataLayout &DL,
+ uint64_t &CurrentLocOps, DIExprBuf &OpsToAppend,
+ SmallVectorImpl<Value *> &AdditionalValues) {
+ Value *FromValue = CI->getOperand(0);
+ // No-op casts are irrelevant for debug info.
+ if (CI->isNoopCast(DL)) {
+ return FromValue;
+ }
+
+ Type *Type = CI->getType();
+ if (Type->isPointerTy())
+ Type = DL.getIntPtrType(Type);
+ // Casts other than Trunc, SExt, or ZExt to scalar types cannot be salvaged.
+ if (Type->isVectorTy() ||
+ !(isa<TruncInst>(CI) || isa<SExtInst>(CI) || isa<ZExtInst>(CI) ||
+ isa<IntToPtrInst>(CI) || isa<PtrToIntInst>(CI)))
+ return nullptr;
+
+ llvm::Type *FromType = FromValue->getType();
+ if (FromType->isPointerTy())
+ FromType = DL.getIntPtrType(FromType);
+
+ unsigned FromTypeBitSize = FromType->getScalarSizeInBits();
+ unsigned ToTypeBitSize = Type->getScalarSizeInBits();
+
+ OpsToAppend.appendRaw(
+ DIExprRef::getExtOps(FromTypeBitSize, ToTypeBitSize, isa<SExtInst>(CI)));
+ return FromValue;
+}
+
Value *getSalvageOpsForGEP(GetElementPtrInst *GEP, const DataLayout &DL,
- uint64_t CurrentLocOps,
- SmallVectorImpl<uint64_t> &Opcodes,
+ uint64_t &CurrentLocOps, DIExprBuf &OpsToAppend,
SmallVectorImpl<Value *> &AdditionalValues) {
unsigned BitWidth = DL.getIndexSizeInBits(GEP->getPointerAddressSpace());
// Rewrite a GEP into a DIExpression.
@@ -2126,65 +2165,65 @@ Value *getSalvageOpsForGEP(GetElementPtrInst *GEP, const DataLayout &DL,
if (!GEP->collectOffset(DL, BitWidth, VariableOffsets, ConstantOffset))
return nullptr;
if (!VariableOffsets.empty() && !CurrentLocOps) {
- Opcodes.insert(Opcodes.begin(), {dwarf::DW_OP_LLVM_arg, 0});
+ OpsToAppend.convertToVariadicExpressionUnchecked();
CurrentLocOps = 1;
}
for (const auto &Offset : VariableOffsets) {
AdditionalValues.push_back(Offset.first);
assert(Offset.second.isStrictlyPositive() &&
"Expected strictly positive multiplier for offset.");
- Opcodes.append({dwarf::DW_OP_LLVM_arg, CurrentLocOps++, dwarf::DW_OP_constu,
- Offset.second.getZExtValue(), dwarf::DW_OP_mul,
- dwarf::DW_OP_plus});
+ OpsToAppend.appendRaw({DIOp::LLVMArg(CurrentLocOps++),
+ DIOp::ConstU(Offset.second.getZExtValue()),
+ DIOp::Mul(), DIOp::Plus()});
}
- DIExpression::appendOffset(Opcodes, ConstantOffset.getSExtValue());
+ OpsToAppend.appendOffset(ConstantOffset.getSExtValue());
return GEP->getOperand(0);
}
-uint64_t getDwarfOpForBinOp(Instruction::BinaryOps Opcode) {
+std::optional<DIOp::Op> getDwarfOpForBinOp(Instruction::BinaryOps Opcode) {
switch (Opcode) {
case Instruction::Add:
- return dwarf::DW_OP_plus;
+ return DIOp::Plus();
case Instruction::Sub:
- return dwarf::DW_OP_minus;
+ return DIOp::Minus();
case Instruction::Mul:
- return dwarf::DW_OP_mul;
+ return DIOp::Mul();
case Instruction::SDiv:
- return dwarf::DW_OP_div;
+ return DIOp::Div();
case Instruction::SRem:
- return dwarf::DW_OP_mod;
+ return DIOp::Mod();
case Instruction::Or:
- return dwarf::DW_OP_or;
+ return DIOp::Or();
case Instruction::And:
- return dwarf::DW_OP_and;
+ return DIOp::And();
case Instruction::Xor:
- return dwarf::DW_OP_xor;
+ return DIOp::Xor();
case Instruction::Shl:
- return dwarf::DW_OP_shl;
+ return DIOp::Shl();
case Instruction::LShr:
- return dwarf::DW_OP_shr;
+ return DIOp::Shr();
case Instruction::AShr:
- return dwarf::DW_OP_shra;
+ return DIOp::Shra();
default:
// TODO: Salvage from each kind of binop we know about.
- return 0;
+ return std::nullopt;
}
}
-static void handleSSAValueOperands(uint64_t CurrentLocOps,
- SmallVectorImpl<uint64_t> &Opcodes,
+static void handleSSAValueOperands(uint64_t &CurrentLocOps,
+ DIExprBuf &OpsToAppend,
SmallVectorImpl<Value *> &AdditionalValues,
Instruction *I) {
if (!CurrentLocOps) {
- Opcodes.append({dwarf::DW_OP_LLVM_arg, 0});
+ OpsToAppend.convertToVariadicExpression();
CurrentLocOps = 1;
}
- Opcodes.append({dwarf::DW_OP_LLVM_arg, CurrentLocOps});
+ OpsToAppend.appendRaw({DIOp::LLVMArg(CurrentLocOps++)});
AdditionalValues.push_back(I->getOperand(1));
}
-Value *getSalvageOpsForBinOp(BinaryOperator *BI, uint64_t CurrentLocOps,
- SmallVectorImpl<uint64_t> &Opcodes,
+Value *getSalvageOpsForBinOp(BinaryOperator *BI, uint64_t &CurrentLocOps,
+ DIExprBuf &OpsToAppend,
SmallVectorImpl<Value *> &AdditionalValues) {
// Handle binary operations with constant integer operands as a special case.
auto *ConstInt = dyn_cast<ConstantInt>(BI->getOperand(1));
@@ -2200,50 +2239,50 @@ Value *getSalvageOpsForBinOp(BinaryOperator *BI, uint64_t CurrentLocOps,
// simplified.
if (BinOpcode == Instruction::Add || BinOpcode == Instruction::Sub) {
uint64_t Offset = BinOpcode == Instruction::Add ? Val : -int64_t(Val);
- DIExpression::appendOffset(Opcodes, Offset);
+ OpsToAppend.appendOffset(Offset);
return BI->getOperand(0);
}
- Opcodes.append({dwarf::DW_OP_constu, Val});
+ OpsToAppend.appendRaw({DIOp::ConstU(Val)});
} else {
- handleSSAValueOperands(CurrentLocOps, Opcodes, AdditionalValues, BI);
+ handleSSAValueOperands(CurrentLocOps, OpsToAppend, AdditionalValues, BI);
}
// Add salvaged binary operator to expression stack, if it has a valid
// representation in a DIExpression.
- uint64_t DwarfBinOp = getDwarfOpForBinOp(BinOpcode);
- if (!DwarfBinOp)
- return nullptr;
- Opcodes.push_back(DwarfBinOp);
- return BI->getOperand(0);
+ if (std::optional<DIOp::Op> DwarfBinOp = getDwarfOpForBinOp(BinOpcode)) {
+ OpsToAppend.appendRaw({*DwarfBinOp});
+ return BI->getOperand(0);
+ }
+ return nullptr;
}
-uint64_t getDwarfOpForIcmpPred(CmpInst::Predicate Pred) {
+std::optional<DIOp::Op> getDwarfOpForIcmpPred(CmpInst::Predicate Pred) {
// The signedness of the operation is implicit in the typed stack, signed and
// unsigned instructions map to the same DWARF opcode.
switch (Pred) {
case CmpInst::ICMP_EQ:
- return dwarf::DW_OP_eq;
+ return DIOp::Eq();
case CmpInst::ICMP_NE:
- return dwarf::DW_OP_ne;
+ return DIOp::Ne();
case CmpInst::ICMP_UGT:
case CmpInst::ICMP_SGT:
- return dwarf::DW_OP_gt;
+ return DIOp::Gt();
case CmpInst::ICMP_UGE:
case CmpInst::ICMP_SGE:
- return dwarf::DW_OP_ge;
+ return DIOp::Ge();
case CmpInst::ICMP_ULT:
case CmpInst::ICMP_SLT:
- return dwarf::DW_OP_lt;
+ return DIOp::Lt();
case CmpInst::ICMP_ULE:
case CmpInst::ICMP_SLE:
- return dwarf::DW_OP_le;
+ return DIOp::Le();
default:
- return 0;
+ return std::nullopt;
}
}
-Value *getSalvageOpsForIcmpOp(ICmpInst *Icmp, uint64_t CurrentLocOps,
- SmallVectorImpl<uint64_t> &Opcodes,
+Value *getSalvageOpsForIcmpOp(ICmpInst *Icmp, uint64_t &CurrentLocOps,
+ DIExprBuf &OpsToAppend,
SmallVectorImpl<Value *> &AdditionalValues) {
// Handle icmp operations with constant integer operands as a special case.
auto *ConstInt = dyn_cast<ConstantInt>(Icmp->getOperand(1));
@@ -2252,66 +2291,42 @@ Value *getSalvageOpsForIcmpOp(ICmpInst *Icmp, uint64_t CurrentLocOps,
return nullptr;
// Push any Constant Int operand onto the expression stack.
if (ConstInt) {
- if (Icmp->isSigned())
- Opcodes.push_back(dwarf::DW_OP_consts);
- else
- Opcodes.push_back(dwarf::DW_OP_constu);
- uint64_t Val = ConstInt->getSExtValue();
- Opcodes.push_back(Val);
+ OpsToAppend.appendConstant(Icmp->isSigned()
+ ? SignedOrUnsignedConstant::SignedConstant
+ : SignedOrUnsignedConstant::UnsignedConstant,
+ ConstInt->getSExtValue());
} else {
- handleSSAValueOperands(CurrentLocOps, Opcodes, AdditionalValues, Icmp);
+ handleSSAValueOperands(CurrentLocOps, OpsToAppend, AdditionalValues, Icmp);
}
// Add salvaged binary operator to expression stack, if it has a valid
// representation in a DIExpression.
- uint64_t DwarfIcmpOp = getDwarfOpForIcmpPred(Icmp->getPredicate());
- if (!DwarfIcmpOp)
- return nullptr;
- Opcodes.push_back(DwarfIcmpOp);
- return Icmp->getOperand(0);
+ if (std::optional<DIOp::Op> DwarfIcmpOp =
+ getDwarfOpForIcmpPred(Icmp->getPredicate())) {
+ OpsToAppend.appendRaw({*DwarfIcmpOp});
+ return Icmp->getOperand(0);
+ }
+ return nullptr;
}
-Value *llvm::salvageDebugInfoImpl(Instruction &I, uint64_t CurrentLocOps,
- SmallVectorImpl<uint64_t> &Ops,
+Value *llvm::salvageDebugInfoImpl(Instruction &I, uint64_t &CurrentLocOps,
+ DIExprBuf &OpsToAppend,
SmallVectorImpl<Value *> &AdditionalValues) {
auto &M = *I.getModule();
auto &DL = M.getDataLayout();
- if (auto *CI = dyn_cast<CastInst>(&I)) {
- Value *FromValue = CI->getOperand(0);
- // No-op casts are irrelevant for debug info.
- if (CI->isNoopCast(DL)) {
- return FromValue;
- }
-
- Type *Type = CI->getType();
- if (Type->isPointerTy())
- Type = DL.getIntPtrType(Type);
- // Casts other than Trunc, SExt, or ZExt to scalar types cannot be salvaged.
- if (Type->isVectorTy() ||
- !(isa<TruncInst>(&I) || isa<SExtInst>(&I) || isa<ZExtInst>(&I) ||
- isa<IntToPtrInst>(&I) || isa<PtrToIntInst>(&I)))
- return nullptr;
-
- llvm::Type *FromType = FromValue->getType();
- if (FromType->isPointerTy())
- FromType = DL.getIntPtrType(FromType);
-
- unsigned FromTypeBitSize = FromType->getScalarSizeInBits();
- unsigned ToTypeBitSize = Type->getScalarSizeInBits();
-
- auto ExtOps = DIExpression::getExtOps(FromTypeBitSize, ToTypeBitSize,
- isa<SExtInst>(&I));
- Ops.append(ExtOps.begin(), ExtOps.end());
- return FromValue;
- }
-
+ if (auto *CI = dyn_cast<CastInst>(&I))
+ return getSalvageOpsForCast(CI, DL, CurrentLocOps, OpsToAppend,
+ AdditionalValues);
if (auto *GEP = dyn_cast<GetElementPtrInst>(&I))
- return getSalvageOpsForGEP(GEP, DL, CurrentLocOps, Ops, AdditionalValues);
+ return getSalvageOpsForGEP(GEP, DL, CurrentLocOps, OpsToAppend,
+ AdditionalValues);
if (auto *BI = dyn_cast<BinaryOperator>(&I))
- return getSalvageOpsForBinOp(BI, CurrentLocOps, Ops, AdditionalValues);
+ return getSalvageOpsForBinOp(BI, CurrentLocOps, OpsToAppend,
+ AdditionalValues);
if (auto *IC = dyn_cast<ICmpInst>(&I))
- return getSalvageOpsForIcmpOp(IC, CurrentLocOps, Ops, AdditionalValues);
+ return getSalvageOpsForIcmpOp(IC, CurrentLocOps, OpsToAppend,
+ AdditionalValues);
// *Not* to do: we should not attempt to salvage load instructions,
// because the validity and lifetime of a dbg.value containing
More information about the llvm-commits
mailing list