[Inline] Allow inlining with null_pointer_is_valid mismatch (#190510)
If the callee has null_pointer_is_valid but the caller does not, we
should still inline and add null_pointer_is_valid to the caller (which
is handled by an already existing inline adjustment rule).
This does mean that optimizations in the caller may be reduced by
unnecessarily preserving null checks, but that's still better than not
inlining at all. In particular, this check causes issues with LTO in the
Linux kernel, as the C portions are compiled with null_pointer_is_valid,
but the Rust portions are not.
The test is modified to show that the previous alwaysinline behavior now
always holds.
diff --git a/llvm/lib/Analysis/InlineCost.cpp b/llvm/lib/Analysis/InlineCost.cpp
index f0ce00b..e1e47b6 100644
--- a/llvm/lib/Analysis/InlineCost.cpp
+++ b/llvm/lib/Analysis/InlineCost.cpp
@@ -3240,11 +3240,6 @@
if (Caller->hasOptNone())
return InlineResult::failure("optnone attribute");
- // Don't inline a function that treats null pointer as valid into a caller
- // that does not have this attribute.
- if (!Caller->nullPointerIsDefined() && Callee->nullPointerIsDefined())
- return InlineResult::failure("nullptr definitions incompatible");
-
// Don't inline functions which can be interposed at link-time.
if (Callee->isInterposable())
return InlineResult::failure("interposable");
diff --git a/llvm/test/Transforms/Inline/attributes.ll b/llvm/test/Transforms/Inline/attributes.ll
index cfc393a..9052d27 100644
--- a/llvm/test/Transforms/Inline/attributes.ll
+++ b/llvm/test/Transforms/Inline/attributes.ll
@@ -429,10 +429,9 @@
; CHECK-NEXT: ret i32
}
-; Callee with null_pointer_is_valid attribute should not be inlined
-; into a caller without this attribute.
-; Exception: alwaysinline callee can still be inlined but
-; null_pointer_is_valid should get copied to caller.
+; Callee with null_pointer_is_valid attribute should be inlined
+; into a caller without this attribute, and set the attribute on
+; the caller.
define i32 @null-pointer-is-valid_callee0(i32 %i) null_pointer_is_valid {
ret i32 %i
@@ -440,43 +439,24 @@
; CHECK-NEXT: ret i32
}
-define i32 @null-pointer-is-valid_callee1(i32 %i) alwaysinline null_pointer_is_valid {
+define i32 @null-pointer-is-valid_callee1(i32 %i) {
ret i32 %i
; CHECK: @null-pointer-is-valid_callee1(i32 %i)
; CHECK-NEXT: ret i32
}
-define i32 @null-pointer-is-valid_callee2(i32 %i) {
- ret i32 %i
-; CHECK: @null-pointer-is-valid_callee2(i32 %i)
-; CHECK-NEXT: ret i32
-}
-
-; No inlining since caller does not have null_pointer_is_valid attribute.
define i32 @test_null-pointer-is-valid0(i32 %i) {
%1 = call i32 @null-pointer-is-valid_callee0(i32 %i)
ret i32 %1
-; CHECK: @test_null-pointer-is-valid0(
-; CHECK: call i32 @null-pointer-is-valid_callee0
+; CHECK: @test_null-pointer-is-valid0(i32 %i) [[NULLPOINTERISVALID:#[0-9]+]] {
; CHECK-NEXT: ret i32
}
-; alwaysinline should force inlining even when caller does not have
-; null_pointer_is_valid attribute. However, the attribute should be
-; copied to caller.
-define i32 @test_null-pointer-is-valid1(i32 %i) {
+; Nothing to do since calleer already has null_pointer_is_valid attribute.
+define i32 @test_null-pointer-is-valid1(i32 %i) null_pointer_is_valid {
%1 = call i32 @null-pointer-is-valid_callee1(i32 %i)
ret i32 %1
-; CHECK: @test_null-pointer-is-valid1(i32 %i) [[NULLPOINTERISVALID:#[0-9]+]] {
-; CHECK-NEXT: ret i32
-}
-
-; Can inline since both caller and callee have null_pointer_is_valid
-; attribute.
-define i32 @test_null-pointer-is-valid2(i32 %i) null_pointer_is_valid {
- %1 = call i32 @null-pointer-is-valid_callee2(i32 %i)
- ret i32 %1
-; CHECK: @test_null-pointer-is-valid2(i32 %i) [[NULLPOINTERISVALID]] {
+; CHECK: @test_null-pointer-is-valid1(i32 %i) [[NULLPOINTERISVALID]] {
; CHECK-NEXT: ret i32
}