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 (Const2
>= std::numeric_limits
<uint64_t>::digits
||
63 static_cast<uint64_t>(countl_zero(Const1
)) < Const2
)
65 return Const1
<< Const2
;
67 case dwarf::DW_OP_shr
: {
68 if (Const2
>= std::numeric_limits
<uint64_t>::digits
||
69 static_cast<uint64_t>(countr_zero(Const1
)) < Const2
)
71 return Const1
>> Const2
;
73 case dwarf::DW_OP_mul
: {
74 auto Result
= SaturatingMultiply(Const1
, Const2
, &ResultOverflowed
);
79 case dwarf::DW_OP_div
: {
81 return Const1
/ Const2
;
89 /// Returns true if the two operations \p Operator1 and \p Operator2 are
90 /// commutative and can be folded.
91 static bool operationsAreFoldableAndCommutative(dwarf::LocationAtom Operator1
,
92 dwarf::LocationAtom Operator2
) {
93 return Operator1
== Operator2
&&
94 (Operator1
== dwarf::DW_OP_plus
|| Operator1
== dwarf::DW_OP_mul
);
97 /// Consume one operator and its operand(s).
98 static void consumeOneOperator(DIExpressionCursor
&Cursor
, uint64_t &Loc
,
99 const DIExpression::ExprOperand
&Op
) {
101 Loc
= Loc
+ Op
.getSize();
104 /// Reset the Cursor to the beginning of the WorkingOps.
105 void startFromBeginning(uint64_t &Loc
, DIExpressionCursor
&Cursor
,
106 ArrayRef
<uint64_t> WorkingOps
) {
107 Cursor
.assignNewExpr(WorkingOps
);
111 /// This function will canonicalize:
112 /// 1. DW_OP_plus_uconst to DW_OP_constu <const-val> DW_OP_plus
113 /// 2. DW_OP_lit<n> to DW_OP_constu <n>
114 static SmallVector
<uint64_t>
115 canonicalizeDwarfOperations(ArrayRef
<uint64_t> WorkingOps
) {
116 DIExpressionCursor
Cursor(WorkingOps
);
118 SmallVector
<uint64_t> ResultOps
;
119 while (Loc
< WorkingOps
.size()) {
120 auto Op
= Cursor
.peek();
121 /// Expression has no operations, break.
124 auto OpRaw
= Op
->getOp();
126 if (OpRaw
>= dwarf::DW_OP_lit0
&& OpRaw
<= dwarf::DW_OP_lit31
) {
127 ResultOps
.push_back(dwarf::DW_OP_constu
);
128 ResultOps
.push_back(OpRaw
- dwarf::DW_OP_lit0
);
129 consumeOneOperator(Cursor
, Loc
, *Cursor
.peek());
132 if (OpRaw
== dwarf::DW_OP_plus_uconst
) {
133 ResultOps
.push_back(dwarf::DW_OP_constu
);
134 ResultOps
.push_back(Op
->getArg(0));
135 ResultOps
.push_back(dwarf::DW_OP_plus
);
136 consumeOneOperator(Cursor
, Loc
, *Cursor
.peek());
139 uint64_t PrevLoc
= Loc
;
140 consumeOneOperator(Cursor
, Loc
, *Cursor
.peek());
141 ResultOps
.append(WorkingOps
.begin() + PrevLoc
, WorkingOps
.begin() + Loc
);
146 /// This function will convert:
147 /// 1. DW_OP_constu <const-val> DW_OP_plus to DW_OP_plus_uconst
148 /// 2. DW_OP_constu, 0 to DW_OP_lit0
149 static SmallVector
<uint64_t>
150 optimizeDwarfOperations(ArrayRef
<uint64_t> WorkingOps
) {
151 DIExpressionCursor
Cursor(WorkingOps
);
153 SmallVector
<uint64_t> ResultOps
;
154 while (Loc
< WorkingOps
.size()) {
155 auto Op1
= Cursor
.peek();
156 /// Expression has no operations, exit.
159 auto Op1Raw
= Op1
->getOp();
161 if (Op1Raw
== dwarf::DW_OP_constu
&& Op1
->getArg(0) == 0) {
162 ResultOps
.push_back(dwarf::DW_OP_lit0
);
163 consumeOneOperator(Cursor
, Loc
, *Cursor
.peek());
167 auto Op2
= Cursor
.peekNext();
168 /// Expression has no more operations, copy into ResultOps and exit.
170 uint64_t PrevLoc
= Loc
;
171 consumeOneOperator(Cursor
, Loc
, *Cursor
.peek());
172 ResultOps
.append(WorkingOps
.begin() + PrevLoc
, WorkingOps
.begin() + Loc
);
175 auto Op2Raw
= Op2
->getOp();
177 if (Op1Raw
== dwarf::DW_OP_constu
&& Op2Raw
== dwarf::DW_OP_plus
) {
178 ResultOps
.push_back(dwarf::DW_OP_plus_uconst
);
179 ResultOps
.push_back(Op1
->getArg(0));
180 consumeOneOperator(Cursor
, Loc
, *Cursor
.peek());
181 consumeOneOperator(Cursor
, Loc
, *Cursor
.peek());
184 uint64_t PrevLoc
= Loc
;
185 consumeOneOperator(Cursor
, Loc
, *Cursor
.peek());
186 ResultOps
.append(WorkingOps
.begin() + PrevLoc
, WorkingOps
.begin() + Loc
);
191 /// {DW_OP_constu, 0, DW_OP_[plus, minus, shl, shr]} -> {}
192 /// {DW_OP_constu, 1, DW_OP_[mul, div]} -> {}
193 static bool tryFoldNoOpMath(uint64_t Const1
,
194 ArrayRef
<DIExpression::ExprOperand
> Ops
,
195 uint64_t &Loc
, DIExpressionCursor
&Cursor
,
196 SmallVectorImpl
<uint64_t> &WorkingOps
) {
198 if (isNeutralElement(Ops
[1].getOp(), Const1
)) {
199 WorkingOps
.erase(WorkingOps
.begin() + Loc
, WorkingOps
.begin() + Loc
+ 3);
200 startFromBeginning(Loc
, Cursor
, WorkingOps
);
206 /// {DW_OP_constu, Const1, DW_OP_constu, Const2, DW_OP_[plus,
207 /// minus, mul, div, shl, shr] -> {DW_OP_constu, Const1 [+, -, *, /, <<, >>]
209 static bool tryFoldConstants(uint64_t Const1
,
210 ArrayRef
<DIExpression::ExprOperand
> Ops
,
211 uint64_t &Loc
, DIExpressionCursor
&Cursor
,
212 SmallVectorImpl
<uint64_t> &WorkingOps
) {
214 auto Const2
= isConstantVal(Ops
[1]);
218 auto Result
= foldOperationIfPossible(
219 Const1
, *Const2
, static_cast<dwarf::LocationAtom
>(Ops
[2].getOp()));
221 consumeOneOperator(Cursor
, Loc
, Ops
[0]);
224 WorkingOps
.erase(WorkingOps
.begin() + Loc
+ 2, WorkingOps
.begin() + Loc
+ 5);
225 WorkingOps
[Loc
] = dwarf::DW_OP_constu
;
226 WorkingOps
[Loc
+ 1] = *Result
;
227 startFromBeginning(Loc
, Cursor
, WorkingOps
);
231 /// {DW_OP_constu, Const1, DW_OP_[plus, mul], DW_OP_constu, Const2,
232 /// DW_OP_[plus, mul]} -> {DW_OP_constu, Const1 [+, *] Const2, DW_OP_[plus,
234 static bool tryFoldCommutativeMath(uint64_t Const1
,
235 ArrayRef
<DIExpression::ExprOperand
> Ops
,
236 uint64_t &Loc
, DIExpressionCursor
&Cursor
,
237 SmallVectorImpl
<uint64_t> &WorkingOps
) {
239 auto Const2
= isConstantVal(Ops
[2]);
240 auto Operand1
= static_cast<dwarf::LocationAtom
>(Ops
[1].getOp());
241 auto Operand2
= static_cast<dwarf::LocationAtom
>(Ops
[3].getOp());
243 if (!Const2
|| !operationsAreFoldableAndCommutative(Operand1
, Operand2
))
246 auto Result
= foldOperationIfPossible(Const1
, *Const2
, Operand1
);
248 consumeOneOperator(Cursor
, Loc
, Ops
[0]);
251 WorkingOps
.erase(WorkingOps
.begin() + Loc
+ 3, WorkingOps
.begin() + Loc
+ 6);
252 WorkingOps
[Loc
] = dwarf::DW_OP_constu
;
253 WorkingOps
[Loc
+ 1] = *Result
;
254 startFromBeginning(Loc
, Cursor
, WorkingOps
);
258 /// {DW_OP_constu, Const1, DW_OP_[plus, mul], DW_OP_LLVM_arg, Arg1,
259 /// DW_OP_[plus, mul], DW_OP_constu, Const2, DW_OP_[plus, mul]} ->
260 /// {DW_OP_constu, Const1 [+, *] Const2, DW_OP_[plus, mul], DW_OP_LLVM_arg,
261 /// Arg1, DW_OP_[plus, mul]}
262 static bool tryFoldCommutativeMathWithArgInBetween(
263 uint64_t Const1
, ArrayRef
<DIExpression::ExprOperand
> Ops
, uint64_t &Loc
,
264 DIExpressionCursor
&Cursor
, SmallVectorImpl
<uint64_t> &WorkingOps
) {
266 auto Const2
= isConstantVal(Ops
[4]);
267 auto Operand1
= static_cast<dwarf::LocationAtom
>(Ops
[1].getOp());
268 auto Operand2
= static_cast<dwarf::LocationAtom
>(Ops
[3].getOp());
269 auto Operand3
= static_cast<dwarf::LocationAtom
>(Ops
[5].getOp());
271 if (!Const2
|| Ops
[2].getOp() != dwarf::DW_OP_LLVM_arg
||
272 !operationsAreFoldableAndCommutative(Operand1
, Operand2
) ||
273 !operationsAreFoldableAndCommutative(Operand2
, Operand3
))
276 auto Result
= foldOperationIfPossible(Const1
, *Const2
, Operand1
);
278 consumeOneOperator(Cursor
, Loc
, Ops
[0]);
281 WorkingOps
.erase(WorkingOps
.begin() + Loc
+ 6, WorkingOps
.begin() + Loc
+ 9);
282 WorkingOps
[Loc
] = dwarf::DW_OP_constu
;
283 WorkingOps
[Loc
+ 1] = *Result
;
284 startFromBeginning(Loc
, Cursor
, WorkingOps
);
288 DIExpression
*DIExpression::foldConstantMath() {
290 SmallVector
<uint64_t, 8> WorkingOps(Elements
.begin(), Elements
.end());
292 SmallVector
<uint64_t> ResultOps
= canonicalizeDwarfOperations(WorkingOps
);
293 DIExpressionCursor
Cursor(ResultOps
);
294 SmallVector
<DIExpression::ExprOperand
, 8> Ops
;
296 // Iterate over all Operations in a DIExpression to match the smallest pattern
297 // that can be folded.
298 while (Loc
< ResultOps
.size()) {
301 auto Op
= Cursor
.peek();
302 // Expression has no operations, exit.
306 auto Const1
= isConstantVal(*Op
);
309 // Early exit, all of the following patterns start with a constant value.
310 consumeOneOperator(Cursor
, Loc
, *Op
);
316 Op
= Cursor
.peekNext();
317 // All following patterns require at least 2 Operations, exit.
323 // Try to fold a constant no-op, such as {+ 0}
324 if (tryFoldNoOpMath(*Const1
, Ops
, Loc
, Cursor
, ResultOps
))
327 Op
= Cursor
.peekNextN(2);
328 // Op[1] could still match a pattern, skip iteration.
330 consumeOneOperator(Cursor
, Loc
, Ops
[0]);
336 // Try to fold a pattern of two constants such as {C1 + C2}.
337 if (tryFoldConstants(*Const1
, Ops
, Loc
, Cursor
, ResultOps
))
340 Op
= Cursor
.peekNextN(3);
341 // Op[1] and Op[2] could still match a pattern, skip iteration.
343 consumeOneOperator(Cursor
, Loc
, Ops
[0]);
349 // Try to fold commutative constant math, such as {C1 + C2 +}.
350 if (tryFoldCommutativeMath(*Const1
, Ops
, Loc
, Cursor
, ResultOps
))
353 Op
= Cursor
.peekNextN(4);
355 consumeOneOperator(Cursor
, Loc
, Ops
[0]);
360 Op
= Cursor
.peekNextN(5);
362 consumeOneOperator(Cursor
, Loc
, Ops
[0]);
368 // Try to fold commutative constant math with an LLVM_Arg in between, such
369 // as {C1 + Arg + C2 +}.
370 if (tryFoldCommutativeMathWithArgInBetween(*Const1
, Ops
, Loc
, Cursor
,
374 consumeOneOperator(Cursor
, Loc
, Ops
[0]);
376 ResultOps
= optimizeDwarfOperations(ResultOps
);
377 auto *Result
= DIExpression::get(getContext(), ResultOps
);
378 assert(Result
->isValid() && "concatenated expression is not valid");