Skip to content

Commit 703aaa0

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 703aaa0

File tree

6 files changed

+908
-1
lines changed

6 files changed

+908
-1
lines changed
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
//===- 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+
// This file declares functions that are used to constant fold DIExpressions.
11+
// The functions attempt to simplify complex DIExpressions by folding any math
12+
// that is constant at compile time by way of simple pattern matching.
13+
//
14+
//===----------------------------------------------------------------------===//
15+
16+
#ifndef LLVM_IR_DIEXPRESSIONOPTIMIZER_H
17+
#define LLVM_IR_DIEXPRESSIONOPTIMIZER_H
18+
19+
#include "llvm/IR/DebugInfoMetadata.h"
20+
21+
namespace llvm {
22+
23+
/// Returns true if the Op is a DW_OP_constu.
24+
bool isConstantVal(uint64_t Op);
25+
26+
/// Returns true if an operation and operand result in a No Op.
27+
bool isNeutralElement(uint64_t Op, uint64_t Val);
28+
29+
/// Try to fold constant math operations and return the result if possible.
30+
std::optional<uint64_t> foldOperationIfPossible(uint64_t Op, uint64_t Operand1,
31+
uint64_t Operand2);
32+
33+
/// Returns true if the two operations are commutative and can be folded.
34+
bool operationsAreFoldableAndCommutative(uint64_t Op1, uint64_t Op2);
35+
36+
/// Consume one operator and its operand(s).
37+
void consumeOneOperator(DIExpressionCursor &Cursor, uint64_t &Loc,
38+
const DIExpression::ExprOperand &Op);
39+
40+
/// Reset the Cursor to the beginning of the WorkingOps.
41+
void startFromBeginning(uint64_t &Loc, DIExpressionCursor &Cursor,
42+
ArrayRef<uint64_t> WorkingOps);
43+
44+
/// This function will canonicalize:
45+
/// 1. DW_OP_plus_uconst to DW_OP_constu <const-val> DW_OP_plus
46+
/// 2. DW_OP_lit<n> to DW_OP_constu <n>
47+
SmallVector<uint64_t>
48+
canonicalizeDwarfOperations(ArrayRef<uint64_t> WorkingOps);
49+
50+
/// This function will convert:
51+
/// 1. DW_OP_constu <const-val> DW_OP_plus to DW_OP_plus_uconst
52+
/// 2. DW_OP_constu, 0 to DW_OP_lit0
53+
SmallVector<uint64_t> optimizeDwarfOperations(ArrayRef<uint64_t> WorkingOps);
54+
55+
/// {DW_OP_constu, 0, DW_OP_[plus, minus, shl, shr]} -> {}
56+
/// {DW_OP_constu, 1, DW_OP_[mul, div]} -> {}
57+
bool tryFoldNoOpMath(ArrayRef<DIExpression::ExprOperand> Ops, uint64_t &Loc,
58+
DIExpressionCursor &Cursor,
59+
SmallVectorImpl<uint64_t> &WorkingOps);
60+
61+
/// {DW_OP_constu, Const1, DW_OP_constu, Const2, DW_OP_[plus,
62+
/// minus, mul, div, shl, shr] -> {DW_OP_constu, Const1 [+, -, *, /, <<, >>]
63+
/// Const2}
64+
bool tryFoldConstants(ArrayRef<DIExpression::ExprOperand> Ops, uint64_t &Loc,
65+
DIExpressionCursor &Cursor,
66+
SmallVectorImpl<uint64_t> &WorkingOps);
67+
68+
/// {DW_OP_constu, Const1, DW_OP_[plus, mul], DW_OP_constu, Const2,
69+
/// DW_OP_[plus, mul]} -> {DW_OP_constu, Const1 [+, *] Const2, DW_OP_[plus,
70+
/// mul]}
71+
bool tryFoldCommutativeMath(ArrayRef<DIExpression::ExprOperand> Ops,
72+
uint64_t &Loc, DIExpressionCursor &Cursor,
73+
SmallVectorImpl<uint64_t> &WorkingOps);
74+
75+
/// {DW_OP_constu, Const1, DW_OP_[plus, mul], DW_OP_LLVM_arg, Arg1,
76+
/// DW_OP_[plus, mul], DW_OP_constu, Const2, DW_OP_[plus, mul]} ->
77+
/// {DW_OP_constu, Const1 [+, *] Const2, DW_OP_[plus, mul], DW_OP_LLVM_arg,
78+
/// Arg1, DW_OP_[plus, mul]}
79+
bool tryFoldCommutativeMathWithArgInBetween(
80+
ArrayRef<DIExpression::ExprOperand> Ops, uint64_t &Loc,
81+
DIExpressionCursor &Cursor, SmallVectorImpl<uint64_t> &WorkingOps);
82+
83+
} // end namespace llvm
84+
85+
#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: 277 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,277 @@
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. Which were
10+
// declared in DIExpressionOptimizer.h
11+
//
12+
//===----------------------------------------------------------------------===//
13+
14+
#include "llvm/IR/DIExpressionOptimizer.h"
15+
#include "llvm/BinaryFormat/Dwarf.h"
16+
17+
namespace llvm {
18+
19+
bool isConstantVal(uint64_t Op) { return Op == dwarf::DW_OP_constu; }
20+
21+
bool isNeutralElement(uint64_t Op, uint64_t Val) {
22+
switch (Op) {
23+
case dwarf::DW_OP_plus:
24+
case dwarf::DW_OP_minus:
25+
case dwarf::DW_OP_shl:
26+
case dwarf::DW_OP_shr:
27+
return Val == 0;
28+
case dwarf::DW_OP_mul:
29+
case dwarf::DW_OP_div:
30+
return Val == 1;
31+
default:
32+
return false;
33+
}
34+
}
35+
36+
std::optional<uint64_t> foldOperationIfPossible(uint64_t Op, uint64_t Operand1,
37+
uint64_t Operand2) {
38+
bool ResultOverflowed;
39+
switch (Op) {
40+
case dwarf::DW_OP_plus: {
41+
auto Result = SaturatingAdd(Operand1, Operand2, &ResultOverflowed);
42+
if (ResultOverflowed)
43+
return std::nullopt;
44+
return Result;
45+
}
46+
case dwarf::DW_OP_minus: {
47+
if (Operand1 < Operand2)
48+
return std::nullopt;
49+
return Operand1 - Operand2;
50+
}
51+
case dwarf::DW_OP_shl: {
52+
if ((uint64_t)countl_zero(Operand1) < Operand2)
53+
return std::nullopt;
54+
return Operand1 << Operand2;
55+
}
56+
case dwarf::DW_OP_shr: {
57+
if ((uint64_t)countr_zero(Operand1) < Operand2)
58+
return std::nullopt;
59+
return Operand1 >> Operand2;
60+
}
61+
case dwarf::DW_OP_mul: {
62+
auto Result = SaturatingMultiply(Operand1, Operand2, &ResultOverflowed);
63+
if (ResultOverflowed)
64+
return std::nullopt;
65+
return Result;
66+
}
67+
case dwarf::DW_OP_div: {
68+
if (Operand2)
69+
return Operand1 / Operand2;
70+
return std::nullopt;
71+
}
72+
default:
73+
return std::nullopt;
74+
}
75+
}
76+
77+
bool operationsAreFoldableAndCommutative(uint64_t Op1, uint64_t Op2) {
78+
return Op1 == Op2 && (Op1 == dwarf::DW_OP_plus || Op1 == dwarf::DW_OP_mul);
79+
}
80+
81+
void consumeOneOperator(DIExpressionCursor &Cursor, uint64_t &Loc,
82+
const DIExpression::ExprOperand &Op) {
83+
Cursor.consume(1);
84+
Loc = Loc + Op.getSize();
85+
}
86+
87+
void startFromBeginning(uint64_t &Loc, DIExpressionCursor &Cursor,
88+
ArrayRef<uint64_t> WorkingOps) {
89+
Cursor.assignNewExpr(WorkingOps);
90+
Loc = 0;
91+
}
92+
93+
SmallVector<uint64_t>
94+
canonicalizeDwarfOperations(ArrayRef<uint64_t> WorkingOps) {
95+
DIExpressionCursor Cursor(WorkingOps);
96+
uint64_t Loc = 0;
97+
SmallVector<uint64_t> ResultOps;
98+
while (Loc < WorkingOps.size()) {
99+
auto Op = Cursor.peek();
100+
/// Expression has no operations, break.
101+
if (!Op)
102+
break;
103+
auto OpRaw = Op->getOp();
104+
auto OpArg = Op->getArg(0);
105+
106+
if (OpRaw >= dwarf::DW_OP_lit0 && OpRaw <= dwarf::DW_OP_lit31) {
107+
ResultOps.push_back(dwarf::DW_OP_constu);
108+
ResultOps.push_back(OpRaw - dwarf::DW_OP_lit0);
109+
consumeOneOperator(Cursor, Loc, *Cursor.peek());
110+
continue;
111+
}
112+
if (OpRaw == dwarf::DW_OP_plus_uconst) {
113+
ResultOps.push_back(dwarf::DW_OP_constu);
114+
ResultOps.push_back(OpArg);
115+
ResultOps.push_back(dwarf::DW_OP_plus);
116+
consumeOneOperator(Cursor, Loc, *Cursor.peek());
117+
continue;
118+
}
119+
uint64_t PrevLoc = Loc;
120+
consumeOneOperator(Cursor, Loc, *Cursor.peek());
121+
ResultOps.append(WorkingOps.begin() + PrevLoc, WorkingOps.begin() + Loc);
122+
}
123+
return ResultOps;
124+
}
125+
126+
SmallVector<uint64_t> optimizeDwarfOperations(ArrayRef<uint64_t> WorkingOps) {
127+
DIExpressionCursor Cursor(WorkingOps);
128+
uint64_t Loc = 0;
129+
SmallVector<uint64_t> ResultOps;
130+
while (Loc < WorkingOps.size()) {
131+
auto Op1 = Cursor.peek();
132+
/// Expression has no operations, exit.
133+
if (!Op1)
134+
break;
135+
auto Op1Raw = Op1->getOp();
136+
auto Op1Arg = Op1->getArg(0);
137+
138+
if (Op1Raw == dwarf::DW_OP_constu && Op1Arg == 0) {
139+
ResultOps.push_back(dwarf::DW_OP_lit0);
140+
consumeOneOperator(Cursor, Loc, *Cursor.peek());
141+
continue;
142+
}
143+
144+
auto Op2 = Cursor.peekNext();
145+
/// Expression has no more operations, copy into ResultOps and exit.
146+
if (!Op2) {
147+
uint64_t PrevLoc = Loc;
148+
consumeOneOperator(Cursor, Loc, *Cursor.peek());
149+
ResultOps.append(WorkingOps.begin() + PrevLoc, WorkingOps.begin() + Loc);
150+
break;
151+
}
152+
auto Op2Raw = Op2->getOp();
153+
154+
if (Op1Raw == dwarf::DW_OP_constu && Op2Raw == dwarf::DW_OP_plus) {
155+
ResultOps.push_back(dwarf::DW_OP_plus_uconst);
156+
ResultOps.push_back(Op1Arg);
157+
consumeOneOperator(Cursor, Loc, *Cursor.peek());
158+
consumeOneOperator(Cursor, Loc, *Cursor.peek());
159+
continue;
160+
}
161+
uint64_t PrevLoc = Loc;
162+
consumeOneOperator(Cursor, Loc, *Cursor.peek());
163+
ResultOps.append(WorkingOps.begin() + PrevLoc, WorkingOps.begin() + Loc);
164+
}
165+
return ResultOps;
166+
}
167+
168+
bool tryFoldNoOpMath(ArrayRef<DIExpression::ExprOperand> Ops, uint64_t &Loc,
169+
DIExpressionCursor &Cursor,
170+
SmallVectorImpl<uint64_t> &WorkingOps) {
171+
auto Op1 = Ops[0];
172+
auto Op2 = Ops[1];
173+
auto Op1Raw = Op1.getOp();
174+
auto Op1Arg = Op1.getArg(0);
175+
auto Op2Raw = Op2.getOp();
176+
177+
if (isConstantVal(Op1Raw) && isNeutralElement(Op2Raw, Op1Arg)) {
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+
auto Op1 = Ops[0];
189+
auto Op2 = Ops[1];
190+
auto Op3 = Ops[2];
191+
auto Op1Raw = Op1.getOp();
192+
auto Op1Arg = Op1.getArg(0);
193+
auto Op2Raw = Op2.getOp();
194+
auto Op2Arg = Op2.getArg(0);
195+
auto Op3Raw = Op3.getOp();
196+
197+
if (isConstantVal(Op1Raw) && isConstantVal(Op2Raw)) {
198+
auto Result = foldOperationIfPossible(Op3Raw, Op1Arg, Op2Arg);
199+
if (!Result) {
200+
consumeOneOperator(Cursor, Loc, Op1);
201+
return true;
202+
}
203+
WorkingOps.erase(WorkingOps.begin() + Loc + 2,
204+
WorkingOps.begin() + Loc + 5);
205+
WorkingOps[Loc] = dwarf::DW_OP_constu;
206+
WorkingOps[Loc + 1] = *Result;
207+
startFromBeginning(Loc, Cursor, WorkingOps);
208+
return true;
209+
}
210+
return false;
211+
}
212+
213+
bool tryFoldCommutativeMath(ArrayRef<DIExpression::ExprOperand> Ops,
214+
uint64_t &Loc, DIExpressionCursor &Cursor,
215+
SmallVectorImpl<uint64_t> &WorkingOps) {
216+
217+
auto Op1 = Ops[0];
218+
auto Op2 = Ops[1];
219+
auto Op3 = Ops[2];
220+
auto Op4 = Ops[3];
221+
auto Op1Raw = Op1.getOp();
222+
auto Op1Arg = Op1.getArg(0);
223+
auto Op2Raw = Op2.getOp();
224+
auto Op3Raw = Op3.getOp();
225+
auto Op3Arg = Op3.getArg(0);
226+
auto Op4Raw = Op4.getOp();
227+
228+
if (isConstantVal(Op1Raw) && isConstantVal(Op3Raw) &&
229+
operationsAreFoldableAndCommutative(Op2Raw, Op4Raw)) {
230+
auto Result = foldOperationIfPossible(Op2Raw, Op1Arg, Op3Arg);
231+
if (!Result)
232+
return false;
233+
WorkingOps.erase(WorkingOps.begin() + Loc + 3,
234+
WorkingOps.begin() + Loc + 6);
235+
WorkingOps[Loc] = dwarf::DW_OP_constu;
236+
WorkingOps[Loc + 1] = *Result;
237+
startFromBeginning(Loc, Cursor, WorkingOps);
238+
return true;
239+
}
240+
return false;
241+
}
242+
243+
bool tryFoldCommutativeMathWithArgInBetween(
244+
ArrayRef<DIExpression::ExprOperand> Ops, uint64_t &Loc,
245+
DIExpressionCursor &Cursor, SmallVectorImpl<uint64_t> &WorkingOps) {
246+
auto Op1 = Ops[0];
247+
auto Op2 = Ops[1];
248+
auto Op3 = Ops[2];
249+
auto Op4 = Ops[3];
250+
auto Op5 = Ops[4];
251+
auto Op6 = Ops[5];
252+
auto Op1Raw = Op1.getOp();
253+
auto Op1Arg = Op1.getArg(0);
254+
auto Op2Raw = Op2.getOp();
255+
auto Op3Raw = Op3.getOp();
256+
auto Op4Raw = Op4.getOp();
257+
auto Op5Raw = Op5.getOp();
258+
auto Op5Arg = Op5.getArg(0);
259+
auto Op6Raw = Op6.getOp();
260+
261+
if (isConstantVal(Op1Raw) && Op3Raw == dwarf::DW_OP_LLVM_arg &&
262+
isConstantVal(Op5Raw) &&
263+
operationsAreFoldableAndCommutative(Op2Raw, Op4Raw) &&
264+
operationsAreFoldableAndCommutative(Op4Raw, Op6Raw)) {
265+
auto Result = foldOperationIfPossible(Op2Raw, Op1Arg, Op5Arg);
266+
if (!Result)
267+
return false;
268+
WorkingOps.erase(WorkingOps.begin() + Loc + 6,
269+
WorkingOps.begin() + Loc + 9);
270+
WorkingOps[Loc] = dwarf::DW_OP_constu;
271+
WorkingOps[Loc + 1] = *Result;
272+
startFromBeginning(Loc, Cursor, WorkingOps);
273+
return true;
274+
}
275+
return false;
276+
}
277+
} // end namespace llvm

0 commit comments

Comments
 (0)