[llvm] [TailRecElim] Introduce support for shift accumulator optimization (PR #181331)

Antonio Frighetto via llvm-commits llvm-commits at lists.llvm.org
Wed Jun 10 03:52:01 PDT 2026


================
@@ -0,0 +1,179 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
+; RUN: opt < %s -passes="tailcallelim" -verify-dom-info -S | FileCheck %s
+
+; NOTE: All the following test cases are generate from the underlying C code (-O1)
+;   before that the shift accumulator optimization was implemented
+
+
+
+; InstCombine strength-reduce `f(x-1) + f(x-1)` to shl:
+; int f(int x) {
+;     if (x == 1) return 7;
+;     return f(x-1) + f(x-1); // f(x-1) * 2
+; }
+define i32 @test_shl_const_accumulator(i32 %x) {
+; CHECK-LABEL: define i32 @test_shl_const_accumulator(
+; CHECK-SAME: i32 [[X:%.*]]) {
+; CHECK-NEXT:  [[TAILRECURSE:.*:]]
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i32 [[X]], 1
+; CHECK-NEXT:    br i1 [[CMP]], label %[[COMMON_RET:.*]], label %[[IF_END:.*]]
+; CHECK:       [[COMMON_RET]]:
+; CHECK-NEXT:    ret i32 7
+; CHECK:       [[IF_END]]:
+; CHECK-NEXT:    [[SUB:%.*]] = add nsw i32 [[X]], -1
+; CHECK-NEXT:    [[ACCUMULATOR_TR:%.*]] = tail call i32 @test_shl_const_accumulator(i32 [[SUB]])
+; CHECK-NEXT:    [[ADD:%.*]] = shl nsw i32 [[ACCUMULATOR_TR]], 1
----------------
antoniofrighetto wrote:

Hmm, it looks like we actually lost the tail elimination while trying to replace walking the use-def chain starting from the return value with the pattern match (01dc728e94ec86ff6c710b2bf0f3c5fc8799109e), this call shouldn't be here.

Looking at this better, possibly pattern-matching the PHI/select here is not anything useful. Could you please evaluate whether we actually need a worklist here? If we just need to check whether the shift is using the result of the recursive call, maybe we could go with something along the following lines?
```cpp
static bool usesRecursiveCall(Value *V, Function &F) {
  auto *I = dyn_cast<Instruction>(V);
  if (!I)
    return false;
  for (Use &U : I->operands())
    if (auto *CI = dyn_cast<CallInst>(U.get()))
      if (CI->getCalledFunction() == &F)
        return true;
  return false;
}
```

https://github.com/llvm/llvm-project/pull/181331


More information about the llvm-commits mailing list