[llvm] 1a4bc95 - [DebugInfo][NFC] Add typed DIExpression operand views (#215682)
via llvm-commits
llvm-commits at lists.llvm.org
Tue Aug 11 21:12:59 PDT 2026
Author: Eric Christopher
Date: 2026-08-12T04:12:54Z
New Revision: 1a4bc95e1a995ec001480315ed2139f5a781b647
URL: https://github.com/llvm/llvm-project/commit/1a4bc95e1a995ec001480315ed2139f5a781b647
DIFF: https://github.com/llvm/llvm-project/commit/1a4bc95e1a995ec001480315ed2139f5a781b647.diff
LOG: [DebugInfo][NFC] Add typed DIExpression operand views (#215682)
DIExpression users check an opcode and then read its arguments with
numbered getArg() calls. Add typed views for the operations with
repeated raw accesses and use their named accessors in those consumers.
The views reuse ExprOperand's pointer storage and LLVM's cast helpers. A
failed dyn_cast returns an empty view, so conditional matches don't need
an optional wrapper. Reading one is a bug, so getOp() and getArg() now
assert the operand is there rather than dereferencing null.
The only interesting change is that getActiveBits loses the fallthrough
from its extract case into its fragment case. The two shared one read
because both opcodes keep their size in the same argument; now each
names its own through its view and they share a lambda for
the narrowing. A unit test covers both paths.
Overall a bit more code, but quite a bit more readable IMO.
Tested with check-llvm and the IR unit tests.
Assisted by AI.
Added:
Modified:
llvm/include/llvm/IR/DebugInfoMetadata.h
llvm/lib/CodeGen/AsmPrinter/DebugHandlerBase.cpp
llvm/lib/CodeGen/AsmPrinter/DwarfExpression.cpp
llvm/lib/DebugInfo/LogicalView/Readers/LVIRReader.cpp
llvm/lib/IR/AsmWriter.cpp
llvm/lib/IR/DIExpressionOptimizer.cpp
llvm/lib/IR/DebugInfoMetadata.cpp
llvm/lib/Transforms/Scalar/LoopStrengthReduce.cpp
llvm/lib/Transforms/Scalar/SROA.cpp
llvm/unittests/IR/MetadataTest.cpp
Removed:
################################################################################
diff --git a/llvm/include/llvm/IR/DebugInfoMetadata.h b/llvm/include/llvm/IR/DebugInfoMetadata.h
index 33a1a6e482555..a1916675b5dc6 100644
--- a/llvm/include/llvm/IR/DebugInfoMetadata.h
+++ b/llvm/include/llvm/IR/DebugInfoMetadata.h
@@ -34,6 +34,7 @@
#include <cstdint>
#include <iterator>
#include <optional>
+#include <type_traits>
#include <vector>
// Helper macros for defining get() overrides.
@@ -3534,15 +3535,29 @@ class DIExpression : public MDNode {
ExprOperand() = default;
explicit ExprOperand(const uint64_t *Op) : Op(Op) {}
+ explicit operator bool() const { return Op != nullptr; }
+
const uint64_t *get() const { return Op; }
/// Get the operand code.
- uint64_t getOp() const { return *Op; }
+ ///
+ /// The operand has to be present.
+ uint64_t getOp() const {
+ assert(Op && "operand is not present");
+ return *Op;
+ }
+
+ /// Return true if this is \p Opcode.
+ bool is(uint64_t Opcode) const { return getOp() == Opcode; }
/// Get an argument to the operand.
///
- /// Never returns the operand itself.
- uint64_t getArg(unsigned I) const { return Op[I + 1]; }
+ /// Never returns the operand itself. The operand has to be present and \p I
+ /// has to be less than getNumArgs().
+ uint64_t getArg(unsigned I) const {
+ assert(Op && "operand is not present");
+ return Op[I + 1];
+ }
unsigned getNumArgs() const { return getSize() - 1; }
@@ -3561,6 +3576,146 @@ class DIExpression : public MDNode {
}
};
+ // Typed views name an ExprOperand's arguments. Use cast<FragmentOp>(Op) for a
+ // known opcode and dyn_cast<ArgOp>(Op) for a conditional match. A failed
+ // dyn_cast returns an empty view, which tests false and holds no operand to
+ // read, so check it before calling an accessor. Keep using ExprOperand for
+ // operations without a typed view.
+ //
+ // A view takes an operand rather than an optional one. A cursor hands back
+ // std::optional<ExprOperand>, so check it and then dereference it.
+ // dyn_cast_if_present does not compile on std::optional<ExprOperand>, because
+ // an operand is constructible from a null pointer, which leaves
+ // ValueIsPresent ambiguous between its optional and its nullable
+ // specialization.
+
+ /// A view of a DW_OP_LLVM_arg operation.
+ class ArgOp : public ExprOperand {
+ template <typename To, typename From, typename Enable>
+ friend struct llvm::CastInfo;
+
+ explicit ArgOp(ExprOperand Op) : ExprOperand(Op) {}
+
+ public:
+ /// Return the location operand index.
+ uint64_t getIndex() const { return getArg(0); }
+
+ LLVM_ABI static bool classof(const ExprOperand *Op);
+ };
+
+ /// A view of a DW_OP_LLVM_fragment operation.
+ class FragmentOp : public ExprOperand {
+ template <typename To, typename From, typename Enable>
+ friend struct llvm::CastInfo;
+
+ explicit FragmentOp(ExprOperand Op) : ExprOperand(Op) {}
+
+ public:
+ /// Return the fragment offset in bits.
+ uint64_t getOffsetInBits() const { return getArg(0); }
+
+ /// Return the fragment size in bits.
+ uint64_t getSizeInBits() const { return getArg(1); }
+
+ LLVM_ABI static bool classof(const ExprOperand *Op);
+ };
+
+ /// A view of the DW_OP_LLVM_extract_bits_[sz]ext operations.
+ class ExtractBitsOp : public ExprOperand {
+ template <typename To, typename From, typename Enable>
+ friend struct llvm::CastInfo;
+
+ explicit ExtractBitsOp(ExprOperand Op) : ExprOperand(Op) {}
+
+ public:
+ /// Return the extract offset in bits.
+ uint64_t getOffsetInBits() const { return getArg(0); }
+
+ /// Return the extract size in bits.
+ uint64_t getSizeInBits() const { return getArg(1); }
+
+ /// Return whether the extracted value is sign-extended.
+ LLVM_ABI bool isSigned() const;
+
+ LLVM_ABI static bool classof(const ExprOperand *Op);
+ };
+
+ /// A view of a DW_OP_LLVM_convert operation.
+ class ConvertOp : public ExprOperand {
+ template <typename To, typename From, typename Enable>
+ friend struct llvm::CastInfo;
+
+ explicit ConvertOp(ExprOperand Op) : ExprOperand(Op) {}
+
+ public:
+ /// Return the destination size in bits.
+ uint64_t getBitSize() const { return getArg(0); }
+
+ /// Return the raw destination type encoding.
+ uint64_t getEncoding() const { return getArg(1); }
+
+ LLVM_ABI static bool classof(const ExprOperand *Op);
+ };
+
+ /// A view of a DW_OP_LLVM_entry_value operation.
+ class EntryValueOp : public ExprOperand {
+ template <typename To, typename From, typename Enable>
+ friend struct llvm::CastInfo;
+
+ explicit EntryValueOp(ExprOperand Op) : ExprOperand(Op) {}
+
+ public:
+ /// Return the number of operations the entry value covers. The count
+ /// includes the operation that precedes it, so the operations that follow
+ /// are one fewer than this.
+ uint64_t getNumOperations() const { return getArg(0); }
+
+ LLVM_ABI static bool classof(const ExprOperand *Op);
+ };
+
+ /// A view of a DW_OP_LLVM_tag_offset operation.
+ class TagOffsetOp : public ExprOperand {
+ template <typename To, typename From, typename Enable>
+ friend struct llvm::CastInfo;
+
+ explicit TagOffsetOp(ExprOperand Op) : ExprOperand(Op) {}
+
+ public:
+ /// Return the offset a memory tag is derived from. How a target derives
+ /// the tag from it is implementation defined.
+ uint64_t getTagOffset() const { return getArg(0); }
+
+ LLVM_ABI static bool classof(const ExprOperand *Op);
+ };
+
+ /// A view of a DW_OP_constu operation.
+ class ConstuOp : public ExprOperand {
+ template <typename To, typename From, typename Enable>
+ friend struct llvm::CastInfo;
+
+ explicit ConstuOp(ExprOperand Op) : ExprOperand(Op) {}
+
+ public:
+ /// Return the unsigned constant value.
+ uint64_t getValue() const { return getArg(0); }
+
+ LLVM_ABI static bool classof(const ExprOperand *Op);
+ };
+
+ /// A view of a DW_OP_plus_uconst operation.
+ class PlusUconstOp : public ExprOperand {
+ template <typename To, typename From, typename Enable>
+ friend struct llvm::CastInfo;
+
+ explicit PlusUconstOp(ExprOperand Op) : ExprOperand(Op) {}
+
+ public:
+ /// Return the unsigned offset.
+ uint64_t getOffset() const { return getArg(0); }
+
+ LLVM_ABI static bool classof(const ExprOperand *Op);
+ };
+
/// An iterator for expression operands.
class expr_op_iterator {
ExprOperand Op;
@@ -3930,6 +4085,31 @@ class DIExpression : public MDNode {
LLVM_ABI DIExpression *foldConstantMath();
};
+template <typename To, typename From>
+struct CastInfo<
+ To, From,
+ std::enable_if_t<
+ std::is_same_v<std::remove_const_t<From>, DIExpression::ExprOperand> &&
+ !std::is_same_v<std::remove_const_t<To>, DIExpression::ExprOperand>>>
+ : CastIsPossible<To, From>,
+ DefaultDoCastIfPossible<To, From, CastInfo<To, From>> {
+ static To doCast(const From &Op) { return To(Op); }
+ static To castFailed() { return To(DIExpression::ExprOperand()); }
+};
+
+/// Treat a default-constructed expression operand as absent.
+template <> struct ValueIsPresent<DIExpression::ExprOperand> {
+ using UnwrappedType = DIExpression::ExprOperand;
+
+ static bool isPresent(const DIExpression::ExprOperand &Op) {
+ return bool(Op);
+ }
+
+ static DIExpression::ExprOperand &unwrapValue(DIExpression::ExprOperand &Op) {
+ return Op;
+ }
+};
+
inline bool operator==(const DIExpression::FragmentInfo &A,
const DIExpression::FragmentInfo &B) {
return std::tie(A.SizeInBits, A.OffsetInBits) ==
diff --git a/llvm/lib/CodeGen/AsmPrinter/DebugHandlerBase.cpp b/llvm/lib/CodeGen/AsmPrinter/DebugHandlerBase.cpp
index 8787c12bf3a5f..79576dc5610eb 100644
--- a/llvm/lib/CodeGen/AsmPrinter/DebugHandlerBase.cpp
+++ b/llvm/lib/CodeGen/AsmPrinter/DebugHandlerBase.cpp
@@ -58,7 +58,7 @@ DbgVariableLocation::extractFromMachineInstruction(
while (Op != DIExpr->expr_op_end()) {
switch (Op->getOp()) {
case dwarf::DW_OP_constu: {
- int Value = Op->getArg(0);
+ int Value = cast<DIExpression::ConstuOp>(*Op).getValue();
++Op;
if (Op != DIExpr->expr_op_end()) {
switch (Op->getOp()) {
@@ -74,11 +74,14 @@ DbgVariableLocation::extractFromMachineInstruction(
}
} break;
case dwarf::DW_OP_plus_uconst:
- Offset += Op->getArg(0);
+ Offset += cast<DIExpression::PlusUconstOp>(*Op).getOffset();
break;
- case dwarf::DW_OP_LLVM_fragment:
- Location.FragmentInfo = {Op->getArg(1), Op->getArg(0)};
+ case dwarf::DW_OP_LLVM_fragment: {
+ auto Fragment = cast<DIExpression::FragmentOp>(*Op);
+ Location.FragmentInfo = {Fragment.getSizeInBits(),
+ Fragment.getOffsetInBits()};
break;
+ }
case dwarf::DW_OP_deref:
Location.LoadChain.push_back(Offset);
Offset = 0;
diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfExpression.cpp b/llvm/lib/CodeGen/AsmPrinter/DwarfExpression.cpp
index 70113e7910d3e..2e022f30842e7 100644
--- a/llvm/lib/CodeGen/AsmPrinter/DwarfExpression.cpp
+++ b/llvm/lib/CodeGen/AsmPrinter/DwarfExpression.cpp
@@ -394,9 +394,10 @@ bool DwarfExpression::addMachineRegExpression(const TargetRegisterInfo &TRI,
// Record the tag offset here because addExpression won't see a consumed
// operation.
while (auto Op = ExprCursor.peek()) {
- if (Op->getOp() != dwarf::DW_OP_LLVM_tag_offset)
+ auto Tag = dyn_cast<DIExpression::TagOffsetOp>(*Op);
+ if (!Tag)
break;
- TagOffset = Op->getArg(0);
+ TagOffset = Tag.getTagOffset();
ExprCursor.take();
}
@@ -406,30 +407,30 @@ bool DwarfExpression::addMachineRegExpression(const TargetRegisterInfo &TRI,
assert(!Reg.isSubRegister() && "full register expected");
// Pattern-match combinations for which more efficient representations exist.
- // [Reg, DW_OP_plus_uconst, Offset] --> [DW_OP_breg, Offset].
- if (Op && (Op->getOp() == dwarf::DW_OP_plus_uconst)) {
- uint64_t Offset = Op->getArg(0);
- uint64_t IntMax = static_cast<uint64_t>(std::numeric_limits<int>::max());
- if (Offset <= IntMax) {
- SignedOffset = Offset;
- ExprCursor.take();
- }
- }
-
- // [Reg, DW_OP_constu, Offset, DW_OP_plus] --> [DW_OP_breg, Offset]
- // [Reg, DW_OP_constu, Offset, DW_OP_minus] --> [DW_OP_breg,-Offset]
- // If Reg is a subregister we need to mask it out before subtracting.
- if (Op && Op->getOp() == dwarf::DW_OP_constu) {
- uint64_t Offset = Op->getArg(0);
- uint64_t IntMax = static_cast<uint64_t>(std::numeric_limits<int>::max());
- auto N = ExprCursor.peekNext();
- if (N && N->getOp() == dwarf::DW_OP_plus && Offset <= IntMax) {
- SignedOffset = Offset;
- ExprCursor.consume(2);
- } else if (N && N->getOp() == dwarf::DW_OP_minus &&
- !SubRegisterSizeInBits && Offset <= IntMax + 1) {
- SignedOffset = -static_cast<int64_t>(Offset);
- ExprCursor.consume(2);
+ if (Op) {
+ const uint64_t IntMax =
+ static_cast<uint64_t>(std::numeric_limits<int>::max());
+ // [Reg, DW_OP_plus_uconst, Offset] --> [DW_OP_breg, Offset].
+ if (auto PlusUconst = dyn_cast<DIExpression::PlusUconstOp>(*Op)) {
+ uint64_t Offset = PlusUconst.getOffset();
+ if (Offset <= IntMax) {
+ SignedOffset = Offset;
+ ExprCursor.take();
+ }
+ } else if (auto Constant = dyn_cast<DIExpression::ConstuOp>(*Op)) {
+ // [Reg, DW_OP_constu, Offset, DW_OP_plus] --> [DW_OP_breg, Offset]
+ // [Reg, DW_OP_constu, Offset, DW_OP_minus] --> [DW_OP_breg,-Offset]
+ // If Reg is a subregister we need to mask it out before subtracting.
+ uint64_t Offset = Constant.getValue();
+ auto N = ExprCursor.peekNext();
+ if (N && N->getOp() == dwarf::DW_OP_plus && Offset <= IntMax) {
+ SignedOffset = Offset;
+ ExprCursor.consume(2);
+ } else if (N && N->getOp() == dwarf::DW_OP_minus &&
+ !SubRegisterSizeInBits && Offset <= IntMax + 1) {
+ SignedOffset = -static_cast<int64_t>(Offset);
+ ExprCursor.consume(2);
+ }
}
}
@@ -466,9 +467,9 @@ void DwarfExpression::beginEntryValueExpression(
DIExpressionCursor &ExprCursor) {
auto Op = ExprCursor.take();
(void)Op;
- assert(Op && Op->getOp() == dwarf::DW_OP_LLVM_entry_value);
+ assert(Op && isa<DIExpression::EntryValueOp>(*Op));
assert(!IsEmittingEntryValue && "Already emitting entry value?");
- assert(Op->getArg(0) == 1 &&
+ assert(cast<DIExpression::EntryValueOp>(*Op).getNumOperations() == 1 &&
"Can currently only emit entry values covering a single operation");
SavedLocationKind = LocationKind;
@@ -554,7 +555,7 @@ bool DwarfExpression::addExpression(
// and not any other parts of the following DWARF expression.
assert(!IsEmittingEntryValue && "Can't emit entry value around expression");
- std::optional<DIExpression::ExprOperand> PrevConvertOp;
+ std::optional<DIExpression::ConvertOp> PrevConvertOp;
while (ExprCursor) {
auto Op = ExprCursor.take();
@@ -570,14 +571,15 @@ bool DwarfExpression::addExpression(
switch (OpNum) {
case dwarf::DW_OP_LLVM_arg:
- if (!InsertArg(Op->getArg(0), ExprCursor)) {
+ if (!InsertArg(cast<DIExpression::ArgOp>(*Op).getIndex(), ExprCursor)) {
LocationKind = Unknown;
return false;
}
break;
case dwarf::DW_OP_LLVM_fragment: {
- unsigned SizeInBits = Op->getArg(1);
- unsigned FragmentOffset = Op->getArg(0);
+ auto Fragment = cast<DIExpression::FragmentOp>(*Op);
+ unsigned SizeInBits = Fragment.getSizeInBits();
+ unsigned FragmentOffset = Fragment.getOffsetInBits();
// The fragment offset must have already been adjusted by emitting an
// empty DW_OP_piece / DW_OP_bit_piece before we emitted the base
// location.
@@ -607,8 +609,10 @@ bool DwarfExpression::addExpression(
}
case dwarf::DW_OP_LLVM_extract_bits_sext:
case dwarf::DW_OP_LLVM_extract_bits_zext: {
- unsigned SizeInBits = Op->getArg(1);
- unsigned BitOffset = Op->getArg(0);
+ auto Extract = cast<DIExpression::ExtractBitsOp>(*Op);
+ unsigned SizeInBits = Extract.getSizeInBits();
+ unsigned BitOffset = Extract.getOffsetInBits();
+ bool IsSigned = Extract.isSigned();
unsigned DerefSize = 0;
// Operations are done in the DWARF "generic type" whose size
// is the size of a pointer.
@@ -630,7 +634,7 @@ bool DwarfExpression::addExpression(
// If a dereference was emitted for an unsigned value, and
// there's no bit offset, then a bit of optimization is
// possible.
- if (OpNum == dwarf::DW_OP_LLVM_extract_bits_zext && BitOffset == 0) {
+ if (!IsSigned && BitOffset == 0) {
if (8 * DerefSize == SizeInBits) {
// The correct value is already on the stack.
} else {
@@ -653,9 +657,7 @@ bool DwarfExpression::addExpression(
if (RightShift) {
emitOp(dwarf::DW_OP_constu);
emitUnsigned(RightShift);
- emitOp(OpNum == dwarf::DW_OP_LLVM_extract_bits_sext
- ? dwarf::DW_OP_shra
- : dwarf::DW_OP_shr);
+ emitOp(IsSigned ? dwarf::DW_OP_shra : dwarf::DW_OP_shr);
}
}
@@ -667,7 +669,7 @@ bool DwarfExpression::addExpression(
case dwarf::DW_OP_plus_uconst:
assert(!isRegisterLocation());
emitOp(dwarf::DW_OP_plus_uconst);
- emitUnsigned(Op->getArg(0));
+ emitUnsigned(cast<DIExpression::PlusUconstOp>(*Op).getOffset());
break;
case dwarf::DW_OP_plus:
case dwarf::DW_OP_minus:
@@ -707,7 +709,7 @@ bool DwarfExpression::addExpression(
break;
case dwarf::DW_OP_constu:
assert(!isRegisterLocation());
- emitConstu(Op->getArg(0));
+ emitConstu(cast<DIExpression::ConstuOp>(*Op).getValue());
break;
case dwarf::DW_OP_consts:
assert(!isRegisterLocation());
@@ -715,8 +717,10 @@ bool DwarfExpression::addExpression(
emitSigned(Op->getArg(0));
break;
case dwarf::DW_OP_LLVM_convert: {
- unsigned BitSize = Op->getArg(0);
- dwarf::TypeKind Encoding = static_cast<dwarf::TypeKind>(Op->getArg(1));
+ auto Convert = cast<DIExpression::ConvertOp>(*Op);
+ unsigned BitSize = Convert.getBitSize();
+ dwarf::TypeKind Encoding =
+ static_cast<dwarf::TypeKind>(Convert.getEncoding());
if (DwarfVersion >= 5 && CU.getDwarfDebug().useOpConvert()) {
emitOp(dwarf::DW_OP_convert);
// If targeting a location-list; simply emit the index into the raw
@@ -727,14 +731,14 @@ bool DwarfExpression::addExpression(
// DIE value list.
emitBaseTypeRef(getOrCreateBaseType(BitSize, Encoding));
} else {
- if (PrevConvertOp && PrevConvertOp->getArg(0) < BitSize) {
+ if (PrevConvertOp && PrevConvertOp->getBitSize() < BitSize) {
if (Encoding == dwarf::DW_ATE_signed)
- emitLegacySExt(PrevConvertOp->getArg(0));
+ emitLegacySExt(PrevConvertOp->getBitSize());
else if (Encoding == dwarf::DW_ATE_unsigned)
- emitLegacyZExt(PrevConvertOp->getArg(0));
+ emitLegacyZExt(PrevConvertOp->getBitSize());
PrevConvertOp = std::nullopt;
} else {
- PrevConvertOp = Op;
+ PrevConvertOp = Convert;
}
}
break;
@@ -755,7 +759,7 @@ bool DwarfExpression::addExpression(
emitData1(Op->getArg(0));
break;
case dwarf::DW_OP_LLVM_tag_offset:
- TagOffset = Op->getArg(0);
+ TagOffset = cast<DIExpression::TagOffsetOp>(*Op).getTagOffset();
break;
case dwarf::DW_OP_regx:
emitOp(dwarf::DW_OP_regx);
diff --git a/llvm/lib/DebugInfo/LogicalView/Readers/LVIRReader.cpp b/llvm/lib/DebugInfo/LogicalView/Readers/LVIRReader.cpp
index 5726955a029ef..dbc8268df5d3f 100644
--- a/llvm/lib/DebugInfo/LogicalView/Readers/LVIRReader.cpp
+++ b/llvm/lib/DebugInfo/LogicalView/Readers/LVIRReader.cpp
@@ -2123,9 +2123,8 @@ void LVIRReader::processBasicBlocks(Function &F) {
DIExpression::convertToVariadicExpression(DV.Expression));
RawLocationWrapper Locations(DV.Locations);
for (DIExpression::ExprOperand ExprOp : CanonicalExpr->expr_ops()) {
- if (ExprOp.getOp() == dwarf::DW_OP_LLVM_arg) {
- AddLocationOp(Locations.getVariableLocationOp(ExprOp.getArg(0)),
- IsMem);
+ if (auto Arg = dyn_cast<DIExpression::ArgOp>(ExprOp)) {
+ AddLocationOp(Locations.getVariableLocationOp(Arg.getIndex()), IsMem);
} else {
if (ExprOp.getOp() > std::numeric_limits<uint8_t>::max())
LLVM_DEBUG(dbgs() << "Bad DWARF op: " << ExprOp.getOp() << "\n");
diff --git a/llvm/lib/IR/AsmWriter.cpp b/llvm/lib/IR/AsmWriter.cpp
index cad4f17b0db91..5b577d76bf4df 100644
--- a/llvm/lib/IR/AsmWriter.cpp
+++ b/llvm/lib/IR/AsmWriter.cpp
@@ -2646,9 +2646,9 @@ static void writeDIExpression(raw_ostream &Out, const DIExpression *N,
assert(!OpStr.empty() && "Expected valid opcode");
Out << FS << OpStr;
- if (Op.getOp() == dwarf::DW_OP_LLVM_convert) {
- Out << FS << Op.getArg(0);
- Out << FS << dwarf::AttributeEncodingString(Op.getArg(1));
+ if (auto Convert = dyn_cast<DIExpression::ConvertOp>(Op)) {
+ Out << FS << Convert.getBitSize();
+ Out << FS << dwarf::AttributeEncodingString(Convert.getEncoding());
} else {
for (unsigned A = 0, AE = Op.getNumArgs(); A != AE; ++A)
Out << FS << Op.getArg(A);
diff --git a/llvm/lib/IR/DIExpressionOptimizer.cpp b/llvm/lib/IR/DIExpressionOptimizer.cpp
index be9e13a34235a..7e19da303d6b8 100644
--- a/llvm/lib/IR/DIExpressionOptimizer.cpp
+++ b/llvm/lib/IR/DIExpressionOptimizer.cpp
@@ -18,8 +18,8 @@ using namespace llvm;
/// Returns true if the Op is a DW_OP_constu.
static std::optional<uint64_t> isConstantVal(DIExpression::ExprOperand Op) {
- if (Op.getOp() == dwarf::DW_OP_constu)
- return Op.getArg(0);
+ if (auto Constant = dyn_cast<DIExpression::ConstuOp>(Op))
+ return Constant.getValue();
return std::nullopt;
}
@@ -129,9 +129,9 @@ canonicalizeDwarfOperations(ArrayRef<uint64_t> WorkingOps) {
consumeOneOperator(Cursor, Loc, *Cursor.peek());
continue;
}
- if (OpRaw == dwarf::DW_OP_plus_uconst) {
+ if (auto PlusUconst = dyn_cast<DIExpression::PlusUconstOp>(*Op)) {
ResultOps.push_back(dwarf::DW_OP_constu);
- ResultOps.push_back(Op->getArg(0));
+ ResultOps.push_back(PlusUconst.getOffset());
ResultOps.push_back(dwarf::DW_OP_plus);
consumeOneOperator(Cursor, Loc, *Cursor.peek());
continue;
@@ -156,9 +156,9 @@ optimizeDwarfOperations(ArrayRef<uint64_t> WorkingOps) {
/// Expression has no operations, exit.
if (!Op1)
break;
- auto Op1Raw = Op1->getOp();
- if (Op1Raw == dwarf::DW_OP_constu && Op1->getArg(0) == 0) {
+ auto Constant = dyn_cast<DIExpression::ConstuOp>(*Op1);
+ if (Constant && Constant.getValue() == 0) {
ResultOps.push_back(dwarf::DW_OP_lit0);
consumeOneOperator(Cursor, Loc, *Cursor.peek());
continue;
@@ -174,9 +174,9 @@ optimizeDwarfOperations(ArrayRef<uint64_t> WorkingOps) {
}
auto Op2Raw = Op2->getOp();
- if (Op1Raw == dwarf::DW_OP_constu && Op2Raw == dwarf::DW_OP_plus) {
+ if (Constant && Op2Raw == dwarf::DW_OP_plus) {
ResultOps.push_back(dwarf::DW_OP_plus_uconst);
- ResultOps.push_back(Op1->getArg(0));
+ ResultOps.push_back(Constant.getValue());
consumeOneOperator(Cursor, Loc, *Cursor.peek());
consumeOneOperator(Cursor, Loc, *Cursor.peek());
continue;
diff --git a/llvm/lib/IR/DebugInfoMetadata.cpp b/llvm/lib/IR/DebugInfoMetadata.cpp
index 5e9e0e4e81c6e..134f1382dfc25 100644
--- a/llvm/lib/IR/DebugInfoMetadata.cpp
+++ b/llvm/lib/IR/DebugInfoMetadata.cpp
@@ -1761,6 +1761,43 @@ bool DIExpression::ExprOperand::isNonEmitting() const {
return getOp() == dwarf::DW_OP_LLVM_tag_offset;
}
+bool DIExpression::ArgOp::classof(const ExprOperand *Op) {
+ return Op->is(dwarf::DW_OP_LLVM_arg);
+}
+
+bool DIExpression::FragmentOp::classof(const ExprOperand *Op) {
+ return Op->is(dwarf::DW_OP_LLVM_fragment);
+}
+
+bool DIExpression::ExtractBitsOp::classof(const ExprOperand *Op) {
+ return Op->is(dwarf::DW_OP_LLVM_extract_bits_sext) ||
+ Op->is(dwarf::DW_OP_LLVM_extract_bits_zext);
+}
+
+bool DIExpression::ExtractBitsOp::isSigned() const {
+ return is(dwarf::DW_OP_LLVM_extract_bits_sext);
+}
+
+bool DIExpression::ConvertOp::classof(const ExprOperand *Op) {
+ return Op->is(dwarf::DW_OP_LLVM_convert);
+}
+
+bool DIExpression::EntryValueOp::classof(const ExprOperand *Op) {
+ return Op->is(dwarf::DW_OP_LLVM_entry_value);
+}
+
+bool DIExpression::TagOffsetOp::classof(const ExprOperand *Op) {
+ return Op->is(dwarf::DW_OP_LLVM_tag_offset);
+}
+
+bool DIExpression::ConstuOp::classof(const ExprOperand *Op) {
+ return Op->is(dwarf::DW_OP_constu);
+}
+
+bool DIExpression::PlusUconstOp::classof(const ExprOperand *Op) {
+ return Op->is(dwarf::DW_OP_plus_uconst);
+}
+
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.
@@ -1807,9 +1844,10 @@ bool DIExpression::isValid() const {
// register location. One reason for this is that we currently can't
// calculate the size of the resulting DWARF block for other expressions.
auto FirstOp = expr_op_begin();
- if (FirstOp->getOp() == dwarf::DW_OP_LLVM_arg && FirstOp->getArg(0) == 0)
+ if (auto Arg = dyn_cast<ArgOp>(*FirstOp); Arg && Arg.getIndex() == 0)
++FirstOp;
- if (I->get() != FirstOp->get() || I->getArg(0) != 1)
+ if (I->get() != FirstOp->get() ||
+ cast<EntryValueOp>(*I).getNumOperations() != 1)
return false;
break;
}
@@ -1910,15 +1948,14 @@ bool DIExpression::isSingleLocationExpression() const {
auto ExprOpBegin = expr_ops().begin();
auto ExprOpEnd = expr_ops().end();
- if (ExprOpBegin->getOp() == dwarf::DW_OP_LLVM_arg) {
- if (ExprOpBegin->getArg(0) != 0)
+ if (auto Arg = dyn_cast<ArgOp>(*ExprOpBegin)) {
+ if (Arg.getIndex() != 0)
return false;
++ExprOpBegin;
}
- return !std::any_of(ExprOpBegin, ExprOpEnd, [](auto Op) {
- return Op.getOp() == dwarf::DW_OP_LLVM_arg;
- });
+ return !std::any_of(ExprOpBegin, ExprOpEnd,
+ [](auto Op) { return Op.is(dwarf::DW_OP_LLVM_arg); });
}
std::optional<ArrayRef<uint64_t>>
@@ -2017,16 +2054,18 @@ bool DIExpression::isEqualExpression(const DIExpression *FirstExpr,
std::optional<DIExpression::FragmentInfo>
DIExpression::getFragmentInfo(expr_op_iterator Start, expr_op_iterator End) {
for (auto I = Start; I != End; ++I)
- if (I->getOp() == dwarf::DW_OP_LLVM_fragment) {
- DIExpression::FragmentInfo Info = {I->getArg(1), I->getArg(0)};
- return Info;
- }
+ if (auto Fragment = dyn_cast<FragmentOp>(*I))
+ return FragmentInfo{Fragment.getSizeInBits(), Fragment.getOffsetInBits()};
return std::nullopt;
}
std::optional<uint64_t> DIExpression::getActiveBits(DIVariable *Var) {
std::optional<uint64_t> InitialActiveBits = Var->getSizeInBits();
std::optional<uint64_t> ActiveBits = InitialActiveBits;
+ auto NarrowActiveBits = [&](uint64_t SizeInBits) {
+ ActiveBits = ActiveBits ? std::min(*ActiveBits, SizeInBits) : SizeInBits;
+ };
+
for (auto Op : expr_ops()) {
switch (Op.getOp()) {
default:
@@ -2036,23 +2075,20 @@ std::optional<uint64_t> DIExpression::getActiveBits(DIVariable *Var) {
break;
case dwarf::DW_OP_LLVM_extract_bits_zext:
case dwarf::DW_OP_LLVM_extract_bits_sext: {
+ auto Extract = cast<ExtractBitsOp>(Op);
// We can't handle an extract whose sign doesn't match that of the
// variable.
std::optional<DIBasicType::Signedness> VarSign = Var->getSignedness();
bool VarSigned = (VarSign == DIBasicType::Signedness::Signed);
- bool OpSigned = (Op.getOp() == dwarf::DW_OP_LLVM_extract_bits_sext);
- if (!VarSign || VarSigned != OpSigned) {
+ if (!VarSign || VarSigned != Extract.isSigned()) {
ActiveBits = InitialActiveBits;
break;
}
- [[fallthrough]];
+ NarrowActiveBits(Extract.getSizeInBits());
+ break;
}
case dwarf::DW_OP_LLVM_fragment:
- // Extract or fragment narrows the active bits
- if (ActiveBits)
- ActiveBits = std::min(*ActiveBits, Op.getArg(1));
- else
- ActiveBits = Op.getArg(1);
+ NarrowActiveBits(cast<FragmentOp>(Op).getSizeInBits());
break;
}
}
@@ -2120,10 +2156,10 @@ bool DIExpression::extractLeadingOffset(
Op == dwarf::DW_OP_LLVM_extract_bits_zext ||
Op == dwarf::DW_OP_LLVM_extract_bits_sext) {
break;
- } else if (Op == dwarf::DW_OP_plus_uconst) {
- OffsetInBytes += ExprOpIt->getArg(0);
- } else if (Op == dwarf::DW_OP_constu) {
- uint64_t Value = ExprOpIt->getArg(0);
+ } else if (auto PlusUconst = dyn_cast<PlusUconstOp>(*ExprOpIt)) {
+ OffsetInBytes += PlusUconst.getOffset();
+ } else if (auto Constant = dyn_cast<ConstuOp>(*ExprOpIt)) {
+ uint64_t Value = Constant.getValue();
++ExprOpIt;
if (ExprOpIt->getOp() == dwarf::DW_OP_plus)
OffsetInBytes += Value;
@@ -2156,8 +2192,8 @@ bool DIExpression::extractLeadingOffset(
bool DIExpression::hasAllLocationOps(unsigned N) const {
SmallDenseSet<uint64_t, 4> SeenOps;
for (auto ExprOp : expr_ops())
- if (ExprOp.getOp() == dwarf::DW_OP_LLVM_arg)
- SeenOps.insert(ExprOp.getArg(0));
+ if (auto Arg = dyn_cast<ArgOp>(ExprOp))
+ SeenOps.insert(Arg.getIndex());
for (uint64_t Idx = 0; Idx < N; ++Idx)
if (!SeenOps.contains(Idx))
return false;
@@ -2212,7 +2248,7 @@ DIExpression *DIExpression::appendOpsToArg(const DIExpression *Expr,
// Handle non-variadic intrinsics by prepending the opcodes.
if (!any_of(Expr->expr_ops(),
- [](auto Op) { return Op.getOp() == dwarf::DW_OP_LLVM_arg; })) {
+ [](auto Op) { return Op.is(dwarf::DW_OP_LLVM_arg); })) {
assert(ArgNo == 0 &&
"Location Index must be 0 for a non-variadic expression.");
SmallVector<uint64_t, 8> NewOps(Ops);
@@ -2231,7 +2267,7 @@ DIExpression *DIExpression::appendOpsToArg(const DIExpression *Expr,
}
}
Op.appendToVector(NewOps);
- if (Op.getOp() == dwarf::DW_OP_LLVM_arg && Op.getArg(0) == ArgNo)
+ if (auto Arg = dyn_cast<ArgOp>(Op); Arg && Arg.getIndex() == ArgNo)
llvm::append_range(NewOps, Ops);
}
if (StackValue)
@@ -2247,17 +2283,18 @@ DIExpression *DIExpression::replaceArg(const DIExpression *Expr,
SmallVector<uint64_t, 8> NewOps;
for (auto Op : Expr->expr_ops()) {
- if (Op.getOp() != dwarf::DW_OP_LLVM_arg || Op.getArg(0) < OldArg) {
+ auto Arg = dyn_cast<ArgOp>(Op);
+ if (!Arg || Arg.getIndex() < OldArg) {
Op.appendToVector(NewOps);
continue;
}
NewOps.push_back(dwarf::DW_OP_LLVM_arg);
- uint64_t Arg = Op.getArg(0) == OldArg ? NewArg : Op.getArg(0);
+ uint64_t ArgIndex = Arg.getIndex() == OldArg ? NewArg : Arg.getIndex();
// OldArg has been deleted from the Op list, so decrement all indices
// greater than it.
- if (Arg > OldArg)
- --Arg;
- NewOps.push_back(Arg);
+ if (ArgIndex > OldArg)
+ --ArgIndex;
+ NewOps.push_back(ArgIndex);
}
return DIExpression::get(Expr->getContext(), NewOps);
}
@@ -2396,14 +2433,15 @@ std::optional<DIExpression *> DIExpression::createFragmentExpression(
return std::nullopt;
break;
case dwarf::DW_OP_LLVM_fragment: {
+ auto Fragment = cast<FragmentOp>(Op);
// 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 std::nullopt;
// Make the new offset point into the existing fragment.
- uint64_t FragmentOffsetInBits = Op.getArg(0);
- uint64_t FragmentSizeInBits = Op.getArg(1);
+ uint64_t FragmentOffsetInBits = Fragment.getOffsetInBits();
+ uint64_t FragmentSizeInBits = Fragment.getSizeInBits();
(void)FragmentSizeInBits;
assert((OffsetInBits + SizeInBits <= FragmentSizeInBits) &&
"new fragment outside of original fragment");
@@ -2412,11 +2450,12 @@ std::optional<DIExpression *> DIExpression::createFragmentExpression(
}
case dwarf::DW_OP_LLVM_extract_bits_zext:
case dwarf::DW_OP_LLVM_extract_bits_sext: {
+ auto Extract = cast<ExtractBitsOp>(Op);
// 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);
+ uint64_t ExtractOffsetInBits = Extract.getOffsetInBits();
+ uint64_t ExtractSizeInBits = Extract.getSizeInBits();
if (ExtractOffsetInBits >= OffsetInBits &&
ExtractOffsetInBits + ExtractSizeInBits <=
OffsetInBits + SizeInBits) {
@@ -2533,18 +2572,21 @@ DIExpression::constantFold(const ConstantInt *CI) {
return {this, CI};
First = false;
break;
- case dwarf::DW_OP_LLVM_convert:
+ case dwarf::DW_OP_LLVM_convert: {
if (!First)
break;
Changed = true;
- if (Op.getArg(1) == dwarf::DW_ATE_signed)
- NewInt = NewInt.sextOrTrunc(Op.getArg(0));
+ auto Convert = cast<ConvertOp>(Op);
+ if (Convert.getEncoding() == dwarf::DW_ATE_signed)
+ NewInt = NewInt.sextOrTrunc(Convert.getBitSize());
else {
- assert(Op.getArg(1) == dwarf::DW_ATE_unsigned && "Unexpected operand");
- NewInt = NewInt.zextOrTrunc(Op.getArg(0));
+ assert(Convert.getEncoding() == dwarf::DW_ATE_unsigned &&
+ "Unexpected operand");
+ NewInt = NewInt.zextOrTrunc(Convert.getBitSize());
}
continue;
}
+ }
Op.appendToVector(Ops);
}
if (!Changed)
@@ -2556,8 +2598,8 @@ DIExpression::constantFold(const ConstantInt *CI) {
uint64_t DIExpression::getNumLocationOperands() const {
uint64_t Result = 0;
for (auto ExprOp : expr_ops())
- if (ExprOp.getOp() == dwarf::DW_OP_LLVM_arg)
- Result = std::max(Result, ExprOp.getArg(0) + 1);
+ if (auto Arg = dyn_cast<ArgOp>(ExprOp))
+ Result = std::max(Result, Arg.getIndex() + 1);
assert(hasAllLocationOps(Result) &&
"Expression is missing one or more location operands.");
return Result;
diff --git a/llvm/lib/Transforms/Scalar/LoopStrengthReduce.cpp b/llvm/lib/Transforms/Scalar/LoopStrengthReduce.cpp
index 4ba8d55a918b4..e2ca5f166dbb5 100644
--- a/llvm/lib/Transforms/Scalar/LoopStrengthReduce.cpp
+++ b/llvm/lib/Transforms/Scalar/LoopStrengthReduce.cpp
@@ -6798,7 +6798,8 @@ struct SCEVDbgValueBuilder {
}
for (const auto &Op : expr_ops()) {
- if (Op.getOp() != dwarf::DW_OP_LLVM_arg) {
+ auto Arg = dyn_cast<DIExpression::ArgOp>(Op);
+ if (!Arg) {
Op.appendToVector(DestExpr);
continue;
}
@@ -6806,7 +6807,7 @@ struct SCEVDbgValueBuilder {
DestExpr.push_back(dwarf::DW_OP_LLVM_arg);
// `DW_OP_LLVM_arg n` represents the nth LocationOp in this SCEV,
// DestIndexMap[n] contains its new index in DestLocations.
- uint64_t NewIndex = DestIndexMap[Op.getArg(0)];
+ uint64_t NewIndex = DestIndexMap[Arg.getIndex()];
DestExpr.push_back(NewIndex);
}
}
@@ -7028,12 +7029,13 @@ static bool SalvageDVI(llvm::Loop *L, ScalarEvolution &SE,
}
for (const auto &Op : DVIRec.Expr->expr_ops()) {
// Most Ops needn't be updated.
- if (Op.getOp() != dwarf::DW_OP_LLVM_arg) {
+ auto Arg = dyn_cast<DIExpression::ArgOp>(Op);
+ if (!Arg) {
Op.appendToVector(NewExpr);
continue;
}
- uint64_t LocationArgIndex = Op.getArg(0);
+ uint64_t LocationArgIndex = Arg.getIndex();
SCEVDbgValueBuilder *DbgBuilder =
DVIRec.RecoveryExprs[LocationArgIndex].get();
// The location doesn't have s SCEVDbgValueBuilder, so LSR did not
@@ -7041,9 +7043,9 @@ static bool SalvageDVI(llvm::Loop *L, ScalarEvolution &SE,
// location index.
if (!DbgBuilder) {
NewExpr.push_back(dwarf::DW_OP_LLVM_arg);
- assert(LocationOpIndexMap[Op.getArg(0)] != -1 &&
+ assert(LocationOpIndexMap[LocationArgIndex] != -1 &&
"Expected a positive index for the location-op position.");
- NewExpr.push_back(LocationOpIndexMap[Op.getArg(0)]);
+ NewExpr.push_back(LocationOpIndexMap[LocationArgIndex]);
continue;
}
// The location has a recovery expression.
diff --git a/llvm/lib/Transforms/Scalar/SROA.cpp b/llvm/lib/Transforms/Scalar/SROA.cpp
index bf31273b6e405..e4770154e2998 100644
--- a/llvm/lib/Transforms/Scalar/SROA.cpp
+++ b/llvm/lib/Transforms/Scalar/SROA.cpp
@@ -5718,11 +5718,10 @@ static DIExpression *createOrReplaceFragment(const DIExpression *Expr,
HasFragment = true;
continue;
}
- if (Op.getOp() == dwarf::DW_OP_LLVM_extract_bits_zext ||
- Op.getOp() == dwarf::DW_OP_LLVM_extract_bits_sext) {
+ if (auto Extract = dyn_cast<DIExpression::ExtractBitsOp>(Op)) {
HasBitExtract = true;
- int64_t ExtractOffsetInBits = Op.getArg(0);
- int64_t ExtractSizeInBits = Op.getArg(1);
+ int64_t ExtractOffsetInBits = Extract.getOffsetInBits();
+ int64_t ExtractSizeInBits = Extract.getSizeInBits();
// DIExpression::createFragmentExpression doesn't know how to handle
// a fragment that is smaller than the extract. Copy the behaviour
@@ -5940,9 +5939,8 @@ bool SROA::splitAlloca(AllocaInst &AI, AllocaSlices &AS) {
// Offset defined by a DW_OP_LLVM_extract_bits_[sz]ext.
int64_t ExtractOffsetInBits = 0;
for (auto Op : getAddressExpression(DbgVariable)->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);
+ if (auto Extract = dyn_cast<DIExpression::ExtractBitsOp>(Op)) {
+ ExtractOffsetInBits = Extract.getOffsetInBits();
break;
}
}
diff --git a/llvm/unittests/IR/MetadataTest.cpp b/llvm/unittests/IR/MetadataTest.cpp
index d601605986061..07496cec696ca 100644
--- a/llvm/unittests/IR/MetadataTest.cpp
+++ b/llvm/unittests/IR/MetadataTest.cpp
@@ -25,6 +25,7 @@
#include "llvm/Support/raw_ostream.h"
#include "gtest/gtest.h"
#include <optional>
+#include <type_traits>
using namespace llvm;
namespace llvm {
@@ -3617,6 +3618,16 @@ TEST_F(DILocalVariableTest, getArg256) {
typedef MetadataTest DIExpressionTest;
+template <typename ViewT>
+static ViewT checkOperandView(DIExpression::ExprOperand Operand,
+ DIExpression::ExprOperand NonMatch) {
+ EXPECT_TRUE(isa<ViewT>(Operand));
+ EXPECT_FALSE(isa<ViewT>(NonMatch));
+ EXPECT_TRUE(dyn_cast<ViewT>(Operand));
+ EXPECT_FALSE(dyn_cast<ViewT>(NonMatch));
+ return cast<ViewT>(Operand);
+}
+
TEST_F(DIExpressionTest, get) {
uint64_t Elements[] = {2, 6, 9, 78, 0};
auto *N = DIExpression::get(Context, Elements);
@@ -3656,6 +3667,161 @@ TEST_F(DIExpressionTest, get) {
EXPECT_EQ(N0WithPrependedOps, N2);
}
+TEST_F(DIExpressionTest, ExprOperandCasts) {
+ constexpr uint64_t RawEncoding = (uint64_t{1} << 32) | dwarf::DW_ATE_signed;
+ uint64_t Elements[] = {
+ dwarf::DW_OP_LLVM_arg,
+ 7,
+ dwarf::DW_OP_LLVM_convert,
+ 32,
+ RawEncoding,
+ dwarf::DW_OP_LLVM_extract_bits_sext,
+ 1,
+ 8,
+ dwarf::DW_OP_LLVM_extract_bits_zext,
+ 2,
+ 16,
+ dwarf::DW_OP_constu,
+ 42,
+ dwarf::DW_OP_plus_uconst,
+ 12,
+ dwarf::DW_OP_LLVM_tag_offset,
+ 3,
+ dwarf::DW_OP_LLVM_fragment,
+ 4,
+ 24,
+ };
+ auto *Expr = DIExpression::get(Context, Elements);
+ ASSERT_TRUE(Expr->isValid());
+
+ auto I = Expr->expr_op_begin();
+ auto ArgOperand = *I++;
+ auto ConvertOperand = *I++;
+ auto SignedExtractOperand = *I++;
+ auto UnsignedExtractOperand = *I++;
+ auto ConstuOperand = *I++;
+ auto PlusUconstOperand = *I++;
+ auto TagOffsetOperand = *I++;
+ auto FragmentOperand = *I++;
+ ASSERT_EQ(I, Expr->expr_op_end());
+
+ using ArgOp = DIExpression::ArgOp;
+ using ConstuOp = DIExpression::ConstuOp;
+ using ConvertOp = DIExpression::ConvertOp;
+ using EntryValueOp = DIExpression::EntryValueOp;
+ using ExtractBitsOp = DIExpression::ExtractBitsOp;
+ using FragmentOp = DIExpression::FragmentOp;
+ using PlusUconstOp = DIExpression::PlusUconstOp;
+ using TagOffsetOp = DIExpression::TagOffsetOp;
+
+ const auto ConstArgOperand = ArgOperand;
+ static_assert(std::is_same_v<decltype(cast<ArgOp>(ArgOperand)), ArgOp>);
+ static_assert(std::is_same_v<decltype(cast<ArgOp>(ConstArgOperand)), ArgOp>);
+ static_assert(std::is_same_v<decltype(dyn_cast<ArgOp>(ArgOperand)), ArgOp>);
+ static_assert(
+ std::is_same_v<decltype(dyn_cast<ArgOp>(ConstArgOperand)), ArgOp>);
+ EXPECT_TRUE(ArgOperand.is(dwarf::DW_OP_LLVM_arg));
+ EXPECT_FALSE(ArgOperand.is(dwarf::DW_OP_LLVM_fragment));
+
+ auto Arg = checkOperandView<ArgOp>(ArgOperand, FragmentOperand);
+ EXPECT_EQ(7u, Arg.getIndex());
+
+ auto Fragment = checkOperandView<FragmentOp>(FragmentOperand, ConvertOperand);
+ EXPECT_EQ(4u, Fragment.getOffsetInBits());
+ EXPECT_EQ(24u, Fragment.getSizeInBits());
+
+ auto SignedExtract =
+ checkOperandView<ExtractBitsOp>(SignedExtractOperand, ArgOperand);
+ EXPECT_EQ(1u, SignedExtract.getOffsetInBits());
+ EXPECT_EQ(8u, SignedExtract.getSizeInBits());
+ EXPECT_TRUE(SignedExtract.isSigned());
+
+ auto UnsignedExtract =
+ checkOperandView<ExtractBitsOp>(UnsignedExtractOperand, ArgOperand);
+ EXPECT_EQ(2u, UnsignedExtract.getOffsetInBits());
+ EXPECT_EQ(16u, UnsignedExtract.getSizeInBits());
+ EXPECT_FALSE(UnsignedExtract.isSigned());
+
+ auto Convert =
+ checkOperandView<ConvertOp>(ConvertOperand, UnsignedExtractOperand);
+ EXPECT_EQ(32u, Convert.getBitSize());
+ EXPECT_EQ(RawEncoding, Convert.getEncoding());
+
+ auto Constant = checkOperandView<ConstuOp>(ConstuOperand, PlusUconstOperand);
+ EXPECT_EQ(42u, Constant.getValue());
+
+ auto PlusUconst =
+ checkOperandView<PlusUconstOp>(PlusUconstOperand, ConstuOperand);
+ EXPECT_EQ(12u, PlusUconst.getOffset());
+
+ auto Tag = checkOperandView<TagOffsetOp>(TagOffsetOperand, ConstuOperand);
+ EXPECT_EQ(3u, Tag.getTagOffset());
+
+ // An entry value has to be the first operation of the expression it belongs
+ // to, or the second behind DW_OP_LLVM_arg 0, so it needs one of its own. One
+ // is the only count LLVM accepts today.
+ uint64_t EntryValueElements[] = {dwarf::DW_OP_LLVM_entry_value, 1};
+ auto *EntryValueExpr = DIExpression::get(Context, EntryValueElements);
+ ASSERT_TRUE(EntryValueExpr->isValid());
+ auto EntryValue = checkOperandView<EntryValueOp>(
+ *EntryValueExpr->expr_op_begin(), ConstuOperand);
+ EXPECT_EQ(1u, EntryValue.getNumOperations());
+
+ auto MaybeConstArg = dyn_cast<ArgOp>(ConstArgOperand);
+ ASSERT_TRUE(MaybeConstArg);
+ EXPECT_EQ(7u, MaybeConstArg.getIndex());
+
+ DIExpression::ExprOperand EmptyOperand;
+ static_assert(
+ std::is_same_v<decltype(cast_if_present<ArgOp>(EmptyOperand)), ArgOp>);
+ static_assert(
+ std::is_same_v<decltype(dyn_cast_if_present<ArgOp>(EmptyOperand)),
+ ArgOp>);
+ EXPECT_EQ(7u, cast_if_present<ArgOp>(ArgOperand).getIndex());
+ EXPECT_FALSE(cast_if_present<ArgOp>(EmptyOperand));
+ EXPECT_EQ(7u, dyn_cast_if_present<ArgOp>(ArgOperand).getIndex());
+ EXPECT_FALSE(dyn_cast_if_present<ArgOp>(EmptyOperand));
+}
+
+TEST_F(DIExpressionTest, GetActiveBits) {
+ auto MakeVariable = [&](StringRef Name, unsigned Encoding) {
+ auto *Type = DIBasicType::get(Context, dwarf::DW_TAG_base_type, Name, 64, 0,
+ Encoding, DINode::FlagZero);
+ return DILocalVariable::get(Context, getSubprogram(), Name, getFile(), 0,
+ Type, 0, DINode::FlagZero, 0, nullptr);
+ };
+ auto *SignedVar = MakeVariable("signed", dwarf::DW_ATE_signed);
+ auto *UnsignedVar = MakeVariable("unsigned", dwarf::DW_ATE_unsigned);
+
+ auto *UnsignedExtract =
+ DIExpression::get(Context, {dwarf::DW_OP_LLVM_extract_bits_zext, 0, 8});
+ EXPECT_EQ(std::optional<uint64_t>(8),
+ UnsignedExtract->getActiveBits(UnsignedVar));
+
+ auto *SignedExtract =
+ DIExpression::get(Context, {dwarf::DW_OP_LLVM_extract_bits_sext, 0, 16});
+ EXPECT_EQ(std::optional<uint64_t>(16),
+ SignedExtract->getActiveBits(SignedVar));
+ EXPECT_EQ(std::optional<uint64_t>(64),
+ SignedExtract->getActiveBits(UnsignedVar));
+
+ // An extract and a fragment both narrow to the smaller of the two, so each
+ // one has to be the deciding width in one of these to show that both were
+ // read. Stop reading the deciding width in either expression and the answer
+ // becomes the other operation's 32.
+ auto *FragmentNarrower =
+ DIExpression::get(Context, {dwarf::DW_OP_LLVM_extract_bits_zext, 0, 32,
+ dwarf::DW_OP_LLVM_fragment, 0, 24});
+ EXPECT_EQ(std::optional<uint64_t>(24),
+ FragmentNarrower->getActiveBits(UnsignedVar));
+
+ auto *ExtractNarrower =
+ DIExpression::get(Context, {dwarf::DW_OP_LLVM_extract_bits_zext, 0, 24,
+ dwarf::DW_OP_LLVM_fragment, 0, 32});
+ EXPECT_EQ(std::optional<uint64_t>(24),
+ ExtractNarrower->getActiveBits(UnsignedVar));
+}
+
TEST_F(DIExpressionTest, Fold) {
// Remove a No-op DW_OP_plus_uconst from an expression.
More information about the llvm-commits
mailing list