[clang] [llvm] [Inliner] Propagate more attributes to params when inlining (PR #91101)
via llvm-commits
llvm-commits at lists.llvm.org
Wed Oct 2 07:59:34 PDT 2024
https://github.com/goldsteinn updated https://github.com/llvm/llvm-project/pull/91101
>From 74e64cd80956d599548041d25e4fdfd1cd1c8d68 Mon Sep 17 00:00:00 2001
From: Noah Goldstein <goldstein.w.n at gmail.com>
Date: Sat, 4 May 2024 18:12:34 -0500
Subject: [PATCH 1/4] [Inliner] Add tests for propagating more parameter
attributes; NFC
---
.../Inline/access-attributes-prop.ll | 154 +++++++++++++++++-
1 file changed, 152 insertions(+), 2 deletions(-)
diff --git a/llvm/test/Transforms/Inline/access-attributes-prop.ll b/llvm/test/Transforms/Inline/access-attributes-prop.ll
index 2c55f5f3b1f6ca..9af5b481da2f2e 100644
--- a/llvm/test/Transforms/Inline/access-attributes-prop.ll
+++ b/llvm/test/Transforms/Inline/access-attributes-prop.ll
@@ -47,7 +47,6 @@ define dso_local void @foo3_writable(ptr %p) {
ret void
}
-
define dso_local void @foo1_bar_aligned64_deref512(ptr %p) {
; CHECK-LABEL: define {{[^@]+}}@foo1_bar_aligned64_deref512
; CHECK-SAME: (ptr [[P:%.*]]) {
@@ -333,6 +332,16 @@ define void @prop_param_nonnull_and_align(ptr %p) {
ret void
}
+define void @prop_param_nofree_and_align(ptr %p) {
+; CHECK-LABEL: define {{[^@]+}}@prop_param_nofree_and_align
+; CHECK-SAME: (ptr [[P:%.*]]) {
+; CHECK-NEXT: call void @bar1(ptr [[P]])
+; CHECK-NEXT: ret void
+;
+ call void @foo1(ptr nofree align 32 %p)
+ ret void
+}
+
define void @prop_param_deref_align_no_update(ptr %p) {
; CHECK-LABEL: define {{[^@]+}}@prop_param_deref_align_no_update
; CHECK-SAME: (ptr [[P:%.*]]) {
@@ -539,7 +548,6 @@ define void @prop_no_conflict_writable(ptr %p) {
ret void
}
-
define void @prop_no_conflict_writable2(ptr %p) {
; CHECK-LABEL: define {{[^@]+}}@prop_no_conflict_writable2
; CHECK-SAME: (ptr [[P:%.*]]) {
@@ -580,3 +588,145 @@ define ptr @callee_bad_param_prop(ptr readonly %x) {
%r = tail call ptr @llvm.ptrmask(ptr %x, i64 -1)
ret ptr %r
}
+
+declare void @bar5(i32)
+
+define dso_local void @foo4_range_0_10(i32 %v) {
+; CHECK-LABEL: define {{[^@]+}}@foo4_range_0_10
+; CHECK-SAME: (i32 [[V:%.*]]) {
+; CHECK-NEXT: call void @bar5(i32 range(i32 0, 10) [[V]])
+; CHECK-NEXT: ret void
+;
+ call void @bar5(i32 range(i32 0, 10) %v)
+ ret void
+}
+
+define dso_local void @foo4_range_10_40(i32 %v) {
+; CHECK-LABEL: define {{[^@]+}}@foo4_range_10_40
+; CHECK-SAME: (i32 [[V:%.*]]) {
+; CHECK-NEXT: call void @bar5(i32 range(i32 10, 40) [[V]])
+; CHECK-NEXT: ret void
+;
+ call void @bar5(i32 range(i32 10, 40) %v)
+ ret void
+}
+
+define dso_local void @foo4_2_range_0_10(i32 range(i32 0, 10) %v) {
+; CHECK-LABEL: define {{[^@]+}}@foo4_2_range_0_10
+; CHECK-SAME: (i32 range(i32 0, 10) [[V:%.*]]) {
+; CHECK-NEXT: call void @bar5(i32 [[V]])
+; CHECK-NEXT: ret void
+;
+ call void @bar5(i32 %v)
+ ret void
+}
+
+define dso_local void @foo4(i32 %v) {
+; CHECK-LABEL: define {{[^@]+}}@foo4
+; CHECK-SAME: (i32 [[V:%.*]]) {
+; CHECK-NEXT: call void @bar5(i32 [[V]])
+; CHECK-NEXT: ret void
+;
+ call void @bar5(i32 %v)
+ ret void
+}
+
+define void @prop_range_empty_intersect(i32 %v) {
+; CHECK-LABEL: define {{[^@]+}}@prop_range_empty_intersect
+; CHECK-SAME: (i32 [[V:%.*]]) {
+; CHECK-NEXT: call void @bar5(i32 range(i32 0, 10) [[V]])
+; CHECK-NEXT: ret void
+;
+ call void @foo4_range_0_10(i32 range(i32 11, 50) %v)
+ ret void
+}
+
+define void @prop_range_empty(i32 %v) {
+; CHECK-LABEL: define {{[^@]+}}@prop_range_empty
+; CHECK-SAME: (i32 [[V:%.*]]) {
+; CHECK-NEXT: call void @bar5(i32 [[V]])
+; CHECK-NEXT: ret void
+;
+ call void @foo4(i32 range(i32 1, 0) %v)
+ ret void
+}
+
+define void @prop_range_empty_with_intersect(i32 %v) {
+; CHECK-LABEL: define {{[^@]+}}@prop_range_empty_with_intersect
+; CHECK-SAME: (i32 [[V:%.*]]) {
+; CHECK-NEXT: call void @bar5(i32 range(i32 0, 10) [[V]])
+; CHECK-NEXT: ret void
+;
+ call void @foo4_range_0_10(i32 range(i32 1, 0) %v)
+ ret void
+}
+
+define void @prop_range_intersect1(i32 %v) {
+; CHECK-LABEL: define {{[^@]+}}@prop_range_intersect1
+; CHECK-SAME: (i32 [[V:%.*]]) {
+; CHECK-NEXT: call void @bar5(i32 range(i32 0, 10) [[V]])
+; CHECK-NEXT: ret void
+;
+ call void @foo4_range_0_10(i32 range(i32 0, 9) %v)
+ ret void
+}
+
+define void @prop_range_intersect2(i32 %v) {
+; CHECK-LABEL: define {{[^@]+}}@prop_range_intersect2
+; CHECK-SAME: (i32 [[V:%.*]]) {
+; CHECK-NEXT: call void @bar5(i32 range(i32 0, 10) [[V]])
+; CHECK-NEXT: ret void
+;
+ call void @foo4_range_0_10(i32 range(i32 1, 9) %v)
+ ret void
+}
+
+define void @prop_range_intersect3(i32 %v) {
+; CHECK-LABEL: define {{[^@]+}}@prop_range_intersect3
+; CHECK-SAME: (i32 [[V:%.*]]) {
+; CHECK-NEXT: call void @bar5(i32 [[V]])
+; CHECK-NEXT: ret void
+;
+ call void @foo4_2_range_0_10(i32 range(i32 0, 11) %v)
+ ret void
+}
+
+define void @prop_range_intersect4(i32 %v) {
+; CHECK-LABEL: define {{[^@]+}}@prop_range_intersect4
+; CHECK-SAME: (i32 [[V:%.*]]) {
+; CHECK-NEXT: call void @bar5(i32 range(i32 0, 10) [[V]])
+; CHECK-NEXT: ret void
+;
+ call void @foo4_range_0_10(i32 range(i32 40, 5) %v)
+ ret void
+}
+
+define void @prop_range_intersect5(i32 %v) {
+; CHECK-LABEL: define {{[^@]+}}@prop_range_intersect5
+; CHECK-SAME: (i32 [[V:%.*]]) {
+; CHECK-NEXT: call void @bar5(i32 range(i32 10, 40) [[V]])
+; CHECK-NEXT: ret void
+;
+ call void @foo4_range_10_40(i32 range(i32 30, 20) %v)
+ ret void
+}
+
+define void @prop_range_keep(i32 %v) {
+; CHECK-LABEL: define {{[^@]+}}@prop_range_keep
+; CHECK-SAME: (i32 [[V:%.*]]) {
+; CHECK-NEXT: call void @bar5(i32 range(i32 10, 40) [[V]])
+; CHECK-NEXT: ret void
+;
+ call void @foo4_range_10_40(i32 %v)
+ ret void
+}
+
+define void @prop_range_direct(i32 %v) {
+; CHECK-LABEL: define {{[^@]+}}@prop_range_direct
+; CHECK-SAME: (i32 [[V:%.*]]) {
+; CHECK-NEXT: call void @bar5(i32 [[V]])
+; CHECK-NEXT: ret void
+;
+ call void @foo4(i32 range(i32 1, 11) %v)
+ ret void
+}
>From 5a3845f584898bde196123ba88630640066c35a8 Mon Sep 17 00:00:00 2001
From: Noah Goldstein <goldstein.w.n at gmail.com>
Date: Sat, 4 May 2024 13:57:45 -0500
Subject: [PATCH 2/4] [Inliner] Propagate more attributes to params when
inlining
Add support for propagating:
- `derefereancable`
- `derefereancable_or_null`
- `align`
- `nonnull`
These are only propagated if the parameter to the to-be-inlined
callsite match the exact parameter used in the to-be-inlined function.
---
.../test/CodeGen/attr-counted-by-pr88931.cpp | 2 +-
clang/test/OpenMP/bug57757.cpp | 2 +-
llvm/lib/Transforms/Utils/InlineFunction.cpp | 82 +++++++++++++++----
.../Inline/access-attributes-prop.ll | 14 ++--
.../Inline/assumptions-from-callsite-attrs.ll | 2 +-
llvm/test/Transforms/Inline/byval.ll | 4 +-
llvm/test/Transforms/PhaseOrdering/pr95152.ll | 2 +-
7 files changed, 78 insertions(+), 30 deletions(-)
diff --git a/clang/test/CodeGen/attr-counted-by-pr88931.cpp b/clang/test/CodeGen/attr-counted-by-pr88931.cpp
index 2a8cc1d07e50d9..6d0c46bbbe8f9c 100644
--- a/clang/test/CodeGen/attr-counted-by-pr88931.cpp
+++ b/clang/test/CodeGen/attr-counted-by-pr88931.cpp
@@ -13,7 +13,7 @@ void init(void * __attribute__((pass_dynamic_object_size(0))));
// CHECK-LABEL: define dso_local void @_ZN3foo3barC1Ev(
// CHECK-SAME: ptr noundef nonnull align 4 dereferenceable(1) [[THIS:%.*]]) unnamed_addr #[[ATTR0:[0-9]+]] align 2 {
// CHECK-NEXT: entry:
-// CHECK-NEXT: tail call void @_Z4initPvU25pass_dynamic_object_size0(ptr noundef nonnull [[THIS]], i64 noundef -1) #[[ATTR2:[0-9]+]]
+// CHECK-NEXT: tail call void @_Z4initPvU25pass_dynamic_object_size0(ptr noundef nonnull align 4 dereferenceable(1) [[THIS]], i64 noundef -1) #[[ATTR2:[0-9]+]]
// CHECK-NEXT: ret void
//
foo::bar::bar() {
diff --git a/clang/test/OpenMP/bug57757.cpp b/clang/test/OpenMP/bug57757.cpp
index 240b22a3067143..eabf233dde2474 100644
--- a/clang/test/OpenMP/bug57757.cpp
+++ b/clang/test/OpenMP/bug57757.cpp
@@ -39,7 +39,7 @@ void foo() {
// CHECK-NEXT: ]
// CHECK: .untied.jmp..i:
// CHECK-NEXT: store i32 1, ptr [[TMP2]], align 4, !tbaa [[TBAA16]], !alias.scope [[META13]], !noalias [[META17]]
-// CHECK-NEXT: [[TMP4:%.*]] = tail call i32 @__kmpc_omp_task(ptr nonnull @[[GLOB1]], i32 [[TMP0]], ptr [[TMP1]]), !noalias [[META13]]
+// CHECK-NEXT: [[TMP4:%.*]] = tail call i32 @__kmpc_omp_task(ptr nonnull @[[GLOB1]], i32 [[TMP0]], ptr nonnull [[TMP1]]), !noalias [[META13]]
// CHECK-NEXT: br label [[DOTOMP_OUTLINED__EXIT]]
// CHECK: .untied.next..i:
// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw i8, ptr [[TMP1]], i64 40
diff --git a/llvm/lib/Transforms/Utils/InlineFunction.cpp b/llvm/lib/Transforms/Utils/InlineFunction.cpp
index 671b0d0822a5d9..c51dc86190fa32 100644
--- a/llvm/lib/Transforms/Utils/InlineFunction.cpp
+++ b/llvm/lib/Transforms/Utils/InlineFunction.cpp
@@ -34,6 +34,7 @@
#include "llvm/Analysis/VectorUtils.h"
#include "llvm/IR/Argument.h"
#include "llvm/IR/AttributeMask.h"
+#include "llvm/IR/Attributes.h"
#include "llvm/IR/BasicBlock.h"
#include "llvm/IR/CFG.h"
#include "llvm/IR/Constant.h"
@@ -1358,18 +1359,37 @@ static void AddParamAndFnBasicAttributes(const CallBase &CB,
auto &Context = CalledFunction->getContext();
// Collect valid attributes for all params.
- SmallVector<AttrBuilder> ValidParamAttrs;
+ SmallVector<AttrBuilder> ValidObjParamAttrs, ValidExactParamAttrs;
bool HasAttrToPropagate = false;
+ // Attributes we can only propagate if the exact parameter is forwarded.
+ // We can propagate both poison generating and UB generating attributes
+ // without any extra checks. The only attribute that is tricky to propagate
+ // is `noundef` (skipped for now) as that can create new UB where previous
+ // behavior was just using a poison value.
+ static const Attribute::AttrKind ExactAttrsToPropagate[] = {
+ Attribute::Dereferenceable, Attribute::DereferenceableOrNull,
+ Attribute::NonNull, Attribute::Alignment};
+
for (unsigned I = 0, E = CB.arg_size(); I < E; ++I) {
- ValidParamAttrs.emplace_back(AttrBuilder{CB.getContext()});
+ ValidObjParamAttrs.emplace_back(AttrBuilder{CB.getContext()});
+ ValidExactParamAttrs.emplace_back(AttrBuilder{CB.getContext()});
// Access attributes can be propagated to any param with the same underlying
// object as the argument.
if (CB.paramHasAttr(I, Attribute::ReadNone))
- ValidParamAttrs.back().addAttribute(Attribute::ReadNone);
+ ValidObjParamAttrs.back().addAttribute(Attribute::ReadNone);
if (CB.paramHasAttr(I, Attribute::ReadOnly))
- ValidParamAttrs.back().addAttribute(Attribute::ReadOnly);
- HasAttrToPropagate |= ValidParamAttrs.back().hasAttributes();
+ ValidObjParamAttrs.back().addAttribute(Attribute::ReadOnly);
+ HasAttrToPropagate |= ValidObjParamAttrs.back().hasAttributes();
+
+ for (Attribute::AttrKind AK : ExactAttrsToPropagate) {
+ Attribute Attr = CB.getParamAttr(I, AK);
+ if (Attr.isValid())
+ ValidExactParamAttrs.back().addAttribute(Attr);
+ }
+
+ HasAttrToPropagate |= ValidObjParamAttrs.back().hasAttributes();
+ HasAttrToPropagate |= ValidExactParamAttrs.back().hasAttributes();
}
// Won't be able to propagate anything.
@@ -1391,22 +1411,50 @@ static void AddParamAndFnBasicAttributes(const CallBase &CB,
AttributeList AL = NewInnerCB->getAttributes();
for (unsigned I = 0, E = InnerCB->arg_size(); I < E; ++I) {
- // Check if the underlying value for the parameter is an argument.
- const Value *UnderlyingV =
- getUnderlyingObject(InnerCB->getArgOperand(I));
- const Argument *Arg = dyn_cast<Argument>(UnderlyingV);
- if (!Arg)
- continue;
-
+ // It's unsound or requires special handling to propagate attributes to
+ // byval arguments. For reach even if CalledFunction doesn't e.g. write
+ // to the argument (readonly), the call to NewInnerCB may write to its
+ // by-value copy.
if (AL.hasParamAttr(I, Attribute::ByVal))
- // It's unsound to propagate memory attributes to byval arguments.
- // Even if CalledFunction doesn't e.g. write to the argument,
- // the call to NewInnerCB may write to its by-value copy.
continue;
- unsigned ArgNo = Arg->getArgNo();
+ // Check if the underlying value for the parameter is an argument.
+ const Argument *Arg = dyn_cast<Argument>(InnerCB->getArgOperand(I));
+ unsigned ArgNo;
+ if (Arg) {
+ ArgNo = Arg->getArgNo();
+ // For dereferenceable, dereferenceable_or_null, align, etc...
+ // we don't want to propagate if the existing param has the same
+ // attribute with "better" constraints. So remove from the
+ // new AL if the region of the existing param is larger than
+ // what we can propagate.
+ AttrBuilder NewAL(Context);
+ for (auto Attr : ValidExactParamAttrs[ArgNo].attrs())
+ NewAL.addAttribute(Attr);
+ if (AL.getParamDereferenceableBytes(I) >
+ NewAL.getDereferenceableBytes())
+ NewAL.removeAttribute(Attribute::Dereferenceable);
+ if (AL.getParamDereferenceableOrNullBytes(I) >
+ NewAL.getDereferenceableOrNullBytes())
+ NewAL.removeAttribute(Attribute::Dereferenceable);
+ if (AL.getParamAlignment(I).valueOrOne() >
+ NewAL.getAlignment().valueOrOne())
+ NewAL.removeAttribute(Attribute::Alignment);
+
+ AL = AL.addParamAttributes(Context, I, NewAL);
+
+ } else {
+ // Check if the underlying value for the parameter is an argument.
+ const Value *UnderlyingV =
+ getUnderlyingObject(InnerCB->getArgOperand(I));
+ Arg = dyn_cast<Argument>(UnderlyingV);
+ if (!Arg)
+ continue;
+ ArgNo = Arg->getArgNo();
+ }
+
// If so, propagate its access attributes.
- AL = AL.addParamAttributes(Context, I, ValidParamAttrs[ArgNo]);
+ AL = AL.addParamAttributes(Context, I, ValidObjParamAttrs[ArgNo]);
// We can have conflicting attributes from the inner callsite and
// to-be-inlined callsite. In that case, choose the most
// restrictive.
diff --git a/llvm/test/Transforms/Inline/access-attributes-prop.ll b/llvm/test/Transforms/Inline/access-attributes-prop.ll
index 9af5b481da2f2e..0b73549461b779 100644
--- a/llvm/test/Transforms/Inline/access-attributes-prop.ll
+++ b/llvm/test/Transforms/Inline/access-attributes-prop.ll
@@ -305,7 +305,7 @@ define void @prop_param_callbase_def_1x_partial_3(ptr %p, ptr %p2) {
define void @prop_deref(ptr %p) {
; CHECK-LABEL: define {{[^@]+}}@prop_deref
; CHECK-SAME: (ptr [[P:%.*]]) {
-; CHECK-NEXT: call void @bar1(ptr [[P]])
+; CHECK-NEXT: call void @bar1(ptr dereferenceable(16) [[P]])
; CHECK-NEXT: ret void
;
call void @foo1(ptr dereferenceable(16) %p)
@@ -315,7 +315,7 @@ define void @prop_deref(ptr %p) {
define void @prop_deref_or_null(ptr %p) {
; CHECK-LABEL: define {{[^@]+}}@prop_deref_or_null
; CHECK-SAME: (ptr [[P:%.*]]) {
-; CHECK-NEXT: call void @bar1(ptr [[P]])
+; CHECK-NEXT: call void @bar1(ptr dereferenceable_or_null(256) [[P]])
; CHECK-NEXT: ret void
;
call void @foo1(ptr dereferenceable_or_null(256) %p)
@@ -325,7 +325,7 @@ define void @prop_deref_or_null(ptr %p) {
define void @prop_param_nonnull_and_align(ptr %p) {
; CHECK-LABEL: define {{[^@]+}}@prop_param_nonnull_and_align
; CHECK-SAME: (ptr [[P:%.*]]) {
-; CHECK-NEXT: call void @bar1(ptr [[P]])
+; CHECK-NEXT: call void @bar1(ptr nonnull align 32 [[P]])
; CHECK-NEXT: ret void
;
call void @foo1(ptr nonnull align 32 %p)
@@ -335,7 +335,7 @@ define void @prop_param_nonnull_and_align(ptr %p) {
define void @prop_param_nofree_and_align(ptr %p) {
; CHECK-LABEL: define {{[^@]+}}@prop_param_nofree_and_align
; CHECK-SAME: (ptr [[P:%.*]]) {
-; CHECK-NEXT: call void @bar1(ptr [[P]])
+; CHECK-NEXT: call void @bar1(ptr align 32 [[P]])
; CHECK-NEXT: ret void
;
call void @foo1(ptr nofree align 32 %p)
@@ -355,7 +355,7 @@ define void @prop_param_deref_align_no_update(ptr %p) {
define void @prop_param_deref_align_update(ptr %p) {
; CHECK-LABEL: define {{[^@]+}}@prop_param_deref_align_update
; CHECK-SAME: (ptr [[P:%.*]]) {
-; CHECK-NEXT: call void @bar1(ptr align 64 dereferenceable(512) [[P]])
+; CHECK-NEXT: call void @bar1(ptr align 128 dereferenceable(1024) [[P]])
; CHECK-NEXT: ret void
;
call void @foo1_bar_aligned64_deref512(ptr align 128 dereferenceable(1024) %p)
@@ -365,7 +365,7 @@ define void @prop_param_deref_align_update(ptr %p) {
define void @prop_param_deref_or_null_update(ptr %p) {
; CHECK-LABEL: define {{[^@]+}}@prop_param_deref_or_null_update
; CHECK-SAME: (ptr [[P:%.*]]) {
-; CHECK-NEXT: call void @bar1(ptr align 512 dereferenceable_or_null(512) [[P]])
+; CHECK-NEXT: call void @bar1(ptr align 512 dereferenceable_or_null(1024) [[P]])
; CHECK-NEXT: ret void
;
call void @foo1_bar_aligned512_deref_or_null512(ptr dereferenceable_or_null(1024) %p)
@@ -375,7 +375,7 @@ define void @prop_param_deref_or_null_update(ptr %p) {
define void @prop_param_deref_or_null_no_update(ptr %p) {
; CHECK-LABEL: define {{[^@]+}}@prop_param_deref_or_null_no_update
; CHECK-SAME: (ptr [[P:%.*]]) {
-; CHECK-NEXT: call void @bar1(ptr align 512 dereferenceable_or_null(512) [[P]])
+; CHECK-NEXT: call void @bar1(ptr align 512 dereferenceable_or_null(32) [[P]])
; CHECK-NEXT: ret void
;
call void @foo1_bar_aligned512_deref_or_null512(ptr dereferenceable_or_null(32) %p)
diff --git a/llvm/test/Transforms/Inline/assumptions-from-callsite-attrs.ll b/llvm/test/Transforms/Inline/assumptions-from-callsite-attrs.ll
index 1a219a22019c43..c0943f4aefb8f9 100644
--- a/llvm/test/Transforms/Inline/assumptions-from-callsite-attrs.ll
+++ b/llvm/test/Transforms/Inline/assumptions-from-callsite-attrs.ll
@@ -8,7 +8,7 @@ declare void @h(ptr %p, ptr %q, ptr %z)
define void @f(ptr %p, ptr %q, ptr %z) {
; CHECK-LABEL: define void @f
; CHECK-SAME: (ptr [[P:%.*]], ptr [[Q:%.*]], ptr [[Z:%.*]]) {
-; CHECK-NEXT: call void @h(ptr [[P]], ptr [[Q]], ptr [[Z]])
+; CHECK-NEXT: call void @h(ptr nonnull [[P]], ptr [[Q]], ptr nonnull [[Z]])
; CHECK-NEXT: ret void
;
call void @g(ptr nonnull %p, ptr %q, ptr nonnull %z)
diff --git a/llvm/test/Transforms/Inline/byval.ll b/llvm/test/Transforms/Inline/byval.ll
index dd5be40b90a8f2..1a70da8472cb1e 100644
--- a/llvm/test/Transforms/Inline/byval.ll
+++ b/llvm/test/Transforms/Inline/byval.ll
@@ -106,7 +106,7 @@ define void @test3() nounwind {
; CHECK-NEXT: [[S:%.*]] = alloca [[STRUCT_SS]], align 1
; CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 12, ptr [[S1]])
; CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 1 [[S1]], ptr align 1 [[S]], i64 12, i1 false)
-; CHECK-NEXT: call void @g3(ptr [[S1]]) #[[ATTR0]]
+; CHECK-NEXT: call void @g3(ptr align 64 [[S1]]) #[[ATTR0]]
; CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 12, ptr [[S1]])
; CHECK-NEXT: ret void
;
@@ -131,7 +131,7 @@ define i32 @test4() nounwind {
; CHECK-SAME: ) #[[ATTR0]] {
; CHECK-NEXT: entry:
; CHECK-NEXT: [[S:%.*]] = alloca [[STRUCT_SS:%.*]], align 64
-; CHECK-NEXT: call void @g3(ptr [[S]]) #[[ATTR0]]
+; CHECK-NEXT: call void @g3(ptr align 64 [[S]]) #[[ATTR0]]
; CHECK-NEXT: ret i32 4
;
entry:
diff --git a/llvm/test/Transforms/PhaseOrdering/pr95152.ll b/llvm/test/Transforms/PhaseOrdering/pr95152.ll
index 16610c439f4c04..fff94673a1a519 100644
--- a/llvm/test/Transforms/PhaseOrdering/pr95152.ll
+++ b/llvm/test/Transforms/PhaseOrdering/pr95152.ll
@@ -47,7 +47,7 @@ define void @f(ptr dead_on_unwind noalias %p) {
; CHECK-LABEL: define void @f(
; CHECK-SAME: ptr dead_on_unwind noalias [[P:%.*]]) local_unnamed_addr {
; CHECK-NEXT: store i64 3, ptr [[P]], align 4
-; CHECK-NEXT: tail call void @j(ptr nonnull [[P]])
+; CHECK-NEXT: tail call void @j(ptr nonnull align 8 dereferenceable(8) [[P]])
; CHECK-NEXT: store i64 43, ptr [[P]], align 4
; CHECK-NEXT: ret void
;
>From 2418d678e9ad559681ec760d783bef19689dd633 Mon Sep 17 00:00:00 2001
From: Noah Goldstein <goldstein.w.n at gmail.com>
Date: Sat, 4 May 2024 13:57:54 -0500
Subject: [PATCH 3/4] [Inliner] Propagate `range` attributes to params when
inlining
---
llvm/include/llvm/IR/Attributes.h | 12 ++++++++++
llvm/include/llvm/IR/InstrTypes.h | 4 ++++
llvm/lib/IR/Attributes.cpp | 22 +++++++++++++++++++
llvm/lib/IR/Instructions.cpp | 7 ++++++
llvm/lib/Transforms/Utils/InlineFunction.cpp | 12 +++++++++-
.../Inline/access-attributes-prop.ll | 16 +++++++-------
6 files changed, 64 insertions(+), 9 deletions(-)
diff --git a/llvm/include/llvm/IR/Attributes.h b/llvm/include/llvm/IR/Attributes.h
index 57db52e4879b5b..b09c58ba2ab8e7 100644
--- a/llvm/include/llvm/IR/Attributes.h
+++ b/llvm/include/llvm/IR/Attributes.h
@@ -787,6 +787,11 @@ class AttributeList {
[[nodiscard]] AttributeList addRangeRetAttr(LLVMContext &C,
const ConstantRange &CR) const;
+ /// Add the range attribute to the attribute set at the given arg index.
+ /// Returns a new list because attribute lists are immutable.
+ [[nodiscard]] AttributeList addRangeParamAttr(LLVMContext &C, unsigned Index,
+ const ConstantRange &CR) const;
+
/// Add the allocsize attribute to the attribute set at the given arg index.
/// Returns a new list because attribute lists are immutable.
[[nodiscard]] AttributeList
@@ -947,6 +952,9 @@ class AttributeList {
/// arg.
uint64_t getParamDereferenceableOrNullBytes(unsigned ArgNo) const;
+ /// Get range (or std::nullopt if unknown) of an arg.
+ std::optional<ConstantRange> getParamRange(unsigned ArgNo) const;
+
/// Get the disallowed floating-point classes of the return value.
FPClassTest getRetNoFPClass() const;
@@ -1123,6 +1131,10 @@ class AttrBuilder {
/// invalid if the Kind is not present in the builder.
Attribute getAttribute(StringRef Kind) const;
+ /// Retrieve the range if the attribute exists (std::nullopt is returned
+ /// otherwise).
+ std::optional<ConstantRange> getRange() const;
+
/// Return raw (possibly packed/encoded) value of integer attribute or
/// std::nullopt if not set.
std::optional<uint64_t> getRawIntAttr(Attribute::AttrKind Kind) const;
diff --git a/llvm/include/llvm/IR/InstrTypes.h b/llvm/include/llvm/IR/InstrTypes.h
index 86d88da3d9460e..670e02ad1536c9 100644
--- a/llvm/include/llvm/IR/InstrTypes.h
+++ b/llvm/include/llvm/IR/InstrTypes.h
@@ -1868,6 +1868,10 @@ class CallBase : public Instruction {
/// parameter.
FPClassTest getParamNoFPClass(unsigned i) const;
+ /// If arg ArgNo has a range attribute, return the value range of the
+ /// argument. Otherwise, std::nullopt is returned.
+ std::optional<ConstantRange> getParamRange(unsigned ArgNo) const;
+
/// If this return value has a range attribute, return the value range of the
/// argument. Otherwise, std::nullopt is returned.
std::optional<ConstantRange> getRange() const;
diff --git a/llvm/lib/IR/Attributes.cpp b/llvm/lib/IR/Attributes.cpp
index 692207e45be234..ec3479cd0060b0 100644
--- a/llvm/lib/IR/Attributes.cpp
+++ b/llvm/lib/IR/Attributes.cpp
@@ -1778,6 +1778,13 @@ AttributeList::addDereferenceableOrNullParamAttr(LLVMContext &C, unsigned Index,
return addParamAttributes(C, Index, B);
}
+AttributeList AttributeList::addRangeParamAttr(LLVMContext &C, unsigned Index,
+ const ConstantRange &CR) const {
+ AttrBuilder B(C);
+ B.addRangeAttr(CR);
+ return addParamAttributes(C, Index, B);
+}
+
AttributeList AttributeList::addRangeRetAttr(LLVMContext &C,
const ConstantRange &CR) const {
AttrBuilder B(C);
@@ -1932,6 +1939,14 @@ AttributeList::getParamDereferenceableOrNullBytes(unsigned Index) const {
return getParamAttrs(Index).getDereferenceableOrNullBytes();
}
+std::optional<ConstantRange>
+AttributeList::getParamRange(unsigned Index) const {
+ auto RangeAttr = getParamAttrs(Index).getAttribute(Attribute::Range);
+ if (RangeAttr.isValid())
+ return RangeAttr.getRange();
+ return std::nullopt;
+}
+
FPClassTest AttributeList::getRetNoFPClass() const {
return getRetAttrs().getNoFPClass();
}
@@ -2278,6 +2293,13 @@ Attribute AttrBuilder::getAttribute(StringRef A) const {
return {};
}
+std::optional<ConstantRange> AttrBuilder::getRange() const {
+ const Attribute RangeAttr = getAttribute(Attribute::Range);
+ if (RangeAttr.isValid())
+ return RangeAttr.getRange();
+ return std::nullopt;
+}
+
bool AttrBuilder::contains(Attribute::AttrKind A) const {
return getAttribute(A).isValid();
}
diff --git a/llvm/lib/IR/Instructions.cpp b/llvm/lib/IR/Instructions.cpp
index 009e0c03957c97..ece91bf4d5b9b6 100644
--- a/llvm/lib/IR/Instructions.cpp
+++ b/llvm/lib/IR/Instructions.cpp
@@ -373,6 +373,13 @@ FPClassTest CallBase::getParamNoFPClass(unsigned i) const {
return Mask;
}
+std::optional<ConstantRange> CallBase::getParamRange(unsigned ArgNo) const {
+ const Attribute RangeAttr = getParamAttr(ArgNo, llvm::Attribute::Range);
+ if (RangeAttr.isValid())
+ return RangeAttr.getRange();
+ return std::nullopt;
+}
+
std::optional<ConstantRange> CallBase::getRange() const {
const Attribute RangeAttr = getRetAttr(llvm::Attribute::Range);
if (RangeAttr.isValid())
diff --git a/llvm/lib/Transforms/Utils/InlineFunction.cpp b/llvm/lib/Transforms/Utils/InlineFunction.cpp
index c51dc86190fa32..bb4477d64fae42 100644
--- a/llvm/lib/Transforms/Utils/InlineFunction.cpp
+++ b/llvm/lib/Transforms/Utils/InlineFunction.cpp
@@ -1369,7 +1369,7 @@ static void AddParamAndFnBasicAttributes(const CallBase &CB,
// behavior was just using a poison value.
static const Attribute::AttrKind ExactAttrsToPropagate[] = {
Attribute::Dereferenceable, Attribute::DereferenceableOrNull,
- Attribute::NonNull, Attribute::Alignment};
+ Attribute::NonNull, Attribute::Alignment, Attribute::Range};
for (unsigned I = 0, E = CB.arg_size(); I < E; ++I) {
ValidObjParamAttrs.emplace_back(AttrBuilder{CB.getContext()});
@@ -1441,8 +1441,18 @@ static void AddParamAndFnBasicAttributes(const CallBase &CB,
NewAL.getAlignment().valueOrOne())
NewAL.removeAttribute(Attribute::Alignment);
+ auto ExistingRange = AL.getParamRange(I);
AL = AL.addParamAttributes(Context, I, NewAL);
+ // For range we use the intersection.
+ if (ExistingRange.has_value()) {
+ if (auto NewRange = NewAL.getRange()) {
+ ConstantRange CombinedRange =
+ ExistingRange->intersectWith(*NewRange);
+ AL = AL.removeParamAttribute(Context, I, Attribute::Range);
+ AL = AL.addRangeParamAttr(Context, I, CombinedRange);
+ }
+ }
} else {
// Check if the underlying value for the parameter is an argument.
const Value *UnderlyingV =
diff --git a/llvm/test/Transforms/Inline/access-attributes-prop.ll b/llvm/test/Transforms/Inline/access-attributes-prop.ll
index 0b73549461b779..85d0863d114ee8 100644
--- a/llvm/test/Transforms/Inline/access-attributes-prop.ll
+++ b/llvm/test/Transforms/Inline/access-attributes-prop.ll
@@ -634,7 +634,7 @@ define dso_local void @foo4(i32 %v) {
define void @prop_range_empty_intersect(i32 %v) {
; CHECK-LABEL: define {{[^@]+}}@prop_range_empty_intersect
; CHECK-SAME: (i32 [[V:%.*]]) {
-; CHECK-NEXT: call void @bar5(i32 range(i32 0, 10) [[V]])
+; CHECK-NEXT: call void @bar5(i32 range(i32 0, 0) [[V]])
; CHECK-NEXT: ret void
;
call void @foo4_range_0_10(i32 range(i32 11, 50) %v)
@@ -644,7 +644,7 @@ define void @prop_range_empty_intersect(i32 %v) {
define void @prop_range_empty(i32 %v) {
; CHECK-LABEL: define {{[^@]+}}@prop_range_empty
; CHECK-SAME: (i32 [[V:%.*]]) {
-; CHECK-NEXT: call void @bar5(i32 [[V]])
+; CHECK-NEXT: call void @bar5(i32 range(i32 1, 0) [[V]])
; CHECK-NEXT: ret void
;
call void @foo4(i32 range(i32 1, 0) %v)
@@ -654,7 +654,7 @@ define void @prop_range_empty(i32 %v) {
define void @prop_range_empty_with_intersect(i32 %v) {
; CHECK-LABEL: define {{[^@]+}}@prop_range_empty_with_intersect
; CHECK-SAME: (i32 [[V:%.*]]) {
-; CHECK-NEXT: call void @bar5(i32 range(i32 0, 10) [[V]])
+; CHECK-NEXT: call void @bar5(i32 range(i32 1, 10) [[V]])
; CHECK-NEXT: ret void
;
call void @foo4_range_0_10(i32 range(i32 1, 0) %v)
@@ -664,7 +664,7 @@ define void @prop_range_empty_with_intersect(i32 %v) {
define void @prop_range_intersect1(i32 %v) {
; CHECK-LABEL: define {{[^@]+}}@prop_range_intersect1
; CHECK-SAME: (i32 [[V:%.*]]) {
-; CHECK-NEXT: call void @bar5(i32 range(i32 0, 10) [[V]])
+; CHECK-NEXT: call void @bar5(i32 range(i32 0, 9) [[V]])
; CHECK-NEXT: ret void
;
call void @foo4_range_0_10(i32 range(i32 0, 9) %v)
@@ -674,7 +674,7 @@ define void @prop_range_intersect1(i32 %v) {
define void @prop_range_intersect2(i32 %v) {
; CHECK-LABEL: define {{[^@]+}}@prop_range_intersect2
; CHECK-SAME: (i32 [[V:%.*]]) {
-; CHECK-NEXT: call void @bar5(i32 range(i32 0, 10) [[V]])
+; CHECK-NEXT: call void @bar5(i32 range(i32 1, 9) [[V]])
; CHECK-NEXT: ret void
;
call void @foo4_range_0_10(i32 range(i32 1, 9) %v)
@@ -684,7 +684,7 @@ define void @prop_range_intersect2(i32 %v) {
define void @prop_range_intersect3(i32 %v) {
; CHECK-LABEL: define {{[^@]+}}@prop_range_intersect3
; CHECK-SAME: (i32 [[V:%.*]]) {
-; CHECK-NEXT: call void @bar5(i32 [[V]])
+; CHECK-NEXT: call void @bar5(i32 range(i32 0, 11) [[V]])
; CHECK-NEXT: ret void
;
call void @foo4_2_range_0_10(i32 range(i32 0, 11) %v)
@@ -694,7 +694,7 @@ define void @prop_range_intersect3(i32 %v) {
define void @prop_range_intersect4(i32 %v) {
; CHECK-LABEL: define {{[^@]+}}@prop_range_intersect4
; CHECK-SAME: (i32 [[V:%.*]]) {
-; CHECK-NEXT: call void @bar5(i32 range(i32 0, 10) [[V]])
+; CHECK-NEXT: call void @bar5(i32 range(i32 0, 5) [[V]])
; CHECK-NEXT: ret void
;
call void @foo4_range_0_10(i32 range(i32 40, 5) %v)
@@ -724,7 +724,7 @@ define void @prop_range_keep(i32 %v) {
define void @prop_range_direct(i32 %v) {
; CHECK-LABEL: define {{[^@]+}}@prop_range_direct
; CHECK-SAME: (i32 [[V:%.*]]) {
-; CHECK-NEXT: call void @bar5(i32 [[V]])
+; CHECK-NEXT: call void @bar5(i32 range(i32 1, 11) [[V]])
; CHECK-NEXT: ret void
;
call void @foo4(i32 range(i32 1, 11) %v)
>From f1ecf0b99e5d7b60f64691b29d49cf3e996fb2e3 Mon Sep 17 00:00:00 2001
From: Noah Goldstein <goldstein.w.n at gmail.com>
Date: Wed, 2 Oct 2024 09:58:35 -0500
Subject: [PATCH 4/4] Skip constants
---
llvm/lib/Transforms/Utils/InlineFunction.cpp | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/llvm/lib/Transforms/Utils/InlineFunction.cpp b/llvm/lib/Transforms/Utils/InlineFunction.cpp
index bb4477d64fae42..df580325e25876 100644
--- a/llvm/lib/Transforms/Utils/InlineFunction.cpp
+++ b/llvm/lib/Transforms/Utils/InlineFunction.cpp
@@ -60,6 +60,7 @@
#include "llvm/IR/MDBuilder.h"
#include "llvm/IR/Metadata.h"
#include "llvm/IR/Module.h"
+#include "llvm/IR/PatternMatch.h"
#include "llvm/IR/ProfDataUtils.h"
#include "llvm/IR/Type.h"
#include "llvm/IR/User.h"
@@ -1418,6 +1419,11 @@ static void AddParamAndFnBasicAttributes(const CallBase &CB,
if (AL.hasParamAttr(I, Attribute::ByVal))
continue;
+ // Don't both propagating attrs to constants.
+ if (match(NewInnerCB->getArgOperand(I),
+ llvm::PatternMatch::m_ImmConstant()))
+ continue;
+
// Check if the underlying value for the parameter is an argument.
const Argument *Arg = dyn_cast<Argument>(InnerCB->getArgOperand(I));
unsigned ArgNo;
More information about the llvm-commits
mailing list