From f4b20e44467dbe4477ea78568cec3aff83781a9e Mon Sep 17 00:00:00 2001 From: Florian Hahn Date: Fri, 29 Aug 2025 15:07:44 +0100 Subject: [PATCH 1/6] [InstCombine] Add tests with foldable and redundant align assumptions. --- .../Transforms/InstCombine/assume-align.ll | 55 ++++++++++++++++++- 1 file changed, 54 insertions(+), 1 deletion(-) diff --git a/llvm/test/Transforms/InstCombine/assume-align.ll b/llvm/test/Transforms/InstCombine/assume-align.ll index f0e0257433086..64537ea95ba05 100644 --- a/llvm/test/Transforms/InstCombine/assume-align.ll +++ b/llvm/test/Transforms/InstCombine/assume-align.ll @@ -1,5 +1,5 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --check-globals smart -; RUN: opt -S -passes=instcombine,simplifycfg < %s 2>&1 | FileCheck %s +; RUN: opt -S -passes='instcombine,simplifycfg' < %s 2>&1 | FileCheck %s declare void @llvm.assume(i1 noundef) @@ -135,6 +135,17 @@ define ptr @fold_assume_align_pow2_of_loaded_pointer_into_align_metadata(ptr %p) ret ptr %p2 } +define ptr @fold_assume_align_i32_pow2_of_loaded_pointer_into_align_metadata(ptr %p) { +; CHECK-LABEL: @fold_assume_align_i32_pow2_of_loaded_pointer_into_align_metadata( +; CHECK-NEXT: [[P2:%.*]] = load ptr, ptr [[P:%.*]], align 8 +; CHECK-NEXT: call void @llvm.assume(i1 true) [ "align"(ptr [[P2]], i32 8) ] +; CHECK-NEXT: ret ptr [[P2]] +; + %p2 = load ptr, ptr %p + call void @llvm.assume(i1 true) [ "align"(ptr %p2, i32 8) ] + ret ptr %p2 +} + define ptr @dont_fold_assume_align_pow2_of_loaded_pointer_into_align_metadata_due_to_call(ptr %p) { ; CHECK-LABEL: @dont_fold_assume_align_pow2_of_loaded_pointer_into_align_metadata_due_to_call( ; CHECK-NEXT: [[P2:%.*]] = load ptr, ptr [[P:%.*]], align 8 @@ -249,7 +260,49 @@ define ptr @redundant_assume_align_8_via_asume(ptr %p) { ret ptr %p } +define void @redundant_arg_passed_to_intrinsic(ptr %dst, ptr %src) { +; CHECK-LABEL: @redundant_arg_passed_to_intrinsic( +; CHECK-NEXT: call void @llvm.assume(i1 true) [ "align"(ptr [[DST:%.*]], i32 8) ] +; CHECK-NEXT: call void @bar() +; CHECK-NEXT: call void @llvm.assume(i1 true) [ "align"(ptr [[SRC:%.*]], i32 8) ] +; CHECK-NEXT: call void @llvm.memmove.p0.p0.i64(ptr noundef nonnull align 8 dereferenceable(16) [[DST]], ptr noundef nonnull align 8 dereferenceable(16) [[SRC]], i64 16, i1 false) +; CHECK-NEXT: ret void +; + call void @llvm.assume(i1 true) [ "align"(ptr %dst, i32 8) ] + call void @bar() + call void @llvm.assume(i1 true) [ "align"(ptr %src, i32 8) ] + call void @llvm.memmove.p0.p0.i64(ptr align 8 %dst, ptr %src, i64 16, i1 false) + ret void +} + +define void @test_store(ptr %ptr) { +; CHECK-LABEL: @test_store( +; CHECK-NEXT: entry: +; CHECK-NEXT: call void @llvm.assume(i1 true) [ "align"(ptr [[PTR:%.*]], i64 2) ] +; CHECK-NEXT: store i16 0, ptr [[PTR]], align 1 +; CHECK-NEXT: ret void +; +entry: + call void @llvm.assume(i1 true) [ "align"(ptr %ptr, i64 2) ] + store i16 0, ptr %ptr, align 1 + ret void +} + declare void @foo(ptr) +declare void @bar() + +; !align must have a constant integer alignment. +define ptr @dont_fold_assume_align_not_constant_of_loaded_pointer_into_align_metadata(ptr %p, i64 %align) { +; CHECK-LABEL: @dont_fold_assume_align_not_constant_of_loaded_pointer_into_align_metadata( +; CHECK-NEXT: [[P2:%.*]] = load ptr, ptr [[P:%.*]], align 8 +; CHECK-NEXT: call void @llvm.assume(i1 true) [ "align"(ptr [[P2]], i64 [[ALIGN:%.*]]) ] +; CHECK-NEXT: ret ptr [[P2]] +; + %p2 = load ptr, ptr %p + call void @llvm.assume(i1 true) [ "align"(ptr %p2, i64 %align) ] + ret ptr %p2 +} + ;. ; CHECK: [[META0]] = !{i64 8} ;. From b507b93ec3560a8a95f2524b3f57a9d078bc6feb Mon Sep 17 00:00:00 2001 From: Florian Hahn Date: Fri, 29 Aug 2025 15:05:15 +0100 Subject: [PATCH 2/6] [InstCombine] Remove redundant alignment assumptions. --- .../InstCombine/InstCombineCalls.cpp | 33 ++++++++++++++++--- .../Transforms/InstCombine/assume-align.ll | 3 -- 2 files changed, 29 insertions(+), 7 deletions(-) diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp index 33b66aeaffe60..7a844defcddb0 100644 --- a/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp +++ b/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp @@ -3385,12 +3385,13 @@ Instruction *InstCombinerImpl::visitCallInst(CallInst &CI) { // TODO: apply range metadata for range check patterns? } - // Separate storage assumptions apply to the underlying allocations, not any - // particular pointer within them. When evaluating the hints for AA purposes - // we getUnderlyingObject them; by precomputing the answers here we can - // avoid having to do so repeatedly there. for (unsigned Idx = 0; Idx < II->getNumOperandBundles(); Idx++) { OperandBundleUse OBU = II->getOperandBundleAt(Idx); + + // Separate storage assumptions apply to the underlying allocations, not + // any particular pointer within them. When evaluating the hints for AA + // purposes we getUnderlyingObject them; by precomputing the answers here + // we can avoid having to do so repeatedly there. if (OBU.getTagName() == "separate_storage") { assert(OBU.Inputs.size() == 2); auto MaybeSimplifyHint = [&](const Use &U) { @@ -3404,6 +3405,30 @@ Instruction *InstCombinerImpl::visitCallInst(CallInst &CI) { MaybeSimplifyHint(OBU.Inputs[0]); MaybeSimplifyHint(OBU.Inputs[1]); } + + // Try to fold alignment assumption into a load's !align metadata, if the + // assumption is valid in the load's context and remove redundant ones. + if (OBU.getTagName() == "align" && OBU.Inputs.size() == 2) { + RetainedKnowledge RK = getKnowledgeFromBundle( + *cast(II), II->bundle_op_info_begin()[Idx]); + if (!RK || RK.AttrKind != Attribute::Alignment || + !isPowerOf2_64(RK.ArgValue)) + continue; + + // Don't try to remove align assumptions for pointers derived from + // arguments. We might lose information if the function gets inline and + // the align argument attribute disappears. + Value *UO = getUnderlyingObject(RK.WasOn); + if (!UO || isa(UO)) + continue; + + KnownBits Known = computeKnownBits(RK.WasOn, nullptr); + unsigned TZ = std::min(Known.countMinTrailingZeros(), 63u); + if ((1ULL << TZ) < RK.ArgValue) + continue; + auto *New = CallBase::removeOperandBundle(II, OBU.getTagID()); + return New; + } } // Convert nonnull assume like: diff --git a/llvm/test/Transforms/InstCombine/assume-align.ll b/llvm/test/Transforms/InstCombine/assume-align.ll index 64537ea95ba05..80441c428cbff 100644 --- a/llvm/test/Transforms/InstCombine/assume-align.ll +++ b/llvm/test/Transforms/InstCombine/assume-align.ll @@ -186,7 +186,6 @@ define ptr @dont_fold_assume_align_zero_of_loaded_pointer_into_align_metadata(pt define ptr @redundant_assume_align_1(ptr %p) { ; CHECK-LABEL: @redundant_assume_align_1( ; CHECK-NEXT: [[P2:%.*]] = load ptr, ptr [[P:%.*]], align 8 -; CHECK-NEXT: call void @llvm.assume(i1 true) [ "align"(ptr [[P2]], i32 1) ] ; CHECK-NEXT: call void @foo(ptr [[P2]]) ; CHECK-NEXT: ret ptr [[P2]] ; @@ -200,7 +199,6 @@ define ptr @redundant_assume_align_1(ptr %p) { define ptr @redundant_assume_align_8_via_align_metadata(ptr %p) { ; CHECK-LABEL: @redundant_assume_align_8_via_align_metadata( ; CHECK-NEXT: [[P2:%.*]] = load ptr, ptr [[P:%.*]], align 8, !align [[META0:![0-9]+]] -; CHECK-NEXT: call void @llvm.assume(i1 true) [ "align"(ptr [[P2]], i32 8) ] ; CHECK-NEXT: call void @foo(ptr [[P2]]) ; CHECK-NEXT: ret ptr [[P2]] ; @@ -295,7 +293,6 @@ declare void @bar() define ptr @dont_fold_assume_align_not_constant_of_loaded_pointer_into_align_metadata(ptr %p, i64 %align) { ; CHECK-LABEL: @dont_fold_assume_align_not_constant_of_loaded_pointer_into_align_metadata( ; CHECK-NEXT: [[P2:%.*]] = load ptr, ptr [[P:%.*]], align 8 -; CHECK-NEXT: call void @llvm.assume(i1 true) [ "align"(ptr [[P2]], i64 [[ALIGN:%.*]]) ] ; CHECK-NEXT: ret ptr [[P2]] ; %p2 = load ptr, ptr %p From 520b119333fc4fd82f031d615c21138b3c25533e Mon Sep 17 00:00:00 2001 From: Florian Hahn Date: Mon, 1 Sep 2025 17:36:20 +0100 Subject: [PATCH 3/6] !fixup address comments, update tests --- .../InstCombine/InstCombineCalls.cpp | 11 ++++---- llvm/test/Analysis/BasicAA/featuretest.ll | 26 ++++++------------- 2 files changed, 14 insertions(+), 23 deletions(-) diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp index 7a844defcddb0..a8c685755972e 100644 --- a/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp +++ b/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp @@ -3406,8 +3406,7 @@ Instruction *InstCombinerImpl::visitCallInst(CallInst &CI) { MaybeSimplifyHint(OBU.Inputs[1]); } - // Try to fold alignment assumption into a load's !align metadata, if the - // assumption is valid in the load's context and remove redundant ones. + // Try to remove redundant alignment assumptions. if (OBU.getTagName() == "align" && OBU.Inputs.size() == 2) { RetainedKnowledge RK = getKnowledgeFromBundle( *cast(II), II->bundle_op_info_begin()[Idx]); @@ -3422,12 +3421,14 @@ Instruction *InstCombinerImpl::visitCallInst(CallInst &CI) { if (!UO || isa(UO)) continue; - KnownBits Known = computeKnownBits(RK.WasOn, nullptr); + // Compute known bits for the pointer, passing nullptr as context to + // avoid computeKnownBits using the assumption we are about to remove + // for reasoning. + KnownBits Known = computeKnownBits(RK.WasOn, /*CtxI=*/nullptr); unsigned TZ = std::min(Known.countMinTrailingZeros(), 63u); if ((1ULL << TZ) < RK.ArgValue) continue; - auto *New = CallBase::removeOperandBundle(II, OBU.getTagID()); - return New; + return CallBase::removeOperandBundle(II, OBU.getTagID()); } } diff --git a/llvm/test/Analysis/BasicAA/featuretest.ll b/llvm/test/Analysis/BasicAA/featuretest.ll index e4cb009f0c633..04c4725d26c1d 100644 --- a/llvm/test/Analysis/BasicAA/featuretest.ll +++ b/llvm/test/Analysis/BasicAA/featuretest.ll @@ -15,24 +15,14 @@ declare void @llvm.assume(i1) ; operations on another array. Important for scientific codes. ; define i32 @different_array_test(i64 %A, i64 %B) { -; NO_ASSUME-LABEL: @different_array_test( -; NO_ASSUME-NEXT: [[ARRAY11:%.*]] = alloca [100 x i32], align 4 -; NO_ASSUME-NEXT: [[ARRAY22:%.*]] = alloca [200 x i32], align 4 -; NO_ASSUME-NEXT: call void @llvm.assume(i1 true) [ "align"(ptr [[ARRAY11]], i32 4) ] -; NO_ASSUME-NEXT: call void @external(ptr nonnull [[ARRAY11]]) -; NO_ASSUME-NEXT: call void @external(ptr nonnull [[ARRAY22]]) -; NO_ASSUME-NEXT: [[POINTER2:%.*]] = getelementptr i32, ptr [[ARRAY22]], i64 [[B:%.*]] -; NO_ASSUME-NEXT: store i32 7, ptr [[POINTER2]], align 4 -; NO_ASSUME-NEXT: ret i32 0 -; -; USE_ASSUME-LABEL: @different_array_test( -; USE_ASSUME-NEXT: [[ARRAY11:%.*]] = alloca [100 x i32], align 4 -; USE_ASSUME-NEXT: [[ARRAY22:%.*]] = alloca [200 x i32], align 4 -; USE_ASSUME-NEXT: call void @external(ptr nonnull [[ARRAY11]]) -; USE_ASSUME-NEXT: call void @external(ptr nonnull [[ARRAY22]]) -; USE_ASSUME-NEXT: [[POINTER2:%.*]] = getelementptr i32, ptr [[ARRAY22]], i64 [[B:%.*]] -; USE_ASSUME-NEXT: store i32 7, ptr [[POINTER2]], align 4 -; USE_ASSUME-NEXT: ret i32 0 +; CHECK-LABEL: @different_array_test( +; CHECK-NEXT: [[ARRAY11:%.*]] = alloca [100 x i32], align 4 +; CHECK-NEXT: [[ARRAY22:%.*]] = alloca [200 x i32], align 4 +; CHECK-NEXT: call void @external(ptr nonnull [[ARRAY11]]) +; CHECK-NEXT: call void @external(ptr nonnull [[ARRAY22]]) +; CHECK-NEXT: [[POINTER2:%.*]] = getelementptr i32, ptr [[ARRAY22]], i64 [[B:%.*]] +; CHECK-NEXT: store i32 7, ptr [[POINTER2]], align 4 +; CHECK-NEXT: ret i32 0 ; %Array1 = alloca i32, i32 100 %Array2 = alloca i32, i32 200 From ceb820081734bb78bd76941aa5bea131f2f4f8ba Mon Sep 17 00:00:00 2001 From: Florian Hahn Date: Tue, 2 Sep 2025 13:32:37 +0100 Subject: [PATCH 4/6] !fixup address comments, strip some tests --- .../InstCombine/InstCombineCalls.cpp | 4 +- .../Transforms/InstCombine/assume-align.ll | 47 ++----------------- 2 files changed, 6 insertions(+), 45 deletions(-) diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp index a8c685755972e..a40d915a6f08d 100644 --- a/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp +++ b/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp @@ -3411,7 +3411,7 @@ Instruction *InstCombinerImpl::visitCallInst(CallInst &CI) { RetainedKnowledge RK = getKnowledgeFromBundle( *cast(II), II->bundle_op_info_begin()[Idx]); if (!RK || RK.AttrKind != Attribute::Alignment || - !isPowerOf2_64(RK.ArgValue)) + !isPowerOf2_64(RK.ArgValue) || !isa(RK.IRArgValue)) continue; // Don't try to remove align assumptions for pointers derived from @@ -3426,7 +3426,7 @@ Instruction *InstCombinerImpl::visitCallInst(CallInst &CI) { // for reasoning. KnownBits Known = computeKnownBits(RK.WasOn, /*CtxI=*/nullptr); unsigned TZ = std::min(Known.countMinTrailingZeros(), 63u); - if ((1ULL << TZ) < RK.ArgValue) + if ((1ULL << std::min(TZ, Value::MaxAlignmentExponent)) < RK.ArgValue) continue; return CallBase::removeOperandBundle(II, OBU.getTagID()); } diff --git a/llvm/test/Transforms/InstCombine/assume-align.ll b/llvm/test/Transforms/InstCombine/assume-align.ll index 80441c428cbff..274632658496b 100644 --- a/llvm/test/Transforms/InstCombine/assume-align.ll +++ b/llvm/test/Transforms/InstCombine/assume-align.ll @@ -1,5 +1,5 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --check-globals smart -; RUN: opt -S -passes='instcombine,simplifycfg' < %s 2>&1 | FileCheck %s +; RUN: opt -S -passes=instcombine,simplifycfg < %s 2>&1 | FileCheck %s declare void @llvm.assume(i1 noundef) @@ -135,17 +135,6 @@ define ptr @fold_assume_align_pow2_of_loaded_pointer_into_align_metadata(ptr %p) ret ptr %p2 } -define ptr @fold_assume_align_i32_pow2_of_loaded_pointer_into_align_metadata(ptr %p) { -; CHECK-LABEL: @fold_assume_align_i32_pow2_of_loaded_pointer_into_align_metadata( -; CHECK-NEXT: [[P2:%.*]] = load ptr, ptr [[P:%.*]], align 8 -; CHECK-NEXT: call void @llvm.assume(i1 true) [ "align"(ptr [[P2]], i32 8) ] -; CHECK-NEXT: ret ptr [[P2]] -; - %p2 = load ptr, ptr %p - call void @llvm.assume(i1 true) [ "align"(ptr %p2, i32 8) ] - ret ptr %p2 -} - define ptr @dont_fold_assume_align_pow2_of_loaded_pointer_into_align_metadata_due_to_call(ptr %p) { ; CHECK-LABEL: @dont_fold_assume_align_pow2_of_loaded_pointer_into_align_metadata_due_to_call( ; CHECK-NEXT: [[P2:%.*]] = load ptr, ptr [[P:%.*]], align 8 @@ -258,41 +247,13 @@ define ptr @redundant_assume_align_8_via_asume(ptr %p) { ret ptr %p } -define void @redundant_arg_passed_to_intrinsic(ptr %dst, ptr %src) { -; CHECK-LABEL: @redundant_arg_passed_to_intrinsic( -; CHECK-NEXT: call void @llvm.assume(i1 true) [ "align"(ptr [[DST:%.*]], i32 8) ] -; CHECK-NEXT: call void @bar() -; CHECK-NEXT: call void @llvm.assume(i1 true) [ "align"(ptr [[SRC:%.*]], i32 8) ] -; CHECK-NEXT: call void @llvm.memmove.p0.p0.i64(ptr noundef nonnull align 8 dereferenceable(16) [[DST]], ptr noundef nonnull align 8 dereferenceable(16) [[SRC]], i64 16, i1 false) -; CHECK-NEXT: ret void -; - call void @llvm.assume(i1 true) [ "align"(ptr %dst, i32 8) ] - call void @bar() - call void @llvm.assume(i1 true) [ "align"(ptr %src, i32 8) ] - call void @llvm.memmove.p0.p0.i64(ptr align 8 %dst, ptr %src, i64 16, i1 false) - ret void -} - -define void @test_store(ptr %ptr) { -; CHECK-LABEL: @test_store( -; CHECK-NEXT: entry: -; CHECK-NEXT: call void @llvm.assume(i1 true) [ "align"(ptr [[PTR:%.*]], i64 2) ] -; CHECK-NEXT: store i16 0, ptr [[PTR]], align 1 -; CHECK-NEXT: ret void -; -entry: - call void @llvm.assume(i1 true) [ "align"(ptr %ptr, i64 2) ] - store i16 0, ptr %ptr, align 1 - ret void -} - declare void @foo(ptr) -declare void @bar() ; !align must have a constant integer alignment. -define ptr @dont_fold_assume_align_not_constant_of_loaded_pointer_into_align_metadata(ptr %p, i64 %align) { -; CHECK-LABEL: @dont_fold_assume_align_not_constant_of_loaded_pointer_into_align_metadata( +define ptr @assume_load_pointer_result(ptr %p, i64 %align) { +; CHECK-LABEL: @assume_load_pointer_result( ; CHECK-NEXT: [[P2:%.*]] = load ptr, ptr [[P:%.*]], align 8 +; CHECK-NEXT: call void @llvm.assume(i1 true) [ "align"(ptr [[P2]], i64 [[ALIGN:%.*]]) ] ; CHECK-NEXT: ret ptr [[P2]] ; %p2 = load ptr, ptr %p From bf73b927e928ee72ca9e4df63ff296f0fbce628b Mon Sep 17 00:00:00 2001 From: Florian Hahn Date: Tue, 2 Sep 2025 13:38:26 +0100 Subject: [PATCH 5/6] !fixup move to correct place --- llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp index a40d915a6f08d..9ec084d2ec4f0 100644 --- a/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp +++ b/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp @@ -3425,8 +3425,9 @@ Instruction *InstCombinerImpl::visitCallInst(CallInst &CI) { // avoid computeKnownBits using the assumption we are about to remove // for reasoning. KnownBits Known = computeKnownBits(RK.WasOn, /*CtxI=*/nullptr); - unsigned TZ = std::min(Known.countMinTrailingZeros(), 63u); - if ((1ULL << std::min(TZ, Value::MaxAlignmentExponent)) < RK.ArgValue) + unsigned TZ = std::min(Known.countMinTrailingZeros(), + Value::MaxAlignmentExponent); + if ((1ULL << TZ) < RK.ArgValue) continue; return CallBase::removeOperandBundle(II, OBU.getTagID()); } From 697bdf28a8bacad67dd0039a4d0b347593309464 Mon Sep 17 00:00:00 2001 From: Florian Hahn Date: Thu, 11 Sep 2025 14:10:24 +0100 Subject: [PATCH 6/6] !fixup use getKnowledgeFromOperandInAssume --- llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp index 9ec084d2ec4f0..11bac7bdb6eb2 100644 --- a/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp +++ b/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp @@ -3408,8 +3408,8 @@ Instruction *InstCombinerImpl::visitCallInst(CallInst &CI) { // Try to remove redundant alignment assumptions. if (OBU.getTagName() == "align" && OBU.Inputs.size() == 2) { - RetainedKnowledge RK = getKnowledgeFromBundle( - *cast(II), II->bundle_op_info_begin()[Idx]); + RetainedKnowledge RK = getKnowledgeFromOperandInAssume( + *cast(II), II->arg_size() + Idx); if (!RK || RK.AttrKind != Attribute::Alignment || !isPowerOf2_64(RK.ArgValue) || !isa(RK.IRArgValue)) continue;