[llvm] Introduce DIExpression::foldConstantMath() (PR #71718)

Shubham Sandeep Rastogi via llvm-commits llvm-commits at lists.llvm.org
Wed Mar 13 10:58:12 PDT 2024


================
@@ -2011,6 +2010,321 @@ DIExpression::constantFold(const ConstantInt *CI) {
           ConstantInt::get(getContext(), NewInt)};
 }
 
+static bool isConstantVal(uint64_t Op) {
+  return Op == dwarf::DW_OP_constu || Op == dwarf::DW_OP_consts;
+}
+
+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;
+  }
+}
+
+static std::optional<uint64_t>
+foldOperationIfPossible(uint64_t Op, uint64_t Operand1, uint64_t Operand2) {
+  switch (Op) {
+  case dwarf::DW_OP_plus:
+    return Operand1 + Operand2;
+  case dwarf::DW_OP_minus:
+    return Operand1 - Operand2;
+  case dwarf::DW_OP_shl:
+    return Operand1 << Operand2;
+  case dwarf::DW_OP_shr:
+    return Operand1 >> Operand2;
+  case dwarf::DW_OP_mul:
+    return Operand1 * Operand2;
+  case dwarf::DW_OP_div:
+    return Operand1 / Operand2;
+  default:
+    return std::nullopt;
+  }
+}
+
+static bool operationsAreFoldableAndCommutative(uint64_t Op1, uint64_t Op2) {
+  if (Op1 != Op2)
+    return false;
+  switch (Op1) {
+  case dwarf::DW_OP_plus:
+  case dwarf::DW_OP_mul:
+    return true;
+  default:
+    return false;
+  }
+}
+
+static void consumeOneOperator(DIExpressionCursor &Cursor, uint64_t &Loc,
+                               const DIExpression::ExprOperand &Op) {
+  Cursor.consume(1);
+  Loc = Loc + Op.getSize();
+}
+
+static void startFromBeginning(uint64_t &Loc, DIExpressionCursor &Cursor,
+                               ArrayRef<uint64_t> WorkingOps) {
+  Cursor.assignNewExpr(WorkingOps);
+  Loc = 0;
+}
+
+static void moveCursorToCurrentOp(uint64_t &Loc, DIExpressionCursor &Cursor,
+                                  ArrayRef<uint64_t> WorkingOps,
+                                  uint32_t NumOfOpsConsumed) {
+  Cursor.assignNewExpr(WorkingOps);
+  Cursor.consume(NumOfOpsConsumed);
+  assert(Cursor.peek()->getOp() == WorkingOps[Loc] &&
+         "Cursor position does not match position of iterator 'Loc' to "
+         "WorkingOps");
+}
+
+// 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 void canonicalizeDwarfOperations(SmallVectorImpl<uint64_t> &WorkingOps) {
+  DIExpressionCursor Cursor(WorkingOps);
+  uint64_t Loc = 0;
+  uint32_t NumOfOpsConsumed = 0;
+  while (Loc < WorkingOps.size()) {
+    auto Op = Cursor.peek();
+    // Expression has no operations, break.
+    if (!Op)
+      break;
+    auto OpRaw = Op->getOp();
+    auto OpArg = Op->getArg(0);
+
+    if (OpRaw >= dwarf::DW_OP_lit0 && OpRaw <= dwarf::DW_OP_lit31) {
+      WorkingOps[Loc] = dwarf::DW_OP_constu;
+      WorkingOps.insert(WorkingOps.begin() + Loc + 1,
+                        OpRaw - dwarf::DW_OP_lit0);
+    } else if (OpRaw == dwarf::DW_OP_plus_uconst) {
+      WorkingOps[Loc] = dwarf::DW_OP_constu;
+      WorkingOps[Loc + 1] = OpArg;
+      WorkingOps.insert(WorkingOps.begin() + Loc + 2, dwarf::DW_OP_plus);
+      moveCursorToCurrentOp(Loc, Cursor, WorkingOps, NumOfOpsConsumed);
+    }
+    consumeOneOperator(Cursor, Loc, *Cursor.peek());
+    NumOfOpsConsumed++;
+  }
+}
+
+// This function will convert:
+// DW_OP_constu <const-val> DW_OP_plus to DW_OP_plus_uconst
+static void optimizeDwarfOperations(SmallVectorImpl<uint64_t> &WorkingOps) {
+  DIExpressionCursor Cursor(WorkingOps);
+  uint64_t Loc = 0;
+  uint32_t NumOfOpsConsumed = 0;
+  while (Loc < WorkingOps.size()) {
+    auto Op1 = Cursor.peek();
+    // Expression has no operations, exit.
+    if (!Op1)
+      break;
+    auto Op1Raw = Op1->getOp();
+    auto Op1Arg = Op1->getArg(0);
+
+    if (Op1Raw == dwarf::DW_OP_constu && Op1Arg == 0) {
+      WorkingOps[Loc] = dwarf::DW_OP_lit0;
+      WorkingOps.erase(WorkingOps.begin() + Loc + 1);
+      moveCursorToCurrentOp(Loc, Cursor, WorkingOps, NumOfOpsConsumed);
+      continue;
+    }
+
+    auto Op2 = Cursor.peekNext();
+    // Expression has no more operations, exit.
+    if (!Op2)
+      break;
+    auto Op2Raw = Op2->getOp();
+
+    if (Op1Raw == dwarf::DW_OP_constu && Op2Raw == dwarf::DW_OP_plus) {
+      WorkingOps.erase(WorkingOps.begin() + Loc + 2);
+      WorkingOps[Loc] = dwarf::DW_OP_plus_uconst;
+      WorkingOps[Loc + 1] = Op1Arg;
+      moveCursorToCurrentOp(Loc, Cursor, WorkingOps, NumOfOpsConsumed);
+    }
+    consumeOneOperator(Cursor, Loc, *Cursor.peek());
+    NumOfOpsConsumed++;
+  }
+}
+
+// {DW_OP_const[u, s], 0, DW_OP_[plus, minus, shl, shr]} -> {}
+// {DW_OP_const[u, s], 1, DW_OP_[mul, div]} -> {}
+static bool tryFoldNoOpMath(uint64_t Op1Raw, uint64_t Op1Arg, uint64_t Op2Raw,
+                            uint64_t &Loc, DIExpressionCursor &Cursor,
+                            SmallVectorImpl<uint64_t> &WorkingOps) {
+  if (isConstantVal(Op1Raw) && isNeutralElement(Op2Raw, Op1Arg)) {
+    WorkingOps.erase(WorkingOps.begin() + Loc, WorkingOps.begin() + Loc + 3);
+    startFromBeginning(Loc, Cursor, WorkingOps);
+    return true;
+  }
+  return false;
+}
+
+// {DW_OP_const[u, s], Const1, DW_OP_const[u, s], Const2, DW_OP_[plus,
+// minus, mul, div, shl, shr] -> {DW_OP_constu, Const1 [+, -, *, /, <<, >>]
+// Const2}
+static bool tryFoldConstants(std::optional<DIExpression::ExprOperand> Op1,
+                             uint64_t Op1Raw, uint64_t Op1Arg, uint64_t Op2Raw,
+                             uint64_t Op2Arg, uint64_t Op3Raw, uint64_t &Loc,
+                             DIExpressionCursor &Cursor,
+                             SmallVectorImpl<uint64_t> &WorkingOps) {
+  if (isConstantVal(Op1Raw) && isConstantVal(Op2Raw)) {
+    auto Result = foldOperationIfPossible(Op3Raw, Op1Arg, Op2Arg);
+    if (!Result) {
+      consumeOneOperator(Cursor, Loc, *Op1);
+      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;
+  }
+  return false;
+}
+
+// {DW_OP_const[u, s], Const1, DW_OP_[plus, mul], DW_OP_const[u, s], Const2,
+// DW_OP_[plus, mul]} -> {DW_OP_constu, Const1 [+, *] Const2, DW_OP_[plus, mul]}
+static bool tryFoldCommutativeMath(uint64_t Op1Raw, uint64_t Op1Arg,
----------------
rastogishubham wrote:

No, Op1 .. Op4 are relative, where Op1 is the first current Op in the sequence, and Op4 is the 4th one in the sequence, that is why we use `Loc` to figure out what to erase

https://github.com/llvm/llvm-project/pull/71718


More information about the llvm-commits mailing list