Path: blob/main/contrib/llvm-project/llvm/lib/IR/DIExpressionOptimizer.cpp
35233 views
//===- DIExpressionOptimizer.cpp - Constant folding of DIExpressions ------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7//8// This file implements functions to constant fold DIExpressions. Which were9// declared in DIExpressionOptimizer.h10//11//===----------------------------------------------------------------------===//1213#include "llvm/BinaryFormat/Dwarf.h"14#include "llvm/IR/DebugInfoMetadata.h"1516using namespace llvm;1718/// Returns true if the Op is a DW_OP_constu.19static std::optional<uint64_t> isConstantVal(DIExpression::ExprOperand Op) {20if (Op.getOp() == dwarf::DW_OP_constu)21return Op.getArg(0);22return std::nullopt;23}2425/// Returns true if an operation and operand result in a No Op.26static bool isNeutralElement(uint64_t Op, uint64_t Val) {27switch (Op) {28case dwarf::DW_OP_plus:29case dwarf::DW_OP_minus:30case dwarf::DW_OP_shl:31case dwarf::DW_OP_shr:32return Val == 0;33case dwarf::DW_OP_mul:34case dwarf::DW_OP_div:35return Val == 1;36default:37return false;38}39}4041/// Try to fold \p Const1 and \p Const2 by applying \p Operator and returning42/// the result, if there is an overflow, return a std::nullopt.43static std::optional<uint64_t>44foldOperationIfPossible(uint64_t Const1, uint64_t Const2,45dwarf::LocationAtom Operator) {4647bool ResultOverflowed;48switch (Operator) {49case dwarf::DW_OP_plus: {50auto Result = SaturatingAdd(Const1, Const2, &ResultOverflowed);51if (ResultOverflowed)52return std::nullopt;53return Result;54}55case dwarf::DW_OP_minus: {56if (Const1 < Const2)57return std::nullopt;58return Const1 - Const2;59}60case dwarf::DW_OP_shl: {61if ((uint64_t)countl_zero(Const1) < Const2)62return std::nullopt;63return Const1 << Const2;64}65case dwarf::DW_OP_shr: {66if ((uint64_t)countr_zero(Const1) < Const2)67return std::nullopt;68return Const1 >> Const2;69}70case dwarf::DW_OP_mul: {71auto Result = SaturatingMultiply(Const1, Const2, &ResultOverflowed);72if (ResultOverflowed)73return std::nullopt;74return Result;75}76case dwarf::DW_OP_div: {77if (Const2)78return Const1 / Const2;79return std::nullopt;80}81default:82return std::nullopt;83}84}8586/// Returns true if the two operations \p Operator1 and \p Operator2 are87/// commutative and can be folded.88static bool operationsAreFoldableAndCommutative(dwarf::LocationAtom Operator1,89dwarf::LocationAtom Operator2) {90return Operator1 == Operator2 &&91(Operator1 == dwarf::DW_OP_plus || Operator1 == dwarf::DW_OP_mul);92}9394/// Consume one operator and its operand(s).95static void consumeOneOperator(DIExpressionCursor &Cursor, uint64_t &Loc,96const DIExpression::ExprOperand &Op) {97Cursor.consume(1);98Loc = Loc + Op.getSize();99}100101/// Reset the Cursor to the beginning of the WorkingOps.102void startFromBeginning(uint64_t &Loc, DIExpressionCursor &Cursor,103ArrayRef<uint64_t> WorkingOps) {104Cursor.assignNewExpr(WorkingOps);105Loc = 0;106}107108/// This function will canonicalize:109/// 1. DW_OP_plus_uconst to DW_OP_constu <const-val> DW_OP_plus110/// 2. DW_OP_lit<n> to DW_OP_constu <n>111static SmallVector<uint64_t>112canonicalizeDwarfOperations(ArrayRef<uint64_t> WorkingOps) {113DIExpressionCursor Cursor(WorkingOps);114uint64_t Loc = 0;115SmallVector<uint64_t> ResultOps;116while (Loc < WorkingOps.size()) {117auto Op = Cursor.peek();118/// Expression has no operations, break.119if (!Op)120break;121auto OpRaw = Op->getOp();122123if (OpRaw >= dwarf::DW_OP_lit0 && OpRaw <= dwarf::DW_OP_lit31) {124ResultOps.push_back(dwarf::DW_OP_constu);125ResultOps.push_back(OpRaw - dwarf::DW_OP_lit0);126consumeOneOperator(Cursor, Loc, *Cursor.peek());127continue;128}129if (OpRaw == dwarf::DW_OP_plus_uconst) {130ResultOps.push_back(dwarf::DW_OP_constu);131ResultOps.push_back(Op->getArg(0));132ResultOps.push_back(dwarf::DW_OP_plus);133consumeOneOperator(Cursor, Loc, *Cursor.peek());134continue;135}136uint64_t PrevLoc = Loc;137consumeOneOperator(Cursor, Loc, *Cursor.peek());138ResultOps.append(WorkingOps.begin() + PrevLoc, WorkingOps.begin() + Loc);139}140return ResultOps;141}142143/// This function will convert:144/// 1. DW_OP_constu <const-val> DW_OP_plus to DW_OP_plus_uconst145/// 2. DW_OP_constu, 0 to DW_OP_lit0146static SmallVector<uint64_t>147optimizeDwarfOperations(ArrayRef<uint64_t> WorkingOps) {148DIExpressionCursor Cursor(WorkingOps);149uint64_t Loc = 0;150SmallVector<uint64_t> ResultOps;151while (Loc < WorkingOps.size()) {152auto Op1 = Cursor.peek();153/// Expression has no operations, exit.154if (!Op1)155break;156auto Op1Raw = Op1->getOp();157158if (Op1Raw == dwarf::DW_OP_constu && Op1->getArg(0) == 0) {159ResultOps.push_back(dwarf::DW_OP_lit0);160consumeOneOperator(Cursor, Loc, *Cursor.peek());161continue;162}163164auto Op2 = Cursor.peekNext();165/// Expression has no more operations, copy into ResultOps and exit.166if (!Op2) {167uint64_t PrevLoc = Loc;168consumeOneOperator(Cursor, Loc, *Cursor.peek());169ResultOps.append(WorkingOps.begin() + PrevLoc, WorkingOps.begin() + Loc);170break;171}172auto Op2Raw = Op2->getOp();173174if (Op1Raw == dwarf::DW_OP_constu && Op2Raw == dwarf::DW_OP_plus) {175ResultOps.push_back(dwarf::DW_OP_plus_uconst);176ResultOps.push_back(Op1->getArg(0));177consumeOneOperator(Cursor, Loc, *Cursor.peek());178consumeOneOperator(Cursor, Loc, *Cursor.peek());179continue;180}181uint64_t PrevLoc = Loc;182consumeOneOperator(Cursor, Loc, *Cursor.peek());183ResultOps.append(WorkingOps.begin() + PrevLoc, WorkingOps.begin() + Loc);184}185return ResultOps;186}187188/// {DW_OP_constu, 0, DW_OP_[plus, minus, shl, shr]} -> {}189/// {DW_OP_constu, 1, DW_OP_[mul, div]} -> {}190static bool tryFoldNoOpMath(uint64_t Const1,191ArrayRef<DIExpression::ExprOperand> Ops,192uint64_t &Loc, DIExpressionCursor &Cursor,193SmallVectorImpl<uint64_t> &WorkingOps) {194195if (isNeutralElement(Ops[1].getOp(), Const1)) {196WorkingOps.erase(WorkingOps.begin() + Loc, WorkingOps.begin() + Loc + 3);197startFromBeginning(Loc, Cursor, WorkingOps);198return true;199}200return false;201}202203/// {DW_OP_constu, Const1, DW_OP_constu, Const2, DW_OP_[plus,204/// minus, mul, div, shl, shr] -> {DW_OP_constu, Const1 [+, -, *, /, <<, >>]205/// Const2}206static bool tryFoldConstants(uint64_t Const1,207ArrayRef<DIExpression::ExprOperand> Ops,208uint64_t &Loc, DIExpressionCursor &Cursor,209SmallVectorImpl<uint64_t> &WorkingOps) {210211auto Const2 = isConstantVal(Ops[1]);212if (!Const2)213return false;214215auto Result = foldOperationIfPossible(216Const1, *Const2, static_cast<dwarf::LocationAtom>(Ops[2].getOp()));217if (!Result) {218consumeOneOperator(Cursor, Loc, Ops[0]);219return true;220}221WorkingOps.erase(WorkingOps.begin() + Loc + 2, WorkingOps.begin() + Loc + 5);222WorkingOps[Loc] = dwarf::DW_OP_constu;223WorkingOps[Loc + 1] = *Result;224startFromBeginning(Loc, Cursor, WorkingOps);225return true;226}227228/// {DW_OP_constu, Const1, DW_OP_[plus, mul], DW_OP_constu, Const2,229/// DW_OP_[plus, mul]} -> {DW_OP_constu, Const1 [+, *] Const2, DW_OP_[plus,230/// mul]}231static bool tryFoldCommutativeMath(uint64_t Const1,232ArrayRef<DIExpression::ExprOperand> Ops,233uint64_t &Loc, DIExpressionCursor &Cursor,234SmallVectorImpl<uint64_t> &WorkingOps) {235236auto Const2 = isConstantVal(Ops[2]);237auto Operand1 = static_cast<dwarf::LocationAtom>(Ops[1].getOp());238auto Operand2 = static_cast<dwarf::LocationAtom>(Ops[3].getOp());239240if (!Const2 || !operationsAreFoldableAndCommutative(Operand1, Operand2))241return false;242243auto Result = foldOperationIfPossible(Const1, *Const2, Operand1);244if (!Result) {245consumeOneOperator(Cursor, Loc, Ops[0]);246return true;247}248WorkingOps.erase(WorkingOps.begin() + Loc + 3, WorkingOps.begin() + Loc + 6);249WorkingOps[Loc] = dwarf::DW_OP_constu;250WorkingOps[Loc + 1] = *Result;251startFromBeginning(Loc, Cursor, WorkingOps);252return true;253}254255/// {DW_OP_constu, Const1, DW_OP_[plus, mul], DW_OP_LLVM_arg, Arg1,256/// DW_OP_[plus, mul], DW_OP_constu, Const2, DW_OP_[plus, mul]} ->257/// {DW_OP_constu, Const1 [+, *] Const2, DW_OP_[plus, mul], DW_OP_LLVM_arg,258/// Arg1, DW_OP_[plus, mul]}259static bool tryFoldCommutativeMathWithArgInBetween(260uint64_t Const1, ArrayRef<DIExpression::ExprOperand> Ops, uint64_t &Loc,261DIExpressionCursor &Cursor, SmallVectorImpl<uint64_t> &WorkingOps) {262263auto Const2 = isConstantVal(Ops[4]);264auto Operand1 = static_cast<dwarf::LocationAtom>(Ops[1].getOp());265auto Operand2 = static_cast<dwarf::LocationAtom>(Ops[3].getOp());266auto Operand3 = static_cast<dwarf::LocationAtom>(Ops[5].getOp());267268if (!Const2 || Ops[2].getOp() != dwarf::DW_OP_LLVM_arg ||269!operationsAreFoldableAndCommutative(Operand1, Operand2) ||270!operationsAreFoldableAndCommutative(Operand2, Operand3))271return false;272273auto Result = foldOperationIfPossible(Const1, *Const2, Operand1);274if (!Result) {275consumeOneOperator(Cursor, Loc, Ops[0]);276return true;277}278WorkingOps.erase(WorkingOps.begin() + Loc + 6, WorkingOps.begin() + Loc + 9);279WorkingOps[Loc] = dwarf::DW_OP_constu;280WorkingOps[Loc + 1] = *Result;281startFromBeginning(Loc, Cursor, WorkingOps);282return true;283}284285DIExpression *DIExpression::foldConstantMath() {286287SmallVector<uint64_t, 8> WorkingOps(Elements.begin(), Elements.end());288uint64_t Loc = 0;289SmallVector<uint64_t> ResultOps = canonicalizeDwarfOperations(WorkingOps);290DIExpressionCursor Cursor(ResultOps);291SmallVector<DIExpression::ExprOperand, 8> Ops;292293// Iterate over all Operations in a DIExpression to match the smallest pattern294// that can be folded.295while (Loc < ResultOps.size()) {296Ops.clear();297298auto Op = Cursor.peek();299// Expression has no operations, exit.300if (!Op)301break;302303auto Const1 = isConstantVal(*Op);304305if (!Const1) {306// Early exit, all of the following patterns start with a constant value.307consumeOneOperator(Cursor, Loc, *Op);308continue;309}310311Ops.push_back(*Op);312313Op = Cursor.peekNext();314// All following patterns require at least 2 Operations, exit.315if (!Op)316break;317318Ops.push_back(*Op);319320// Try to fold a constant no-op, such as {+ 0}321if (tryFoldNoOpMath(*Const1, Ops, Loc, Cursor, ResultOps))322continue;323324Op = Cursor.peekNextN(2);325// Op[1] could still match a pattern, skip iteration.326if (!Op) {327consumeOneOperator(Cursor, Loc, Ops[0]);328continue;329}330331Ops.push_back(*Op);332333// Try to fold a pattern of two constants such as {C1 + C2}.334if (tryFoldConstants(*Const1, Ops, Loc, Cursor, ResultOps))335continue;336337Op = Cursor.peekNextN(3);338// Op[1] and Op[2] could still match a pattern, skip iteration.339if (!Op) {340consumeOneOperator(Cursor, Loc, Ops[0]);341continue;342}343344Ops.push_back(*Op);345346// Try to fold commutative constant math, such as {C1 + C2 +}.347if (tryFoldCommutativeMath(*Const1, Ops, Loc, Cursor, ResultOps))348continue;349350Op = Cursor.peekNextN(4);351if (!Op) {352consumeOneOperator(Cursor, Loc, Ops[0]);353continue;354}355356Ops.push_back(*Op);357Op = Cursor.peekNextN(5);358if (!Op) {359consumeOneOperator(Cursor, Loc, Ops[0]);360continue;361}362363Ops.push_back(*Op);364365// Try to fold commutative constant math with an LLVM_Arg in between, such366// as {C1 + Arg + C2 +}.367if (tryFoldCommutativeMathWithArgInBetween(*Const1, Ops, Loc, Cursor,368ResultOps))369continue;370371consumeOneOperator(Cursor, Loc, Ops[0]);372}373ResultOps = optimizeDwarfOperations(ResultOps);374auto *Result = DIExpression::get(getContext(), ResultOps);375assert(Result->isValid() && "concatenated expression is not valid");376return Result;377}378379380