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/BinaryFormat/Dwarf.h"
15 #include "llvm/IR/DebugInfoMetadata.h"
16
17 using namespace llvm;
18
19 /// Returns true if the Op is a DW_OP_constu.
isConstantVal(DIExpression::ExprOperand Op)20 static std::optional<uint64_t> isConstantVal(DIExpression::ExprOperand Op) {
21 if (Op.getOp() == dwarf::DW_OP_constu)
22 return Op.getArg(0);
23 return std::nullopt;
24 }
25
26 /// Returns true if an operation and operand result in a No Op.
isNeutralElement(uint64_t Op,uint64_t Val)27 static bool isNeutralElement(uint64_t Op, uint64_t Val) {
28 switch (Op) {
29 case dwarf::DW_OP_plus:
30 case dwarf::DW_OP_minus:
31 case dwarf::DW_OP_shl:
32 case dwarf::DW_OP_shr:
33 return Val == 0;
34 case dwarf::DW_OP_mul:
35 case dwarf::DW_OP_div:
36 return Val == 1;
37 default:
38 return false;
39 }
40 }
41
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>
foldOperationIfPossible(uint64_t Const1,uint64_t Const2,dwarf::LocationAtom Operator)45 foldOperationIfPossible(uint64_t Const1, uint64_t Const2,
46 dwarf::LocationAtom Operator) {
47
48 bool ResultOverflowed;
49 switch (Operator) {
50 case dwarf::DW_OP_plus: {
51 auto Result = SaturatingAdd(Const1, Const2, &ResultOverflowed);
52 if (ResultOverflowed)
53 return std::nullopt;
54 return Result;
55 }
56 case dwarf::DW_OP_minus: {
57 if (Const1 < Const2)
58 return std::nullopt;
59 return Const1 - Const2;
60 }
61 case dwarf::DW_OP_shl: {
62 if ((uint64_t)countl_zero(Const1) < Const2)
63 return std::nullopt;
64 return Const1 << Const2;
65 }
66 case dwarf::DW_OP_shr: {
67 if ((uint64_t)countr_zero(Const1) < Const2)
68 return std::nullopt;
69 return Const1 >> Const2;
70 }
71 case dwarf::DW_OP_mul: {
72 auto Result = SaturatingMultiply(Const1, Const2, &ResultOverflowed);
73 if (ResultOverflowed)
74 return std::nullopt;
75 return Result;
76 }
77 case dwarf::DW_OP_div: {
78 if (Const2)
79 return Const1 / Const2;
80 return std::nullopt;
81 }
82 default:
83 return std::nullopt;
84 }
85 }
86
87 /// Returns true if the two operations \p Operator1 and \p Operator2 are
88 /// commutative and can be folded.
operationsAreFoldableAndCommutative(dwarf::LocationAtom Operator1,dwarf::LocationAtom Operator2)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);
93 }
94
95 /// Consume one operator and its operand(s).
consumeOneOperator(DIExpressionCursor & Cursor,uint64_t & Loc,const DIExpression::ExprOperand & Op)96 static void consumeOneOperator(DIExpressionCursor &Cursor, uint64_t &Loc,
97 const DIExpression::ExprOperand &Op) {
98 Cursor.consume(1);
99 Loc = Loc + Op.getSize();
100 }
101
102 /// Reset the Cursor to the beginning of the WorkingOps.
startFromBeginning(uint64_t & Loc,DIExpressionCursor & Cursor,ArrayRef<uint64_t> WorkingOps)103 void startFromBeginning(uint64_t &Loc, DIExpressionCursor &Cursor,
104 ArrayRef<uint64_t> WorkingOps) {
105 Cursor.assignNewExpr(WorkingOps);
106 Loc = 0;
107 }
108
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>
canonicalizeDwarfOperations(ArrayRef<uint64_t> WorkingOps)113 canonicalizeDwarfOperations(ArrayRef<uint64_t> WorkingOps) {
114 DIExpressionCursor Cursor(WorkingOps);
115 uint64_t Loc = 0;
116 SmallVector<uint64_t> ResultOps;
117 while (Loc < WorkingOps.size()) {
118 auto Op = Cursor.peek();
119 /// Expression has no operations, break.
120 if (!Op)
121 break;
122 auto OpRaw = Op->getOp();
123
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());
128 continue;
129 }
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());
135 continue;
136 }
137 uint64_t PrevLoc = Loc;
138 consumeOneOperator(Cursor, Loc, *Cursor.peek());
139 ResultOps.append(WorkingOps.begin() + PrevLoc, WorkingOps.begin() + Loc);
140 }
141 return ResultOps;
142 }
143
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>
optimizeDwarfOperations(ArrayRef<uint64_t> WorkingOps)148 optimizeDwarfOperations(ArrayRef<uint64_t> WorkingOps) {
149 DIExpressionCursor Cursor(WorkingOps);
150 uint64_t Loc = 0;
151 SmallVector<uint64_t> ResultOps;
152 while (Loc < WorkingOps.size()) {
153 auto Op1 = Cursor.peek();
154 /// Expression has no operations, exit.
155 if (!Op1)
156 break;
157 auto Op1Raw = Op1->getOp();
158
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());
162 continue;
163 }
164
165 auto Op2 = Cursor.peekNext();
166 /// Expression has no more operations, copy into ResultOps and exit.
167 if (!Op2) {
168 uint64_t PrevLoc = Loc;
169 consumeOneOperator(Cursor, Loc, *Cursor.peek());
170 ResultOps.append(WorkingOps.begin() + PrevLoc, WorkingOps.begin() + Loc);
171 break;
172 }
173 auto Op2Raw = Op2->getOp();
174
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());
180 continue;
181 }
182 uint64_t PrevLoc = Loc;
183 consumeOneOperator(Cursor, Loc, *Cursor.peek());
184 ResultOps.append(WorkingOps.begin() + PrevLoc, WorkingOps.begin() + Loc);
185 }
186 return ResultOps;
187 }
188
189 /// {DW_OP_constu, 0, DW_OP_[plus, minus, shl, shr]} -> {}
190 /// {DW_OP_constu, 1, DW_OP_[mul, div]} -> {}
tryFoldNoOpMath(uint64_t Const1,ArrayRef<DIExpression::ExprOperand> Ops,uint64_t & Loc,DIExpressionCursor & Cursor,SmallVectorImpl<uint64_t> & WorkingOps)191 static bool tryFoldNoOpMath(uint64_t Const1,
192 ArrayRef<DIExpression::ExprOperand> Ops,
193 uint64_t &Loc, DIExpressionCursor &Cursor,
194 SmallVectorImpl<uint64_t> &WorkingOps) {
195
196 if (isNeutralElement(Ops[1].getOp(), Const1)) {
197 WorkingOps.erase(WorkingOps.begin() + Loc, WorkingOps.begin() + Loc + 3);
198 startFromBeginning(Loc, Cursor, WorkingOps);
199 return true;
200 }
201 return false;
202 }
203
204 /// {DW_OP_constu, Const1, DW_OP_constu, Const2, DW_OP_[plus,
205 /// minus, mul, div, shl, shr] -> {DW_OP_constu, Const1 [+, -, *, /, <<, >>]
206 /// Const2}
tryFoldConstants(uint64_t Const1,ArrayRef<DIExpression::ExprOperand> Ops,uint64_t & Loc,DIExpressionCursor & Cursor,SmallVectorImpl<uint64_t> & WorkingOps)207 static bool tryFoldConstants(uint64_t Const1,
208 ArrayRef<DIExpression::ExprOperand> Ops,
209 uint64_t &Loc, DIExpressionCursor &Cursor,
210 SmallVectorImpl<uint64_t> &WorkingOps) {
211
212 auto Const2 = isConstantVal(Ops[1]);
213 if (!Const2)
214 return false;
215
216 auto Result = foldOperationIfPossible(
217 Const1, *Const2, static_cast<dwarf::LocationAtom>(Ops[2].getOp()));
218 if (!Result) {
219 consumeOneOperator(Cursor, Loc, Ops[0]);
220 return true;
221 }
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);
226 return true;
227 }
228
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,
231 /// mul]}
tryFoldCommutativeMath(uint64_t Const1,ArrayRef<DIExpression::ExprOperand> Ops,uint64_t & Loc,DIExpressionCursor & Cursor,SmallVectorImpl<uint64_t> & WorkingOps)232 static bool tryFoldCommutativeMath(uint64_t Const1,
233 ArrayRef<DIExpression::ExprOperand> Ops,
234 uint64_t &Loc, DIExpressionCursor &Cursor,
235 SmallVectorImpl<uint64_t> &WorkingOps) {
236
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());
240
241 if (!Const2 || !operationsAreFoldableAndCommutative(Operand1, Operand2))
242 return false;
243
244 auto Result = foldOperationIfPossible(Const1, *Const2, Operand1);
245 if (!Result) {
246 consumeOneOperator(Cursor, Loc, Ops[0]);
247 return true;
248 }
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);
253 return true;
254 }
255
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]}
tryFoldCommutativeMathWithArgInBetween(uint64_t Const1,ArrayRef<DIExpression::ExprOperand> Ops,uint64_t & Loc,DIExpressionCursor & Cursor,SmallVectorImpl<uint64_t> & WorkingOps)260 static bool tryFoldCommutativeMathWithArgInBetween(
261 uint64_t Const1, ArrayRef<DIExpression::ExprOperand> Ops, uint64_t &Loc,
262 DIExpressionCursor &Cursor, SmallVectorImpl<uint64_t> &WorkingOps) {
263
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());
268
269 if (!Const2 || Ops[2].getOp() != dwarf::DW_OP_LLVM_arg ||
270 !operationsAreFoldableAndCommutative(Operand1, Operand2) ||
271 !operationsAreFoldableAndCommutative(Operand2, Operand3))
272 return false;
273
274 auto Result = foldOperationIfPossible(Const1, *Const2, Operand1);
275 if (!Result) {
276 consumeOneOperator(Cursor, Loc, Ops[0]);
277 return true;
278 }
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);
283 return true;
284 }
285
foldConstantMath()286 DIExpression *DIExpression::foldConstantMath() {
287
288 SmallVector<uint64_t, 8> WorkingOps(Elements.begin(), Elements.end());
289 uint64_t Loc = 0;
290 SmallVector<uint64_t> ResultOps = canonicalizeDwarfOperations(WorkingOps);
291 DIExpressionCursor Cursor(ResultOps);
292 SmallVector<DIExpression::ExprOperand, 8> Ops;
293
294 // Iterate over all Operations in a DIExpression to match the smallest pattern
295 // that can be folded.
296 while (Loc < ResultOps.size()) {
297 Ops.clear();
298
299 auto Op = Cursor.peek();
300 // Expression has no operations, exit.
301 if (!Op)
302 break;
303
304 auto Const1 = isConstantVal(*Op);
305
306 if (!Const1) {
307 // Early exit, all of the following patterns start with a constant value.
308 consumeOneOperator(Cursor, Loc, *Op);
309 continue;
310 }
311
312 Ops.push_back(*Op);
313
314 Op = Cursor.peekNext();
315 // All following patterns require at least 2 Operations, exit.
316 if (!Op)
317 break;
318
319 Ops.push_back(*Op);
320
321 // Try to fold a constant no-op, such as {+ 0}
322 if (tryFoldNoOpMath(*Const1, Ops, Loc, Cursor, ResultOps))
323 continue;
324
325 Op = Cursor.peekNextN(2);
326 // Op[1] could still match a pattern, skip iteration.
327 if (!Op) {
328 consumeOneOperator(Cursor, Loc, Ops[0]);
329 continue;
330 }
331
332 Ops.push_back(*Op);
333
334 // Try to fold a pattern of two constants such as {C1 + C2}.
335 if (tryFoldConstants(*Const1, Ops, Loc, Cursor, ResultOps))
336 continue;
337
338 Op = Cursor.peekNextN(3);
339 // Op[1] and Op[2] could still match a pattern, skip iteration.
340 if (!Op) {
341 consumeOneOperator(Cursor, Loc, Ops[0]);
342 continue;
343 }
344
345 Ops.push_back(*Op);
346
347 // Try to fold commutative constant math, such as {C1 + C2 +}.
348 if (tryFoldCommutativeMath(*Const1, Ops, Loc, Cursor, ResultOps))
349 continue;
350
351 Op = Cursor.peekNextN(4);
352 if (!Op) {
353 consumeOneOperator(Cursor, Loc, Ops[0]);
354 continue;
355 }
356
357 Ops.push_back(*Op);
358 Op = Cursor.peekNextN(5);
359 if (!Op) {
360 consumeOneOperator(Cursor, Loc, Ops[0]);
361 continue;
362 }
363
364 Ops.push_back(*Op);
365
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,
369 ResultOps))
370 continue;
371
372 consumeOneOperator(Cursor, Loc, Ops[0]);
373 }
374 ResultOps = optimizeDwarfOperations(ResultOps);
375 auto *Result = DIExpression::get(getContext(), ResultOps);
376 assert(Result->isValid() && "concatenated expression is not valid");
377 return Result;
378 }
379