[llvm] Introduce DIExpressionOptimizer (PR #69769)

Shubham Sandeep Rastogi via llvm-commits llvm-commits at lists.llvm.org
Fri Oct 20 13:28:12 PDT 2023


https://github.com/rastogishubham created https://github.com/llvm/llvm-project/pull/69769

DIExpressionOptimizer is a lightweight class that can be used to optimize DIExpressions that can get very large, by involving some simple constant folding and removing appends of unnecessary operations.

>From d16393bd60213e2c83b9275bcc7d9f5bc4060997 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/2] [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 b347664883fd9f2..86ee9f004cfd42b 100644
--- a/llvm/include/llvm/IR/DebugInfoMetadata.h
+++ b/llvm/include/llvm/IR/DebugInfoMetadata.h
@@ -3073,6 +3073,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 667a9efc6f6c04f..4daa78b15b8e298 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 412d655a76bad1b687946f9c3ef316ad5e10db82 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 2/2] Introduce DIExpressionOptimizer

DIExpressionOptimizer is a lightweight class that can be used to
optimize DIExpressions that can get very large, by involving some simple
constant folding, and removing appends of unecessary operations.
---
 llvm/include/llvm/IR/DebugInfoMetadata.h |  42 +++++++
 llvm/lib/IR/DebugInfoMetadata.cpp        | 137 +++++++++++++++++++++++
 2 files changed, 179 insertions(+)

diff --git a/llvm/include/llvm/IR/DebugInfoMetadata.h b/llvm/include/llvm/IR/DebugInfoMetadata.h
index 86ee9f004cfd42b..b9df644e44ccf36 100644
--- a/llvm/include/llvm/IR/DebugInfoMetadata.h
+++ b/llvm/include/llvm/IR/DebugInfoMetadata.h
@@ -3073,6 +3073,48 @@ template <> struct DenseMapInfo<DIExpression::FragmentInfo> {
   static bool isEqual(const FragInfo &A, const FragInfo &B) { return A == B; }
 };
 
+class DIExpressionOptimizer {
+
+  // When looking at a DIExpression such as {DW_OP_constu, 1, DW_OP_constu, 2,
+  // DW_OP_plus} and trying to append {DW_OP_consts, 3, DW_OP_minus}
+  // NewMathOperator = DW_OP_minus
+  // OperandRight = 3
+  // OperatorRight = DW_OP_consts
+  // CurrMathOperator = DW_OP_plus
+  // OperandLeft = 2
+  // OperandLeft = DW_OP_constu
+
+  /// The math operator of the new subexpression being appended to the
+  /// DIExpression.
+  uint64_t NewMathOperator = 0;
+
+  /// The operand of the new subexpression being appended to the DIExpression.
+  uint64_t OperandRight;
+
+  /// The operator attached to OperandRight.
+  uint64_t OperatorRight = 0;
+
+  /// The math operator at the end of the current DIExpression.
+  uint64_t CurrMathOperator = 0;
+
+  /// The operand at the end of the current DIExpression at the top of the
+  /// stack.
+  uint64_t OperandLeft;
+
+  /// The operator attached to OperandLeft.
+  uint64_t OperatorLeft = 0;
+
+  bool operatorIsCommutative(uint64_t Operator);
+
+  SmallVector<uint64_t> getOperatorLocations(ArrayRef<uint64_t> Ops);
+
+  void reset();
+
+public:
+  bool operatorCanBeOptimized(uint64_t Operator);
+  void optimize(SmallVectorImpl<uint64_t> &NewOps, ArrayRef<uint64_t> Ops);
+};
+
 /// Holds a DIExpression and keeps track of how many operands have been consumed
 /// so far.
 class DIExpressionCursor {
diff --git a/llvm/lib/IR/DebugInfoMetadata.cpp b/llvm/lib/IR/DebugInfoMetadata.cpp
index f7f36129ec8557c..dfda851b77777ad 100644
--- a/llvm/lib/IR/DebugInfoMetadata.cpp
+++ b/llvm/lib/IR/DebugInfoMetadata.cpp
@@ -1734,6 +1734,143 @@ const DIExpression *DIExpression::extractAddressClass(const DIExpression *Expr,
   return Expr;
 }
 
+SmallVector<uint64_t>
+DIExpressionOptimizer::getOperatorLocations(ArrayRef<uint64_t> Ops) {
+  SmallVector<uint64_t> OpLocs;
+  uint64_t Loc = 0;
+  DIExpressionCursor Cursor(Ops);
+
+  while (Cursor.peek()) {
+    OpLocs.push_back(Loc);
+    auto Size = Cursor.peek()->getSize();
+    Loc += Size;
+    Cursor.consume(1);
+  }
+
+  return OpLocs;
+}
+
+bool DIExpressionOptimizer::operatorIsCommutative(uint64_t Operator) {
+  return Operator == dwarf::DW_OP_mul || Operator == dwarf::DW_OP_plus;
+}
+
+bool DIExpressionOptimizer::operatorCanBeOptimized(uint64_t Operator) {
+  switch (Operator) {
+  case dwarf::DW_OP_plus:
+  case dwarf::DW_OP_plus_uconst:
+  case dwarf::DW_OP_minus:
+  case dwarf::DW_OP_mul:
+  case dwarf::DW_OP_div:
+  case dwarf::DW_OP_shl:
+  case dwarf::DW_OP_shr:
+    return true;
+  default:
+    return false;
+  }
+}
+
+void DIExpressionOptimizer::reset() {
+  NewMathOperator = 0;
+  CurrMathOperator = 0;
+  OperatorLeft = 0;
+  OperatorRight = 0;
+}
+
+void DIExpressionOptimizer::optimize(SmallVectorImpl<uint64_t> &NewOps,
+                                     ArrayRef<uint64_t> Ops) {
+
+  if (Ops.size() == 2 && Ops[0] == dwarf::DW_OP_plus_uconst) {
+    // Convert a {DW_OP_plus_uconst, <constant value>} expression to
+    // {DW_OP_constu, <constant value>, DW_OP_plus}
+    NewMathOperator = dwarf::DW_OP_plus;
+    OperandRight = Ops[1];
+    OperatorRight = dwarf::DW_OP_constu;
+  } else if (Ops.size() == 3) {
+    NewMathOperator = Ops[2];
+    OperandRight = Ops[1];
+    OperatorRight = Ops[0];
+  } else {
+    // Currently, DIExpressionOptimizer only supports arithmetic operations
+    // such as Ops = {DW_OP_constu, 1, DW_OP_mul}
+    NewOps.append(Ops.begin(), Ops.end());
+    return;
+  }
+
+  if (OperatorRight != dwarf::DW_OP_constu &&
+      OperatorRight != dwarf::DW_OP_consts) {
+    NewOps.append(Ops.begin(), Ops.end());
+    return;
+  }
+
+  uint64_t CurrOperator;
+
+  if ((NewMathOperator == dwarf::DW_OP_mul ||
+       NewMathOperator == dwarf::DW_OP_div) &&
+      OperandRight == 1) {
+    // It is a multiply or divide by 1
+    reset();
+    return;
+  }
+  if ((NewMathOperator == dwarf::DW_OP_plus ||
+       NewMathOperator == dwarf::DW_OP_minus ||
+       NewMathOperator == dwarf::DW_OP_shl ||
+       NewMathOperator == dwarf::DW_OP_shr) &&
+      OperandRight == 0) {
+    // It is a add, subtract, shift left, or shift right with 0
+    reset();
+    return;
+  }
+
+  // Get the locations of the operators in the DIExpression
+  auto OperatorLocs = getOperatorLocations(NewOps);
+
+  for (auto It = OperatorLocs.rbegin(); It != OperatorLocs.rend(); It++) {
+    // Only look at the last two operators of the DIExpression to see if the new
+    // operators can be constant folded with them.
+    if (std::distance(OperatorLocs.rbegin(), It) > 1)
+      break;
+    CurrOperator = NewOps[*It];
+    if (CurrOperator == dwarf::DW_OP_mul || CurrOperator == dwarf::DW_OP_div ||
+        CurrOperator == dwarf::DW_OP_plus ||
+        CurrOperator == dwarf::DW_OP_minus) {
+      CurrMathOperator = CurrOperator;
+      continue;
+    }
+    if (CurrOperator == dwarf::DW_OP_plus_uconst &&
+        NewMathOperator == dwarf::DW_OP_plus) {
+      NewOps[*It + 1] = OperandRight + NewOps[*It + 1];
+      reset();
+      return;
+    }
+    if (CurrOperator == dwarf::DW_OP_constu ||
+        CurrOperator == dwarf::DW_OP_consts) {
+      OperatorLeft = CurrOperator;
+      OperandLeft = NewOps[*It + 1];
+
+      if (NewMathOperator == CurrMathOperator &&
+          operatorIsCommutative(NewMathOperator)) {
+        switch (NewMathOperator) {
+        case dwarf::DW_OP_plus:
+          NewOps[*It + 1] = OperandRight + OperandLeft;
+          break;
+        case dwarf::DW_OP_mul:
+          NewOps[*It + 1] = OperandRight * OperandLeft;
+          break;
+        default:
+          llvm_unreachable("Operator is not multiplication or addition!");
+        }
+        reset();
+        return;
+      }
+      break;
+    }
+    break;
+  }
+  NewOps.append(Ops.begin(), Ops.end());
+  reset();
+  return;
+}
+
 DIExpression *DIExpression::prepend(const DIExpression *Expr, uint8_t Flags,
                                     int64_t Offset) {
   SmallVector<uint64_t, 8> Ops;



More information about the llvm-commits mailing list