[llvm] Use DIExpression::foldConstantMath() at the result of an append() (PR #71719)
Shubham Sandeep Rastogi via llvm-commits
llvm-commits at lists.llvm.org
Thu May 23 15:29:31 PDT 2024
https://github.com/rastogishubham updated https://github.com/llvm/llvm-project/pull/71719
>From 39ed4b472a05b53e639aac86eedec49b92cdb5a2 Mon Sep 17 00:00:00 2001
From: Shubham Sandeep Rastogi <srastogi22 at apple.com>
Date: Wed, 4 Oct 2023 11:34:19 -0700
Subject: [PATCH 1/4] [NFC] Move DIExpressionCursor to DebugInfoMetadata.h
---
llvm/include/llvm/IR/DebugInfoMetadata.h | 61 +++++++++++++++++++
llvm/lib/CodeGen/AsmPrinter/DwarfExpression.h | 61 -------------------
2 files changed, 61 insertions(+), 61 deletions(-)
diff --git a/llvm/include/llvm/IR/DebugInfoMetadata.h b/llvm/include/llvm/IR/DebugInfoMetadata.h
index 42291d45da2be..a1c554677f8bf 100644
--- a/llvm/include/llvm/IR/DebugInfoMetadata.h
+++ b/llvm/include/llvm/IR/DebugInfoMetadata.h
@@ -3150,6 +3150,67 @@ template <> struct DenseMapInfo<DIExpression::FragmentInfo> {
static bool isEqual(const FragInfo &A, const FragInfo &B) { return A == B; }
};
+/// Holds a DIExpression and keeps track of how many operands have been consumed
+/// so far.
+class DIExpressionCursor {
+ DIExpression::expr_op_iterator Start, End;
+
+public:
+ DIExpressionCursor(const DIExpression *Expr) {
+ if (!Expr) {
+ assert(Start == End);
+ return;
+ }
+ Start = Expr->expr_op_begin();
+ End = Expr->expr_op_end();
+ }
+
+ DIExpressionCursor(ArrayRef<uint64_t> Expr)
+ : Start(Expr.begin()), End(Expr.end()) {}
+
+ DIExpressionCursor(const DIExpressionCursor &) = default;
+
+ /// Consume one operation.
+ std::optional<DIExpression::ExprOperand> take() {
+ if (Start == End)
+ return std::nullopt;
+ return *(Start++);
+ }
+
+ /// Consume N operations.
+ void consume(unsigned N) { std::advance(Start, N); }
+
+ /// Return the current operation.
+ std::optional<DIExpression::ExprOperand> peek() const {
+ if (Start == End)
+ return std::nullopt;
+ return *(Start);
+ }
+
+ /// Return the next operation.
+ std::optional<DIExpression::ExprOperand> peekNext() const {
+ if (Start == End)
+ return std::nullopt;
+
+ auto Next = Start.getNext();
+ if (Next == End)
+ return std::nullopt;
+
+ return *Next;
+ }
+
+ /// 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; }
+
+ /// Retrieve the fragment information, if any.
+ std::optional<DIExpression::FragmentInfo> getFragmentInfo() const {
+ return DIExpression::getFragmentInfo(Start, End);
+ }
+};
+
/// Global variables.
///
/// TODO: Remove DisplayName. It's always equal to Name.
diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfExpression.h b/llvm/lib/CodeGen/AsmPrinter/DwarfExpression.h
index 667a9efc6f6c0..4daa78b15b8e2 100644
--- a/llvm/lib/CodeGen/AsmPrinter/DwarfExpression.h
+++ b/llvm/lib/CodeGen/AsmPrinter/DwarfExpression.h
@@ -31,67 +31,6 @@ class DIELoc;
class TargetRegisterInfo;
class MachineLocation;
-/// Holds a DIExpression and keeps track of how many operands have been consumed
-/// so far.
-class DIExpressionCursor {
- DIExpression::expr_op_iterator Start, End;
-
-public:
- DIExpressionCursor(const DIExpression *Expr) {
- if (!Expr) {
- assert(Start == End);
- return;
- }
- Start = Expr->expr_op_begin();
- End = Expr->expr_op_end();
- }
-
- DIExpressionCursor(ArrayRef<uint64_t> Expr)
- : Start(Expr.begin()), End(Expr.end()) {}
-
- DIExpressionCursor(const DIExpressionCursor &) = default;
-
- /// Consume one operation.
- std::optional<DIExpression::ExprOperand> take() {
- if (Start == End)
- return std::nullopt;
- return *(Start++);
- }
-
- /// Consume N operations.
- void consume(unsigned N) { std::advance(Start, N); }
-
- /// Return the current operation.
- std::optional<DIExpression::ExprOperand> peek() const {
- if (Start == End)
- return std::nullopt;
- return *(Start);
- }
-
- /// Return the next operation.
- std::optional<DIExpression::ExprOperand> peekNext() const {
- if (Start == End)
- return std::nullopt;
-
- auto Next = Start.getNext();
- if (Next == End)
- return std::nullopt;
-
- return *Next;
- }
-
- /// 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; }
-
- /// Retrieve the fragment information, if any.
- std::optional<DIExpression::FragmentInfo> getFragmentInfo() const {
- return DIExpression::getFragmentInfo(Start, End);
- }
-};
-
/// Base class containing the logic for constructing DWARF expressions
/// independently of whether they are emitted into a DIE or into a .debug_loc
/// entry.
>From 417a5b8df934e214562464096cbe566c6824c032 Mon Sep 17 00:00:00 2001
From: Shubham Sandeep Rastogi <srastogi22 at apple.com>
Date: Wed, 1 Nov 2023 17:25:26 -0700
Subject: [PATCH 2/4] Add functions peekNextN(unsigned) and
assignNewExpr(ArrayRef<uint64_t>) to DIExpressionCursor
This commit adds two functions to the DIExpressionCursor class.
peekNextN(unsigned) works like peekNext, but lets you peek the next Nth
element
assignNewExpr(ArrayRef<uint64_t>) lets you assign a new expression to
the same DIExpressionCursor object
---
llvm/include/llvm/IR/DebugInfoMetadata.h | 17 +++++++++++++++++
1 file changed, 17 insertions(+)
diff --git a/llvm/include/llvm/IR/DebugInfoMetadata.h b/llvm/include/llvm/IR/DebugInfoMetadata.h
index a1c554677f8bf..555bd623ad9ef 100644
--- a/llvm/include/llvm/IR/DebugInfoMetadata.h
+++ b/llvm/include/llvm/IR/DebugInfoMetadata.h
@@ -3199,6 +3199,23 @@ class DIExpressionCursor {
return *Next;
}
+ std::optional<DIExpression::ExprOperand> peekNextN(unsigned N) const {
+ if (Start == End)
+ return std::nullopt;
+ DIExpression::expr_op_iterator Nth = Start;
+ for (unsigned I = 0; I < N; I++) {
+ Nth = Nth.getNext();
+ if (Nth == End)
+ return std::nullopt;
+ }
+ return *Nth;
+ }
+
+ void assignNewExpr(ArrayRef<uint64_t> Expr) {
+ this->Start = DIExpression::expr_op_iterator(Expr.begin());
+ this->End = DIExpression::expr_op_iterator(Expr.end());
+ }
+
/// Determine whether there are any operations left in this expression.
operator bool() const { return Start != End; }
>From 4784d66d2aa05b88301559a1631a18f8c7eaa551 Mon Sep 17 00:00:00 2001
From: Shubham Sandeep Rastogi <srastogi22 at apple.com>
Date: Fri, 20 Oct 2023 10:44:22 -0700
Subject: [PATCH 3/4] Introduce DIExpression::foldConstantMath()
DIExpressions can get very long and have a lot of redundant operations.
This function uses simple pattern matching to fold constant math that
can be evaluated at compile time.
---
llvm/include/llvm/IR/DebugInfoMetadata.h | 5 +
llvm/lib/IR/CMakeLists.txt | 1 +
llvm/lib/IR/DIExpressionOptimizer.cpp | 379 +++++++++++++++++++
llvm/lib/IR/DebugInfoMetadata.cpp | 1 -
llvm/unittests/IR/MetadataTest.cpp | 457 +++++++++++++++++++++++
5 files changed, 842 insertions(+), 1 deletion(-)
create mode 100644 llvm/lib/IR/DIExpressionOptimizer.cpp
diff --git a/llvm/include/llvm/IR/DebugInfoMetadata.h b/llvm/include/llvm/IR/DebugInfoMetadata.h
index 555bd623ad9ef..18873a551595a 100644
--- a/llvm/include/llvm/IR/DebugInfoMetadata.h
+++ b/llvm/include/llvm/IR/DebugInfoMetadata.h
@@ -3121,6 +3121,11 @@ class DIExpression : public MDNode {
/// expression and constant on failure.
std::pair<DIExpression *, const ConstantInt *>
constantFold(const ConstantInt *CI);
+
+ /// Try to shorten an expression with constant math operations that can be
+ /// evaluated at compile time. Returns a new expression on success, or the old
+ /// expression if there is nothing to be reduced.
+ DIExpression *foldConstantMath();
};
inline bool operator==(const DIExpression::FragmentInfo &A,
diff --git a/llvm/lib/IR/CMakeLists.txt b/llvm/lib/IR/CMakeLists.txt
index b5fb7409d8e88..20f169913087a 100644
--- a/llvm/lib/IR/CMakeLists.txt
+++ b/llvm/lib/IR/CMakeLists.txt
@@ -17,6 +17,7 @@ add_llvm_component_library(LLVMCore
DataLayout.cpp
DebugInfo.cpp
DebugInfoMetadata.cpp
+ DIExpressionOptimizer.cpp
DebugProgramInstruction.cpp
DebugLoc.cpp
DiagnosticHandler.cpp
diff --git a/llvm/lib/IR/DIExpressionOptimizer.cpp b/llvm/lib/IR/DIExpressionOptimizer.cpp
new file mode 100644
index 0000000000000..74e8833bb552f
--- /dev/null
+++ b/llvm/lib/IR/DIExpressionOptimizer.cpp
@@ -0,0 +1,379 @@
+//===- DIExpressionOptimizer.cpp - Constant folding of DIExpressions ------===//
+//
+// 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 functions to constant fold DIExpressions. Which were
+// declared in DIExpressionOptimizer.h
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/BinaryFormat/Dwarf.h"
+#include "llvm/IR/DebugInfoMetadata.h"
+
+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);
+ return std::nullopt;
+}
+
+/// Returns true if an operation and operand result in a No Op.
+static bool isNeutralElement(uint64_t Op, uint64_t Val) {
+ switch (Op) {
+ case dwarf::DW_OP_plus:
+ case dwarf::DW_OP_minus:
+ case dwarf::DW_OP_shl:
+ case dwarf::DW_OP_shr:
+ return Val == 0;
+ case dwarf::DW_OP_mul:
+ case dwarf::DW_OP_div:
+ return Val == 1;
+ default:
+ return false;
+ }
+}
+
+/// Try to fold \p Const1 and \p Const2 by applying \p Operator and returning
+/// the result, if there is an overflow, return a std::nullopt.
+static std::optional<uint64_t>
+foldOperationIfPossible(uint64_t Const1, uint64_t Const2,
+ dwarf::LocationAtom Operator) {
+
+ bool ResultOverflowed;
+ switch (Operator) {
+ case dwarf::DW_OP_plus: {
+ auto Result = SaturatingAdd(Const1, Const2, &ResultOverflowed);
+ if (ResultOverflowed)
+ return std::nullopt;
+ return Result;
+ }
+ case dwarf::DW_OP_minus: {
+ if (Const1 < Const2)
+ return std::nullopt;
+ return Const1 - Const2;
+ }
+ case dwarf::DW_OP_shl: {
+ if ((uint64_t)countl_zero(Const1) < Const2)
+ return std::nullopt;
+ return Const1 << Const2;
+ }
+ case dwarf::DW_OP_shr: {
+ if ((uint64_t)countr_zero(Const1) < Const2)
+ return std::nullopt;
+ return Const1 >> Const2;
+ }
+ case dwarf::DW_OP_mul: {
+ auto Result = SaturatingMultiply(Const1, Const2, &ResultOverflowed);
+ if (ResultOverflowed)
+ return std::nullopt;
+ return Result;
+ }
+ case dwarf::DW_OP_div: {
+ if (Const2)
+ return Const1 / Const2;
+ return std::nullopt;
+ }
+ default:
+ return std::nullopt;
+ }
+}
+
+/// Returns true if the two operations \p Operator1 and \p Operator2 are
+/// commutative and can be folded.
+static bool operationsAreFoldableAndCommutative(dwarf::LocationAtom Operator1,
+ dwarf::LocationAtom Operator2) {
+ return Operator1 == Operator2 &&
+ (Operator1 == dwarf::DW_OP_plus || Operator1 == dwarf::DW_OP_mul);
+}
+
+/// Consume one operator and its operand(s).
+static void consumeOneOperator(DIExpressionCursor &Cursor, uint64_t &Loc,
+ const DIExpression::ExprOperand &Op) {
+ Cursor.consume(1);
+ Loc = Loc + Op.getSize();
+}
+
+/// Reset the Cursor to the beginning of the WorkingOps.
+void startFromBeginning(uint64_t &Loc, DIExpressionCursor &Cursor,
+ ArrayRef<uint64_t> WorkingOps) {
+ Cursor.assignNewExpr(WorkingOps);
+ Loc = 0;
+}
+
+/// This function will canonicalize:
+/// 1. DW_OP_plus_uconst to DW_OP_constu <const-val> DW_OP_plus
+/// 2. DW_OP_lit<n> to DW_OP_constu <n>
+static SmallVector<uint64_t>
+canonicalizeDwarfOperations(ArrayRef<uint64_t> WorkingOps) {
+ DIExpressionCursor Cursor(WorkingOps);
+ uint64_t Loc = 0;
+ SmallVector<uint64_t> ResultOps;
+ while (Loc < WorkingOps.size()) {
+ auto Op = Cursor.peek();
+ /// Expression has no operations, break.
+ if (!Op)
+ break;
+ auto OpRaw = Op->getOp();
+
+ if (OpRaw >= dwarf::DW_OP_lit0 && OpRaw <= dwarf::DW_OP_lit31) {
+ ResultOps.push_back(dwarf::DW_OP_constu);
+ ResultOps.push_back(OpRaw - dwarf::DW_OP_lit0);
+ consumeOneOperator(Cursor, Loc, *Cursor.peek());
+ continue;
+ }
+ if (OpRaw == dwarf::DW_OP_plus_uconst) {
+ ResultOps.push_back(dwarf::DW_OP_constu);
+ ResultOps.push_back(Op->getArg(0));
+ ResultOps.push_back(dwarf::DW_OP_plus);
+ consumeOneOperator(Cursor, Loc, *Cursor.peek());
+ continue;
+ }
+ uint64_t PrevLoc = Loc;
+ consumeOneOperator(Cursor, Loc, *Cursor.peek());
+ ResultOps.append(WorkingOps.begin() + PrevLoc, WorkingOps.begin() + Loc);
+ }
+ return ResultOps;
+}
+
+/// This function will convert:
+/// 1. DW_OP_constu <const-val> DW_OP_plus to DW_OP_plus_uconst
+/// 2. DW_OP_constu, 0 to DW_OP_lit0
+static SmallVector<uint64_t>
+optimizeDwarfOperations(ArrayRef<uint64_t> WorkingOps) {
+ DIExpressionCursor Cursor(WorkingOps);
+ uint64_t Loc = 0;
+ SmallVector<uint64_t> ResultOps;
+ while (Loc < WorkingOps.size()) {
+ auto Op1 = Cursor.peek();
+ /// Expression has no operations, exit.
+ if (!Op1)
+ break;
+ auto Op1Raw = Op1->getOp();
+
+ if (Op1Raw == dwarf::DW_OP_constu && Op1->getArg(0) == 0) {
+ ResultOps.push_back(dwarf::DW_OP_lit0);
+ consumeOneOperator(Cursor, Loc, *Cursor.peek());
+ continue;
+ }
+
+ auto Op2 = Cursor.peekNext();
+ /// Expression has no more operations, copy into ResultOps and exit.
+ if (!Op2) {
+ uint64_t PrevLoc = Loc;
+ consumeOneOperator(Cursor, Loc, *Cursor.peek());
+ ResultOps.append(WorkingOps.begin() + PrevLoc, WorkingOps.begin() + Loc);
+ break;
+ }
+ auto Op2Raw = Op2->getOp();
+
+ if (Op1Raw == dwarf::DW_OP_constu && Op2Raw == dwarf::DW_OP_plus) {
+ ResultOps.push_back(dwarf::DW_OP_plus_uconst);
+ ResultOps.push_back(Op1->getArg(0));
+ consumeOneOperator(Cursor, Loc, *Cursor.peek());
+ consumeOneOperator(Cursor, Loc, *Cursor.peek());
+ continue;
+ }
+ uint64_t PrevLoc = Loc;
+ consumeOneOperator(Cursor, Loc, *Cursor.peek());
+ ResultOps.append(WorkingOps.begin() + PrevLoc, WorkingOps.begin() + Loc);
+ }
+ return ResultOps;
+}
+
+/// {DW_OP_constu, 0, DW_OP_[plus, minus, shl, shr]} -> {}
+/// {DW_OP_constu, 1, DW_OP_[mul, div]} -> {}
+static bool tryFoldNoOpMath(uint64_t Const1,
+ ArrayRef<DIExpression::ExprOperand> Ops,
+ uint64_t &Loc, DIExpressionCursor &Cursor,
+ SmallVectorImpl<uint64_t> &WorkingOps) {
+
+ if (isNeutralElement(Ops[1].getOp(), Const1)) {
+ WorkingOps.erase(WorkingOps.begin() + Loc, WorkingOps.begin() + Loc + 3);
+ startFromBeginning(Loc, Cursor, WorkingOps);
+ return true;
+ }
+ return false;
+}
+
+/// {DW_OP_constu, Const1, DW_OP_constu, Const2, DW_OP_[plus,
+/// minus, mul, div, shl, shr] -> {DW_OP_constu, Const1 [+, -, *, /, <<, >>]
+/// Const2}
+static bool tryFoldConstants(uint64_t Const1,
+ ArrayRef<DIExpression::ExprOperand> Ops,
+ uint64_t &Loc, DIExpressionCursor &Cursor,
+ SmallVectorImpl<uint64_t> &WorkingOps) {
+
+ auto Const2 = isConstantVal(Ops[1]);
+ if (!Const2)
+ return false;
+
+ auto Result = foldOperationIfPossible(
+ Const1, *Const2, static_cast<dwarf::LocationAtom>(Ops[2].getOp()));
+ if (!Result) {
+ consumeOneOperator(Cursor, Loc, Ops[0]);
+ return true;
+ }
+ WorkingOps.erase(WorkingOps.begin() + Loc + 2,
+ WorkingOps.begin() + Loc + 5);
+ WorkingOps[Loc] = dwarf::DW_OP_constu;
+ WorkingOps[Loc + 1] = *Result;
+ startFromBeginning(Loc, Cursor, WorkingOps);
+ return true;
+}
+
+/// {DW_OP_constu, Const1, DW_OP_[plus, mul], DW_OP_constu, Const2,
+/// DW_OP_[plus, mul]} -> {DW_OP_constu, Const1 [+, *] Const2, DW_OP_[plus,
+/// mul]}
+static bool tryFoldCommutativeMath(uint64_t Const1,
+ ArrayRef<DIExpression::ExprOperand> Ops,
+ uint64_t &Loc, DIExpressionCursor &Cursor,
+ SmallVectorImpl<uint64_t> &WorkingOps) {
+
+ auto Const2 = isConstantVal(Ops[2]);
+ auto Operand1 = static_cast<dwarf::LocationAtom>(Ops[1].getOp());
+ auto Operand2 = static_cast<dwarf::LocationAtom>(Ops[3].getOp());
+
+ if (!Const2 || !operationsAreFoldableAndCommutative(Operand1, Operand2))
+ return false;
+
+ auto Result = foldOperationIfPossible(Const1, *Const2, Operand1);
+ if (!Result) {
+ consumeOneOperator(Cursor, Loc, Ops[0]);
+ return true;
+ }
+ WorkingOps.erase(WorkingOps.begin() + Loc + 3, WorkingOps.begin() + Loc + 6);
+ WorkingOps[Loc] = dwarf::DW_OP_constu;
+ WorkingOps[Loc + 1] = *Result;
+ startFromBeginning(Loc, Cursor, WorkingOps);
+ return true;
+}
+
+/// {DW_OP_constu, Const1, DW_OP_[plus, mul], DW_OP_LLVM_arg, Arg1,
+/// DW_OP_[plus, mul], DW_OP_constu, Const2, DW_OP_[plus, mul]} ->
+/// {DW_OP_constu, Const1 [+, *] Const2, DW_OP_[plus, mul], DW_OP_LLVM_arg,
+/// Arg1, DW_OP_[plus, mul]}
+static bool tryFoldCommutativeMathWithArgInBetween(
+ uint64_t Const1, ArrayRef<DIExpression::ExprOperand> Ops, uint64_t &Loc,
+ DIExpressionCursor &Cursor, SmallVectorImpl<uint64_t> &WorkingOps) {
+
+ auto Const2 = isConstantVal(Ops[4]);
+ auto Operand1 = static_cast<dwarf::LocationAtom>(Ops[1].getOp());
+ auto Operand2 = static_cast<dwarf::LocationAtom>(Ops[3].getOp());
+ auto Operand3 = static_cast<dwarf::LocationAtom>(Ops[5].getOp());
+
+ if (!Const2 || Ops[2].getOp() != dwarf::DW_OP_LLVM_arg ||
+ !operationsAreFoldableAndCommutative(Operand1, Operand2) ||
+ !operationsAreFoldableAndCommutative(Operand2, Operand3))
+ return false;
+
+ auto Result = foldOperationIfPossible(Const1, *Const2, Operand1);
+ if (!Result) {
+ consumeOneOperator(Cursor, Loc, Ops[0]);
+ return true;
+ }
+ WorkingOps.erase(WorkingOps.begin() + Loc + 6, WorkingOps.begin() + Loc + 9);
+ WorkingOps[Loc] = dwarf::DW_OP_constu;
+ WorkingOps[Loc + 1] = *Result;
+ startFromBeginning(Loc, Cursor, WorkingOps);
+ return true;
+}
+
+DIExpression *DIExpression::foldConstantMath() {
+
+ SmallVector<uint64_t, 8> WorkingOps(Elements.begin(), Elements.end());
+ uint64_t Loc = 0;
+ SmallVector<uint64_t> ResultOps = canonicalizeDwarfOperations(WorkingOps);
+ DIExpressionCursor Cursor(ResultOps);
+ SmallVector<DIExpression::ExprOperand, 8> Ops;
+
+ // Iterate over all Operations in a DIExpression to match the smallest pattern
+ // that can be folded.
+ while (Loc < ResultOps.size()) {
+ Ops.clear();
+
+ auto Op = Cursor.peek();
+ // Expression has no operations, exit.
+ if (!Op)
+ break;
+
+ auto Const1 = isConstantVal(*Op);
+
+ if (!Const1) {
+ // Early exit, all of the following patterns start with a constant value.
+ consumeOneOperator(Cursor, Loc, *Op);
+ continue;
+ }
+
+ Ops.push_back(*Op);
+
+ Op = Cursor.peekNext();
+ // All following patterns require at least 2 Operations, exit.
+ if (!Op)
+ break;
+
+ Ops.push_back(*Op);
+
+ // Try to fold a constant no-op, such as {+ 0}
+ if (tryFoldNoOpMath(*Const1, Ops, Loc, Cursor, ResultOps))
+ continue;
+
+ Op = Cursor.peekNextN(2);
+ // Op[1] could still match a pattern, skip iteration.
+ if (!Op) {
+ consumeOneOperator(Cursor, Loc, Ops[0]);
+ continue;
+ }
+
+ Ops.push_back(*Op);
+
+ // Try to fold a pattern of two constants such as {C1 + C2}.
+ if (tryFoldConstants(*Const1, Ops, Loc, Cursor, ResultOps))
+ continue;
+
+ Op = Cursor.peekNextN(3);
+ // Op[1] and Op[2] could still match a pattern, skip iteration.
+ if (!Op) {
+ consumeOneOperator(Cursor, Loc, Ops[0]);
+ continue;
+ }
+
+ Ops.push_back(*Op);
+
+ // Try to fold commutative constant math, such as {C1 + C2 +}.
+ if (tryFoldCommutativeMath(*Const1, Ops, Loc, Cursor, ResultOps))
+ continue;
+
+ Op = Cursor.peekNextN(4);
+ if (!Op) {
+ consumeOneOperator(Cursor, Loc, Ops[0]);
+ continue;
+ }
+
+ Ops.push_back(*Op);
+ Op = Cursor.peekNextN(5);
+ if (!Op) {
+ consumeOneOperator(Cursor, Loc, Ops[0]);
+ continue;
+ }
+
+ Ops.push_back(*Op);
+
+ // Try to fold commutative constant math with an LLVM_Arg in between, such
+ // as {C1 + Arg + C2 +}.
+ if (tryFoldCommutativeMathWithArgInBetween(*Const1, Ops, Loc, Cursor,
+ ResultOps))
+ continue;
+
+ consumeOneOperator(Cursor, Loc, Ops[0]);
+ }
+ ResultOps = optimizeDwarfOperations(ResultOps);
+ auto *Result = DIExpression::get(getContext(), ResultOps);
+ assert(Result->isValid() && "concatenated expression is not valid");
+ return Result;
+}
diff --git a/llvm/lib/IR/DebugInfoMetadata.cpp b/llvm/lib/IR/DebugInfoMetadata.cpp
index 570515505607f..229ee2bd0164c 100644
--- a/llvm/lib/IR/DebugInfoMetadata.cpp
+++ b/llvm/lib/IR/DebugInfoMetadata.cpp
@@ -1880,7 +1880,6 @@ DIExpression *DIExpression::append(const DIExpression *Expr,
}
Op.appendToVector(NewOps);
}
-
NewOps.append(Ops.begin(), Ops.end());
auto *result = DIExpression::get(Expr->getContext(), NewOps);
assert(result->isValid() && "concatenated expression is not valid");
diff --git a/llvm/unittests/IR/MetadataTest.cpp b/llvm/unittests/IR/MetadataTest.cpp
index 4c2e5f77a5403..9647ac8c43966 100644
--- a/llvm/unittests/IR/MetadataTest.cpp
+++ b/llvm/unittests/IR/MetadataTest.cpp
@@ -3153,6 +3153,463 @@ TEST_F(DIExpressionTest, get) {
EXPECT_EQ(N0WithPrependedOps, N2);
}
+TEST_F(DIExpressionTest, Fold) {
+
+ // Remove a No-op DW_OP_plus_uconst from an expression.
+ SmallVector<uint64_t, 8> Ops = {dwarf::DW_OP_plus_uconst, 0};
+ auto *Expr = DIExpression::get(Context, Ops);
+ auto *E = Expr->foldConstantMath();
+ SmallVector<uint64_t, 8> ResOps;
+ auto *EmptyExpr = DIExpression::get(Context, ResOps);
+ EXPECT_EQ(E, EmptyExpr);
+
+ // Remove a No-op add from an expression.
+ Ops.clear();
+ Ops.push_back(dwarf::DW_OP_constu);
+ Ops.push_back(0);
+ Ops.push_back(dwarf::DW_OP_plus);
+ Expr = DIExpression::get(Context, Ops);
+ E = Expr->foldConstantMath();
+ EXPECT_EQ(E, EmptyExpr);
+
+ // Remove a No-op subtract from an expression.
+ Ops.clear();
+ Ops.push_back(dwarf::DW_OP_constu);
+ Ops.push_back(0);
+ Ops.push_back(dwarf::DW_OP_minus);
+ Expr = DIExpression::get(Context, Ops);
+ E = Expr->foldConstantMath();
+ EXPECT_EQ(E, EmptyExpr);
+
+ // Remove a No-op shift left from an expression.
+ Ops.clear();
+ Ops.push_back(dwarf::DW_OP_constu);
+ Ops.push_back(0);
+ Ops.push_back(dwarf::DW_OP_shl);
+ Expr = DIExpression::get(Context, Ops);
+ E = Expr->foldConstantMath();
+ EXPECT_EQ(E, EmptyExpr);
+
+ // Remove a No-op shift right from an expression.
+ Ops.clear();
+ Ops.push_back(dwarf::DW_OP_constu);
+ Ops.push_back(0);
+ Ops.push_back(dwarf::DW_OP_shr);
+ Expr = DIExpression::get(Context, Ops);
+ E = Expr->foldConstantMath();
+ EXPECT_EQ(E, EmptyExpr);
+
+ // Remove a No-op multiply from an expression.
+ Ops.clear();
+ Ops.push_back(dwarf::DW_OP_constu);
+ Ops.push_back(1);
+ Ops.push_back(dwarf::DW_OP_mul);
+ Expr = DIExpression::get(Context, Ops);
+ E = Expr->foldConstantMath();
+ EXPECT_EQ(E, EmptyExpr);
+
+ // Remove a No-op divide from an expression.
+ Ops.clear();
+ Ops.push_back(dwarf::DW_OP_constu);
+ Ops.push_back(1);
+ Ops.push_back(dwarf::DW_OP_div);
+ Expr = DIExpression::get(Context, Ops);
+ E = Expr->foldConstantMath();
+ EXPECT_EQ(E, EmptyExpr);
+
+ // Test fold {DW_OP_plus_uconst, Const1, DW_OP_plus_uconst, Const2} ->
+ // {DW_OP_plus_uconst, Const1 + Const2}
+ Ops.clear();
+ Ops.push_back(dwarf::DW_OP_plus_uconst);
+ Ops.push_back(2);
+ Ops.push_back(dwarf::DW_OP_plus_uconst);
+ Ops.push_back(3);
+ Expr = DIExpression::get(Context, Ops);
+ E = Expr->foldConstantMath();
+ ResOps.push_back(dwarf::DW_OP_plus_uconst);
+ ResOps.push_back(5);
+ auto *ResExpr = DIExpression::get(Context, ResOps);
+ EXPECT_EQ(E, ResExpr);
+
+ // Test {DW_OP_constu, Const1, DW_OP_plus_uconst, Const2} -> {DW_OP_constu,
+ // Const1 + Const2}
+ Ops.clear();
+ Ops.push_back(dwarf::DW_OP_constu);
+ Ops.push_back(2);
+ Ops.push_back(dwarf::DW_OP_plus_uconst);
+ Ops.push_back(3);
+ Expr = DIExpression::get(Context, Ops);
+ E = Expr->foldConstantMath();
+ ResOps.clear();
+ ResOps.push_back(dwarf::DW_OP_constu);
+ ResOps.push_back(5);
+ ResExpr = DIExpression::get(Context, ResOps);
+ EXPECT_EQ(E, ResExpr);
+
+ // Test {DW_OP_constu, Const1, DW_OP_constu, Const2, DW_OP_plus} ->
+ // {DW_OP_constu, Const1 + Const2}
+ Ops.clear();
+ Ops.push_back(dwarf::DW_OP_constu);
+ Ops.push_back(8);
+ Ops.push_back(dwarf::DW_OP_constu);
+ Ops.push_back(2);
+ Ops.push_back(dwarf::DW_OP_plus);
+ Expr = DIExpression::get(Context, Ops);
+ E = Expr->foldConstantMath();
+ ResOps.clear();
+ ResOps.push_back(dwarf::DW_OP_constu);
+ ResOps.push_back(10);
+ ResExpr = DIExpression::get(Context, ResOps);
+ EXPECT_EQ(E, ResExpr);
+
+ // Test {DW_OP_constu, Const1, DW_OP_constu, Const2, DW_OP_minus} ->
+ // {DW_OP_constu, Const1 - Const2}
+ Ops.clear();
+ Ops.push_back(dwarf::DW_OP_constu);
+ Ops.push_back(8);
+ Ops.push_back(dwarf::DW_OP_constu);
+ Ops.push_back(2);
+ Ops.push_back(dwarf::DW_OP_minus);
+ Expr = DIExpression::get(Context, Ops);
+ E = Expr->foldConstantMath();
+ ResOps.clear();
+ ResOps.push_back(dwarf::DW_OP_constu);
+ ResOps.push_back(6);
+ ResExpr = DIExpression::get(Context, ResOps);
+ EXPECT_EQ(E, ResExpr);
+
+ // Test {DW_OP_constu, Const1, DW_OP_constu, Const2, DW_OP_mul} ->
+ // {DW_OP_constu, Const1 * Const2}
+ Ops.clear();
+ Ops.push_back(dwarf::DW_OP_constu);
+ Ops.push_back(8);
+ Ops.push_back(dwarf::DW_OP_constu);
+ Ops.push_back(2);
+ Ops.push_back(dwarf::DW_OP_mul);
+ Expr = DIExpression::get(Context, Ops);
+ E = Expr->foldConstantMath();
+ ResOps.clear();
+ ResOps.push_back(dwarf::DW_OP_constu);
+ ResOps.push_back(16);
+ ResExpr = DIExpression::get(Context, ResOps);
+ EXPECT_EQ(E, ResExpr);
+
+ // Test {DW_OP_constu, Const1, DW_OP_constu, Const2, DW_OP_div} ->
+ // {DW_OP_constu, Const1 / Const2}
+ Ops.clear();
+ Ops.push_back(dwarf::DW_OP_constu);
+ Ops.push_back(8);
+ Ops.push_back(dwarf::DW_OP_constu);
+ Ops.push_back(2);
+ Ops.push_back(dwarf::DW_OP_div);
+ Expr = DIExpression::get(Context, Ops);
+ E = Expr->foldConstantMath();
+ ResOps.clear();
+ ResOps.push_back(dwarf::DW_OP_constu);
+ ResOps.push_back(4);
+ ResExpr = DIExpression::get(Context, ResOps);
+ EXPECT_EQ(E, ResExpr);
+
+ // Test {DW_OP_constu, Const1, DW_OP_constu, Const2, DW_OP_shl} ->
+ // {DW_OP_constu, Const1 << Const2}
+ Ops.clear();
+ Ops.push_back(dwarf::DW_OP_constu);
+ Ops.push_back(8);
+ Ops.push_back(dwarf::DW_OP_constu);
+ Ops.push_back(2);
+ Ops.push_back(dwarf::DW_OP_shl);
+ Expr = DIExpression::get(Context, Ops);
+ E = Expr->foldConstantMath();
+ ResOps.clear();
+ ResOps.push_back(dwarf::DW_OP_constu);
+ ResOps.push_back(32);
+ ResExpr = DIExpression::get(Context, ResOps);
+ EXPECT_EQ(E, ResExpr);
+
+ // Test {DW_OP_constu, Const1, DW_OP_constu, Const2, DW_OP_shr} ->
+ // {DW_OP_constu, Const1 >> Const2}
+ Ops.clear();
+ Ops.push_back(dwarf::DW_OP_constu);
+ Ops.push_back(8);
+ Ops.push_back(dwarf::DW_OP_constu);
+ Ops.push_back(2);
+ Ops.push_back(dwarf::DW_OP_shr);
+ Expr = DIExpression::get(Context, Ops);
+ E = Expr->foldConstantMath();
+ ResOps.clear();
+ ResOps.push_back(dwarf::DW_OP_constu);
+ ResOps.push_back(2);
+ ResExpr = DIExpression::get(Context, ResOps);
+ EXPECT_EQ(E, ResExpr);
+
+ // Test {DW_OP_plus_uconst, Const1, DW_OP_constu, Const2, DW_OP_plus} ->
+ // {DW_OP_plus_uconst, Const1 + Const2}
+ Ops.clear();
+ Ops.push_back(dwarf::DW_OP_plus_uconst);
+ Ops.push_back(8);
+ Ops.push_back(dwarf::DW_OP_constu);
+ Ops.push_back(2);
+ Ops.push_back(dwarf::DW_OP_plus);
+ Expr = DIExpression::get(Context, Ops);
+ E = Expr->foldConstantMath();
+ ResOps.clear();
+ ResOps.push_back(dwarf::DW_OP_plus_uconst);
+ ResOps.push_back(10);
+ ResExpr = DIExpression::get(Context, ResOps);
+ EXPECT_EQ(E, ResExpr);
+
+ // Test {DW_OP_constu, Const1, DW_OP_plus, DW_OP_plus_uconst, Const2} ->
+ // {DW_OP_plus_uconst, Const1 + Const2}
+ Ops.clear();
+ Ops.push_back(dwarf::DW_OP_constu);
+ Ops.push_back(8);
+ Ops.push_back(dwarf::DW_OP_plus);
+ Ops.push_back(dwarf::DW_OP_plus_uconst);
+ Ops.push_back(2);
+ Expr = DIExpression::get(Context, Ops);
+ E = Expr->foldConstantMath();
+ ResOps.clear();
+ ResOps.push_back(dwarf::DW_OP_plus_uconst);
+ ResOps.push_back(10);
+ ResExpr = DIExpression::get(Context, ResOps);
+ EXPECT_EQ(E, ResExpr);
+
+ // Test {DW_OP_constu, Const1, DW_OP_plus, DW_OP_constu, Const2, DW_OP_plus}
+ // -> {DW_OP_plus_uconst, Const1 + Const2}
+ Ops.clear();
+ Ops.push_back(dwarf::DW_OP_constu);
+ Ops.push_back(8);
+ Ops.push_back(dwarf::DW_OP_plus);
+ Ops.push_back(dwarf::DW_OP_constu);
+ Ops.push_back(2);
+ Ops.push_back(dwarf::DW_OP_plus);
+ Expr = DIExpression::get(Context, Ops);
+ E = Expr->foldConstantMath();
+ ResOps.clear();
+ ResOps.push_back(dwarf::DW_OP_plus_uconst);
+ ResOps.push_back(10);
+ ResExpr = DIExpression::get(Context, ResOps);
+ EXPECT_EQ(E, ResExpr);
+
+ // Test {DW_OP_constu, Const1, DW_OP_mul, DW_OP_constu, Const2, DW_OP_mul} ->
+ // {DW_OP_constu, Const1 * Const2, DW_OP_mul}
+ Ops.clear();
+ Ops.push_back(dwarf::DW_OP_constu);
+ Ops.push_back(8);
+ Ops.push_back(dwarf::DW_OP_mul);
+ Ops.push_back(dwarf::DW_OP_constu);
+ Ops.push_back(2);
+ Ops.push_back(dwarf::DW_OP_mul);
+ Expr = DIExpression::get(Context, Ops);
+ E = Expr->foldConstantMath();
+ ResOps.clear();
+ ResOps.push_back(dwarf::DW_OP_constu);
+ ResOps.push_back(16);
+ ResOps.push_back(dwarf::DW_OP_mul);
+ ResExpr = DIExpression::get(Context, ResOps);
+ EXPECT_EQ(E, ResExpr);
+
+ // Test {DW_OP_plus_uconst, Const1, DW_OP_plus, DW_OP_LLVM_arg, Arg,
+ // DW_OP_plus, DW_OP_constu, Const2, DW_OP_plus} -> {DW_OP_plus_uconst, Const1
+ // + Const2, DW_OP_LLVM_arg, Arg, DW_OP_plus}
+ Ops.clear();
+ Ops.push_back(dwarf::DW_OP_plus_uconst);
+ Ops.push_back(8);
+ Ops.push_back(dwarf::DW_OP_LLVM_arg);
+ Ops.push_back(0);
+ Ops.push_back(dwarf::DW_OP_plus);
+ Ops.push_back(dwarf::DW_OP_constu);
+ Ops.push_back(2);
+ Ops.push_back(dwarf::DW_OP_plus);
+ Expr = DIExpression::get(Context, Ops);
+ E = Expr->foldConstantMath();
+ ResOps.clear();
+ ResOps.push_back(dwarf::DW_OP_plus_uconst);
+ ResOps.push_back(10);
+ ResOps.push_back(dwarf::DW_OP_LLVM_arg);
+ ResOps.push_back(0);
+ ResOps.push_back(dwarf::DW_OP_plus);
+ ResExpr = DIExpression::get(Context, ResOps);
+ EXPECT_EQ(E, ResExpr);
+
+ // Test {DW_OP_constu, Const1, DW_OP_plus, DW_OP_LLVM_arg, Arg, DW_OP_plus,
+ // DW_OP_plus_uconst, Const2} -> {DW_OP_constu, Const1 + Const2, DW_OP_plus,
+ // DW_OP_LLVM_arg, Arg, DW_OP_plus}
+ Ops.clear();
+ Ops.push_back(dwarf::DW_OP_constu);
+ Ops.push_back(8);
+ Ops.push_back(dwarf::DW_OP_plus);
+ Ops.push_back(dwarf::DW_OP_LLVM_arg);
+ Ops.push_back(0);
+ Ops.push_back(dwarf::DW_OP_plus);
+ Ops.push_back(dwarf::DW_OP_plus_uconst);
+ Ops.push_back(2);
+ Expr = DIExpression::get(Context, Ops);
+ E = Expr->foldConstantMath();
+ ResOps.clear();
+ ResOps.push_back(dwarf::DW_OP_plus_uconst);
+ ResOps.push_back(10);
+ ResOps.push_back(dwarf::DW_OP_LLVM_arg);
+ ResOps.push_back(0);
+ ResOps.push_back(dwarf::DW_OP_plus);
+ ResExpr = DIExpression::get(Context, ResOps);
+ EXPECT_EQ(E, ResExpr);
+
+ // Test {DW_OP_constu, Const1, DW_OP_plus, DW_OP_LLVM_arg, Arg, DW_OP_plus,
+ // DW_OP_constu, Const2, DW_OP_plus} -> {DW_OP_constu, Const1 + Const2,
+ // DW_OP_plus, DW_OP_LLVM_arg, Arg, DW_OP_plus}
+ Ops.clear();
+ Ops.push_back(dwarf::DW_OP_constu);
+ Ops.push_back(8);
+ Ops.push_back(dwarf::DW_OP_plus);
+ Ops.push_back(dwarf::DW_OP_LLVM_arg);
+ Ops.push_back(0);
+ Ops.push_back(dwarf::DW_OP_plus);
+ Ops.push_back(dwarf::DW_OP_constu);
+ Ops.push_back(2);
+ Ops.push_back(dwarf::DW_OP_plus);
+ Expr = DIExpression::get(Context, Ops);
+ E = Expr->foldConstantMath();
+ ResOps.clear();
+ ResOps.push_back(dwarf::DW_OP_plus_uconst);
+ ResOps.push_back(10);
+ ResOps.push_back(dwarf::DW_OP_LLVM_arg);
+ ResOps.push_back(0);
+ ResOps.push_back(dwarf::DW_OP_plus);
+ ResExpr = DIExpression::get(Context, ResOps);
+ EXPECT_EQ(E, ResExpr);
+
+ // Test {DW_OP_constu, Const1, DW_OP_mul, DW_OP_LLVM_arg, Arg, DW_OP_mul,
+ // DW_OP_constu, Const2, DW_OP_mul} -> {DW_OP_constu, Const1 * Const2,
+ // DW_OP_mul, DW_OP_LLVM_arg, Arg, DW_OP_mul}
+ Ops.clear();
+ Ops.push_back(dwarf::DW_OP_constu);
+ Ops.push_back(8);
+ Ops.push_back(dwarf::DW_OP_mul);
+ Ops.push_back(dwarf::DW_OP_LLVM_arg);
+ Ops.push_back(0);
+ Ops.push_back(dwarf::DW_OP_mul);
+ Ops.push_back(dwarf::DW_OP_constu);
+ Ops.push_back(2);
+ Ops.push_back(dwarf::DW_OP_mul);
+ Expr = DIExpression::get(Context, Ops);
+ E = Expr->foldConstantMath();
+ ResOps.clear();
+ ResOps.push_back(dwarf::DW_OP_constu);
+ ResOps.push_back(16);
+ ResOps.push_back(dwarf::DW_OP_mul);
+ ResOps.push_back(dwarf::DW_OP_LLVM_arg);
+ ResOps.push_back(0);
+ ResOps.push_back(dwarf::DW_OP_mul);
+ ResExpr = DIExpression::get(Context, ResOps);
+ EXPECT_EQ(E, ResExpr);
+
+ // Test an overflow addition.
+ Ops.clear();
+ Ops.push_back(dwarf::DW_OP_plus_uconst);
+ Ops.push_back(UINT64_MAX);
+ Ops.push_back(dwarf::DW_OP_plus_uconst);
+ Ops.push_back(2);
+ Expr = DIExpression::get(Context, Ops);
+ E = Expr->foldConstantMath();
+ ResOps.clear();
+ ResOps.push_back(dwarf::DW_OP_plus_uconst);
+ ResOps.push_back(UINT64_MAX);
+ ResOps.push_back(dwarf::DW_OP_plus_uconst);
+ ResOps.push_back(2);
+ ResExpr = DIExpression::get(Context, ResOps);
+ EXPECT_EQ(E, ResExpr);
+
+ // Test an underflow subtraction.
+ Ops.clear();
+ Ops.push_back(dwarf::DW_OP_constu);
+ Ops.push_back(1);
+ Ops.push_back(dwarf::DW_OP_constu);
+ Ops.push_back(2);
+ Ops.push_back(dwarf::DW_OP_minus);
+ Expr = DIExpression::get(Context, Ops);
+ E = Expr->foldConstantMath();
+ ResOps.clear();
+ ResOps.push_back(dwarf::DW_OP_constu);
+ ResOps.push_back(1);
+ ResOps.push_back(dwarf::DW_OP_constu);
+ ResOps.push_back(2);
+ ResOps.push_back(dwarf::DW_OP_minus);
+ ResExpr = DIExpression::get(Context, ResOps);
+ EXPECT_EQ(E, ResExpr);
+
+ // Test a left shift greater than 64.
+ Ops.clear();
+ Ops.push_back(dwarf::DW_OP_constu);
+ Ops.push_back(1);
+ Ops.push_back(dwarf::DW_OP_constu);
+ Ops.push_back(65);
+ Ops.push_back(dwarf::DW_OP_shl);
+ Expr = DIExpression::get(Context, Ops);
+ E = Expr->foldConstantMath();
+ ResOps.clear();
+ ResOps.push_back(dwarf::DW_OP_constu);
+ ResOps.push_back(1);
+ ResOps.push_back(dwarf::DW_OP_constu);
+ ResOps.push_back(65);
+ ResOps.push_back(dwarf::DW_OP_shl);
+ ResExpr = DIExpression::get(Context, ResOps);
+ EXPECT_EQ(E, ResExpr);
+
+ // Test a right shift greater than 64.
+ Ops.clear();
+ Ops.push_back(dwarf::DW_OP_constu);
+ Ops.push_back(1);
+ Ops.push_back(dwarf::DW_OP_constu);
+ Ops.push_back(65);
+ Ops.push_back(dwarf::DW_OP_shr);
+ Expr = DIExpression::get(Context, Ops);
+ E = Expr->foldConstantMath();
+ ResOps.clear();
+ ResOps.push_back(dwarf::DW_OP_constu);
+ ResOps.push_back(1);
+ ResOps.push_back(dwarf::DW_OP_constu);
+ ResOps.push_back(65);
+ ResOps.push_back(dwarf::DW_OP_shr);
+ ResExpr = DIExpression::get(Context, ResOps);
+ EXPECT_EQ(E, ResExpr);
+
+ // Test an overflow multiplication.
+ Ops.clear();
+ Ops.push_back(dwarf::DW_OP_constu);
+ Ops.push_back(UINT64_MAX);
+ Ops.push_back(dwarf::DW_OP_constu);
+ Ops.push_back(2);
+ Ops.push_back(dwarf::DW_OP_mul);
+ Expr = DIExpression::get(Context, Ops);
+ E = Expr->foldConstantMath();
+ ResOps.clear();
+ ResOps.push_back(dwarf::DW_OP_constu);
+ ResOps.push_back(UINT64_MAX);
+ ResOps.push_back(dwarf::DW_OP_constu);
+ ResOps.push_back(2);
+ ResOps.push_back(dwarf::DW_OP_mul);
+ ResExpr = DIExpression::get(Context, ResOps);
+ EXPECT_EQ(E, ResExpr);
+
+ // Test a divide by 0.
+ Ops.clear();
+ Ops.push_back(dwarf::DW_OP_constu);
+ Ops.push_back(2);
+ Ops.push_back(dwarf::DW_OP_constu);
+ Ops.push_back(0);
+ Ops.push_back(dwarf::DW_OP_div);
+ Expr = DIExpression::get(Context, Ops);
+ E = Expr->foldConstantMath();
+ ResOps.clear();
+ ResOps.push_back(dwarf::DW_OP_constu);
+ ResOps.push_back(2);
+ ResOps.push_back(dwarf::DW_OP_lit0);
+ ResOps.push_back(dwarf::DW_OP_div);
+ ResExpr = DIExpression::get(Context, ResOps);
+ EXPECT_EQ(E, ResExpr);
+}
+
TEST_F(DIExpressionTest, isValid) {
#define EXPECT_VALID(...) \
do { \
>From a9383f70b7ca7039dd77a57fd657dbd7799a41d9 Mon Sep 17 00:00:00 2001
From: Shubham Sandeep Rastogi <srastogi22 at apple.com>
Date: Fri, 8 Mar 2024 11:28:34 -0800
Subject: [PATCH 4/4] Use DIExpression:foldConstantMath() at the result of an
append()
---
llvm/lib/IR/DebugInfoMetadata.cpp | 3 +-
llvm/test/Bitcode/upgrade-dbg-addr.ll | 2 +-
.../MIR/AArch64/dbgcall-site-expr-chain.mir | 4 +-
llvm/unittests/IR/MetadataTest.cpp | 138 ++++++++++++++++++
4 files changed, 143 insertions(+), 4 deletions(-)
diff --git a/llvm/lib/IR/DebugInfoMetadata.cpp b/llvm/lib/IR/DebugInfoMetadata.cpp
index 229ee2bd0164c..9bd1d7880c9f8 100644
--- a/llvm/lib/IR/DebugInfoMetadata.cpp
+++ b/llvm/lib/IR/DebugInfoMetadata.cpp
@@ -1881,7 +1881,8 @@ DIExpression *DIExpression::append(const DIExpression *Expr,
Op.appendToVector(NewOps);
}
NewOps.append(Ops.begin(), Ops.end());
- auto *result = DIExpression::get(Expr->getContext(), NewOps);
+ auto *result =
+ DIExpression::get(Expr->getContext(), NewOps)->foldConstantMath();
assert(result->isValid() && "concatenated expression is not valid");
return result;
}
diff --git a/llvm/test/Bitcode/upgrade-dbg-addr.ll b/llvm/test/Bitcode/upgrade-dbg-addr.ll
index 06a411c2c8348..de35609713f93 100644
--- a/llvm/test/Bitcode/upgrade-dbg-addr.ll
+++ b/llvm/test/Bitcode/upgrade-dbg-addr.ll
@@ -9,7 +9,7 @@ entry:
%num.addr = alloca i32, align 4
store i32 %num, ptr %num.addr, align 4
; CHECK-NOT: call void @llvm.dbg.addr
- ; CHECK: call void @llvm.dbg.value(metadata ptr %num.addr, metadata ![[#]], metadata !DIExpression(DW_OP_plus_uconst, 0, DW_OP_deref))
+ ; CHECK: call void @llvm.dbg.value(metadata ptr %num.addr, metadata ![[#]], metadata !DIExpression(DW_OP_deref))
call void @llvm.dbg.addr(metadata ptr %num.addr, metadata !16, metadata !DIExpression(DW_OP_plus_uconst, 0)), !dbg !17
%0 = load i32, ptr %num.addr, align 4
ret i32 %0
diff --git a/llvm/test/DebugInfo/MIR/AArch64/dbgcall-site-expr-chain.mir b/llvm/test/DebugInfo/MIR/AArch64/dbgcall-site-expr-chain.mir
index cb3e780664404..02f4ce1200ca1 100644
--- a/llvm/test/DebugInfo/MIR/AArch64/dbgcall-site-expr-chain.mir
+++ b/llvm/test/DebugInfo/MIR/AArch64/dbgcall-site-expr-chain.mir
@@ -105,7 +105,7 @@ body: |
# CHECK: DW_TAG_GNU_call_site_parameter
# CHECK-NEXT: DW_AT_location (DW_OP_reg2 W2)
-# CHECK-NEXT: DW_AT_GNU_call_site_value (DW_OP_breg19 W19+700, DW_OP_plus_uconst 0x9, DW_OP_plus_uconst 0x50)
+# CHECK-NEXT: DW_AT_GNU_call_site_value (DW_OP_breg19 W19+789)
# CHECK: DW_TAG_GNU_call_site_parameter
# CHECK-NEXT: DW_AT_location (DW_OP_reg1 W1)
@@ -113,4 +113,4 @@ body: |
# CHECK: DW_TAG_GNU_call_site_parameter
# CHECK-NEXT: DW_AT_location (DW_OP_reg0 W0)
-# CHECK-NEXT: DW_AT_GNU_call_site_value (DW_OP_breg19 W19+100, DW_OP_plus_uconst 0x17)
+# CHECK-NEXT: DW_AT_GNU_call_site_value (DW_OP_breg19 W19+123)
diff --git a/llvm/unittests/IR/MetadataTest.cpp b/llvm/unittests/IR/MetadataTest.cpp
index 9647ac8c43966..3f766a414f08f 100644
--- a/llvm/unittests/IR/MetadataTest.cpp
+++ b/llvm/unittests/IR/MetadataTest.cpp
@@ -3610,6 +3610,144 @@ TEST_F(DIExpressionTest, Fold) {
EXPECT_EQ(E, ResExpr);
}
+TEST_F(DIExpressionTest, Append) {
+ // Test appending a {dwarf::DW_OP_constu, <const>, DW_OP_plus} to a DW_OP_plus
+ // expression
+ SmallVector<uint64_t, 8> Ops = {dwarf::DW_OP_LLVM_arg, 0, dwarf::DW_OP_constu,
+ 2, dwarf::DW_OP_plus};
+ auto *Expr = DIExpression::get(Context, Ops);
+ SmallVector<uint64_t, 8> AppendOps = {dwarf::DW_OP_constu, 3,
+ dwarf::DW_OP_plus};
+ auto *AppendExpr = DIExpression::append(Expr, AppendOps);
+ SmallVector<uint64_t, 8> OpsRes = {dwarf::DW_OP_LLVM_arg, 0,
+ dwarf::DW_OP_plus_uconst, 5};
+ auto *ResExpr = DIExpression::get(Context, OpsRes);
+ EXPECT_EQ(ResExpr, AppendExpr);
+
+ // Test appending a {dwarf::DW_OP_plus_uconst, <const>} to a DW_OP_plus
+ // expression uint64_t PlusUConstOps[] = {dwarf::DW_OP_plus_uconst, 3};
+ AppendOps.clear();
+ AppendOps.push_back(dwarf::DW_OP_plus_uconst);
+ AppendOps.push_back(3);
+ AppendExpr = DIExpression::append(Expr, AppendOps);
+ OpsRes.clear();
+ OpsRes.push_back(dwarf::DW_OP_LLVM_arg);
+ OpsRes.push_back(0);
+ OpsRes.push_back(dwarf::DW_OP_plus_uconst);
+ OpsRes.push_back(5);
+ ResExpr = DIExpression::get(Context, OpsRes);
+ EXPECT_EQ(ResExpr, AppendExpr);
+
+ // Test appending a {dwarf::DW_OP_constu, 0, DW_OP_plus} to an expression
+ AppendOps.clear();
+ AppendOps.push_back(dwarf::DW_OP_constu);
+ AppendOps.push_back(0);
+ AppendOps.push_back(dwarf::DW_OP_plus);
+ AppendExpr = DIExpression::append(Expr, AppendOps);
+ OpsRes.clear();
+ OpsRes.push_back(dwarf::DW_OP_LLVM_arg);
+ OpsRes.push_back(0);
+ OpsRes.push_back(dwarf::DW_OP_plus_uconst);
+ OpsRes.push_back(2);
+ ResExpr = DIExpression::get(Context, OpsRes);
+ EXPECT_EQ(ResExpr, AppendExpr);
+
+ // Test appending a {dwarf::DW_OP_constu, 0, DW_OP_minus} to an expression
+ AppendOps.clear();
+ AppendOps.push_back(dwarf::DW_OP_constu);
+ AppendOps.push_back(0);
+ AppendOps.push_back(dwarf::DW_OP_minus);
+ AppendExpr = DIExpression::append(Expr, AppendOps);
+ OpsRes.clear();
+ OpsRes.push_back(dwarf::DW_OP_LLVM_arg);
+ OpsRes.push_back(0);
+ OpsRes.push_back(dwarf::DW_OP_plus_uconst);
+ OpsRes.push_back(2);
+ ResExpr = DIExpression::get(Context, OpsRes);
+ EXPECT_EQ(ResExpr, AppendExpr);
+
+ // Test appending a {dwarf::DW_OP_constu, 0, DW_OP_shl} to an expression
+ AppendOps.clear();
+ AppendOps.push_back(dwarf::DW_OP_constu);
+ AppendOps.push_back(0);
+ AppendOps.push_back(dwarf::DW_OP_shl);
+ AppendExpr = DIExpression::append(Expr, AppendOps);
+ OpsRes.clear();
+ OpsRes.push_back(dwarf::DW_OP_LLVM_arg);
+ OpsRes.push_back(0);
+ OpsRes.push_back(dwarf::DW_OP_plus_uconst);
+ OpsRes.push_back(2);
+ ResExpr = DIExpression::get(Context, OpsRes);
+ EXPECT_EQ(ResExpr, AppendExpr);
+
+ // Test appending a {dwarf::DW_OP_constu, 0, DW_OP_shr} to an expression
+ AppendOps.clear();
+ AppendOps.push_back(dwarf::DW_OP_constu);
+ AppendOps.push_back(0);
+ AppendOps.push_back(dwarf::DW_OP_shr);
+ AppendExpr = DIExpression::append(Expr, AppendOps);
+ OpsRes.clear();
+ OpsRes.push_back(dwarf::DW_OP_LLVM_arg);
+ OpsRes.push_back(0);
+ OpsRes.push_back(dwarf::DW_OP_plus_uconst);
+ OpsRes.push_back(2);
+ ResExpr = DIExpression::get(Context, OpsRes);
+ EXPECT_EQ(ResExpr, AppendExpr);
+
+ // Test appending a {dwarf::DW_OP_constu, <const>, DW_OP_mul} to a DW_OP_mul
+ // expression
+ Ops.clear();
+ Ops.push_back(dwarf::DW_OP_LLVM_arg);
+ Ops.push_back(0);
+ Ops.push_back(dwarf::DW_OP_constu);
+ Ops.push_back(2);
+ Ops.push_back(dwarf::DW_OP_mul);
+ Expr = DIExpression::get(Context, Ops);
+ AppendOps.clear();
+ AppendOps.push_back(dwarf::DW_OP_constu);
+ AppendOps.push_back(3);
+ AppendOps.push_back(dwarf::DW_OP_mul);
+ AppendExpr = DIExpression::append(Expr, AppendOps);
+ OpsRes.clear();
+ OpsRes.push_back(dwarf::DW_OP_LLVM_arg);
+ OpsRes.push_back(0);
+ OpsRes.push_back(dwarf::DW_OP_constu);
+ OpsRes.push_back(6);
+ OpsRes.push_back(dwarf::DW_OP_mul);
+ ResExpr = DIExpression::get(Context, OpsRes);
+ EXPECT_EQ(ResExpr, AppendExpr);
+
+ // Test appending a {dwarf::DW_OP_constu, 1, DW_OP_mul} to an expression
+ AppendOps.clear();
+ AppendOps.push_back(dwarf::DW_OP_constu);
+ AppendOps.push_back(1);
+ AppendOps.push_back(dwarf::DW_OP_mul);
+ AppendExpr = DIExpression::append(Expr, AppendOps);
+ OpsRes.clear();
+ OpsRes.push_back(dwarf::DW_OP_LLVM_arg);
+ OpsRes.push_back(0);
+ OpsRes.push_back(dwarf::DW_OP_constu);
+ OpsRes.push_back(2);
+ OpsRes.push_back(dwarf::DW_OP_mul);
+ ResExpr = DIExpression::get(Context, OpsRes);
+ EXPECT_EQ(ResExpr, AppendExpr);
+
+ // Test appending a {dwarf::DW_OP_constu, 1, DW_OP_div} to an expression
+ AppendOps.clear();
+ AppendOps.push_back(dwarf::DW_OP_constu);
+ AppendOps.push_back(1);
+ AppendOps.push_back(dwarf::DW_OP_div);
+ AppendExpr = DIExpression::append(Expr, AppendOps);
+ OpsRes.clear();
+ OpsRes.push_back(dwarf::DW_OP_LLVM_arg);
+ OpsRes.push_back(0);
+ OpsRes.push_back(dwarf::DW_OP_constu);
+ OpsRes.push_back(2);
+ OpsRes.push_back(dwarf::DW_OP_mul);
+ ResExpr = DIExpression::get(Context, OpsRes);
+ EXPECT_EQ(ResExpr, AppendExpr);
+}
+
TEST_F(DIExpressionTest, isValid) {
#define EXPECT_VALID(...) \
do { \
More information about the llvm-commits
mailing list