[llvm-branch-commits] [llvm] a150010 - [SimplifyCFG] Optimize CFG when null is passed to a function with nonnull argument
Dávid Bolvanský via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Fri Jan 15 14:57:59 PST 2021
Author: Dávid Bolvanský
Date: 2021-01-15T23:53:43+01:00
New Revision: a1500105ee6074f992f376c916dcfa3a54acb717
URL: https://github.com/llvm/llvm-project/commit/a1500105ee6074f992f376c916dcfa3a54acb717
DIFF: https://github.com/llvm/llvm-project/commit/a1500105ee6074f992f376c916dcfa3a54acb717.diff
LOG: [SimplifyCFG] Optimize CFG when null is passed to a function with nonnull argument
Example:
```
__attribute__((nonnull,noinline)) char * pinc(char *p) {
return ++p;
}
char * foo(bool b, char *a) {
return pinc(b ? 0 : a);
}
```
optimize to
```
char * foo(bool b, char *a) {
return pinc(a);
}
```
Reviewed By: jdoerfert
Differential Revision: https://reviews.llvm.org/D94180
Added:
Modified:
llvm/lib/Transforms/Utils/SimplifyCFG.cpp
llvm/test/Transforms/SimplifyCFG/UnreachableEliminate.ll
Removed:
################################################################################
diff --git a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
index 559830ed9a17..7fe33fd3c759 100644
--- a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
+++ b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
@@ -1345,7 +1345,7 @@ static bool isSafeToHoistInvoke(BasicBlock *BB1, BasicBlock *BB2,
return true;
}
-static bool passingValueIsAlwaysUndefined(Value *V, Instruction *I);
+static bool passingValueIsAlwaysUndefined(Value *V, Instruction *I, bool PtrValueMayBeModified = false);
/// Given a conditional branch that goes to BB1 and BB2, hoist any common code
/// in the two blocks up into the branch block. The caller of this function
@@ -6545,7 +6545,7 @@ bool SimplifyCFGOpt::simplifyCondBranch(BranchInst *BI, IRBuilder<> &Builder) {
}
/// Check if passing a value to an instruction will cause undefined behavior.
-static bool passingValueIsAlwaysUndefined(Value *V, Instruction *I) {
+static bool passingValueIsAlwaysUndefined(Value *V, Instruction *I, bool PtrValueMayBeModified) {
Constant *C = dyn_cast<Constant>(V);
if (!C)
return false;
@@ -6568,12 +6568,15 @@ static bool passingValueIsAlwaysUndefined(Value *V, Instruction *I) {
// Look through GEPs. A load from a GEP derived from NULL is still undefined
if (GetElementPtrInst *GEP = dyn_cast<GetElementPtrInst>(Use))
- if (GEP->getPointerOperand() == I)
- return passingValueIsAlwaysUndefined(V, GEP);
+ if (GEP->getPointerOperand() == I) {
+ if (!GEP->isInBounds() || !GEP->hasAllZeroIndices())
+ PtrValueMayBeModified = true;
+ return passingValueIsAlwaysUndefined(V, GEP, PtrValueMayBeModified);
+ }
// Look through bitcasts.
if (BitCastInst *BC = dyn_cast<BitCastInst>(Use))
- return passingValueIsAlwaysUndefined(V, BC);
+ return passingValueIsAlwaysUndefined(V, BC, PtrValueMayBeModified);
// Load from null is undefined.
if (LoadInst *LI = dyn_cast<LoadInst>(Use))
@@ -6588,10 +6591,35 @@ static bool passingValueIsAlwaysUndefined(Value *V, Instruction *I) {
SI->getPointerAddressSpace())) &&
SI->getPointerOperand() == I;
- // A call to null is undefined.
- if (auto *CB = dyn_cast<CallBase>(Use))
- return !NullPointerIsDefined(CB->getFunction()) &&
- CB->getCalledOperand() == I;
+ if (auto *CB = dyn_cast<CallBase>(Use)) {
+ if (C->isNullValue() && NullPointerIsDefined(CB->getFunction()))
+ return false;
+ // A call to null is undefined.
+ if (CB->getCalledOperand() == I)
+ return true;
+
+ if (C->isNullValue()) {
+ for (const llvm::Use &Arg : CB->args())
+ if (Arg == I) {
+ unsigned ArgIdx = CB->getArgOperandNo(&Arg);
+ if (CB->paramHasAttr(ArgIdx, Attribute::NonNull) &&
+ CB->paramHasAttr(ArgIdx, Attribute::NoUndef)) {
+ // Passing null to a nonnnull+noundef argument is undefined.
+ return !PtrValueMayBeModified;
+ }
+ }
+ } else if (isa<UndefValue>(C)) {
+ // Passing undef to a noundef argument is undefined.
+ for (const llvm::Use &Arg : CB->args())
+ if (Arg == I) {
+ unsigned ArgIdx = CB->getArgOperandNo(&Arg);
+ if (CB->paramHasAttr(ArgIdx, Attribute::NoUndef)) {
+ // Passing undef to a noundef argument is undefined.
+ return true;
+ }
+ }
+ }
+ }
}
return false;
}
diff --git a/llvm/test/Transforms/SimplifyCFG/UnreachableEliminate.ll b/llvm/test/Transforms/SimplifyCFG/UnreachableEliminate.ll
index b3b7abe9ff0d..4c984819cfcf 100644
--- a/llvm/test/Transforms/SimplifyCFG/UnreachableEliminate.ll
+++ b/llvm/test/Transforms/SimplifyCFG/UnreachableEliminate.ll
@@ -69,7 +69,6 @@ define void @test5(i1 %cond, i8* %ptr) {
; CHECK-NEXT: store i8 2, i8* [[PTR:%.*]], align 8
; CHECK-NEXT: ret void
;
-
entry:
br i1 %cond, label %bb1, label %bb3
@@ -88,11 +87,10 @@ bb2:
define void @test5_no_null_opt(i1 %cond, i8* %ptr) #0 {
; CHECK-LABEL: @test5_no_null_opt(
; CHECK-NEXT: entry:
-; CHECK-NEXT: [[DOTPTR:%.*]] = select i1 [[COND:%.*]], i8* null, i8* [[PTR:%.*]]
-; CHECK-NEXT: store i8 2, i8* [[DOTPTR]], align 8
+; CHECK-NEXT: [[PTR_2:%.*]] = select i1 [[COND:%.*]], i8* null, i8* [[PTR:%.*]]
+; CHECK-NEXT: store i8 2, i8* [[PTR_2]], align 8
; CHECK-NEXT: ret void
;
-
entry:
br i1 %cond, label %bb1, label %bb3
@@ -206,4 +204,296 @@ else:
ret void
}
+declare i8* @fn_nonnull_noundef_arg(i8* nonnull noundef %p)
+declare i8* @fn_nonnull_arg(i8* nonnull %p)
+declare i8* @fn_noundef_arg(i8* noundef %p)
+
+define void @test9(i1 %X, i8* %Y) {
+; CHECK-LABEL: @test9(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[TMP0:%.*]] = xor i1 [[X:%.*]], true
+; CHECK-NEXT: call void @llvm.assume(i1 [[TMP0]])
+; CHECK-NEXT: [[TMP1:%.*]] = call i8* @fn_nonnull_noundef_arg(i8* [[Y:%.*]])
+; CHECK-NEXT: ret void
+;
+entry:
+ br i1 %X, label %if, label %else
+
+if:
+ br label %else
+
+else:
+ %phi = phi i8* [ %Y, %entry ], [ null, %if ]
+ call i8* @fn_nonnull_noundef_arg(i8* %phi)
+ ret void
+}
+
+define void @test9_undef(i1 %X, i8* %Y) {
+; CHECK-LABEL: @test9_undef(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[TMP0:%.*]] = call i8* @fn_noundef_arg(i8* [[Y:%.*]])
+; CHECK-NEXT: ret void
+;
+entry:
+ br i1 %X, label %if, label %else
+
+if:
+ br label %else
+
+else:
+ %phi = phi i8* [ %Y, %entry ], [ undef, %if ]
+ call i8* @fn_noundef_arg(i8* %phi)
+ ret void
+}
+
+define void @test9_undef_null_defined(i1 %X, i8* %Y) #0 {
+; CHECK-LABEL: @test9_undef_null_defined(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[TMP0:%.*]] = call i8* @fn_noundef_arg(i8* [[Y:%.*]])
+; CHECK-NEXT: ret void
+;
+entry:
+ br i1 %X, label %if, label %else
+
+if:
+ br label %else
+
+else:
+ %phi = phi i8* [ %Y, %entry ], [ undef, %if ]
+ call i8* @fn_noundef_arg(i8* %phi)
+ ret void
+}
+
+define void @test9_null_callsite(i1 %X, i8* %Y) {
+; CHECK-LABEL: @test9_null_callsite(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[TMP0:%.*]] = xor i1 [[X:%.*]], true
+; CHECK-NEXT: call void @llvm.assume(i1 [[TMP0]])
+; CHECK-NEXT: [[TMP1:%.*]] = call i8* @fn_nonnull_arg(i8* noundef nonnull [[Y:%.*]])
+; CHECK-NEXT: ret void
+;
+entry:
+ br i1 %X, label %if, label %else
+
+if:
+ br label %else
+
+else:
+ %phi = phi i8* [ %Y, %entry ], [ null, %if ]
+ call i8* @fn_nonnull_arg(i8* nonnull noundef %phi)
+ ret void
+}
+
+define void @test9_gep_mismatch(i1 %X, i8* %Y, i8* %P) {
+; CHECK-LABEL: @test9_gep_mismatch(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[SPEC_SELECT:%.*]] = select i1 [[X:%.*]], i8* null, i8* [[Y:%.*]]
+; CHECK-NEXT: [[GEP:%.*]] = getelementptr inbounds i8, i8* [[P:%.*]], i64 0
+; CHECK-NEXT: [[TMP0:%.*]] = call i8* @fn_nonnull_noundef_arg(i8* [[GEP]])
+; CHECK-NEXT: ret void
+;
+entry:
+ br i1 %X, label %if, label %else
+
+if:
+ br label %else
+
+else:
+ %phi = phi i8* [ %Y, %entry ], [ null, %if ]
+ %gep = getelementptr inbounds i8, i8* %P, i64 0
+ call i8* @fn_nonnull_noundef_arg(i8* %gep)
+ ret void
+}
+
+define void @test9_gep_zero(i1 %X, i8* %Y) {
+; CHECK-LABEL: @test9_gep_zero(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[TMP0:%.*]] = xor i1 [[X:%.*]], true
+; CHECK-NEXT: call void @llvm.assume(i1 [[TMP0]])
+; CHECK-NEXT: [[GEP:%.*]] = getelementptr inbounds i8, i8* [[Y:%.*]], i64 0
+; CHECK-NEXT: [[TMP1:%.*]] = call i8* @fn_nonnull_noundef_arg(i8* [[GEP]])
+; CHECK-NEXT: ret void
+;
+entry:
+ br i1 %X, label %if, label %else
+
+if:
+ br label %else
+
+else:
+ %phi = phi i8* [ %Y, %entry ], [ null, %if ]
+ %gep = getelementptr inbounds i8, i8* %phi, i64 0
+ call i8* @fn_nonnull_noundef_arg(i8* %gep)
+ ret void
+}
+
+define void @test9_gep_bitcast(i1 %X, i32* %Y) {
+; CHECK-LABEL: @test9_gep_bitcast(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[TMP0:%.*]] = xor i1 [[X:%.*]], true
+; CHECK-NEXT: call void @llvm.assume(i1 [[TMP0]])
+; CHECK-NEXT: [[GEP:%.*]] = getelementptr inbounds i32, i32* [[Y:%.*]], i64 0
+; CHECK-NEXT: [[BC:%.*]] = bitcast i32* [[GEP]] to i8*
+; CHECK-NEXT: [[TMP1:%.*]] = call i8* @fn_nonnull_noundef_arg(i8* [[BC]])
+; CHECK-NEXT: ret void
+;
+entry:
+ br i1 %X, label %if, label %else
+
+if:
+ br label %else
+
+else:
+ %phi = phi i32* [ %Y, %entry ], [ null, %if ]
+ %gep = getelementptr inbounds i32, i32* %phi, i64 0
+ %bc = bitcast i32* %gep to i8*
+ call i8* @fn_nonnull_noundef_arg(i8* %bc)
+ ret void
+}
+
+define void @test9_gep_nonzero(i1 %X, i8* %Y) {
+; CHECK-LABEL: @test9_gep_nonzero(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[SPEC_SELECT:%.*]] = select i1 [[X:%.*]], i8* null, i8* [[Y:%.*]]
+; CHECK-NEXT: [[GEP:%.*]] = getelementptr i8, i8* [[SPEC_SELECT]], i64 12
+; CHECK-NEXT: [[TMP0:%.*]] = call i8* @fn_nonnull_noundef_arg(i8* [[GEP]])
+; CHECK-NEXT: ret void
+;
+entry:
+ br i1 %X, label %if, label %else
+
+if:
+ br label %else
+
+else:
+ %phi = phi i8* [ %Y, %entry ], [ null, %if ]
+ %gep = getelementptr i8, i8* %phi, i64 12
+ call i8* @fn_nonnull_noundef_arg(i8* %gep)
+ ret void
+}
+
+define void @test9_gep_inbounds_nonzero(i1 %X, i8* %Y) {
+; CHECK-LABEL: @test9_gep_inbounds_nonzero(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[SPEC_SELECT:%.*]] = select i1 [[X:%.*]], i8* null, i8* [[Y:%.*]]
+; CHECK-NEXT: [[GEP:%.*]] = getelementptr inbounds i8, i8* [[SPEC_SELECT]], i64 12
+; CHECK-NEXT: [[TMP0:%.*]] = call i8* @fn_nonnull_noundef_arg(i8* [[GEP]])
+; CHECK-NEXT: ret void
+;
+entry:
+ br i1 %X, label %if, label %else
+
+if:
+ br label %else
+
+else:
+ %phi = phi i8* [ %Y, %entry ], [ null, %if ]
+ %gep = getelementptr inbounds i8, i8* %phi, i64 12
+ call i8* @fn_nonnull_noundef_arg(i8* %gep)
+ ret void
+}
+
+
+define void @test9_gep_inbouds_unknown_null(i1 %X, i8* %Y, i64 %I) {
+; CHECK-LABEL: @test9_gep_inbouds_unknown_null(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[SPEC_SELECT:%.*]] = select i1 [[X:%.*]], i8* null, i8* [[Y:%.*]]
+; CHECK-NEXT: [[GEP:%.*]] = getelementptr inbounds i8, i8* [[SPEC_SELECT]], i64 [[I:%.*]]
+; CHECK-NEXT: [[TMP0:%.*]] = call i8* @fn_nonnull_noundef_arg(i8* [[GEP]])
+; CHECK-NEXT: ret void
+;
+entry:
+ br i1 %X, label %if, label %else
+
+if:
+ br label %else
+
+else:
+ %phi = phi i8* [ %Y, %entry ], [ null, %if ]
+ %gep = getelementptr inbounds i8, i8* %phi, i64 %I
+ call i8* @fn_nonnull_noundef_arg(i8* %gep)
+ ret void
+}
+
+define void @test9_gep_unknown_null(i1 %X, i8* %Y, i64 %I) {
+; CHECK-LABEL: @test9_gep_unknown_null(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[SPEC_SELECT:%.*]] = select i1 [[X:%.*]], i8* null, i8* [[Y:%.*]]
+; CHECK-NEXT: [[GEP:%.*]] = getelementptr i8, i8* [[SPEC_SELECT]], i64 [[I:%.*]]
+; CHECK-NEXT: [[TMP0:%.*]] = call i8* @fn_nonnull_noundef_arg(i8* [[GEP]])
+; CHECK-NEXT: ret void
+;
+entry:
+ br i1 %X, label %if, label %else
+
+if:
+ br label %else
+
+else:
+ %phi = phi i8* [ %Y, %entry ], [ null, %if ]
+ %gep = getelementptr i8, i8* %phi, i64 %I
+ call i8* @fn_nonnull_noundef_arg(i8* %gep)
+ ret void
+}
+
+define void @test9_gep_unknown_undef(i1 %X, i8* %Y, i64 %I) {
+; CHECK-LABEL: @test9_gep_unknown_undef(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[GEP:%.*]] = getelementptr i8, i8* [[Y:%.*]], i64 [[I:%.*]]
+; CHECK-NEXT: [[TMP0:%.*]] = call i8* @fn_noundef_arg(i8* [[GEP]])
+; CHECK-NEXT: ret void
+;
+entry:
+ br i1 %X, label %if, label %else
+
+if:
+ br label %else
+
+else:
+ %phi = phi i8* [ %Y, %entry ], [ undef, %if ]
+ %gep = getelementptr i8, i8* %phi, i64 %I
+ call i8* @fn_noundef_arg(i8* %gep)
+ ret void
+}
+
+define void @test9_missing_noundef(i1 %X, i8* %Y) {
+; CHECK-LABEL: @test9_missing_noundef(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[SPEC_SELECT:%.*]] = select i1 [[X:%.*]], i8* null, i8* [[Y:%.*]]
+; CHECK-NEXT: [[TMP0:%.*]] = call i8* @fn_nonnull_arg(i8* [[SPEC_SELECT]])
+; CHECK-NEXT: ret void
+;
+entry:
+ br i1 %X, label %if, label %else
+
+if:
+ br label %else
+
+else:
+ %phi = phi i8* [ %Y, %entry ], [ null, %if ]
+ call i8* @fn_nonnull_arg(i8* %phi)
+ ret void
+}
+
+define void @test9_null_defined(i1 %X, i8* %Y) #0 {
+; CHECK-LABEL: @test9_null_defined(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[SPEC_SELECT:%.*]] = select i1 [[X:%.*]], i8* null, i8* [[Y:%.*]]
+; CHECK-NEXT: [[TMP0:%.*]] = call i8* @fn_nonnull_noundef_arg(i8* [[SPEC_SELECT]])
+; CHECK-NEXT: ret void
+;
+entry:
+ br i1 %X, label %if, label %else
+
+if:
+ br label %else
+
+else:
+ %phi = phi i8* [ %Y, %entry ], [ null, %if ]
+ call i8* @fn_nonnull_noundef_arg(i8* %phi)
+ ret void
+}
+
+
+
attributes #0 = { null_pointer_is_valid }
More information about the llvm-branch-commits
mailing list