-
Notifications
You must be signed in to change notification settings - Fork 13.4k
[Mem2Reg] Generate non-terminator unreachable for !noundef undef #96639
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
When performing a load from uninitialized memory using !noundef, insert a non-terminator unreachable instruction, which will be converted to a proper unreachable by SimplifyCFG. This way we retain the fact that UB occurred on this code path.
@llvm/pr-subscribers-llvm-transforms Author: Nikita Popov (nikic) ChangesWhen performing a load from uninitialized memory using !noundef, insert a non-terminator unreachable instruction, which will be converted to a proper unreachable by SimplifyCFG. This way we retain the fact that UB occurred on this code path. Full diff: https://github.com/llvm/llvm-project/pull/96639.diff 2 Files Affected:
diff --git a/llvm/lib/Transforms/Utils/PromoteMemoryToRegister.cpp b/llvm/lib/Transforms/Utils/PromoteMemoryToRegister.cpp
index 40d0f6b75d69b..a1210ac0f88ac 100644
--- a/llvm/lib/Transforms/Utils/PromoteMemoryToRegister.cpp
+++ b/llvm/lib/Transforms/Utils/PromoteMemoryToRegister.cpp
@@ -453,6 +453,15 @@ static void addAssumeNonNull(AssumptionCache *AC, LoadInst *LI) {
static void convertMetadataToAssumes(LoadInst *LI, Value *Val,
const DataLayout &DL, AssumptionCache *AC,
const DominatorTree *DT) {
+ if (isa<UndefValue>(Val) && LI->hasMetadata(LLVMContext::MD_noundef)) {
+ // Insert non-terminator unreachable.
+ LLVMContext &Ctx = LI->getContext();
+ new StoreInst(ConstantInt::getTrue(Ctx),
+ PoisonValue::get(PointerType::getUnqual(Ctx)),
+ /*isVolatile=*/false, Align(1), LI);
+ return;
+ }
+
// If the load was marked as nonnull we don't want to lose that information
// when we erase this Load. So we preserve it with an assume. As !nonnull
// returns poison while assume violations are immediate undefined behavior,
diff --git a/llvm/test/Transforms/Mem2Reg/preserve-nonnull-load-metadata.ll b/llvm/test/Transforms/Mem2Reg/preserve-nonnull-load-metadata.ll
index e3c14cc662e2a..edc07864795e8 100644
--- a/llvm/test/Transforms/Mem2Reg/preserve-nonnull-load-metadata.ll
+++ b/llvm/test/Transforms/Mem2Reg/preserve-nonnull-load-metadata.ll
@@ -143,6 +143,7 @@ fin:
define ptr @no_store_single_load_noundef() {
; CHECK-LABEL: @no_store_single_load_noundef(
; CHECK-NEXT: entry:
+; CHECK-NEXT: store i1 true, ptr poison, align 1
; CHECK-NEXT: ret ptr undef
;
entry:
@@ -156,8 +157,10 @@ define ptr @no_store_multiple_loads_noundef(i1 %c) {
; CHECK-NEXT: entry:
; CHECK-NEXT: br i1 [[C:%.*]], label [[IF:%.*]], label [[ELSE:%.*]]
; CHECK: if:
+; CHECK-NEXT: store i1 true, ptr poison, align 1
; CHECK-NEXT: ret ptr undef
; CHECK: else:
+; CHECK-NEXT: store i1 true, ptr poison, align 1
; CHECK-NEXT: ret ptr undef
;
entry:
@@ -176,8 +179,7 @@ if:
define ptr @no_store_single_load_nonnull_noundef() {
; CHECK-LABEL: @no_store_single_load_nonnull_noundef(
; CHECK-NEXT: entry:
-; CHECK-NEXT: [[TMP0:%.*]] = icmp ne ptr undef, null
-; CHECK-NEXT: call void @llvm.assume(i1 [[TMP0]])
+; CHECK-NEXT: store i1 true, ptr poison, align 1
; CHECK-NEXT: ret ptr undef
;
entry:
@@ -191,12 +193,10 @@ define ptr @no_store_multiple_loads_nonnull_noundef(i1 %c) {
; CHECK-NEXT: entry:
; CHECK-NEXT: br i1 [[C:%.*]], label [[IF:%.*]], label [[ELSE:%.*]]
; CHECK: if:
-; CHECK-NEXT: [[TMP0:%.*]] = icmp ne ptr undef, null
-; CHECK-NEXT: call void @llvm.assume(i1 [[TMP0]])
+; CHECK-NEXT: store i1 true, ptr poison, align 1
; CHECK-NEXT: ret ptr undef
; CHECK: else:
-; CHECK-NEXT: [[TMP1:%.*]] = icmp ne ptr undef, null
-; CHECK-NEXT: call void @llvm.assume(i1 [[TMP1]])
+; CHECK-NEXT: store i1 true, ptr poison, align 1
; CHECK-NEXT: ret ptr undef
;
entry:
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM. Can we also do this for SROA?
Do we have to, out of curiosity? Thought this was only being done here, exactly to relieve SROA from carrying that out. |
SROA uses mem2reg internally, so this mostly also covers SROA as well (apart from one edge case...) |
Why not use |
Store to poison is the canonical form for non-terminator unreachable. In fact, InstCombine will convert assume(false) to it: https://llvm.godbolt.org/z/rsfd7hzja |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM. Thanks!
This extends llvm#96639 for mem2reg to earlycse. If we try to replace a load with !noundef metadata by undef, insert a non-terminator unreachable. I've added a handleLoadOfUndef() helper for this purpose, which is shared between mem2reg and earlycse (and in the future also gvn etc). This also provides a place to experiment with replacing uninit loads with freeze poison.
…m#96639) When performing a load from uninitialized memory using !noundef, insert a non-terminator unreachable instruction, which will be converted to a proper unreachable by SimplifyCFG. This way we retain the fact that UB occurred on this code path.
…chable (#98910) Add the content from #96639 (comment).
…chable (#98910) Summary: Add the content from #96639 (comment). Test Plan: Reviewers: Subscribers: Tasks: Tags: Differential Revision: https://phabricator.intern.facebook.com/D60251667
When performing a load from uninitialized memory using !noundef, insert a non-terminator unreachable instruction, which will be converted to a proper unreachable by SimplifyCFG. This way we retain the fact that UB occurred on this code path.