diff --git a/llvm/lib/Transforms/IPO/SCCP.cpp b/llvm/lib/Transforms/IPO/SCCP.cpp --- a/llvm/lib/Transforms/IPO/SCCP.cpp +++ b/llvm/lib/Transforms/IPO/SCCP.cpp @@ -338,9 +338,14 @@ // Remove the returned attribute for zapped functions and the // corresponding call sites. + // Also remove any attributes that convert an undef return value into + // immediate undefined behavior + AttributeMask UBImplyingAttributes = + AttributeFuncs::getUBImplyingAttributes(); for (Function *F : FuncZappedReturn) { for (Argument &A : F->args()) F->removeParamAttr(A.getArgNo(), Attribute::Returned); + F->removeRetAttrs(UBImplyingAttributes); for (Use &U : F->uses()) { CallBase *CB = dyn_cast(U.getUser()); if (!CB) { @@ -354,6 +359,7 @@ for (Use &Arg : CB->args()) CB->removeParamAttr(CB->getArgOperandNo(&Arg), Attribute::Returned); + CB->removeRetAttrs(UBImplyingAttributes); } } diff --git a/llvm/test/Transforms/SCCP/ipsccp-noundef.ll b/llvm/test/Transforms/SCCP/ipsccp-noundef.ll --- a/llvm/test/Transforms/SCCP/ipsccp-noundef.ll +++ b/llvm/test/Transforms/SCCP/ipsccp-noundef.ll @@ -2,26 +2,27 @@ @g = external global i8 define internal noundef i32 @ret_noundef() { -; CHECK-LABEL: define internal noundef i32 @ret_noundef() { +; CHECK-LABEL: define internal i32 @ret_noundef() { ; CHECK-NEXT: ret i32 undef ; ret i32 0 } define internal dereferenceable(1) ptr @ret_dereferenceable() { -; CHECK-LABEL: define internal dereferenceable(1) ptr @ret_dereferenceable() { +; CHECK-LABEL: define internal ptr @ret_dereferenceable() { ; CHECK-NEXT: ret ptr undef ; ret ptr @g } define internal dereferenceable_or_null(1) ptr @ret_dereferenceable_or_null() { -; CHECK-LABEL: define internal dereferenceable_or_null(1) ptr @ret_dereferenceable_or_null() { +; CHECK-LABEL: define internal ptr @ret_dereferenceable_or_null() { ; CHECK-NEXT: ret ptr undef ; ret ptr @g } +; Non-null is fine, because it does not cause immediate UB. define internal nonnull ptr @ret_nonnull() { ; CHECK-LABEL: define internal nonnull ptr @ret_nonnull() { ; CHECK-NEXT: ret ptr undef @@ -29,8 +30,8 @@ ret ptr @g } -define internal nonnull noundef ptr @ret_nonnull_noundef() { -; CHECK-LABEL: define internal noundef nonnull ptr @ret_nonnull_noundef() { +define internal nonnull ptr @ret_nonnull_noundef() { +; CHECK-LABEL: define internal nonnull ptr @ret_nonnull_noundef() { ; CHECK-NEXT: ret ptr undef ; ret ptr @g @@ -38,11 +39,11 @@ define void @test() { ; CHECK-LABEL: define void @test() { -; CHECK-NEXT: [[TMP1:%.*]] = call noundef i32 @ret_noundef() -; CHECK-NEXT: [[TMP2:%.*]] = call dereferenceable(1) ptr @ret_dereferenceable() -; CHECK-NEXT: [[TMP3:%.*]] = call dereferenceable_or_null(1) ptr @ret_dereferenceable_or_null() +; CHECK-NEXT: [[TMP1:%.*]] = call i32 @ret_noundef() +; CHECK-NEXT: [[TMP2:%.*]] = call ptr @ret_dereferenceable() +; CHECK-NEXT: [[TMP3:%.*]] = call ptr @ret_dereferenceable_or_null() ; CHECK-NEXT: [[TMP4:%.*]] = call nonnull ptr @ret_nonnull() -; CHECK-NEXT: [[TMP5:%.*]] = call noundef nonnull ptr @ret_nonnull_noundef() +; CHECK-NEXT: [[TMP5:%.*]] = call nonnull ptr @ret_nonnull_noundef() ; CHECK-NEXT: ret void ; call noundef i32 @ret_noundef()