1 //===- DIExpressionOptimizer.cpp - Constant folding of DIExpressions ------===//
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
7 //===----------------------------------------------------------------------===//
9 // This file implements functions to constant fold DIExpressions. Which were
10 // declared in DIExpressionOptimizer.h
12 //===----------------------------------------------------------------------===//
14 #include "llvm/BinaryFormat/Dwarf.h"
15 #include "llvm/IR/DebugInfoMetadata.h"
19 /// Returns true if the Op is a DW_OP_constu.
20 static std::optional
<uint64_t> isConstantVal(DIExpression::ExprOperand Op
) {
21 if (Op
.getOp() == dwarf::DW_OP_constu
)
26 /// Returns true if an operation and operand result in a No Op.
27 static bool isNeutralElement(uint64_t Op
, uint64_t Val
) {
29 case dwarf::DW_OP_plus
:
30 case dwarf::DW_OP_minus
:
31 case dwarf::DW_OP_shl
:
32 case dwarf::DW_OP_shr
:
34 case dwarf::DW_OP_mul
:
35 case dwarf::DW_OP_div
:
42 /// Try to fold \p Const1 and \p Const2 by applying \p Operator and returning
43 /// the result, if there is an overflow, return a std::nullopt.
44 static std::optional
<uint64_t>
45 foldOperationIfPossible(uint64_t Const1
, uint64_t Const2
,
46 dwarf::LocationAtom Operator
) {
48 bool ResultOverflowed
;
50 case dwarf::DW_OP_plus
: {
51 auto Result
= SaturatingAdd(Const1
, Const2
, &ResultOverflowed
);
56 case dwarf::DW_OP_minus
: {
59 return Const1
- Const2
;
61 case dwarf::DW_OP_shl
: {
62 if ((uint64_t)countl_zero(Const1
) < Const2
)
64 return Const1
<< Const2
;
66 case dwarf::DW_OP_shr
: {
67 if ((uint64_t)countr_zero(Const1
) < Const2
)
69 return Const1
>> Const2
;
71 case dwarf::DW_OP_mul
: {
72 auto Result
= SaturatingMultiply(Const1
, Const2
, &ResultOverflowed
);
77 case dwarf::DW_OP_div
: {
79 return Const1
/ Const2
;
87 /// Returns true if the two operations \p Operator1 and \p Operator2 are
88 /// commutative and can be folded.
89 static bool operationsAreFoldableAndCommutative(dwarf::LocationAtom Operator1
,
90 dwarf::LocationAtom Operator2
) {
91 return Operator1
== Operator2
&&
92 (Operator1
== dwarf::DW_OP_plus
|| Operator1
== dwarf::DW_OP_mul
);
95 /// Consume one operator and its operand(s).
96 static void consumeOneOperator(DIExpressionCursor
&Cursor
, uint64_t &Loc
,
97 const DIExpression::ExprOperand
&Op
) {
99 Loc
= Loc
+ Op
.getSize();
102 /// Reset the Cursor to the beginning of the WorkingOps.
103 void startFromBeginning(uint64_t &Loc
, DIExpressionCursor
&Cursor
,
104 ArrayRef
<uint64_t> WorkingOps
) {
105 Cursor
.assignNewExpr(WorkingOps
);
109 /// This function will canonicalize:
110 /// 1. DW_OP_plus_uconst to DW_OP_constu <const-val> DW_OP_plus
111 /// 2. DW_OP_lit<n> to DW_OP_constu <n>
112 static SmallVector
<uint64_t>
113 canonicalizeDwarfOperations(ArrayRef
<uint64_t> WorkingOps
) {
114 DIExpressionCursor
Cursor(WorkingOps
);
116 SmallVector
<uint64_t> ResultOps
;
117 while (Loc
< WorkingOps
.size()) {
118 auto Op
= Cursor
.peek();
119 /// Expression has no operations, break.
122 auto OpRaw
= Op
->getOp();
124 if (OpRaw
>= dwarf::DW_OP_lit0
&& OpRaw
<= dwarf::DW_OP_lit31
) {
125 ResultOps
.push_back(dwarf::DW_OP_constu
);
126 ResultOps
.push_back(OpRaw
- dwarf::DW_OP_lit0
);
127 consumeOneOperator(Cursor
, Loc
, *Cursor
.peek());
130 if (OpRaw
== dwarf::DW_OP_plus_uconst
) {
131 ResultOps
.push_back(dwarf::DW_OP_constu
);
132 ResultOps
.push_back(Op
->getArg(0));
133 ResultOps
.push_back(dwarf::DW_OP_plus
);
134 consumeOneOperator(Cursor
, Loc
, *Cursor
.peek());
137 uint64_t PrevLoc
= Loc
;
138 consumeOneOperator(Cursor
, Loc
, *Cursor
.peek());
139 ResultOps
.append(WorkingOps
.begin() + PrevLoc
, WorkingOps
.begin() + Loc
);
144 /// This function will convert:
145 /// 1. DW_OP_constu <const-val> DW_OP_plus to DW_OP_plus_uconst
146 /// 2. DW_OP_constu, 0 to DW_OP_lit0
147 static SmallVector
<uint64_t>
148 optimizeDwarfOperations(ArrayRef
<uint64_t> WorkingOps
) {
149 DIExpressionCursor
Cursor(WorkingOps
);
151 SmallVector
<uint64_t> ResultOps
;
152 while (Loc
< WorkingOps
.size()) {
153 auto Op1
= Cursor
.peek();
154 /// Expression has no operations, exit.
157 auto Op1Raw
= Op1
->getOp();
159 if (Op1Raw
== dwarf::DW_OP_constu
&& Op1
->getArg(0) == 0) {
160 ResultOps
.push_back(dwarf::DW_OP_lit0
);
161 consumeOneOperator(Cursor
, Loc
, *Cursor
.peek());
165 auto Op2
= Cursor
.peekNext();
166 /// Expression has no more operations, copy into ResultOps and exit.
168 uint64_t PrevLoc
= Loc
;
169 consumeOneOperator(Cursor
, Loc
, *Cursor
.peek());
170 ResultOps
.append(WorkingOps
.begin() + PrevLoc
, WorkingOps
.begin() + Loc
);
173 auto Op2Raw
= Op2
->getOp();
175 if (Op1Raw
== dwarf::DW_OP_constu
&& Op2Raw
== dwarf::DW_OP_plus
) {
176 ResultOps
.push_back(dwarf::DW_OP_plus_uconst
);
177 ResultOps
.push_back(Op1
->getArg(0));
178 consumeOneOperator(Cursor
, Loc
, *Cursor
.peek());
179 consumeOneOperator(Cursor
, Loc
, *Cursor
.peek());
182 uint64_t PrevLoc
= Loc
;
183 consumeOneOperator(Cursor
, Loc
, *Cursor
.peek());
184 ResultOps
.append(WorkingOps
.begin() + PrevLoc
, WorkingOps
.begin() + Loc
);
189 /// {DW_OP_constu, 0, DW_OP_[plus, minus, shl, shr]} -> {}
190 /// {DW_OP_constu, 1, DW_OP_[mul, div]} -> {}
191 static bool tryFoldNoOpMath(uint64_t Const1
,
192 ArrayRef
<DIExpression::ExprOperand
> Ops
,
193 uint64_t &Loc
, DIExpressionCursor
&Cursor
,
194 SmallVectorImpl
<uint64_t> &WorkingOps
) {
196 if (isNeutralElement(Ops
[1].getOp(), Const1
)) {
197 WorkingOps
.erase(WorkingOps
.begin() + Loc
, WorkingOps
.begin() + Loc
+ 3);
198 startFromBeginning(Loc
, Cursor
, WorkingOps
);
204 /// {DW_OP_constu, Const1, DW_OP_constu, Const2, DW_OP_[plus,
205 /// minus, mul, div, shl, shr] -> {DW_OP_constu, Const1 [+, -, *, /, <<, >>]
207 static bool tryFoldConstants(uint64_t Const1
,
208 ArrayRef
<DIExpression::ExprOperand
> Ops
,
209 uint64_t &Loc
, DIExpressionCursor
&Cursor
,
210 SmallVectorImpl
<uint64_t> &WorkingOps
) {
212 auto Const2
= isConstantVal(Ops
[1]);
216 auto Result
= foldOperationIfPossible(
217 Const1
, *Const2
, static_cast<dwarf::LocationAtom
>(Ops
[2].getOp()));
219 consumeOneOperator(Cursor
, Loc
, Ops
[0]);
222 WorkingOps
.erase(WorkingOps
.begin() + Loc
+ 2, WorkingOps
.begin() + Loc
+ 5);
223 WorkingOps
[Loc
] = dwarf::DW_OP_constu
;
224 WorkingOps
[Loc
+ 1] = *Result
;
225 startFromBeginning(Loc
, Cursor
, WorkingOps
);
229 /// {DW_OP_constu, Const1, DW_OP_[plus, mul], DW_OP_constu, Const2,
230 /// DW_OP_[plus, mul]} -> {DW_OP_constu, Const1 [+, *] Const2, DW_OP_[plus,
232 static bool tryFoldCommutativeMath(uint64_t Const1
,
233 ArrayRef
<DIExpression::ExprOperand
> Ops
,
234 uint64_t &Loc
, DIExpressionCursor
&Cursor
,
235 SmallVectorImpl
<uint64_t> &WorkingOps
) {
237 auto Const2
= isConstantVal(Ops
[2]);
238 auto Operand1
= static_cast<dwarf::LocationAtom
>(Ops
[1].getOp());
239 auto Operand2
= static_cast<dwarf::LocationAtom
>(Ops
[3].getOp());
241 if (!Const2
|| !operationsAreFoldableAndCommutative(Operand1
, Operand2
))
244 auto Result
= foldOperationIfPossible(Const1
, *Const2
, Operand1
);
246 consumeOneOperator(Cursor
, Loc
, Ops
[0]);
249 WorkingOps
.erase(WorkingOps
.begin() + Loc
+ 3, WorkingOps
.begin() + Loc
+ 6);
250 WorkingOps
[Loc
] = dwarf::DW_OP_constu
;
251 WorkingOps
[Loc
+ 1] = *Result
;
252 startFromBeginning(Loc
, Cursor
, WorkingOps
);
256 /// {DW_OP_constu, Const1, DW_OP_[plus, mul], DW_OP_LLVM_arg, Arg1,
257 /// DW_OP_[plus, mul], DW_OP_constu, Const2, DW_OP_[plus, mul]} ->
258 /// {DW_OP_constu, Const1 [+, *] Const2, DW_OP_[plus, mul], DW_OP_LLVM_arg,
259 /// Arg1, DW_OP_[plus, mul]}
260 static bool tryFoldCommutativeMathWithArgInBetween(
261 uint64_t Const1
, ArrayRef
<DIExpression::ExprOperand
> Ops
, uint64_t &Loc
,
262 DIExpressionCursor
&Cursor
, SmallVectorImpl
<uint64_t> &WorkingOps
) {
264 auto Const2
= isConstantVal(Ops
[4]);
265 auto Operand1
= static_cast<dwarf::LocationAtom
>(Ops
[1].getOp());
266 auto Operand2
= static_cast<dwarf::LocationAtom
>(Ops
[3].getOp());
267 auto Operand3
= static_cast<dwarf::LocationAtom
>(Ops
[5].getOp());
269 if (!Const2
|| Ops
[2].getOp() != dwarf::DW_OP_LLVM_arg
||
270 !operationsAreFoldableAndCommutative(Operand1
, Operand2
) ||
271 !operationsAreFoldableAndCommutative(Operand2
, Operand3
))
274 auto Result
= foldOperationIfPossible(Const1
, *Const2
, Operand1
);
276 consumeOneOperator(Cursor
, Loc
, Ops
[0]);
279 WorkingOps
.erase(WorkingOps
.begin() + Loc
+ 6, WorkingOps
.begin() + Loc
+ 9);
280 WorkingOps
[Loc
] = dwarf::DW_OP_constu
;
281 WorkingOps
[Loc
+ 1] = *Result
;
282 startFromBeginning(Loc
, Cursor
, WorkingOps
);
286 DIExpression
*DIExpression::foldConstantMath() {
288 SmallVector
<uint64_t, 8> WorkingOps(Elements
.begin(), Elements
.end());
290 SmallVector
<uint64_t> ResultOps
= canonicalizeDwarfOperations(WorkingOps
);
291 DIExpressionCursor
Cursor(ResultOps
);
292 SmallVector
<DIExpression::ExprOperand
, 8> Ops
;
294 // Iterate over all Operations in a DIExpression to match the smallest pattern
295 // that can be folded.
296 while (Loc
< ResultOps
.size()) {
299 auto Op
= Cursor
.peek();
300 // Expression has no operations, exit.
304 auto Const1
= isConstantVal(*Op
);
307 // Early exit, all of the following patterns start with a constant value.
308 consumeOneOperator(Cursor
, Loc
, *Op
);
314 Op
= Cursor
.peekNext();
315 // All following patterns require at least 2 Operations, exit.
321 // Try to fold a constant no-op, such as {+ 0}
322 if (tryFoldNoOpMath(*Const1
, Ops
, Loc
, Cursor
, ResultOps
))
325 Op
= Cursor
.peekNextN(2);
326 // Op[1] could still match a pattern, skip iteration.
328 consumeOneOperator(Cursor
, Loc
, Ops
[0]);
334 // Try to fold a pattern of two constants such as {C1 + C2}.
335 if (tryFoldConstants(*Const1
, Ops
, Loc
, Cursor
, ResultOps
))
338 Op
= Cursor
.peekNextN(3);
339 // Op[1] and Op[2] could still match a pattern, skip iteration.
341 consumeOneOperator(Cursor
, Loc
, Ops
[0]);
347 // Try to fold commutative constant math, such as {C1 + C2 +}.
348 if (tryFoldCommutativeMath(*Const1
, Ops
, Loc
, Cursor
, ResultOps
))
351 Op
= Cursor
.peekNextN(4);
353 consumeOneOperator(Cursor
, Loc
, Ops
[0]);
358 Op
= Cursor
.peekNextN(5);
360 consumeOneOperator(Cursor
, Loc
, Ops
[0]);
366 // Try to fold commutative constant math with an LLVM_Arg in between, such
367 // as {C1 + Arg + C2 +}.
368 if (tryFoldCommutativeMathWithArgInBetween(*Const1
, Ops
, Loc
, Cursor
,
372 consumeOneOperator(Cursor
, Loc
, Ops
[0]);
374 ResultOps
= optimizeDwarfOperations(ResultOps
);
375 auto *Result
= DIExpression::get(getContext(), ResultOps
);
376 assert(Result
->isValid() && "concatenated expression is not valid");