Skip to content

Commit 1721038

Browse files
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.
1 parent 5cfe70a commit 1721038

File tree

6 files changed

+872
-1
lines changed

6 files changed

+872
-1
lines changed
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
//===- llvm/IR/DIExpressionOptimizer.h - Constant folding of DIExpressions --*-
2+
//C++ -*-===//
3+
//
4+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
5+
// See https://llvm.org/LICENSE.txt for license information.
6+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7+
//
8+
//===----------------------------------------------------------------------===//
9+
//
10+
// Declarations for functions to constant fold DIExpressions.
11+
//
12+
//===----------------------------------------------------------------------===//
13+
14+
#ifndef LLVM_IR_DIEXPRESSIONOPTIMIZER_H
15+
#define LLVM_IR_DIEXPRESSIONOPTIMIZER_H
16+
17+
#include "llvm/IR/DebugInfoMetadata.h"
18+
19+
using namespace llvm;
20+
21+
/// Returns true if the Op is a DW_OP_constu.
22+
bool isConstantVal(uint64_t Op);
23+
24+
/// Returns true if an operation and operand result in a No Op.
25+
bool isNeutralElement(uint64_t Op, uint64_t Val);
26+
27+
/// Try to fold constant math operations and return the result if possible.
28+
std::optional<uint64_t> foldOperationIfPossible(uint64_t Op, uint64_t Operand1,
29+
uint64_t Operand2);
30+
31+
/// Returns true if the two operations are commutative and can be folded.
32+
bool operationsAreFoldableAndCommutative(uint64_t Op1, uint64_t Op2);
33+
34+
/// Consume one operator and its operand(s).
35+
void consumeOneOperator(DIExpressionCursor &Cursor, uint64_t &Loc,
36+
const DIExpression::ExprOperand &Op);
37+
38+
/// Reset the Cursor to the beginning of the WorkingOps.
39+
void startFromBeginning(uint64_t &Loc, DIExpressionCursor &Cursor,
40+
ArrayRef<uint64_t> WorkingOps);
41+
42+
/// This function will canonicalize:
43+
/// 1. DW_OP_plus_uconst to DW_OP_constu <const-val> DW_OP_plus
44+
/// 2. DW_OP_lit<n> to DW_OP_constu <n>
45+
SmallVector<uint64_t>
46+
canonicalizeDwarfOperations(ArrayRef<uint64_t> WorkingOps);
47+
48+
/// This function will convert:
49+
/// 1. DW_OP_constu <const-val> DW_OP_plus to DW_OP_plus_uconst
50+
/// 2. DW_OP_constu, 0 to DW_OP_lit0
51+
SmallVector<uint64_t> optimizeDwarfOperations(ArrayRef<uint64_t> WorkingOps);
52+
53+
/// {DW_OP_constu, 0, DW_OP_[plus, minus, shl, shr]} -> {}
54+
/// {DW_OP_constu, 1, DW_OP_[mul, div]} -> {}
55+
bool tryFoldNoOpMath(ArrayRef<DIExpression::ExprOperand> Ops, uint64_t &Loc,
56+
DIExpressionCursor &Cursor,
57+
SmallVectorImpl<uint64_t> &WorkingOps);
58+
59+
/// {DW_OP_constu, Const1, DW_OP_constu, Const2, DW_OP_[plus,
60+
/// minus, mul, div, shl, shr] -> {DW_OP_constu, Const1 [+, -, *, /, <<, >>]
61+
/// Const2}
62+
bool tryFoldConstants(ArrayRef<DIExpression::ExprOperand> Ops, uint64_t &Loc,
63+
DIExpressionCursor &Cursor,
64+
SmallVectorImpl<uint64_t> &WorkingOps);
65+
66+
/// {DW_OP_constu, Const1, DW_OP_[plus, mul], DW_OP_constu, Const2,
67+
/// DW_OP_[plus, mul]} -> {DW_OP_constu, Const1 [+, *] Const2, DW_OP_[plus,
68+
/// mul]}
69+
bool tryFoldCommutativeMath(ArrayRef<DIExpression::ExprOperand> Ops,
70+
uint64_t &Loc, DIExpressionCursor &Cursor,
71+
SmallVectorImpl<uint64_t> &WorkingOps);
72+
73+
/// {DW_OP_constu, Const1, DW_OP_[plus, mul], DW_OP_LLVM_arg, Arg1,
74+
/// DW_OP_[plus, mul], DW_OP_constu, Const2, DW_OP_[plus, mul]} ->
75+
/// {DW_OP_constu, Const1 [+, *] Const2, DW_OP_[plus, mul], DW_OP_LLVM_arg,
76+
/// Arg1, DW_OP_[plus, mul]}
77+
bool tryFoldCommutativeMathWithArgInBetween(
78+
ArrayRef<DIExpression::ExprOperand> Ops, uint64_t &Loc,
79+
DIExpressionCursor &Cursor, SmallVectorImpl<uint64_t> &WorkingOps);
80+
81+
#endif // LLVM_IR_DIEXPRESSIONOPTIMIZER_H

llvm/include/llvm/IR/DebugInfoMetadata.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3121,6 +3121,11 @@ class DIExpression : public MDNode {
31213121
/// expression and constant on failure.
31223122
std::pair<DIExpression *, const ConstantInt *>
31233123
constantFold(const ConstantInt *CI);
3124+
3125+
/// Try to shorten an expression with constant math operations that can be
3126+
/// evaluated at compile time. Returns a new expression on success, or the old
3127+
/// expression if there is nothing to be reduced.
3128+
DIExpression *foldConstantMath();
31243129
};
31253130

31263131
inline bool operator==(const DIExpression::FragmentInfo &A,

llvm/lib/IR/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ add_llvm_component_library(LLVMCore
1717
DataLayout.cpp
1818
DebugInfo.cpp
1919
DebugInfoMetadata.cpp
20+
DIExpressionOptimizer.cpp
2021
DebugProgramInstruction.cpp
2122
DebugLoc.cpp
2223
DiagnosticHandler.cpp

llvm/lib/IR/DIExpressionOptimizer.cpp

Lines changed: 245 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,245 @@
1+
//===- DIExpressionOptimizer.cpp - Constant folding of DIExpressions ------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
//
9+
// This file implements functions to constant fold DIExpressions.
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
#include "llvm/IR/DIExpressionOptimizer.h"
14+
#include "llvm/BinaryFormat/Dwarf.h"
15+
16+
bool isConstantVal(uint64_t Op) { return Op == dwarf::DW_OP_constu; }
17+
18+
bool isNeutralElement(uint64_t Op, uint64_t Val) {
19+
switch (Op) {
20+
case dwarf::DW_OP_plus:
21+
case dwarf::DW_OP_minus:
22+
case dwarf::DW_OP_shl:
23+
case dwarf::DW_OP_shr:
24+
return Val == 0;
25+
case dwarf::DW_OP_mul:
26+
case dwarf::DW_OP_div:
27+
return Val == 1;
28+
default:
29+
return false;
30+
}
31+
}
32+
33+
std::optional<uint64_t> foldOperationIfPossible(uint64_t Op, uint64_t Operand1,
34+
uint64_t Operand2) {
35+
bool ResultOverflowed;
36+
switch (Op) {
37+
case dwarf::DW_OP_plus: {
38+
auto Result = SaturatingAdd(Operand1, Operand2, &ResultOverflowed);
39+
if (ResultOverflowed)
40+
return std::nullopt;
41+
return Result;
42+
}
43+
case dwarf::DW_OP_minus: {
44+
if (Operand1 < Operand2)
45+
return std::nullopt;
46+
return Operand1 - Operand2;
47+
}
48+
case dwarf::DW_OP_shl: {
49+
if ((uint64_t)countl_zero(Operand1) < Operand2)
50+
return std::nullopt;
51+
return Operand1 << Operand2;
52+
}
53+
case dwarf::DW_OP_shr: {
54+
if ((uint64_t)countr_zero(Operand1) < Operand2)
55+
return std::nullopt;
56+
return Operand1 >> Operand2;
57+
}
58+
case dwarf::DW_OP_mul: {
59+
auto Result = SaturatingMultiply(Operand1, Operand2, &ResultOverflowed);
60+
if (ResultOverflowed)
61+
return std::nullopt;
62+
return Result;
63+
}
64+
case dwarf::DW_OP_div: {
65+
if (Operand2)
66+
return Operand1 / Operand2;
67+
return std::nullopt;
68+
}
69+
default:
70+
return std::nullopt;
71+
}
72+
}
73+
74+
bool operationsAreFoldableAndCommutative(uint64_t Op1, uint64_t Op2) {
75+
if (Op1 != Op2)
76+
return false;
77+
switch (Op1) {
78+
case dwarf::DW_OP_plus:
79+
case dwarf::DW_OP_mul:
80+
return true;
81+
default:
82+
return false;
83+
}
84+
}
85+
86+
void consumeOneOperator(DIExpressionCursor &Cursor, uint64_t &Loc,
87+
const DIExpression::ExprOperand &Op) {
88+
Cursor.consume(1);
89+
Loc = Loc + Op.getSize();
90+
}
91+
92+
void startFromBeginning(uint64_t &Loc, DIExpressionCursor &Cursor,
93+
ArrayRef<uint64_t> WorkingOps) {
94+
Cursor.assignNewExpr(WorkingOps);
95+
Loc = 0;
96+
}
97+
98+
SmallVector<uint64_t>
99+
canonicalizeDwarfOperations(ArrayRef<uint64_t> WorkingOps) {
100+
DIExpressionCursor Cursor(WorkingOps);
101+
uint64_t Loc = 0;
102+
SmallVector<uint64_t> ResultOps;
103+
while (Loc < WorkingOps.size()) {
104+
auto Op = Cursor.peek();
105+
/// Expression has no operations, break.
106+
if (!Op)
107+
break;
108+
auto OpRaw = Op->getOp();
109+
auto OpArg = Op->getArg(0);
110+
111+
if (OpRaw >= dwarf::DW_OP_lit0 && OpRaw <= dwarf::DW_OP_lit31) {
112+
ResultOps.push_back(dwarf::DW_OP_constu);
113+
ResultOps.push_back(OpRaw - dwarf::DW_OP_lit0);
114+
consumeOneOperator(Cursor, Loc, *Cursor.peek());
115+
continue;
116+
}
117+
if (OpRaw == dwarf::DW_OP_plus_uconst) {
118+
ResultOps.push_back(dwarf::DW_OP_constu);
119+
ResultOps.push_back(OpArg);
120+
ResultOps.push_back(dwarf::DW_OP_plus);
121+
consumeOneOperator(Cursor, Loc, *Cursor.peek());
122+
continue;
123+
}
124+
uint64_t PrevLoc = Loc;
125+
consumeOneOperator(Cursor, Loc, *Cursor.peek());
126+
ResultOps.append(WorkingOps.begin() + PrevLoc, WorkingOps.begin() + Loc);
127+
}
128+
return ResultOps;
129+
}
130+
131+
SmallVector<uint64_t> optimizeDwarfOperations(ArrayRef<uint64_t> WorkingOps) {
132+
DIExpressionCursor Cursor(WorkingOps);
133+
uint64_t Loc = 0;
134+
SmallVector<uint64_t> ResultOps;
135+
while (Loc < WorkingOps.size()) {
136+
auto Op1 = Cursor.peek();
137+
/// Expression has no operations, exit.
138+
if (!Op1)
139+
break;
140+
auto Op1Raw = Op1->getOp();
141+
auto Op1Arg = Op1->getArg(0);
142+
143+
if (Op1Raw == dwarf::DW_OP_constu && Op1Arg == 0) {
144+
ResultOps.push_back(dwarf::DW_OP_lit0);
145+
consumeOneOperator(Cursor, Loc, *Cursor.peek());
146+
continue;
147+
}
148+
149+
auto Op2 = Cursor.peekNext();
150+
/// Expression has no more operations, copy into ResultOps and exit.
151+
if (!Op2) {
152+
uint64_t PrevLoc = Loc;
153+
consumeOneOperator(Cursor, Loc, *Cursor.peek());
154+
ResultOps.append(WorkingOps.begin() + PrevLoc, WorkingOps.begin() + Loc);
155+
break;
156+
}
157+
auto Op2Raw = Op2->getOp();
158+
159+
if (Op1Raw == dwarf::DW_OP_constu && Op2Raw == dwarf::DW_OP_plus) {
160+
ResultOps.push_back(dwarf::DW_OP_plus_uconst);
161+
ResultOps.push_back(Op1Arg);
162+
consumeOneOperator(Cursor, Loc, *Cursor.peek());
163+
consumeOneOperator(Cursor, Loc, *Cursor.peek());
164+
continue;
165+
}
166+
uint64_t PrevLoc = Loc;
167+
consumeOneOperator(Cursor, Loc, *Cursor.peek());
168+
ResultOps.append(WorkingOps.begin() + PrevLoc, WorkingOps.begin() + Loc);
169+
}
170+
return ResultOps;
171+
}
172+
173+
bool tryFoldNoOpMath(ArrayRef<DIExpression::ExprOperand> Ops, uint64_t &Loc,
174+
DIExpressionCursor &Cursor,
175+
SmallVectorImpl<uint64_t> &WorkingOps) {
176+
if (isConstantVal(Ops[0].getOp()) &&
177+
isNeutralElement(Ops[1].getOp(), Ops[0].getArg(0))) {
178+
WorkingOps.erase(WorkingOps.begin() + Loc, WorkingOps.begin() + Loc + 3);
179+
startFromBeginning(Loc, Cursor, WorkingOps);
180+
return true;
181+
}
182+
return false;
183+
}
184+
185+
bool tryFoldConstants(ArrayRef<DIExpression::ExprOperand> Ops, uint64_t &Loc,
186+
DIExpressionCursor &Cursor,
187+
SmallVectorImpl<uint64_t> &WorkingOps) {
188+
if (isConstantVal(Ops[0].getOp()) && isConstantVal(Ops[1].getOp())) {
189+
auto Result = foldOperationIfPossible(Ops[2].getOp(), Ops[0].getArg(0),
190+
Ops[1].getArg(0));
191+
if (!Result) {
192+
consumeOneOperator(Cursor, Loc, Ops[0]);
193+
return true;
194+
}
195+
WorkingOps.erase(WorkingOps.begin() + Loc + 2,
196+
WorkingOps.begin() + Loc + 5);
197+
WorkingOps[Loc] = dwarf::DW_OP_constu;
198+
WorkingOps[Loc + 1] = *Result;
199+
startFromBeginning(Loc, Cursor, WorkingOps);
200+
return true;
201+
}
202+
return false;
203+
}
204+
205+
bool tryFoldCommutativeMath(ArrayRef<DIExpression::ExprOperand> Ops,
206+
uint64_t &Loc, DIExpressionCursor &Cursor,
207+
SmallVectorImpl<uint64_t> &WorkingOps) {
208+
209+
if (isConstantVal(Ops[0].getOp()) && isConstantVal(Ops[2].getOp()) &&
210+
operationsAreFoldableAndCommutative(Ops[1].getOp(), Ops[3].getOp())) {
211+
auto Result = foldOperationIfPossible(Ops[1].getOp(), Ops[0].getArg(0),
212+
Ops[2].getArg(0));
213+
if (!Result)
214+
return false;
215+
WorkingOps.erase(WorkingOps.begin() + Loc + 3,
216+
WorkingOps.begin() + Loc + 6);
217+
WorkingOps[Loc] = dwarf::DW_OP_constu;
218+
WorkingOps[Loc + 1] = *Result;
219+
startFromBeginning(Loc, Cursor, WorkingOps);
220+
return true;
221+
}
222+
return false;
223+
}
224+
225+
bool tryFoldCommutativeMathWithArgInBetween(
226+
ArrayRef<DIExpression::ExprOperand> Ops, uint64_t &Loc,
227+
DIExpressionCursor &Cursor, SmallVectorImpl<uint64_t> &WorkingOps) {
228+
if (isConstantVal(Ops[0].getOp()) &&
229+
Ops[2].getOp() == dwarf::DW_OP_LLVM_arg &&
230+
isConstantVal(Ops[4].getOp()) &&
231+
operationsAreFoldableAndCommutative(Ops[1].getOp(), Ops[3].getOp()) &&
232+
operationsAreFoldableAndCommutative(Ops[3].getOp(), Ops[5].getOp())) {
233+
auto Result = foldOperationIfPossible(Ops[1].getOp(), Ops[0].getArg(0),
234+
Ops[4].getArg(0));
235+
if (!Result)
236+
return false;
237+
WorkingOps.erase(WorkingOps.begin() + Loc + 6,
238+
WorkingOps.begin() + Loc + 9);
239+
WorkingOps[Loc] = dwarf::DW_OP_constu;
240+
WorkingOps[Loc + 1] = *Result;
241+
startFromBeginning(Loc, Cursor, WorkingOps);
242+
return true;
243+
}
244+
return false;
245+
}

0 commit comments

Comments
 (0)