From b932cfc6eb2a05468d1232a1c2ecdeeec259f414 Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Wed, 24 Sep 2025 09:59:58 +0200 Subject: [PATCH 1/3] [IR] Forbid mixing condition and bundle assumes Assumes either have a boolean condition, or a number of attribute based operand bundles. Currently, we also allow mixing both forms, though we don't make use of this in practice. This adds additional complexity for code dealing with assumes. Forbid mixing both forms, by requiring that assumes with operand bundles have an i1 true condition. --- llvm/docs/LangRef.rst | 4 ++- llvm/lib/IR/Verifier.cpp | 5 ++++ .../AlignmentFromAssumptions/domtree-crash.ll | 4 +-- llvm/test/Transforms/InstCombine/assume.ll | 28 ++----------------- llvm/test/Verifier/assume-bundles.ll | 4 ++- 5 files changed, 15 insertions(+), 30 deletions(-) diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst index b32a27f9555fd..41c2c1a9a0416 100644 --- a/llvm/docs/LangRef.rst +++ b/llvm/docs/LangRef.rst @@ -3013,6 +3013,8 @@ assumptions, such as that a :ref:`parameter attribute ` or a location. Operand bundles enable assumptions that are either hard or impossible to represent as a boolean argument of an :ref:`llvm.assume `. +Assumes with operand bundles must have ``i1 true`` as the condition operand. + An assume operand bundle has the form: :: @@ -3045,7 +3047,7 @@ allows the optimizer to assume that at location of call to .. code-block:: llvm - call void @llvm.assume(i1 %cond) ["cold"(), "nonnull"(ptr %val)] + call void @llvm.assume(i1 true) ["cold"(), "nonnull"(ptr %val)] allows the optimizer to assume that the :ref:`llvm.assume ` call location is cold and that ``%val`` may not be null. diff --git a/llvm/lib/IR/Verifier.cpp b/llvm/lib/IR/Verifier.cpp index 9bde965d660a4..0c6175b1945cc 100644 --- a/llvm/lib/IR/Verifier.cpp +++ b/llvm/lib/IR/Verifier.cpp @@ -5675,6 +5675,11 @@ void Verifier::visitIntrinsicCall(Intrinsic::ID ID, CallBase &Call) { default: break; case Intrinsic::assume: { + if (Call.hasOperandBundles()) { + auto *Cond = dyn_cast(Call.getArgOperand(0)); + Check(Cond && Cond->isOne(), + "assume with operand bundles must have i1 true condition", Call); + } for (auto &Elem : Call.bundle_op_infos()) { unsigned ArgCount = Elem.End - Elem.Begin; // Separate storage assumptions are special insofar as they're the only diff --git a/llvm/test/Transforms/AlignmentFromAssumptions/domtree-crash.ll b/llvm/test/Transforms/AlignmentFromAssumptions/domtree-crash.ll index c7fc1dc699671..f9b9dd13b0d0c 100644 --- a/llvm/test/Transforms/AlignmentFromAssumptions/domtree-crash.ll +++ b/llvm/test/Transforms/AlignmentFromAssumptions/domtree-crash.ll @@ -9,10 +9,10 @@ define void @fn1() { ; CHECK-LABEL: define void @fn1() { -; CHECK-NEXT: call void @llvm.assume(i1 false) [ "align"(ptr @global, i64 1) ] +; CHECK-NEXT: call void @llvm.assume(i1 true) [ "align"(ptr @global, i64 1) ] ; CHECK-NEXT: ret void ; - call void @llvm.assume(i1 false) [ "align"(ptr @global, i64 1) ] + call void @llvm.assume(i1 true) [ "align"(ptr @global, i64 1) ] ret void } diff --git a/llvm/test/Transforms/InstCombine/assume.ll b/llvm/test/Transforms/InstCombine/assume.ll index e87a61a57ea47..c6c1e597654c5 100644 --- a/llvm/test/Transforms/InstCombine/assume.ll +++ b/llvm/test/Transforms/InstCombine/assume.ll @@ -495,30 +495,6 @@ not_taken: ret i1 %rval.2 } -define i1 @nonnull3B(ptr %a, i1 %control) { -; CHECK-LABEL: @nonnull3B( -; CHECK-NEXT: entry: -; CHECK-NEXT: br i1 [[CONTROL:%.*]], label [[TAKEN:%.*]], label [[NOT_TAKEN:%.*]] -; CHECK: taken: -; CHECK-NEXT: [[LOAD:%.*]] = load ptr, ptr [[A:%.*]], align 8 -; CHECK-NEXT: [[CMP:%.*]] = icmp ne ptr [[LOAD]], null -; CHECK-NEXT: call void @llvm.assume(i1 [[CMP]]) [ "nonnull"(ptr [[LOAD]]) ] -; CHECK-NEXT: ret i1 [[CMP]] -; CHECK: not_taken: -; CHECK-NEXT: ret i1 false -; -entry: - %load = load ptr, ptr %a - %cmp = icmp ne ptr %load, null - br i1 %control, label %taken, label %not_taken -taken: - call void @llvm.assume(i1 %cmp) ["nonnull"(ptr %load)] - ret i1 %cmp -not_taken: - call void @llvm.assume(i1 %cmp) ["nonnull"(ptr %load)] - ret i1 %control -} - declare i1 @tmp1(i1) define i1 @nonnull3C(ptr %a, i1 %control) { @@ -544,7 +520,7 @@ taken: br label %exit exit: ; FIXME: this shouldn't be dropped because it is still dominated by the new position of %load - call void @llvm.assume(i1 %cmp) ["nonnull"(ptr %load)] + call void @llvm.assume(i1 %cmp) ret i1 %cmp2 not_taken: call void @llvm.assume(i1 %cmp) @@ -575,7 +551,7 @@ taken: exit: ret i1 %cmp2 not_taken: - call void @llvm.assume(i1 %cmp) ["nonnull"(ptr %load)] + call void @llvm.assume(i1 %cmp) ret i1 %control } diff --git a/llvm/test/Verifier/assume-bundles.ll b/llvm/test/Verifier/assume-bundles.ll index d8037b965edb5..728b118c99fb6 100644 --- a/llvm/test/Verifier/assume-bundles.ll +++ b/llvm/test/Verifier/assume-bundles.ll @@ -3,7 +3,7 @@ declare void @llvm.assume(i1) -define void @func(ptr %P, i32 %P1, ptr %P2, ptr %P3) { +define void @func(ptr %P, i32 %P1, ptr %P2, ptr %P3, i1 %cond) { ; CHECK: tags must be valid attribute names ; CHECK: "adazdazd" call void @llvm.assume(i1 true) ["adazdazd"()] @@ -32,5 +32,7 @@ define void @func(ptr %P, i32 %P1, ptr %P2, ptr %P3) { call void @llvm.assume(i1 true) ["separate_storage"(ptr %P, i32 123)] ; CHECK: dereferenceable assumptions should have 2 arguments call void @llvm.assume(i1 true) ["align"(ptr %P, i32 4), "dereferenceable"(ptr %P)] +; CHECK: assume with operand bundles must have i1 true condition + call void @llvm.assume(i1 %cond) ["nonnull"(ptr %P)] ret void } From ef963eb6c6ffbb67d8e8128a8a116d7fa2c3d6f0 Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Wed, 24 Sep 2025 10:38:47 +0200 Subject: [PATCH 2/3] Update MLIR tests --- mlir/test/Target/LLVMIR/Import/intrinsic.ll | 6 +++--- mlir/test/Target/LLVMIR/llvmir-intrinsics.mlir | 7 ++++--- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/mlir/test/Target/LLVMIR/Import/intrinsic.ll b/mlir/test/Target/LLVMIR/Import/intrinsic.ll index e5f92e4337154..d2bb80982bb3d 100644 --- a/mlir/test/Target/LLVMIR/Import/intrinsic.ll +++ b/mlir/test/Target/LLVMIR/Import/intrinsic.ll @@ -733,12 +733,12 @@ define void @assume(i1 %true) { } ; CHECK-LABEL: @assume_with_opbundles -; CHECK-SAME: %[[TRUE:[a-zA-Z0-9]+]] ; CHECK-SAME: %[[PTR:[a-zA-Z0-9]+]] -define void @assume_with_opbundles(i1 %true, ptr %p) { +define void @assume_with_opbundles(ptr %p) { + ; CHECK: %[[TRUE:.+]] = llvm.mlir.constant(true) : i1 ; CHECK: %[[ALIGN:.+]] = llvm.mlir.constant(8 : i32) : i32 ; CHECK: llvm.intr.assume %[[TRUE]] ["align"(%[[PTR]], %[[ALIGN]] : !llvm.ptr, i32)] : i1 - call void @llvm.assume(i1 %true) ["align"(ptr %p, i32 8)] + call void @llvm.assume(i1 true) ["align"(ptr %p, i32 8)] ret void } diff --git a/mlir/test/Target/LLVMIR/llvmir-intrinsics.mlir b/mlir/test/Target/LLVMIR/llvmir-intrinsics.mlir index 01aa740452b62..cf3e129879d09 100644 --- a/mlir/test/Target/LLVMIR/llvmir-intrinsics.mlir +++ b/mlir/test/Target/LLVMIR/llvmir-intrinsics.mlir @@ -460,10 +460,11 @@ llvm.func @assume_without_opbundles(%cond: i1) { } // CHECK-LABEL: @assume_with_opbundles -llvm.func @assume_with_opbundles(%cond: i1, %p: !llvm.ptr) { +llvm.func @assume_with_opbundles(%p: !llvm.ptr) { + %true = llvm.mlir.constant(true) : i1 %0 = llvm.mlir.constant(8 : i32) : i32 - // CHECK: call void @llvm.assume(i1 %{{.+}}) [ "align"(ptr %{{.+}}, i32 8) ] - llvm.intr.assume %cond ["align"(%p, %0 : !llvm.ptr, i32)] : i1 + // CHECK: call void @llvm.assume(i1 true) [ "align"(ptr %{{.+}}, i32 8) ] + llvm.intr.assume %true ["align"(%p, %0 : !llvm.ptr, i32)] : i1 llvm.return } From de14f87e51762b6a212f706f2847ff97df2a6b74 Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Wed, 24 Sep 2025 11:18:42 +0200 Subject: [PATCH 3/3] Restore test --- llvm/test/Transforms/InstCombine/assume.ll | 24 ++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/llvm/test/Transforms/InstCombine/assume.ll b/llvm/test/Transforms/InstCombine/assume.ll index c6c1e597654c5..7b0b871513513 100644 --- a/llvm/test/Transforms/InstCombine/assume.ll +++ b/llvm/test/Transforms/InstCombine/assume.ll @@ -495,6 +495,30 @@ not_taken: ret i1 %rval.2 } +define i1 @nonnull3B(ptr %a, i1 %control) { +; CHECK-LABEL: @nonnull3B( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[LOAD:%.*]] = load ptr, ptr [[A:%.*]], align 8 +; CHECK-NEXT: br i1 [[CONTROL:%.*]], label [[TAKEN:%.*]], label [[NOT_TAKEN:%.*]] +; CHECK: taken: +; CHECK-NEXT: call void @llvm.assume(i1 true) [ "nonnull"(ptr [[LOAD]]) ] +; CHECK-NEXT: ret i1 true +; CHECK: not_taken: +; CHECK-NEXT: call void @llvm.assume(i1 true) [ "nonnull"(ptr [[LOAD]]) ] +; CHECK-NEXT: ret i1 false +; +entry: + %load = load ptr, ptr %a + %cmp = icmp ne ptr %load, null + br i1 %control, label %taken, label %not_taken +taken: + call void @llvm.assume(i1 true) ["nonnull"(ptr %load)] + ret i1 %cmp +not_taken: + call void @llvm.assume(i1 true) ["nonnull"(ptr %load)] + ret i1 %control +} + declare i1 @tmp1(i1) define i1 @nonnull3C(ptr %a, i1 %control) {