From 61fa59e8f3914875bee229355fcff5920a2f5325 Mon Sep 17 00:00:00 2001 From: Scott Linder 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 +#include +#include + +#include + +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 &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::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 Elements) { + return expr_op_iterator(Elements.begin()); +} +// NOLINTNEXTLINE(readability-identifier-naming) +static inline expr_op_iterator expr_op_end(ArrayRef Elements) { + return expr_op_iterator(Elements.end()); +} +// NOLINTNEXTLINE(readability-identifier-naming) +static inline iterator_range +expr_ops(ArrayRef Elements) { + return {expr_op_begin(Elements), expr_op_end(Elements)}; +} + +void appendOffsetImpl(SmallVectorImpl &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 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 &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 { /// 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 take() { + std::optional 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 peek() const { + std::optional peek() const { if (Start == End) return std::nullopt; return *(Start); } /// Return the next operation. - std::optional peekNext() const { + std::optional peekNext() const { if (Start == End) return std::nullopt; @@ -3825,10 +3745,10 @@ class DIExpressionCursor { return *Next; } - std::optional peekNextN(unsigned N) const { + std::optional 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 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 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 PrevConvertOp; + std::optional 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(Bound)) { if (BE->isConstant() && - DIExpression::SignedOrUnsignedConstant::SignedConstant == - *BE->isConstant()) { + SignedOrUnsignedConstant::SignedConstant == *BE->isConstant()) { if (Attr != dwarf::DW_AT_lower_bound || DefaultLowerBound == -1 || static_cast(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(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 isConstantVal(DIExpression::ExprOperand Op) { +static std::optional 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 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 Ops, +static bool tryFoldNoOpMath(uint64_t Const1, ArrayRef Ops, uint64_t &Loc, DIExpressionCursor &Cursor, SmallVectorImpl &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 Ops, +static bool tryFoldConstants(uint64_t Const1, ArrayRef Ops, uint64_t &Loc, DIExpressionCursor &Cursor, SmallVectorImpl &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 Ops, +static bool tryFoldCommutativeMath(uint64_t Const1, ArrayRef Ops, uint64_t &Loc, DIExpressionCursor &Cursor, SmallVectorImpl &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 Ops, uint64_t &Loc, + uint64_t Const1, ArrayRef Ops, uint64_t &Loc, DIExpressionCursor &Cursor, SmallVectorImpl &WorkingOps) { auto Const2 = isConstantVal(Ops[4]); @@ -291,7 +288,7 @@ DIExpression *DIExpression::foldConstantMath() { uint64_t Loc = 0; SmallVector ResultOps = canonicalizeDwarfOperations(WorkingOps); DIExpressionCursor Cursor(ResultOps); - SmallVector Ops; + SmallVector 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 &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::isConstant() const { - +std::optional 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 +static iterator_range ToDwarfOpIter(SmallVectorImpl &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 expr_ops() { - return ToDwarfOpIter(Expr); - } + iterator_range 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 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 +#include +#include +#include + +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 A) + : Size(static_cast(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 &Out) const; + ArrayRef 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 &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 &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 &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 bool holds() const; + template bool holdsOneOf() const { + return (holds() || ...); + } + template T get() const; + template std::optional getIf() const; + + template + decltype(auto) visitOverload(CallableTs &&...Callables) const; + template + R visitOverload(CallableTs &&...Callables) const; + void toUIntVec(SmallVectorImpl &Out) const; +}; +#define HANDLE_OP(NAME, ENCODING) \ + template <> bool Op::holds() const; \ + template <> DIOp::NAME Op::get() const; \ + template <> std::optional Op::getIf() const; +#include "llvm/IR/DIOps.def" + +template +inline decltype(auto) Op::visitOverload(CallableTs &&...Callables) const { + auto Visitor = makeVisitor(std::forward(Callables)...); + switch (CIS.Tag) { +#define HANDLE_OP(NAME, ENCODING) \ + case impl::TagT::NAME: \ + return Visitor(get()); + HANDLE_OP(LLVMEscape, void) +#include "llvm/IR/DIOps.def" + } + llvm_unreachable("DIOp::visitOverload does not handle all tags"); +} +template +inline R Op::visitOverload(CallableTs &&...Callables) const { + auto Visitor = makeVisitor(std::forward(Callables)...); + switch (CIS.Tag) { +#define HANDLE_OP(NAME, ENCODING) \ + case impl::TagT::NAME: \ + return Visitor(get()); + HANDLE_OP(LLVMEscape, void) +#include "llvm/IR/DIOps.def" + } + llvm_unreachable("DIOp::visitOverload does not handle all tags"); +} + +class FromUIntIterator + : public iterator_facade_base { + 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 makeRange(ArrayRef 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 Ops; + + explicit DIExprRef(iterator_range Ops) : Ops(Ops) {}; + explicit DIExprRef(DIOp::FromUIntIterator Begin, DIOp::FromUIntIterator End) + : Ops(make_range(Begin, End)) {}; + + std::optional maybeAdvance(DIOp::FromUIntIterator &I) const { + if (I != Ops.end()) + return *I++; + return std::nullopt; + }; + std::optional getSingleLocationExprRef() const; + +public: + explicit DIExprRef(const DIExpression *From); + + using ExtOps = std::array; + + /// Returns the ops for a zero- or sign-extension in a DIExpression. + static ExtOps getExtOps(unsigned FromSize, unsigned ToSize, bool Signed); + + std::optional 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 isConstant() const; + uint64_t getNumLocationOperands() const; + std::optional getActiveBits(DIVariable *Var) const; + std::optional extractIfOffset() const; + std::optional> extractLeadingOffset() const; + bool hasAllLocationOps(unsigned N) const; + void toUIntVec(SmallVectorImpl &Out) const; + SmallVector toUIntVec() const; +}; + +class DIExprBuf { + friend class impl::DIExpr; + LLVMContext *Ctx = nullptr; + SmallVector 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 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 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 NewOps); + DIExprBuf &appendRaw(std::initializer_list NewOps); + + DIExprBuf &clear(); + DIExprBuf &convertToUndefExpression(); + DIExprBuf &convertToVariadicExpressionUnchecked(); + DIExprBuf &convertToVariadicExpression(); + bool convertToNonVariadicExpression(); + DIExprBuf &prepend(uint8_t Flags, int64_t Offset = 0); + + DIExprBuf &append(iterator_range NewOps); + DIExprBuf &append(std::initializer_list NewOps); + DIExprBuf &append(DIExprRef NewOps); + DIExprBuf &append(iterator_range NewOps); + + DIExprBuf &prependOpcodes(iterator_range NewOps, + bool StackValue = false, bool EntryValue = false); + DIExprBuf &prependOpcodes(std::initializer_list NewOps, + bool StackValue = false, bool EntryValue = false); + DIExprBuf &prependOpcodes(DIExprRef NewOps, bool StackValue = false, + bool EntryValue = false); + DIExprBuf &prependOpcodes(iterator_range NewOps, + bool StackValue = false, bool EntryValue = false); + + DIExprBuf &appendOpsToArg(iterator_range NewOps, + unsigned ArgIndex, bool StackValue = false); + DIExprBuf &appendOpsToArg(std::initializer_list NewOps, + unsigned ArgIndex, bool StackValue = false); + DIExprBuf &appendOpsToArg(DIExprRef NewOps, unsigned ArgIndex, + bool StackValue = false); + DIExprBuf &appendOpsToArg(iterator_range 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 &Out) const; + SmallVector 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 + +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 &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 &Out) const { \ + Out.append({ENCODING, static_cast(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 &Out) const { \ + Out.append({ENCODING, static_cast(NAME1), \ + static_cast(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() const { \ + return CIS.Tag == impl::TagT::NAME; \ + } \ + template <> DIOp::NAME Op::get() const { \ + assert(holds()); \ + return NAME; \ + } \ + template <> std::optional Op::getIf() const { \ + return (holds()) ? std::optional(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::value); \ + static_assert(std::is_trivially_destructible::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::max()); + return static_cast(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 &Out) const { + auto Data = getData(); + Out.append(Data.begin(), Data.end()); +} + +void DIOp::Op::toUIntVec(SmallVectorImpl &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 DIExprRef::getFragmentInfo() const { + auto I = + find_if(Ops, [](DIOp::Op Op) { return Op.holds(); }); + if (I == Ops.end()) + return std::nullopt; + assert(std::next(I) == Ops.end()); + auto Fragment = I->get(); + 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()) + 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()) + 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()) { + if (Arg->getIndex() != 0u) + return false; + ++I; + } + + return !std::any_of(I, Ops.end(), + [](DIOp::Op Op) { return Op.holds(); }); +} + +std::optional DIExprRef::getSingleLocationExprRef() const { + // Check for `isValid` covered by `isSingleLocationExpression`. + if (!isSingleLocationExpression()) + return std::nullopt; + + if (Ops.empty()) + return *this; + + if (Ops.begin()->holds()) + 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(); + 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(); + return false; +} + +bool DIExprRef::isImplicit() const { + return isValid() && find_if(Ops, [](DIOp::Op Op) { + return Op.holds(); + }) != 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() || + Op.holds() || + Op.holds(); + }) != Ops.end(); +} + +bool DIExprRef::isEntryValue() const { + if (auto SingleLocRef = getSingleLocationExprRef()) + return !SingleLocRef->Ops.empty() && + SingleLocRef->Ops.begin()->holds(); + return false; +} + +std::optional 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() && !Op0->holds())) + return std::nullopt; + + if (Op1 && !Op1->holds()) + return std::nullopt; + + if (Op2 && !Op2->holds()) + return std::nullopt; + + return Op0->holds() ? SignedOrUnsignedConstant::UnsignedConstant + : SignedOrUnsignedConstant::SignedConstant; +} + +uint64_t DIExprRef::getNumLocationOperands() const { + uint64_t Result = 0; + for (auto Op : Ops) + if (auto Arg = Op.getIf()) + Result = std::max(Result, Arg->getIndex() + 1); + /*FIXME: + assert(hasAllLocationOps(Result) && + "Expression is missing one or more location operands."); + */ + return Result; +} + +std::optional DIExprRef::getActiveBits(DIVariable *Var) const { + std::optional InitialActiveBits = Var->getSizeInBits(); + std::optional 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 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 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()) { + if (Op1->holds()) + return ConstU->getValue(); + if (Op1->holds()) + return -ConstU->getValue(); + } + return std::nullopt; + } + + if (Op0) { + if (auto PlusUConst = Op0->getIf()) + return PlusUConst->getValue(); + return std::nullopt; + } + + return 0; +} + +std::optional> +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()) + break; + if (auto P = I->getIf()) { + OffsetInBytes += P->getValue(); + } else if (auto C = I->getIf()) { + uint64_t Value = C->getValue(); + auto J = maybeAdvance(I); + if (!J) + return std::nullopt; + if (J->holds()) + OffsetInBytes += Value; + else if (J->holds()) + 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 SeenIndexes; + for (auto Op : Ops) + if (auto Arg = Op.getIf()) + SeenIndexes.insert(Arg->getIndex()); + for (uint64_t Index = 0; Index < N; ++Index) + if (!SeenIndexes.contains(Index)) + return false; + return true; +} + +void DIExprRef::toUIntVec(SmallVectorImpl &Out) const { + for (auto Op : Ops) + Op.toUIntVec(Out); +} +SmallVector DIExprRef::toUIntVec() const { + SmallVector Ops; + toUIntVec(Ops); + return Ops; +} + +namespace impl { +class DIExpr { +public: + template + static void appendRaw(SmallVectorImpl &Buf, OpsT Ops) { + for (auto Op : Ops) + Op.toUIntVec(Buf); + } + template + static void assignRaw(SmallVectorImpl &Buf, OpsT Ops) { + Buf.clear(); + appendRaw(Buf, Ops); + } + template + 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() || Op.holds(); + }); + DIBuf.NewElements.append(ExistingOps.begin().I, InsertPoint.I); + appendRaw(DIBuf.NewElements, Ops); + DIBuf.NewElements.append(InsertPoint.I, ExistingOps.end().I); + return DIBuf.swap(); + } + template + static DIExprBuf &prependOpcodes(DIExprBuf &DIBuf, OpsT Ops, bool StackValue, + bool EntryValue) { + appendRaw(DIBuf.NewElements, Ops); + return DIBuf.prependOpcodesFinalize(StackValue, EntryValue); + } + template + 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(SmallVectorImpl &Buf, + DIExprRef Ops) { + Buf.append(Ops.Ops.begin().I, Ops.Ops.end().I); +} +template <> +void DIExpr::appendRaw(SmallVectorImpl &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(); })) +Result.Ops.emplace_back(DIOp::LLVMArg(0u)); +for (auto I = From.Ops.begin(), E = From.Ops.end(); I != E; ++I) { +if (NeedsDeref && + (I->holds() || I->holds())) { + 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 NewOps) { + impl::DIExpr::appendRaw(Elements, NewOps); + return *this; +} +DIExprBuf &DIExprBuf::appendRaw(iterator_range 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 NewOps) { + return impl::DIExpr::append(*this, NewOps); +} +DIExprBuf &DIExprBuf::append(std::initializer_list NewOps) { + return impl::DIExpr::append(*this, NewOps); +} +DIExprBuf &DIExprBuf::append(DIExprRef NewOps) { + return impl::DIExpr::append(*this, NewOps); +} +DIExprBuf &DIExprBuf::append(iterator_range NewOps) { + return impl::DIExpr::append(*this, NewOps); +} + +DIExprBuf &DIExprBuf::prependOpcodes(iterator_range NewOps, + bool StackValue, bool EntryValue) { + return impl::DIExpr::prependOpcodes(*this, NewOps, StackValue, EntryValue); +} +DIExprBuf &DIExprBuf::prependOpcodes(std::initializer_list 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 NewOps, + bool StackValue, bool EntryValue) { + return impl::DIExpr::prependOpcodes(*this, NewOps, StackValue, EntryValue); +} + +DIExprBuf &DIExprBuf::appendOpsToArg(iterator_range NewOps, + unsigned ArgIndex, bool StackValue) { + return impl::DIExpr::appendOpsToArg(*this, NewOps, ArgIndex, StackValue); +} +DIExprBuf &DIExprBuf::appendOpsToArg(std::initializer_list 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 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 &Out) const { + asRef().toUIntVec(Out); +} +SmallVector 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 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 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 + 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 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 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 DIExpression::getActiveBits(DIVariable *Var) { + if (isDIRefEnabled()) + return DIExprRef(this).getActiveBits(Var); std::optional InitialActiveBits = Var->getSizeInBits(); std::optional ActiveBits = InitialActiveBits; for (auto Op : expr_ops()) { @@ -1956,20 +2009,17 @@ std::optional DIExpression::getActiveBits(DIVariable *Var) { void DIExpression::appendOffset(SmallVectorImpl &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 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 Ops; if (Flags & DIExpression::DerefBefore) Ops.push_back(dwarf::DW_OP_deref); @@ -2095,6 +2149,13 @@ DIExpression *DIExpression::appendOpsToArg(const DIExpression *Expr, ArrayRef 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 &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 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 NewOps; @@ -2241,6 +2315,12 @@ DIExpression *DIExpression::appendToStack(const DIExpression *Expr, std::optional 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 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::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 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 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 &Ops, +salvageDebugInfoImpl(Instruction &I, uint64_t &CurrentLocOps, + DIExprBuf &OpsToAppend, SmallVectorImpl &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 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 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(V)) { const Instruction &VAsInst = *cast(V); // Temporary "0", awaiting real implementation. - SmallVector Ops; + OpsToAppend.clear(); SmallVector AdditionalValues; - V = salvageDebugInfoImpl(const_cast(VAsInst), - Expr->getNumLocationOperands(), Ops, - AdditionalValues); + V = salvageDebugInfoImpl(const_cast(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 &ArgToAllocaMap, ++InsertPt; Builder.SetInsertPoint(&F->getEntryBlock(), InsertPt); + DIExprBuf DIBuf(Expr); + DIExprBuf OpsToAppend; + uint64_t CurrentLocOps = Expr->getNumLocationOperands(); while (auto *Inst = dyn_cast_or_null(Storage)) { if (auto *LdInst = dyn_cast(Inst)) { Storage = LdInst->getPointerOperand(); @@ -1855,22 +1859,21 @@ salvageDebugInfoImpl(SmallDenseMap &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(Inst)) { Storage = StInst->getValueOperand(); } else { - SmallVector Ops; SmallVector 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 &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 &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 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 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 static void salvageDbgAssignAddress(T *Assign) { // The address component of a dbg.assign cannot be variadic. uint64_t CurrentLocOps = 0; SmallVector AdditionalValues; - SmallVector 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 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 &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(CI) || isa(CI) || isa(CI) || + isa(CI) || isa(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(CI))); + return FromValue; +} + Value *getSalvageOpsForGEP(GetElementPtrInst *GEP, const DataLayout &DL, - uint64_t CurrentLocOps, - SmallVectorImpl &Opcodes, + uint64_t &CurrentLocOps, DIExprBuf &OpsToAppend, SmallVectorImpl &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 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 &Opcodes, +static void handleSSAValueOperands(uint64_t &CurrentLocOps, + DIExprBuf &OpsToAppend, SmallVectorImpl &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 &Opcodes, +Value *getSalvageOpsForBinOp(BinaryOperator *BI, uint64_t &CurrentLocOps, + DIExprBuf &OpsToAppend, SmallVectorImpl &AdditionalValues) { // Handle binary operations with constant integer operands as a special case. auto *ConstInt = dyn_cast(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 DwarfBinOp = getDwarfOpForBinOp(BinOpcode)) { + OpsToAppend.appendRaw({*DwarfBinOp}); + return BI->getOperand(0); + } + return nullptr; } -uint64_t getDwarfOpForIcmpPred(CmpInst::Predicate Pred) { +std::optional 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 &Opcodes, +Value *getSalvageOpsForIcmpOp(ICmpInst *Icmp, uint64_t &CurrentLocOps, + DIExprBuf &OpsToAppend, SmallVectorImpl &AdditionalValues) { // Handle icmp operations with constant integer operands as a special case. auto *ConstInt = dyn_cast(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 DwarfIcmpOp = + getDwarfOpForIcmpPred(Icmp->getPredicate())) { + OpsToAppend.appendRaw({*DwarfIcmpOp}); + return Icmp->getOperand(0); + } + return nullptr; } -Value *llvm::salvageDebugInfoImpl(Instruction &I, uint64_t CurrentLocOps, - SmallVectorImpl &Ops, +Value *llvm::salvageDebugInfoImpl(Instruction &I, uint64_t &CurrentLocOps, + DIExprBuf &OpsToAppend, SmallVectorImpl &AdditionalValues) { auto &M = *I.getModule(); auto &DL = M.getDataLayout(); - if (auto *CI = dyn_cast(&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(&I) || isa(&I) || isa(&I) || - isa(&I) || isa(&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(&I)); - Ops.append(ExtOps.begin(), ExtOps.end()); - return FromValue; - } - + if (auto *CI = dyn_cast(&I)) + return getSalvageOpsForCast(CI, DL, CurrentLocOps, OpsToAppend, + AdditionalValues); if (auto *GEP = dyn_cast(&I)) - return getSalvageOpsForGEP(GEP, DL, CurrentLocOps, Ops, AdditionalValues); + return getSalvageOpsForGEP(GEP, DL, CurrentLocOps, OpsToAppend, + AdditionalValues); if (auto *BI = dyn_cast(&I)) - return getSalvageOpsForBinOp(BI, CurrentLocOps, Ops, AdditionalValues); + return getSalvageOpsForBinOp(BI, CurrentLocOps, OpsToAppend, + AdditionalValues); if (auto *IC = dyn_cast(&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