[clang] [llvm] LowerTypeTests: Remove the optimization for llvm.cond.loop. (PR #181301)
Peter Collingbourne via cfe-commits
cfe-commits at lists.llvm.org
Fri Feb 13 14:16:21 PST 2026
https://github.com/pcc updated https://github.com/llvm/llvm-project/pull/181301
>From b9c689dbc3163a6b54547570c8d92801eabc857b Mon Sep 17 00:00:00 2001
From: Peter Collingbourne <peter at pcc.me.uk>
Date: Thu, 12 Feb 2026 19:38:56 -0800
Subject: [PATCH 1/3] =?UTF-8?q?[=F0=9D=98=80=F0=9D=97=BD=F0=9D=97=BF]=20ch?=
=?UTF-8?q?anges=20to=20main=20this=20commit=20is=20based=20on?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Created using spr 1.3.6-beta.1
[skip ci]
---
clang/lib/CodeGen/CGExpr.cpp | 17 +-
clang/test/CodeGenCXX/sanitize-trap-loop.cpp | 10 +-
llvm/docs/LangRef.rst | 22 +++
llvm/include/llvm/IR/Intrinsics.td | 2 +
llvm/lib/CodeGen/PreISelIntrinsicLowering.cpp | 37 +++++
.../PreISelIntrinsicLowering/looptrap.ll | 155 ++++++++++++++++++
6 files changed, 230 insertions(+), 13 deletions(-)
create mode 100644 llvm/test/Transforms/PreISelIntrinsicLowering/looptrap.ll
diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp
index 8de1c53b1b213..a0a1a5675654c 100644
--- a/clang/lib/CodeGen/CGExpr.cpp
+++ b/clang/lib/CodeGen/CGExpr.cpp
@@ -4438,12 +4438,6 @@ void CodeGenFunction::EmitUnreachable(SourceLocation Loc) {
void CodeGenFunction::EmitTrapCheck(llvm::Value *Checked,
SanitizerHandler CheckHandlerID,
bool NoMerge, const TrapReason *TR) {
- if (CGM.getCodeGenOpts().SanitizeTrapLoop) {
- Builder.CreateCall(CGM.getIntrinsic(llvm::Intrinsic::cond_loop),
- Builder.CreateNot(Checked));
- return;
- }
-
llvm::BasicBlock *Cont = createBasicBlock("cont");
// If we're optimizing, collapse all calls to trap down to just one per
@@ -4495,9 +4489,14 @@ void CodeGenFunction::EmitTrapCheck(llvm::Value *Checked,
ApplyDebugLocation applyTrapDI(*this, TrapLocation);
- llvm::CallInst *TrapCall =
- Builder.CreateCall(CGM.getIntrinsic(llvm::Intrinsic::ubsantrap),
- llvm::ConstantInt::get(CGM.Int8Ty, CheckHandlerID));
+ llvm::CallInst *TrapCall;
+ if (CGM.getCodeGenOpts().SanitizeTrapLoop)
+ TrapCall =
+ Builder.CreateCall(CGM.getIntrinsic(llvm::Intrinsic::looptrap));
+ else
+ TrapCall = Builder.CreateCall(
+ CGM.getIntrinsic(llvm::Intrinsic::ubsantrap),
+ llvm::ConstantInt::get(CGM.Int8Ty, CheckHandlerID));
if (!CGM.getCodeGenOpts().TrapFuncName.empty()) {
auto A = llvm::Attribute::get(getLLVMContext(), "trap-func-name",
diff --git a/clang/test/CodeGenCXX/sanitize-trap-loop.cpp b/clang/test/CodeGenCXX/sanitize-trap-loop.cpp
index ee083c52f5c30..3c7af1af907c0 100644
--- a/clang/test/CodeGenCXX/sanitize-trap-loop.cpp
+++ b/clang/test/CodeGenCXX/sanitize-trap-loop.cpp
@@ -6,15 +6,17 @@ struct A {
void vcall(A *a) {
// CHECK: [[TEST:%.*]] = call i1 @llvm.type.test
- // CHECK-NEXT: [[NOT:%.*]] = xor i1 [[TEST]], true
- // CHECK-NEXT: call void @llvm.cond.loop(i1 [[NOT]])
+ // CHECK-NEXT: br i1 [[TEST]], label %cont, label %trap
+ // CHECK: trap:
+ // CHECK-NEXT: call void @llvm.looptrap()
a->f();
}
int overflow(int a, int b) {
// CHECK: [[OVERFLOW:%.*]] = extractvalue { i32, i1 } %2, 1, !nosanitize
// CHECK-NEXT: [[NOTOVERFLOW:%.*]] = xor i1 [[OVERFLOW]], true, !nosanitize
- // CHECK-NEXT: [[NOTNOTOVERFLOW:%.*]] = xor i1 [[NOTOVERFLOW]], true, !nosanitize
- // CHECK-NEXT: call void @llvm.cond.loop(i1 [[NOTNOTOVERFLOW]])
+ // CHECK-NEXT: br i1 [[NOTOVERFLOW]], label %cont, label %trap
+ // CHECK: trap:
+ // CHECK-NEXT: call void @llvm.looptrap()
return a + b;
}
diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst
index 00a4a00c5bf95..801da46d06658 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)``. Its main raison d'ĂȘtre is that it 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..2dbc20c933a1b 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,36 @@ 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;
+ if (BI->getSuccessor(0) == Call->getParent()) {
+ Cond = BI->getCondition();
+ BI->setCondition(ConstantInt::getFalse(BI->getContext()));
+ } else {
+ 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 +811,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()
>From cbae61b82b36260124b3bd91eb37e7686b836bac Mon Sep 17 00:00:00 2001
From: Peter Collingbourne <peter at pcc.me.uk>
Date: Fri, 13 Feb 2026 14:04:11 -0800
Subject: [PATCH 2/3] =?UTF-8?q?[=F0=9D=98=80=F0=9D=97=BD=F0=9D=97=BF]=20ch?=
=?UTF-8?q?anges=20introduced=20through=20rebase?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Created using spr 1.3.6-beta.1
[skip ci]
---
llvm/docs/LangRef.rst | 14 +++++++-------
llvm/lib/CodeGen/PreISelIntrinsicLowering.cpp | 11 +++++++++++
2 files changed, 18 insertions(+), 7 deletions(-)
diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst
index 801da46d06658..50a2515f69189 100644
--- a/llvm/docs/LangRef.rst
+++ b/llvm/docs/LangRef.rst
@@ -32309,10 +32309,10 @@ Overview:
"""""""""
The '``llvm.looptrap``' intrinsic is equivalent to
-``llvm.cond.loop(true)``. Its main raison d'ĂȘtre is that it 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.
+``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/lib/CodeGen/PreISelIntrinsicLowering.cpp b/llvm/lib/CodeGen/PreISelIntrinsicLowering.cpp
index 2dbc20c933a1b..649f422b822c6 100644
--- a/llvm/lib/CodeGen/PreISelIntrinsicLowering.cpp
+++ b/llvm/lib/CodeGen/PreISelIntrinsicLowering.cpp
@@ -644,10 +644,21 @@ static bool expandLoopTrap(Function &Intr) {
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()));
}
>From 1b93b8a5488de67ca6659f610d0614b89bbbd34b Mon Sep 17 00:00:00 2001
From: Peter Collingbourne <peter at pcc.me.uk>
Date: Fri, 13 Feb 2026 14:10:51 -0800
Subject: [PATCH 3/3] =?UTF-8?q?[=F0=9D=98=80=F0=9D=97=BD=F0=9D=97=BF]=20ch?=
=?UTF-8?q?anges=20introduced=20through=20rebase?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Created using spr 1.3.6-beta.1
[skip ci]
---
llvm/lib/CodeGen/PreISelIntrinsicLowering.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/llvm/lib/CodeGen/PreISelIntrinsicLowering.cpp b/llvm/lib/CodeGen/PreISelIntrinsicLowering.cpp
index 649f422b822c6..0544995f979f7 100644
--- a/llvm/lib/CodeGen/PreISelIntrinsicLowering.cpp
+++ b/llvm/lib/CodeGen/PreISelIntrinsicLowering.cpp
@@ -653,7 +653,7 @@ static bool expandLoopTrap(Function &Intr) {
// "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.
+ // The looptrap is on the true branch.
Cond = BI->getCondition();
BI->setCondition(ConstantInt::getFalse(BI->getContext()));
} else {
More information about the cfe-commits
mailing list