diff --git a/llvm/include/llvm/IR/InstrTypes.h b/llvm/include/llvm/IR/InstrTypes.h --- a/llvm/include/llvm/IR/InstrTypes.h +++ b/llvm/include/llvm/IR/InstrTypes.h @@ -1653,6 +1653,17 @@ paramHasAttr(ArgNo, Attribute::Preallocated); } + /// Determine whether passing undef to this argument is undefined behavior. + /// If passing undef to this argument is UB, passing poison is UB as well + /// because poison is more undefined than undef. + bool isPassingUndefUB(unsigned ArgNo) const { + return paramHasAttr(ArgNo, Attribute::NoUndef) || + // dereferenceable implies noundef. + paramHasAttr(ArgNo, Attribute::Dereferenceable) || + // dereferenceable implies noundef, and null is a well-defined value. + paramHasAttr(ArgNo, Attribute::DereferenceableOrNull); + } + /// Determine if there are is an inalloca argument. Only the last argument can /// have the inalloca attribute. bool hasInAllocaArgument() const { diff --git a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp --- a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp +++ b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp @@ -6433,8 +6433,8 @@ 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)) { + if (CB->isPassingUndefUB(ArgIdx) && + CB->paramHasAttr(ArgIdx, Attribute::NonNull)) { // Passing null to a nonnnull+noundef argument is undefined. return !PtrValueMayBeModified; } @@ -6444,7 +6444,7 @@ for (const llvm::Use &Arg : CB->args()) if (Arg == I) { unsigned ArgIdx = CB->getArgOperandNo(&Arg); - if (CB->paramHasAttr(ArgIdx, Attribute::NoUndef)) { + if (CB->isPassingUndefUB(ArgIdx)) { // Passing undef to a noundef argument is undefined. return true; } diff --git a/llvm/test/Transforms/SimplifyCFG/UnreachableEliminate.ll b/llvm/test/Transforms/SimplifyCFG/UnreachableEliminate.ll --- a/llvm/test/Transforms/SimplifyCFG/UnreachableEliminate.ll +++ b/llvm/test/Transforms/SimplifyCFG/UnreachableEliminate.ll @@ -234,8 +234,9 @@ define void @test9_deref(i1 %X, i8* %Y) { ; CHECK-LABEL: @test9_deref( ; CHECK-NEXT: entry: -; CHECK-NEXT: [[SPEC_SELECT:%.*]] = select i1 [[X:%.*]], i8* null, i8* [[Y:%.*]] -; CHECK-NEXT: [[TMP0:%.*]] = call i8* @fn_nonnull_deref_arg(i8* [[SPEC_SELECT]]) +; CHECK-NEXT: [[TMP0:%.*]] = xor i1 [[X:%.*]], true +; CHECK-NEXT: call void @llvm.assume(i1 [[TMP0]]) +; CHECK-NEXT: [[TMP1:%.*]] = call i8* @fn_nonnull_deref_arg(i8* [[Y:%.*]]) ; CHECK-NEXT: ret void ; entry: @@ -254,8 +255,9 @@ define void @test9_deref_or_null(i1 %X, i8* %Y) { ; CHECK-LABEL: @test9_deref_or_null( ; CHECK-NEXT: entry: -; CHECK-NEXT: [[SPEC_SELECT:%.*]] = select i1 [[X:%.*]], i8* null, i8* [[Y:%.*]] -; CHECK-NEXT: [[TMP0:%.*]] = call i8* @fn_nonnull_deref_or_null_arg(i8* [[SPEC_SELECT]]) +; CHECK-NEXT: [[TMP0:%.*]] = xor i1 [[X:%.*]], true +; CHECK-NEXT: call void @llvm.assume(i1 [[TMP0]]) +; CHECK-NEXT: [[TMP1:%.*]] = call i8* @fn_nonnull_deref_or_null_arg(i8* [[Y:%.*]]) ; CHECK-NEXT: ret void ; entry: