From 3c57bb6098a68b8944d009f5d192e94f8d23791f Mon Sep 17 00:00:00 2001 From: Florian Hahn Date: Wed, 18 Oct 2023 16:09:46 +0100 Subject: [PATCH 1/3] [InstCombine] Add aligned_alloc with pointer icmp as only use. --- llvm/test/Transforms/InstCombine/malloc-free.ll | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/llvm/test/Transforms/InstCombine/malloc-free.ll b/llvm/test/Transforms/InstCombine/malloc-free.ll index dc918a7fc8080..29c757f82564a 100644 --- a/llvm/test/Transforms/InstCombine/malloc-free.ll +++ b/llvm/test/Transforms/InstCombine/malloc-free.ll @@ -26,6 +26,15 @@ define i32 @dead_aligned_alloc(i32 %size, i32 %alignment, i8 %value) { ret i32 0 } +define i1 @aligned_alloc_only_pointe(i32 %size, i32 %alignment, i8 %value) { +; CHECK-LABEL: @aligned_alloc_only_pointe( +; CHECK-NEXT: ret i1 true +; + %aligned_allocation = tail call ptr @aligned_alloc(i32 %alignment, i32 %size) + %cmp = icmp ne ptr %aligned_allocation, null + ret i1 %cmp +} + declare noalias ptr @calloc(i32, i32) nounwind allockind("alloc,zeroed") allocsize(0,1) "alloc-family"="malloc" declare noalias ptr @malloc(i32) allockind("alloc,uninitialized") allocsize(0) "alloc-family"="malloc" declare noalias ptr @aligned_alloc(i32, i32) allockind("alloc,uninitialized,aligned") allocsize(1) "alloc-family"="malloc" From ea429cb1a11581fe919a7ad00ae8ecc52a7fde4d Mon Sep 17 00:00:00 2001 From: Florian Hahn Date: Thu, 19 Oct 2023 14:48:03 +0100 Subject: [PATCH 2/3] [InstCombine] Add additional aligned allocation tests for #69474. --- .../Transforms/InstCombine/malloc-free.ll | 44 ++++++++++++++++++- 1 file changed, 42 insertions(+), 2 deletions(-) diff --git a/llvm/test/Transforms/InstCombine/malloc-free.ll b/llvm/test/Transforms/InstCombine/malloc-free.ll index 29c757f82564a..b77f70f239921 100644 --- a/llvm/test/Transforms/InstCombine/malloc-free.ll +++ b/llvm/test/Transforms/InstCombine/malloc-free.ll @@ -26,8 +26,8 @@ define i32 @dead_aligned_alloc(i32 %size, i32 %alignment, i8 %value) { ret i32 0 } -define i1 @aligned_alloc_only_pointe(i32 %size, i32 %alignment, i8 %value) { -; CHECK-LABEL: @aligned_alloc_only_pointe( +define i1 @aligned_alloc_pointer_only_used_by_cmp(i32 %size, i32 %alignment, i8 %value) { +; CHECK-LABEL: @aligned_alloc_pointer_only_used_by_cmp( ; CHECK-NEXT: ret i1 true ; %aligned_allocation = tail call ptr @aligned_alloc(i32 %alignment, i32 %size) @@ -35,9 +35,49 @@ define i1 @aligned_alloc_only_pointe(i32 %size, i32 %alignment, i8 %value) { ret i1 %cmp } +define i1 @aligned_alloc_pointer_only_used_by_cmp_alignment_and_value_known_ok(i32 %size, i32 %alignment, i8 %value) { +; CHECK-LABEL: @aligned_alloc_pointer_only_used_by_cmp_alignment_and_value_known_ok( +; CHECK-NEXT: ret i1 true +; + %aligned_allocation = tail call ptr @aligned_alloc(i32 8, i32 32) + %cmp = icmp ne ptr %aligned_allocation, null + ret i1 %cmp +} + +define i1 @aligned_alloc_pointer_only_used_by_cmp_alignment_no_power_of_2(i32 %size, i32 %alignment, i8 %value) { +; CHECK-LABEL: @aligned_alloc_pointer_only_used_by_cmp_alignment_no_power_of_2( +; CHECK-NEXT: ret i1 true +; + %aligned_allocation = tail call ptr @aligned_alloc(i32 3, i32 32) + %cmp = icmp ne ptr %aligned_allocation, null + ret i1 %cmp +} + +define i1 @aligned_alloc_pointer_only_used_by_cmp_size_not_multiple_of_alignment(i32 %size, i32 %alignment, i8 %value) { +; CHECK-LABEL: @aligned_alloc_pointer_only_used_by_cmp_size_not_multiple_of_alignment( +; CHECK-NEXT: ret i1 true +; + %aligned_allocation = tail call ptr @aligned_alloc(i32 8, i32 31) + %cmp = icmp ne ptr %aligned_allocation, null + ret i1 %cmp +} + +; This test uses a aligned allocation function different to @aligned_alloc, +; and should be treated as having @aligned_alloc's constraints on alignment +; and size operands. +define i1 @other_aligned_allocation_function(i32 %size, i32 %alignment, i8 %value) { +; CHECK-LABEL: @other_aligned_allocation_function( +; CHECK-NEXT: ret i1 true +; + %aligned_allocation = tail call ptr @other_aligned_alloc(i32 %alignment, i32 %size) + %cmp = icmp ne ptr %aligned_allocation, null + ret i1 %cmp +} + declare noalias ptr @calloc(i32, i32) nounwind allockind("alloc,zeroed") allocsize(0,1) "alloc-family"="malloc" declare noalias ptr @malloc(i32) allockind("alloc,uninitialized") allocsize(0) "alloc-family"="malloc" declare noalias ptr @aligned_alloc(i32, i32) allockind("alloc,uninitialized,aligned") allocsize(1) "alloc-family"="malloc" +declare noalias ptr @other_aligned_alloc(i32, i32) allockind("alloc,uninitialized,aligned") allocsize(1) "alloc-family"="malloc" declare void @free(ptr) allockind("free") "alloc-family"="malloc" define i1 @foo() { From 45b89d1cbef6df502fc5f9b901958030ea66e6fb Mon Sep 17 00:00:00 2001 From: Florian Hahn Date: Thu, 19 Oct 2023 18:35:27 +0100 Subject: [PATCH 3/3] [InstCombine] Don't consider aligned_alloc removable if icmp uses result (#69474) At the moment, all alloc-like functions are assumed to return non-null pointers, if their return value is only used in a compare. This is based on being allowed to substitute the allocation function with one that doesn't fail to allocate the required memory. aligned_alloc however must also return null if the required alignment cannot be satisfied, so I don't think the same reasoning as above can be applied to it. This patch adds a bail-out for aligned_alloc calls to isAllocSiteRemovable. --- .../InstCombine/InstructionCombining.cpp | 20 +++++++++++++++++++ .../Transforms/InstCombine/malloc-free.ll | 16 ++++++++++----- 2 files changed, 31 insertions(+), 5 deletions(-) diff --git a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp index 767b7c7defbb6..9bffcc78a23c9 100644 --- a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp +++ b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp @@ -2362,6 +2362,26 @@ static bool isAllocSiteRemovable(Instruction *AI, unsigned OtherIndex = (ICI->getOperand(0) == PI) ? 1 : 0; if (!isNeverEqualToUnescapedAlloc(ICI->getOperand(OtherIndex), TLI, AI)) return false; + + // Do not fold compares to aligned_alloc calls, as they may have to + // return null in case the required alignment cannot be satisfied, + // unless we can prove that both alignment and size are valid. + auto AlignmentAndSizeKnownValid = [](CallBase *CB) { + // Check if alignment and size of a call to aligned_alloc is valid, + // that is alignment is a power-of-2 and the size is a multiple of the + // alignment. + const APInt *Alignment; + const APInt *Size; + return match(CB->getArgOperand(0), m_APInt(Alignment)) && + match(CB->getArgOperand(1), m_APInt(Size)) && + Alignment->isPowerOf2() && Size->urem(*Alignment).isZero(); + }; + auto *CB = dyn_cast(AI); + LibFunc TheLibFunc; + if (CB && TLI.getLibFunc(*CB->getCalledFunction(), TheLibFunc) && + TLI.has(TheLibFunc) && TheLibFunc == LibFunc_aligned_alloc && + !AlignmentAndSizeKnownValid(CB)) + return false; Users.emplace_back(I); continue; } diff --git a/llvm/test/Transforms/InstCombine/malloc-free.ll b/llvm/test/Transforms/InstCombine/malloc-free.ll index b77f70f239921..10725950a1c73 100644 --- a/llvm/test/Transforms/InstCombine/malloc-free.ll +++ b/llvm/test/Transforms/InstCombine/malloc-free.ll @@ -26,9 +26,11 @@ define i32 @dead_aligned_alloc(i32 %size, i32 %alignment, i8 %value) { ret i32 0 } -define i1 @aligned_alloc_pointer_only_used_by_cmp(i32 %size, i32 %alignment, i8 %value) { -; CHECK-LABEL: @aligned_alloc_pointer_only_used_by_cmp( -; CHECK-NEXT: ret i1 true +define i1 @aligned_alloc_only_pointe(i32 %size, i32 %alignment, i8 %value) { +; CHECK-LABEL: @aligned_alloc_only_pointe( +; CHECK-NEXT: [[ALIGNED_ALLOCATION:%.*]] = tail call ptr @aligned_alloc(i32 [[ALIGNMENT:%.*]], i32 [[SIZE:%.*]]) +; CHECK-NEXT: [[CMP:%.*]] = icmp ne ptr [[ALIGNED_ALLOCATION]], null +; CHECK-NEXT: ret i1 [[CMP]] ; %aligned_allocation = tail call ptr @aligned_alloc(i32 %alignment, i32 %size) %cmp = icmp ne ptr %aligned_allocation, null @@ -46,7 +48,9 @@ define i1 @aligned_alloc_pointer_only_used_by_cmp_alignment_and_value_known_ok(i define i1 @aligned_alloc_pointer_only_used_by_cmp_alignment_no_power_of_2(i32 %size, i32 %alignment, i8 %value) { ; CHECK-LABEL: @aligned_alloc_pointer_only_used_by_cmp_alignment_no_power_of_2( -; CHECK-NEXT: ret i1 true +; CHECK-NEXT: [[ALIGNED_ALLOCATION:%.*]] = tail call dereferenceable_or_null(32) ptr @aligned_alloc(i32 3, i32 32) +; CHECK-NEXT: [[CMP:%.*]] = icmp ne ptr [[ALIGNED_ALLOCATION]], null +; CHECK-NEXT: ret i1 [[CMP]] ; %aligned_allocation = tail call ptr @aligned_alloc(i32 3, i32 32) %cmp = icmp ne ptr %aligned_allocation, null @@ -55,7 +59,9 @@ define i1 @aligned_alloc_pointer_only_used_by_cmp_alignment_no_power_of_2(i32 %s define i1 @aligned_alloc_pointer_only_used_by_cmp_size_not_multiple_of_alignment(i32 %size, i32 %alignment, i8 %value) { ; CHECK-LABEL: @aligned_alloc_pointer_only_used_by_cmp_size_not_multiple_of_alignment( -; CHECK-NEXT: ret i1 true +; CHECK-NEXT: [[ALIGNED_ALLOCATION:%.*]] = tail call dereferenceable_or_null(31) ptr @aligned_alloc(i32 8, i32 31) +; CHECK-NEXT: [[CMP:%.*]] = icmp ne ptr [[ALIGNED_ALLOCATION]], null +; CHECK-NEXT: ret i1 [[CMP]] ; %aligned_allocation = tail call ptr @aligned_alloc(i32 8, i32 31) %cmp = icmp ne ptr %aligned_allocation, null