|
22 | 22 | #include "llvm/IR/Type.h"
|
23 | 23 | #include "llvm/IR/Value.h"
|
24 | 24 |
|
| 25 | +#include <bit> |
25 | 26 | #include <numeric>
|
26 | 27 | #include <optional>
|
27 | 28 |
|
@@ -1880,7 +1881,6 @@ DIExpression *DIExpression::append(const DIExpression *Expr,
|
1880 | 1881 | }
|
1881 | 1882 | Op.appendToVector(NewOps);
|
1882 | 1883 | }
|
1883 |
| - |
1884 | 1884 | NewOps.append(Ops.begin(), Ops.end());
|
1885 | 1885 | auto *result = DIExpression::get(Expr->getContext(), NewOps);
|
1886 | 1886 | assert(result->isValid() && "concatenated expression is not valid");
|
@@ -2022,6 +2022,347 @@ DIExpression::constantFold(const ConstantInt *CI) {
|
2022 | 2022 | ConstantInt::get(getContext(), NewInt)};
|
2023 | 2023 | }
|
2024 | 2024 |
|
| 2025 | +/// Returns true if the Op is a DW_OP_constu. |
| 2026 | +static bool isConstantVal(uint64_t Op) { return Op == dwarf::DW_OP_constu; } |
| 2027 | + |
| 2028 | +/// Returns true if an operation and operand result in a No Op. |
| 2029 | +static bool isNeutralElement(uint64_t Op, uint64_t Val) { |
| 2030 | + switch (Op) { |
| 2031 | + case dwarf::DW_OP_plus: |
| 2032 | + case dwarf::DW_OP_minus: |
| 2033 | + case dwarf::DW_OP_shl: |
| 2034 | + case dwarf::DW_OP_shr: |
| 2035 | + return Val == 0; |
| 2036 | + case dwarf::DW_OP_mul: |
| 2037 | + case dwarf::DW_OP_div: |
| 2038 | + return Val == 1; |
| 2039 | + default: |
| 2040 | + return false; |
| 2041 | + } |
| 2042 | +} |
| 2043 | + |
| 2044 | +/// Try to fold constant math operations and return the result if possible. |
| 2045 | +static std::optional<uint64_t> |
| 2046 | +foldOperationIfPossible(uint64_t Op, uint64_t Operand1, uint64_t Operand2) { |
| 2047 | + bool ResultOverflowed; |
| 2048 | + switch (Op) { |
| 2049 | + case dwarf::DW_OP_plus: { |
| 2050 | + auto Result = SaturatingAdd(Operand1, Operand2, &ResultOverflowed); |
| 2051 | + if (ResultOverflowed) |
| 2052 | + return std::nullopt; |
| 2053 | + return Result; |
| 2054 | + } |
| 2055 | + case dwarf::DW_OP_minus: { |
| 2056 | + if (Operand1 < Operand2) |
| 2057 | + return std::nullopt; |
| 2058 | + return Operand1 - Operand2; |
| 2059 | + } |
| 2060 | + case dwarf::DW_OP_shl: { |
| 2061 | + if ((uint64_t)countl_zero(Operand1) < Operand2) |
| 2062 | + return std::nullopt; |
| 2063 | + return Operand1 << Operand2; |
| 2064 | + } |
| 2065 | + case dwarf::DW_OP_shr: { |
| 2066 | + if ((uint64_t)countr_zero(Operand1) < Operand2) |
| 2067 | + return std::nullopt; |
| 2068 | + return Operand1 >> Operand2; |
| 2069 | + } |
| 2070 | + case dwarf::DW_OP_mul: { |
| 2071 | + auto Result = SaturatingMultiply(Operand1, Operand2, &ResultOverflowed); |
| 2072 | + if (ResultOverflowed) |
| 2073 | + return std::nullopt; |
| 2074 | + return Result; |
| 2075 | + } |
| 2076 | + case dwarf::DW_OP_div: { |
| 2077 | + if (Operand2) |
| 2078 | + return Operand1 / Operand2; |
| 2079 | + return std::nullopt; |
| 2080 | + } |
| 2081 | + default: |
| 2082 | + return std::nullopt; |
| 2083 | + } |
| 2084 | +} |
| 2085 | + |
| 2086 | +/// Returns true if the two operations are commutative and can be folded. |
| 2087 | +static bool operationsAreFoldableAndCommutative(uint64_t Op1, uint64_t Op2) { |
| 2088 | + if (Op1 != Op2) |
| 2089 | + return false; |
| 2090 | + switch (Op1) { |
| 2091 | + case dwarf::DW_OP_plus: |
| 2092 | + case dwarf::DW_OP_mul: |
| 2093 | + return true; |
| 2094 | + default: |
| 2095 | + return false; |
| 2096 | + } |
| 2097 | +} |
| 2098 | + |
| 2099 | +/// Consume one operator and its operand(s). |
| 2100 | +static void consumeOneOperator(DIExpressionCursor &Cursor, uint64_t &Loc, |
| 2101 | + const DIExpression::ExprOperand &Op) { |
| 2102 | + Cursor.consume(1); |
| 2103 | + Loc = Loc + Op.getSize(); |
| 2104 | +} |
| 2105 | + |
| 2106 | +/// Reset the Cursor to the beginning of the WorkingOps. |
| 2107 | +static void startFromBeginning(uint64_t &Loc, DIExpressionCursor &Cursor, |
| 2108 | + ArrayRef<uint64_t> WorkingOps) { |
| 2109 | + Cursor.assignNewExpr(WorkingOps); |
| 2110 | + Loc = 0; |
| 2111 | +} |
| 2112 | + |
| 2113 | +/// This function will canonicalize: |
| 2114 | +/// 1. DW_OP_plus_uconst to DW_OP_constu <const-val> DW_OP_plus |
| 2115 | +/// 2. DW_OP_lit<n> to DW_OP_constu <n> |
| 2116 | +static SmallVector<uint64_t> |
| 2117 | +canonicalizeDwarfOperations(ArrayRef<uint64_t> WorkingOps) { |
| 2118 | + DIExpressionCursor Cursor(WorkingOps); |
| 2119 | + uint64_t Loc = 0; |
| 2120 | + SmallVector<uint64_t> ResultOps; |
| 2121 | + while (Loc < WorkingOps.size()) { |
| 2122 | + auto Op = Cursor.peek(); |
| 2123 | + /// Expression has no operations, break. |
| 2124 | + if (!Op) |
| 2125 | + break; |
| 2126 | + auto OpRaw = Op->getOp(); |
| 2127 | + auto OpArg = Op->getArg(0); |
| 2128 | + |
| 2129 | + if (OpRaw >= dwarf::DW_OP_lit0 && OpRaw <= dwarf::DW_OP_lit31) { |
| 2130 | + ResultOps.push_back(dwarf::DW_OP_constu); |
| 2131 | + ResultOps.push_back(OpRaw - dwarf::DW_OP_lit0); |
| 2132 | + consumeOneOperator(Cursor, Loc, *Cursor.peek()); |
| 2133 | + continue; |
| 2134 | + } |
| 2135 | + if (OpRaw == dwarf::DW_OP_plus_uconst) { |
| 2136 | + ResultOps.push_back(dwarf::DW_OP_constu); |
| 2137 | + ResultOps.push_back(OpArg); |
| 2138 | + ResultOps.push_back(dwarf::DW_OP_plus); |
| 2139 | + consumeOneOperator(Cursor, Loc, *Cursor.peek()); |
| 2140 | + continue; |
| 2141 | + } |
| 2142 | + uint64_t PrevLoc = Loc; |
| 2143 | + consumeOneOperator(Cursor, Loc, *Cursor.peek()); |
| 2144 | + ResultOps.append(WorkingOps.begin() + PrevLoc, WorkingOps.begin() + Loc); |
| 2145 | + } |
| 2146 | + return ResultOps; |
| 2147 | +} |
| 2148 | + |
| 2149 | +/// This function will convert: |
| 2150 | +/// 1. DW_OP_constu <const-val> DW_OP_plus to DW_OP_plus_uconst |
| 2151 | +/// 2. DW_OP_constu, 0 to DW_OP_lit0 |
| 2152 | +static SmallVector<uint64_t> |
| 2153 | +optimizeDwarfOperations(ArrayRef<uint64_t> WorkingOps) { |
| 2154 | + DIExpressionCursor Cursor(WorkingOps); |
| 2155 | + uint64_t Loc = 0; |
| 2156 | + SmallVector<uint64_t> ResultOps; |
| 2157 | + while (Loc < WorkingOps.size()) { |
| 2158 | + auto Op1 = Cursor.peek(); |
| 2159 | + /// Expression has no operations, exit. |
| 2160 | + if (!Op1) |
| 2161 | + break; |
| 2162 | + auto Op1Raw = Op1->getOp(); |
| 2163 | + auto Op1Arg = Op1->getArg(0); |
| 2164 | + |
| 2165 | + if (Op1Raw == dwarf::DW_OP_constu && Op1Arg == 0) { |
| 2166 | + ResultOps.push_back(dwarf::DW_OP_lit0); |
| 2167 | + consumeOneOperator(Cursor, Loc, *Cursor.peek()); |
| 2168 | + continue; |
| 2169 | + } |
| 2170 | + |
| 2171 | + auto Op2 = Cursor.peekNext(); |
| 2172 | + /// Expression has no more operations, copy into ResultOps and exit. |
| 2173 | + if (!Op2) { |
| 2174 | + uint64_t PrevLoc = Loc; |
| 2175 | + consumeOneOperator(Cursor, Loc, *Cursor.peek()); |
| 2176 | + ResultOps.append(WorkingOps.begin() + PrevLoc, WorkingOps.begin() + Loc); |
| 2177 | + break; |
| 2178 | + } |
| 2179 | + auto Op2Raw = Op2->getOp(); |
| 2180 | + |
| 2181 | + if (Op1Raw == dwarf::DW_OP_constu && Op2Raw == dwarf::DW_OP_plus) { |
| 2182 | + ResultOps.push_back(dwarf::DW_OP_plus_uconst); |
| 2183 | + ResultOps.push_back(Op1Arg); |
| 2184 | + consumeOneOperator(Cursor, Loc, *Cursor.peek()); |
| 2185 | + consumeOneOperator(Cursor, Loc, *Cursor.peek()); |
| 2186 | + continue; |
| 2187 | + } |
| 2188 | + uint64_t PrevLoc = Loc; |
| 2189 | + consumeOneOperator(Cursor, Loc, *Cursor.peek()); |
| 2190 | + ResultOps.append(WorkingOps.begin() + PrevLoc, WorkingOps.begin() + Loc); |
| 2191 | + } |
| 2192 | + return ResultOps; |
| 2193 | +} |
| 2194 | + |
| 2195 | +/// {DW_OP_constu, 0, DW_OP_[plus, minus, shl, shr]} -> {} |
| 2196 | +/// {DW_OP_constu, 1, DW_OP_[mul, div]} -> {} |
| 2197 | +static bool tryFoldNoOpMath(uint64_t Op1Raw, uint64_t Op1Arg, uint64_t Op2Raw, |
| 2198 | + uint64_t &Loc, DIExpressionCursor &Cursor, |
| 2199 | + SmallVectorImpl<uint64_t> &WorkingOps) { |
| 2200 | + if (isConstantVal(Op1Raw) && isNeutralElement(Op2Raw, Op1Arg)) { |
| 2201 | + WorkingOps.erase(WorkingOps.begin() + Loc, WorkingOps.begin() + Loc + 3); |
| 2202 | + startFromBeginning(Loc, Cursor, WorkingOps); |
| 2203 | + return true; |
| 2204 | + } |
| 2205 | + return false; |
| 2206 | +} |
| 2207 | + |
| 2208 | +/// {DW_OP_constu, Const1, DW_OP_constu, Const2, DW_OP_[plus, |
| 2209 | +/// minus, mul, div, shl, shr] -> {DW_OP_constu, Const1 [+, -, *, /, <<, >>] |
| 2210 | +/// Const2} |
| 2211 | +static bool tryFoldConstants(std::optional<DIExpression::ExprOperand> Op1, |
| 2212 | + uint64_t Op1Raw, uint64_t Op1Arg, uint64_t Op2Raw, |
| 2213 | + uint64_t Op2Arg, uint64_t Op3Raw, uint64_t &Loc, |
| 2214 | + DIExpressionCursor &Cursor, |
| 2215 | + SmallVectorImpl<uint64_t> &WorkingOps) { |
| 2216 | + if (isConstantVal(Op1Raw) && isConstantVal(Op2Raw)) { |
| 2217 | + auto Result = foldOperationIfPossible(Op3Raw, Op1Arg, Op2Arg); |
| 2218 | + if (!Result) { |
| 2219 | + consumeOneOperator(Cursor, Loc, *Op1); |
| 2220 | + return true; |
| 2221 | + } |
| 2222 | + WorkingOps.erase(WorkingOps.begin() + Loc + 2, |
| 2223 | + WorkingOps.begin() + Loc + 5); |
| 2224 | + WorkingOps[Loc] = dwarf::DW_OP_constu; |
| 2225 | + WorkingOps[Loc + 1] = *Result; |
| 2226 | + startFromBeginning(Loc, Cursor, WorkingOps); |
| 2227 | + return true; |
| 2228 | + } |
| 2229 | + return false; |
| 2230 | +} |
| 2231 | + |
| 2232 | +/// {DW_OP_constu, Const1, DW_OP_[plus, mul], DW_OP_constu, Const2, |
| 2233 | +/// DW_OP_[plus, mul]} -> {DW_OP_constu, Const1 [+, *] Const2, DW_OP_[plus, |
| 2234 | +/// mul]} |
| 2235 | +static bool tryFoldCommutativeMath(uint64_t Op1Raw, uint64_t Op1Arg, |
| 2236 | + uint64_t Op2Raw, uint64_t Op2Arg, |
| 2237 | + uint64_t Op3Raw, uint64_t Op3Arg, |
| 2238 | + uint64_t Op4Raw, uint64_t &Loc, |
| 2239 | + DIExpressionCursor &Cursor, |
| 2240 | + SmallVectorImpl<uint64_t> &WorkingOps) { |
| 2241 | + |
| 2242 | + if (isConstantVal(Op1Raw) && isConstantVal(Op3Raw) && |
| 2243 | + operationsAreFoldableAndCommutative(Op2Raw, Op4Raw)) { |
| 2244 | + auto Result = foldOperationIfPossible(Op2Raw, Op1Arg, Op3Arg); |
| 2245 | + if (!Result) |
| 2246 | + return false; |
| 2247 | + WorkingOps.erase(WorkingOps.begin() + Loc + 3, |
| 2248 | + WorkingOps.begin() + Loc + 6); |
| 2249 | + WorkingOps[Loc] = dwarf::DW_OP_constu; |
| 2250 | + WorkingOps[Loc + 1] = *Result; |
| 2251 | + startFromBeginning(Loc, Cursor, WorkingOps); |
| 2252 | + return true; |
| 2253 | + } |
| 2254 | + return false; |
| 2255 | +} |
| 2256 | + |
| 2257 | +/// {DW_OP_constu, Const1, DW_OP_[plus, mul], DW_OP_LLVM_arg, Arg1, |
| 2258 | +/// DW_OP_[plus, mul], DW_OP_constu, Const2, DW_OP_[plus, mul]} -> |
| 2259 | +/// {DW_OP_constu, Const1 [+, *] Const2, DW_OP_[plus, mul], DW_OP_LLVM_arg, |
| 2260 | +/// Arg1, DW_OP_[plus, mul]} |
| 2261 | +static bool tryFoldCommutativeMathWithArgInBetween( |
| 2262 | + uint64_t Op1Raw, uint64_t Op1Arg, uint64_t Op2Raw, uint64_t Op3Raw, |
| 2263 | + uint64_t Op4Raw, uint64_t Op5Raw, uint64_t Op5Arg, uint64_t Op6Raw, |
| 2264 | + uint64_t &Loc, DIExpressionCursor &Cursor, |
| 2265 | + SmallVectorImpl<uint64_t> &WorkingOps) { |
| 2266 | + if (isConstantVal(Op1Raw) && Op3Raw == dwarf::DW_OP_LLVM_arg && |
| 2267 | + isConstantVal(Op5Raw) && |
| 2268 | + operationsAreFoldableAndCommutative(Op2Raw, Op4Raw) && |
| 2269 | + operationsAreFoldableAndCommutative(Op4Raw, Op6Raw)) { |
| 2270 | + auto Result = foldOperationIfPossible(Op2Raw, Op1Arg, Op5Arg); |
| 2271 | + if (!Result) |
| 2272 | + return false; |
| 2273 | + WorkingOps.erase(WorkingOps.begin() + Loc + 6, |
| 2274 | + WorkingOps.begin() + Loc + 9); |
| 2275 | + WorkingOps[Loc] = dwarf::DW_OP_constu; |
| 2276 | + WorkingOps[Loc + 1] = *Result; |
| 2277 | + startFromBeginning(Loc, Cursor, WorkingOps); |
| 2278 | + return true; |
| 2279 | + } |
| 2280 | + return false; |
| 2281 | +} |
| 2282 | + |
| 2283 | +SmallVector<uint64_t> DIExpression::foldConstantMath() { |
| 2284 | + |
| 2285 | + SmallVector<uint64_t, 8> WorkingOps(Elements.begin(), Elements.end()); |
| 2286 | + uint64_t Loc = 0; |
| 2287 | + SmallVector<uint64_t> ResultOps = canonicalizeDwarfOperations(WorkingOps); |
| 2288 | + DIExpressionCursor Cursor(ResultOps); |
| 2289 | + |
| 2290 | + while (Loc < ResultOps.size()) { |
| 2291 | + |
| 2292 | + auto Op1 = Cursor.peek(); |
| 2293 | + // Expression has no operations, exit. |
| 2294 | + if (!Op1) |
| 2295 | + break; |
| 2296 | + auto Op1Raw = Op1->getOp(); |
| 2297 | + auto Op1Arg = Op1->getArg(0); |
| 2298 | + |
| 2299 | + if (!isConstantVal(Op1Raw)) { |
| 2300 | + // Early exit, all of the following patterns start with a constant value. |
| 2301 | + consumeOneOperator(Cursor, Loc, *Op1); |
| 2302 | + continue; |
| 2303 | + } |
| 2304 | + |
| 2305 | + auto Op2 = Cursor.peekNext(); |
| 2306 | + // All following patterns require at least 2 Operations, exit. |
| 2307 | + if (!Op2) |
| 2308 | + break; |
| 2309 | + auto Op2Raw = Op2->getOp(); |
| 2310 | + |
| 2311 | + if (tryFoldNoOpMath(Op1Raw, Op1Arg, Op2Raw, Loc, Cursor, ResultOps)) |
| 2312 | + continue; |
| 2313 | + |
| 2314 | + auto Op2Arg = Op2->getArg(0); |
| 2315 | + |
| 2316 | + auto Op3 = Cursor.peekNextN(2); |
| 2317 | + // Op2 could still match a pattern, skip iteration. |
| 2318 | + if (!Op3) { |
| 2319 | + consumeOneOperator(Cursor, Loc, *Op1); |
| 2320 | + continue; |
| 2321 | + } |
| 2322 | + auto Op3Raw = Op3->getOp(); |
| 2323 | + |
| 2324 | + if (tryFoldConstants(Op1, Op1Raw, Op1Arg, Op2Raw, Op2Arg, Op3Raw, Loc, |
| 2325 | + Cursor, ResultOps)) |
| 2326 | + continue; |
| 2327 | + |
| 2328 | + auto Op3Arg = Op3->getArg(0); |
| 2329 | + |
| 2330 | + auto Op4 = Cursor.peekNextN(3); |
| 2331 | + // Op2 and Op3 could still match a pattern, skip iteration. |
| 2332 | + if (!Op4) { |
| 2333 | + consumeOneOperator(Cursor, Loc, *Op1); |
| 2334 | + continue; |
| 2335 | + } |
| 2336 | + auto Op4Raw = Op4->getOp(); |
| 2337 | + |
| 2338 | + if (tryFoldCommutativeMath(Op1Raw, Op1Arg, Op2Raw, Op2Arg, Op3Raw, Op3Arg, |
| 2339 | + Op4Raw, Loc, Cursor, ResultOps)) |
| 2340 | + continue; |
| 2341 | + |
| 2342 | + auto Op5 = Cursor.peekNextN(4); |
| 2343 | + if (!Op5) { |
| 2344 | + consumeOneOperator(Cursor, Loc, *Op1); |
| 2345 | + continue; |
| 2346 | + } |
| 2347 | + auto Op5Raw = Op5->getOp(); |
| 2348 | + auto Op5Arg = Op5->getArg(0); |
| 2349 | + auto Op6 = Cursor.peekNextN(5); |
| 2350 | + if (!Op6) { |
| 2351 | + consumeOneOperator(Cursor, Loc, *Op1); |
| 2352 | + continue; |
| 2353 | + } |
| 2354 | + auto Op6Raw = Op6->getOp(); |
| 2355 | + if (tryFoldCommutativeMathWithArgInBetween(Op1Raw, Op1Arg, Op2Raw, Op3Raw, |
| 2356 | + Op4Raw, Op5Raw, Op5Arg, Op6Raw, |
| 2357 | + Loc, Cursor, ResultOps)) |
| 2358 | + continue; |
| 2359 | + |
| 2360 | + consumeOneOperator(Cursor, Loc, *Op1); |
| 2361 | + } |
| 2362 | + ResultOps = optimizeDwarfOperations(ResultOps); |
| 2363 | + return ResultOps; |
| 2364 | +} |
| 2365 | + |
2025 | 2366 | uint64_t DIExpression::getNumLocationOperands() const {
|
2026 | 2367 | uint64_t Result = 0;
|
2027 | 2368 | for (auto ExprOp : expr_ops())
|
|
0 commit comments