Skip to content

Commit 93f4a70

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 be04c15 commit 93f4a70

File tree

3 files changed

+562
-1
lines changed

3 files changed

+562
-1
lines changed

llvm/include/llvm/IR/DebugInfoMetadata.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3044,6 +3044,11 @@ class DIExpression : public MDNode {
30443044
/// expression and constant on failure.
30453045
std::pair<DIExpression *, const ConstantInt *>
30463046
constantFold(const ConstantInt *CI);
3047+
3048+
/// Try to shorten an expression with constant math operations that can be
3049+
/// evaulated at compile time. Returns a new expression on success, or the old
3050+
/// expression if there is nothing to be reduced.
3051+
DIExpression *foldConstantMath();
30473052
};
30483053

30493054
inline bool operator==(const DIExpression::FragmentInfo &A,

llvm/lib/IR/DebugInfoMetadata.cpp

Lines changed: 280 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1857,7 +1857,6 @@ DIExpression *DIExpression::append(const DIExpression *Expr,
18571857
}
18581858
Op.appendToVector(NewOps);
18591859
}
1860-
18611860
NewOps.append(Ops.begin(), Ops.end());
18621861
auto *result = DIExpression::get(Expr->getContext(), NewOps);
18631862
assert(result->isValid() && "concatenated expression is not valid");
@@ -1998,6 +1997,286 @@ DIExpression::constantFold(const ConstantInt *CI) {
19981997
ConstantInt::get(getContext(), NewInt)};
19991998
}
20001999

2000+
static bool isConstantVal(uint64_t Op) {
2001+
return Op == dwarf::DW_OP_constu || Op == dwarf::DW_OP_consts;
2002+
}
2003+
2004+
static bool isNeutralElement(uint64_t Op, uint64_t Val) {
2005+
switch (Op) {
2006+
case dwarf::DW_OP_plus:
2007+
case dwarf::DW_OP_minus:
2008+
case dwarf::DW_OP_shl:
2009+
case dwarf::DW_OP_shr:
2010+
return Val == 0;
2011+
case dwarf::DW_OP_mul:
2012+
case dwarf::DW_OP_div:
2013+
return Val == 1;
2014+
default:
2015+
return false;
2016+
}
2017+
}
2018+
2019+
static std::optional<uint64_t>
2020+
foldOperationIfPossible(uint64_t Op, uint64_t Operand1, uint64_t Operand2) {
2021+
switch (Op) {
2022+
case dwarf::DW_OP_plus:
2023+
return Operand1 + Operand2;
2024+
case dwarf::DW_OP_minus:
2025+
return Operand1 - Operand2;
2026+
case dwarf::DW_OP_shl:
2027+
return Operand1 << Operand2;
2028+
case dwarf::DW_OP_shr:
2029+
return Operand1 >> Operand2;
2030+
case dwarf::DW_OP_mul:
2031+
return Operand1 * Operand2;
2032+
case dwarf::DW_OP_div:
2033+
return Operand1 / Operand2;
2034+
default:
2035+
return std::nullopt;
2036+
}
2037+
}
2038+
2039+
static bool operationsAreFoldableAndCommutative(uint64_t Op1, uint64_t Op2) {
2040+
if (Op1 != Op2)
2041+
return false;
2042+
switch (Op1) {
2043+
case dwarf::DW_OP_plus:
2044+
case dwarf::DW_OP_mul:
2045+
return true;
2046+
default:
2047+
return false;
2048+
}
2049+
}
2050+
2051+
static void consumeOneOperator(DIExpressionCursor &Cursor, uint64_t &Loc,
2052+
const DIExpression::ExprOperand &Op) {
2053+
Cursor.consume(1);
2054+
Loc = Loc + Op.getSize();
2055+
}
2056+
2057+
DIExpression *DIExpression::foldConstantMath() {
2058+
2059+
SmallVector<uint64_t, 8> WorkingOps;
2060+
WorkingOps.append(Elements.begin(), Elements.end());
2061+
uint64_t Loc = 0;
2062+
DIExpressionCursor Cursor(WorkingOps);
2063+
2064+
while (Loc < WorkingOps.size()) {
2065+
2066+
auto Op1 = Cursor.peek();
2067+
// Expression has no operations, exit.
2068+
if (!Op1)
2069+
break;
2070+
auto Op1Raw = Op1->getOp();
2071+
auto Op1Arg = Op1->getArg(0);
2072+
2073+
// {DW_OP_plus_uconst, 0} -> {}
2074+
if (Op1Raw == dwarf::DW_OP_plus_uconst && Op1Arg == 0) {
2075+
WorkingOps.erase(WorkingOps.begin() + Loc, WorkingOps.begin() + Loc + 2);
2076+
consumeOneOperator(Cursor, Loc, *Op1);
2077+
continue;
2078+
}
2079+
2080+
if (!isConstantVal(Op1Raw) && Op1Raw != dwarf::DW_OP_plus_uconst) {
2081+
// Early exit, all of the following patterns start with a constant value.
2082+
consumeOneOperator(Cursor, Loc, *Op1);
2083+
continue;
2084+
}
2085+
2086+
auto Op2 = Cursor.peekNext();
2087+
// All following patterns require at least 2 Operations, exit.
2088+
if (!Op2)
2089+
break;
2090+
auto Op2Raw = Op2->getOp();
2091+
2092+
// {DW_OP_const[u, s], 0, DW_OP_[plus, minus, shl, shr]} -> {}
2093+
// {DW_OP_const[u, s], 1, DW_OP_[mul, div]} -> {}
2094+
if (isConstantVal(Op1Raw) && isNeutralElement(Op2Raw, Op1Arg)) {
2095+
WorkingOps.erase(WorkingOps.begin() + Loc, WorkingOps.begin() + Loc + 3);
2096+
Cursor.assignNewExpr(WorkingOps);
2097+
Loc = 0;
2098+
continue;
2099+
}
2100+
2101+
auto Op2Arg = Op2->getArg(0);
2102+
2103+
// {DW_OP_plus_uconst, Const1, DW_OP_plus_uconst, Const2} ->
2104+
// {DW_OP_plus_uconst, Const1 + Const2}
2105+
if (Op1Raw == dwarf::DW_OP_plus_uconst &&
2106+
Op2Raw == dwarf::DW_OP_plus_uconst) {
2107+
auto Result = Op1Arg + Op2Arg;
2108+
WorkingOps.erase(WorkingOps.begin() + Loc + 2,
2109+
WorkingOps.begin() + Loc + 4);
2110+
WorkingOps[Loc + 1] = Result;
2111+
Cursor.assignNewExpr(WorkingOps);
2112+
Loc = 0;
2113+
continue;
2114+
}
2115+
2116+
// {DW_OP_const[u, s], Const1, DW_OP_plus_uconst Const2} -> {DW_OP_constu,
2117+
// Const1 + Const2}
2118+
if (isConstantVal(Op1Raw) && Op2Raw == dwarf::DW_OP_plus_uconst) {
2119+
auto Result = Op1Arg + Op2Arg;
2120+
WorkingOps.erase(WorkingOps.begin() + Loc + 2,
2121+
WorkingOps.begin() + Loc + 4);
2122+
WorkingOps[Loc] = dwarf::DW_OP_constu;
2123+
WorkingOps[Loc + 1] = Result;
2124+
Cursor.assignNewExpr(WorkingOps);
2125+
Loc = 0;
2126+
continue;
2127+
}
2128+
2129+
auto Op3 = Cursor.peekNextN(2);
2130+
// Op2 could still match a pattern, skip iteration.
2131+
if (!Op3) {
2132+
consumeOneOperator(Cursor, Loc, *Op1);
2133+
continue;
2134+
}
2135+
auto Op3Raw = Op3->getOp();
2136+
2137+
// {DW_OP_const[u, s], Const1, DW_OP_const[u, s], Const2, DW_OP_[plus,
2138+
// minus, mul, div, shl, shr] -> {DW_OP_constu, Const1 [+, -, *, /, <<, >>]
2139+
// Const2}
2140+
if (isConstantVal(Op1Raw) && isConstantVal(Op2Raw)) {
2141+
auto Result = foldOperationIfPossible(Op3Raw, Op1Arg, Op2Arg);
2142+
if (!Result) {
2143+
consumeOneOperator(Cursor, Loc, *Op1);
2144+
continue;
2145+
}
2146+
WorkingOps.erase(WorkingOps.begin() + Loc + 2,
2147+
WorkingOps.begin() + Loc + 5);
2148+
WorkingOps[Loc] = dwarf::DW_OP_constu;
2149+
WorkingOps[Loc + 1] = *Result;
2150+
Cursor.assignNewExpr(WorkingOps);
2151+
Loc = 0;
2152+
continue;
2153+
}
2154+
2155+
// {DW_OP_plus_uconst, Const1, DW_OP_const[u, s], Const1, DW_OP_plus} ->
2156+
// {DW_OP_plus_uconst, Const1 + Const2}
2157+
if (Op1Raw == dwarf::DW_OP_plus_uconst && isConstantVal(Op2Raw) &&
2158+
Op3Raw == dwarf::DW_OP_plus) {
2159+
auto Result = Op1Arg + Op2Arg;
2160+
WorkingOps.erase(WorkingOps.begin() + Loc + 2, WorkingOps.begin() + 5);
2161+
WorkingOps[Loc + 1] = Result;
2162+
Cursor.assignNewExpr(WorkingOps);
2163+
Loc = 0;
2164+
continue;
2165+
}
2166+
2167+
auto Op3Arg = Op3->getArg(0);
2168+
// {DW_OP_const[u, s], Const1, DW_OP_plus, DW_OP_plus_uconst, Const2} ->
2169+
// {DW_OP_plus_uconst, Const1 + Const2}
2170+
if (isConstantVal(Op1Raw) && Op2Raw == dwarf::DW_OP_plus &&
2171+
Op3Raw == dwarf::DW_OP_plus_uconst) {
2172+
auto Result = Op1Arg + Op3Arg;
2173+
WorkingOps.erase(WorkingOps.begin() + Loc + 2, WorkingOps.begin() + 5);
2174+
WorkingOps[Loc] = dwarf::DW_OP_plus_uconst;
2175+
WorkingOps[Loc + 1] = Result;
2176+
Cursor.assignNewExpr(WorkingOps);
2177+
Loc = 0;
2178+
continue;
2179+
}
2180+
2181+
auto Op4 = Cursor.peekNextN(3);
2182+
// Op2 and Op3 could still match a pattern, skip iteration.
2183+
if (!Op4) {
2184+
consumeOneOperator(Cursor, Loc, *Op1);
2185+
continue;
2186+
}
2187+
auto Op4Raw = Op4->getOp();
2188+
2189+
// {DW_OP_const[u, s], Const1, DW_OP_[plus, mul], Const2, DW_OP_[plus, mul]}
2190+
// -> {DW_OP_constu, Const1 [+, *] Const2, DW_OP_[plus, mul]}
2191+
if (isConstantVal(Op1Raw) && isConstantVal(Op3Raw) &&
2192+
operationsAreFoldableAndCommutative(Op2Raw, Op4Raw)) {
2193+
auto Result = foldOperationIfPossible(Op2Raw, Op1Arg, Op3Arg);
2194+
if (!Result)
2195+
llvm_unreachable(
2196+
"Something went very wrong here! This should always work");
2197+
WorkingOps.erase(WorkingOps.begin() + Loc + 3,
2198+
WorkingOps.begin() + Loc + 6);
2199+
WorkingOps[Loc] = dwarf::DW_OP_constu;
2200+
WorkingOps[Loc + 1] = *Result;
2201+
Cursor.assignNewExpr(WorkingOps);
2202+
Loc = 0;
2203+
continue;
2204+
}
2205+
auto Op5 = Cursor.peekNextN(4);
2206+
if (!Op5) {
2207+
consumeOneOperator(Cursor, Loc, *Op1);
2208+
continue;
2209+
}
2210+
auto Op5Raw = Op5->getOp();
2211+
auto Op5Arg = Op5->getArg(0);
2212+
2213+
// {DW_OP_const[u, s], Const1, DW_OP_plus, DW_OP_LLVM_arg, Arg1, DW_OP_plus,
2214+
// DW_OP_plus_uconst, Const2} -> {DW_OP_constu, Const1 + Const2, DW_OP_plus,
2215+
// DW_OP_LLVM_arg, Arg1, DW_OP_plus}
2216+
if (isConstantVal(Op1Raw) && Op3Raw == dwarf::DW_OP_LLVM_arg &&
2217+
Op5Raw == dwarf::DW_OP_plus_uconst && Op2Raw == Op4Raw &&
2218+
Op4Raw == dwarf::DW_OP_plus) {
2219+
auto Result = Op1Arg + Op5Arg;
2220+
WorkingOps.erase(WorkingOps.begin() + Loc + 6,
2221+
WorkingOps.begin() + Loc + 8);
2222+
WorkingOps[Loc] = dwarf::DW_OP_constu;
2223+
WorkingOps[Loc + 1] = Result;
2224+
Cursor.assignNewExpr(WorkingOps);
2225+
Loc = 0;
2226+
continue;
2227+
}
2228+
2229+
auto Op4Arg = Op4->getArg(0);
2230+
2231+
// {DW_OP_plus_uconst, Const1, DW_OP_LLVM_arg, Arg1, DW_OP_plus,
2232+
// DW_OP_const[u, s], Const2, DW_OP_plus} -> {DW_OP_plus_uconst, Const1 +
2233+
// Const2, DW_OP_LLVM_arg, Arg1, DW_OP_plus}
2234+
if (Op1Raw == dwarf::DW_OP_plus_uconst && Op2Raw == dwarf::DW_OP_LLVM_arg &&
2235+
Op3Raw == Op5Raw && Op3Raw == dwarf::DW_OP_plus &&
2236+
isConstantVal(Op4Raw)) {
2237+
auto Result = Op1Arg + Op4Arg;
2238+
WorkingOps.erase(WorkingOps.begin() + Loc + 5,
2239+
WorkingOps.begin() + Loc + 8);
2240+
WorkingOps[Loc + 1] = Result;
2241+
Cursor.assignNewExpr(WorkingOps);
2242+
Loc = 0;
2243+
continue;
2244+
}
2245+
2246+
auto Op6 = Cursor.peekNextN(5);
2247+
if (!Op6) {
2248+
consumeOneOperator(Cursor, Loc, *Op1);
2249+
continue;
2250+
}
2251+
auto Op6Raw = Op6->getOp();
2252+
// {DW_OP_const[u, s], Const1, DW_OP_[plus, mul], DW_OP_LLVM_arg, Arg1,
2253+
// DW_OP_[plus, mul], DW_OP_const[u, s], Const2, DW_OP_[plus, mul]} ->
2254+
// {DW_OP_constu, Const1 [+, *] Const2, DW_OP_[plus, mul], DW_OP_LLVM_arg,
2255+
// Arg1, DW_OP_[plus, mul]}
2256+
if (isConstantVal(Op1Raw) && Op3Raw == dwarf::DW_OP_LLVM_arg &&
2257+
isConstantVal(Op5Raw) &&
2258+
operationsAreFoldableAndCommutative(Op2Raw, Op4Raw) &&
2259+
operationsAreFoldableAndCommutative(Op4Raw, Op6Raw)) {
2260+
auto Result = foldOperationIfPossible(Op2Raw, Op1Arg, Op5Arg);
2261+
if (!Result)
2262+
llvm_unreachable(
2263+
"Something went very wrong here! This should always work");
2264+
WorkingOps.erase(WorkingOps.begin() + Loc + 6,
2265+
WorkingOps.begin() + Loc + 9);
2266+
WorkingOps[Loc] = dwarf::DW_OP_constu;
2267+
WorkingOps[Loc + 1] = *Result;
2268+
Cursor.assignNewExpr(WorkingOps);
2269+
Loc = 0;
2270+
continue;
2271+
}
2272+
2273+
consumeOneOperator(Cursor, Loc, *Op1);
2274+
}
2275+
auto *Result = DIExpression::get(getContext(), WorkingOps);
2276+
assert(Result->isValid() && "concatenated expression is not valid");
2277+
return Result;
2278+
}
2279+
20012280
uint64_t DIExpression::getNumLocationOperands() const {
20022281
uint64_t Result = 0;
20032282
for (auto ExprOp : expr_ops())

0 commit comments

Comments
 (0)