[llvm] b703f63 - Add llvm.looptrap intrinsic.

via llvm-commits llvm-commits at lists.llvm.org
Fri Feb 13 14:12:56 PST 2026


Author: Peter Collingbourne
Date: 2026-02-13T14:12:51-08:00
New Revision: b703f63697167b1cc3d68e6ef101c4fc8d4241a3

URL: https://github.com/llvm/llvm-project/commit/b703f63697167b1cc3d68e6ef101c4fc8d4241a3
DIFF: https://github.com/llvm/llvm-project/commit/b703f63697167b1cc3d68e6ef101c4fc8d4241a3.diff

LOG: Add llvm.looptrap intrinsic.

The '``llvm.looptrap``' intrinsic is equivalent to
``llvm.cond.loop(true)``, but is also considered to be ``noreturn``,
which enables certain optimizations by allowing the optimizer to
assume that a branch leading to a call to this intrinsic was not
taken. A late optimization pass will convert this intrinsic to either
``llvm.cond.loop(true)`` or ``llvm.cond.loop(pred)``, where ``pred``
is a predicate for a conditional branch leading to the intrinsic call,
if possible.

Reviewers: fmayer, vitalybuka

Pull Request: https://github.com/llvm/llvm-project/pull/181299

Added: 
    llvm/test/Transforms/PreISelIntrinsicLowering/looptrap.ll

Modified: 
    llvm/docs/LangRef.rst
    llvm/include/llvm/IR/Intrinsics.td
    llvm/lib/CodeGen/PreISelIntrinsicLowering.cpp

Removed: 
    


################################################################################
diff  --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst
index 00a4a00c5bf95..50a2515f69189 100644
--- a/llvm/docs/LangRef.rst
+++ b/llvm/docs/LangRef.rst
@@ -32294,3 +32294,25 @@ itself. Specifically, the first byte of the instruction will be between
 0x70 and 0x7F, and the second byte will be 0xFE.
 
 There are currently no guarantees about instructions used by other backends.
+
+'``llvm.looptrap``' Intrinsic
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Syntax:
+"""""""
+
+::
+
+      declare void @llvm.looptrap() cold noreturn nounwind
+
+Overview:
+"""""""""
+
+The '``llvm.looptrap``' intrinsic is equivalent to
+``llvm.cond.loop(true)``, but is also considered to be ``noreturn``,
+which enables certain optimizations by allowing the optimizer to
+assume that a branch leading to a call to this intrinsic was not
+taken. A late optimization pass will convert this intrinsic to either
+``llvm.cond.loop(true)`` or ``llvm.cond.loop(pred)``, where ``pred``
+is a predicate for a conditional branch leading to the intrinsic call,
+if possible.

diff  --git a/llvm/include/llvm/IR/Intrinsics.td b/llvm/include/llvm/IR/Intrinsics.td
index 46e7b4b5c9491..a1c91486f7c3c 100644
--- a/llvm/include/llvm/IR/Intrinsics.td
+++ b/llvm/include/llvm/IR/Intrinsics.td
@@ -1911,6 +1911,8 @@ def int_debugtrap : Intrinsic<[]>,
 def int_ubsantrap : Intrinsic<[], [llvm_i8_ty],
                               [IntrNoReturn, IntrCold, ImmArg<ArgIndex<0>>,
                                IntrInaccessibleMemOnly, IntrWriteMem]>;
+def int_looptrap : Intrinsic<[], [], [IntrNoReturn, IntrCold,
+                                      IntrInaccessibleMemOnly, IntrWriteMem]>;
 
 // Return true if ubsan check is allowed.
 def int_allow_ubsan_check : DefaultAttrsIntrinsic<[llvm_i1_ty], [llvm_i8_ty],

diff  --git a/llvm/lib/CodeGen/PreISelIntrinsicLowering.cpp b/llvm/lib/CodeGen/PreISelIntrinsicLowering.cpp
index a8f94afe8c023..0544995f979f7 100644
--- a/llvm/lib/CodeGen/PreISelIntrinsicLowering.cpp
+++ b/llvm/lib/CodeGen/PreISelIntrinsicLowering.cpp
@@ -12,6 +12,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "llvm/CodeGen/PreISelIntrinsicLowering.h"
+#include "llvm/ADT/STLExtras.h"
 #include "llvm/Analysis/ObjCARCInstKind.h"
 #include "llvm/Analysis/ObjCARCUtil.h"
 #include "llvm/Analysis/TargetLibraryInfo.h"
@@ -631,6 +632,47 @@ static bool expandCondLoop(Function &Intr) {
   return true;
 }
 
+static bool expandLoopTrap(Function &Intr) {
+  for (User *U : make_early_inc_range(Intr.users())) {
+    auto *Call = cast<CallInst>(U);
+    if (!Call->getParent()->isEntryBlock() &&
+        std::all_of(Call->getParent()->begin(), BasicBlock::iterator(Call),
+                    [](Instruction &I) { return !I.mayHaveSideEffects(); })) {
+      for (auto *BB : predecessors(Call->getParent())) {
+        auto *BI = dyn_cast<BranchInst>(BB->getTerminator());
+        if (!BI || BI->isUnconditional())
+          continue;
+        IRBuilder<> B(BI);
+        Value *Cond;
+        // The looptrap can either be on the true branch or the false branch.
+        // We insert the cond loop before the branch, which uses the branch's
+        // original condition for going to the looptrap as its condition, and
+        // force the branch to take whichever path does not lead to the
+        // looptrap, as the original path to the looptrap is now unreachable
+        // thanks to the cond loop. The codegenprepare pass will clean up our
+        // "unconditional conditional branch" by combining the two basic blocks
+        // if possible, or replacing it with an unconditional branch.
+        if (BI->getSuccessor(0) == Call->getParent()) {
+          // The looptrap is on the true branch.
+          Cond = BI->getCondition();
+          BI->setCondition(ConstantInt::getFalse(BI->getContext()));
+        } else {
+          // The looptrap is on the false branch, which means that we need to
+          // invert the condition.
+          Cond = B.CreateNot(BI->getCondition());
+          BI->setCondition(ConstantInt::getTrue(BI->getContext()));
+        }
+        B.CreateIntrinsic(Intrinsic::cond_loop, Cond);
+      }
+    }
+    IRBuilder<> B(Call);
+    B.CreateIntrinsic(Intrinsic::cond_loop,
+                      ConstantInt::getTrue(Call->getContext()));
+    Call->eraseFromParent();
+  }
+  return true;
+}
+
 bool PreISelIntrinsicLowering::lowerIntrinsics(Module &M) const {
   // Map unique constants to globals.
   DenseMap<Constant *, GlobalVariable *> CMap;
@@ -780,6 +822,12 @@ bool PreISelIntrinsicLowering::lowerIntrinsics(Module &M) const {
       if (!TM->canLowerCondLoop())
         Changed |= expandCondLoop(F);
       break;
+    case Intrinsic::looptrap:
+      Changed |= expandLoopTrap(F);
+      if (!TM->canLowerCondLoop())
+        if (auto *CondLoop = M.getFunction("llvm.cond.loop"))
+          Changed |= expandCondLoop(*CondLoop);
+      break;
     }
   }
   return Changed;

diff  --git a/llvm/test/Transforms/PreISelIntrinsicLowering/looptrap.ll b/llvm/test/Transforms/PreISelIntrinsicLowering/looptrap.ll
new file mode 100644
index 0000000000000..06b96f76d50b8
--- /dev/null
+++ b/llvm/test/Transforms/PreISelIntrinsicLowering/looptrap.ll
@@ -0,0 +1,155 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
+; REQUIRES: x86-registered-target, mips-registered-target
+; RUN: opt -mtriple=x86_64 -passes=pre-isel-intrinsic-lowering -S < %s | FileCheck --check-prefix=X86 %s
+; RUN: opt -mtriple=mips64 -passes=pre-isel-intrinsic-lowering -S < %s | FileCheck --check-prefix=MIPS %s
+
+define void @f1(i64 %a, i64 %b, i64 %c, i64 %d) {
+; X86-LABEL: define void @f1(
+; X86-SAME: i64 [[A:%.*]], i64 [[B:%.*]], i64 [[C:%.*]], i64 [[D:%.*]]) {
+; X86-NEXT:    [[CMP:%.*]] = icmp ult i64 [[A]], [[B]]
+; X86-NEXT:    call void @llvm.cond.loop(i1 [[CMP]])
+; X86-NEXT:    br i1 false, label %[[TRAP:.*]], label %[[CONT1:.*]]
+; X86:       [[CONT1]]:
+; X86-NEXT:    [[CMP2:%.*]] = icmp ult i64 [[C]], [[D]]
+; X86-NEXT:    [[TMP1:%.*]] = xor i1 [[CMP2]], true
+; X86-NEXT:    call void @llvm.cond.loop(i1 [[TMP1]])
+; X86-NEXT:    br i1 true, label %[[CONT2:.*]], label %[[TRAP]]
+; X86:       [[CONT2]]:
+; X86-NEXT:    ret void
+; X86:       [[TRAP]]:
+; X86-NEXT:    call void @llvm.cond.loop(i1 true)
+; X86-NEXT:    unreachable
+;
+; MIPS-LABEL: define void @f1(
+; MIPS-SAME: i64 [[A:%.*]], i64 [[B:%.*]], i64 [[C:%.*]], i64 [[D:%.*]]) {
+; MIPS-NEXT:    [[CMP:%.*]] = icmp ult i64 [[A]], [[B]]
+; MIPS-NEXT:    br i1 [[CMP]], label %[[BB1:.*]], label %[[BB2:.*]]
+; MIPS:       [[BB1]]:
+; MIPS-NEXT:    br label %[[BB1]]
+; MIPS:       [[BB2]]:
+; MIPS-NEXT:    br i1 false, label %[[TRAP:.*]], label %[[CONT1:.*]]
+; MIPS:       [[CONT1]]:
+; MIPS-NEXT:    [[CMP2:%.*]] = icmp ult i64 [[C]], [[D]]
+; MIPS-NEXT:    [[TMP3:%.*]] = xor i1 [[CMP2]], true
+; MIPS-NEXT:    br i1 [[TMP3]], label %[[BB4:.*]], label %[[BB5:.*]]
+; MIPS:       [[BB4]]:
+; MIPS-NEXT:    br label %[[BB4]]
+; MIPS:       [[BB5]]:
+; MIPS-NEXT:    br i1 true, label %[[CONT2:.*]], label %[[TRAP]]
+; MIPS:       [[CONT2]]:
+; MIPS-NEXT:    ret void
+; MIPS:       [[TRAP]]:
+; MIPS-NEXT:    br i1 true, label %[[BB6:.*]], label %[[BB7:.*]]
+; MIPS:       [[BB6]]:
+; MIPS-NEXT:    br label %[[BB6]]
+; MIPS:       [[BB7]]:
+; MIPS-NEXT:    unreachable
+;
+  %cmp = icmp ult i64 %a, %b
+  br i1 %cmp, label %trap, label %cont1
+
+cont1:
+  %cmp2 = icmp ult i64 %c, %d
+  br i1 %cmp2, label %cont2, label %trap
+
+cont2:
+  ret void
+
+trap:
+  call void @llvm.looptrap()
+  unreachable
+}
+
+define void @f2() {
+; X86-LABEL: define void @f2() {
+; X86-NEXT:    call void @llvm.cond.loop(i1 true)
+; X86-NEXT:    ret void
+;
+; MIPS-LABEL: define void @f2() {
+; MIPS-NEXT:    br i1 true, label %[[BB1:.*]], label %[[BB2:.*]]
+; MIPS:       [[BB1]]:
+; MIPS-NEXT:    br label %[[BB1]]
+; MIPS:       [[BB2]]:
+; MIPS-NEXT:    ret void
+;
+  call void @llvm.looptrap()
+  ret void
+}
+
+define void @f3() personality ptr @foo {
+; X86-LABEL: define void @f3() personality ptr @foo {
+; X86-NEXT:    invoke void @foo()
+; X86-NEXT:            to label %[[CONT:.*]] unwind label %[[TRAP:.*]]
+; X86:       [[CONT]]:
+; X86-NEXT:    ret void
+; X86:       [[TRAP]]:
+; X86-NEXT:    [[PAD:%.*]] = landingpad { ptr, i32 }
+; X86-NEXT:            cleanup
+; X86-NEXT:    call void @llvm.cond.loop(i1 true)
+; X86-NEXT:    unreachable
+;
+; MIPS-LABEL: define void @f3() personality ptr @foo {
+; MIPS-NEXT:    invoke void @foo()
+; MIPS-NEXT:            to label %[[CONT:.*]] unwind label %[[TRAP:.*]]
+; MIPS:       [[CONT]]:
+; MIPS-NEXT:    ret void
+; MIPS:       [[TRAP]]:
+; MIPS-NEXT:    [[PAD:%.*]] = landingpad { ptr, i32 }
+; MIPS-NEXT:            cleanup
+; MIPS-NEXT:    br i1 true, label %[[BB1:.*]], label %[[BB2:.*]]
+; MIPS:       [[BB1]]:
+; MIPS-NEXT:    br label %[[BB1]]
+; MIPS:       [[BB2]]:
+; MIPS-NEXT:    unreachable
+;
+  invoke void @foo() to label %cont unwind label %trap
+
+cont:
+  ret void
+
+trap:
+  %pad = landingpad { ptr, i32 } cleanup
+  call void @llvm.looptrap()
+  unreachable
+}
+
+
+define void @f4(i64 %a, i64 %b, ptr %p) {
+; X86-LABEL: define void @f4(
+; X86-SAME: i64 [[A:%.*]], i64 [[B:%.*]], ptr [[P:%.*]]) {
+; X86-NEXT:    [[CMP:%.*]] = icmp ult i64 [[A]], [[B]]
+; X86-NEXT:    br i1 [[CMP]], label %[[TRAP:.*]], label %[[CONT:.*]]
+; X86:       [[CONT]]:
+; X86-NEXT:    ret void
+; X86:       [[TRAP]]:
+; X86-NEXT:    store volatile i32 1, ptr [[P]], align 4
+; X86-NEXT:    call void @llvm.cond.loop(i1 true)
+; X86-NEXT:    unreachable
+;
+; MIPS-LABEL: define void @f4(
+; MIPS-SAME: i64 [[A:%.*]], i64 [[B:%.*]], ptr [[P:%.*]]) {
+; MIPS-NEXT:    [[CMP:%.*]] = icmp ult i64 [[A]], [[B]]
+; MIPS-NEXT:    br i1 [[CMP]], label %[[TRAP:.*]], label %[[CONT:.*]]
+; MIPS:       [[CONT]]:
+; MIPS-NEXT:    ret void
+; MIPS:       [[TRAP]]:
+; MIPS-NEXT:    store volatile i32 1, ptr [[P]], align 4
+; MIPS-NEXT:    br i1 true, label %[[BB1:.*]], label %[[BB2:.*]]
+; MIPS:       [[BB1]]:
+; MIPS-NEXT:    br label %[[BB1]]
+; MIPS:       [[BB2]]:
+; MIPS-NEXT:    unreachable
+;
+  %cmp = icmp ult i64 %a, %b
+  br i1 %cmp, label %trap, label %cont
+
+cont:
+  ret void
+
+trap:
+  store volatile i32 1, ptr %p
+  call void @llvm.looptrap()
+  unreachable
+}
+
+declare void @foo()


        


More information about the llvm-commits mailing list