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
}
/// 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;
// 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))
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;
}
; CHECK-NEXT: store i8 2, i8* [[PTR:%.*]], align 8
; CHECK-NEXT: ret void
;
-
entry:
br i1 %cond, label %bb1, label %bb3
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
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 }