diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index ddad083571eb1..b0a8c55835868 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -156,6 +156,12 @@ Bug Fixes to Compiler Builtins Bug Fixes to Attribute Support ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Bug Fixes to C Support +^^^^^^^^^^^^^^^^^^^^^^ + +- No longer applying Named Return Value Optimization (NRVO) in C as it is a + non-conforming optimization in C. (#GH100902) + Bug Fixes to C++ Support ^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 694a754646f27..0697d12d2fe0d 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -2176,7 +2176,9 @@ static void CheckPoppedLabel(LabelDecl *L, Sema &S, } void Sema::ActOnPopScope(SourceLocation Loc, Scope *S) { - S->applyNRVO(); + // NRVO is only valid in C++, not in C. + if (getLangOpts().CPlusPlus) + S->applyNRVO(); if (S->decl_empty()) return; assert((S->getFlags() & (Scope::DeclScope | Scope::TemplateParamScope)) && diff --git a/clang/test/CodeGen/nrvo.c b/clang/test/CodeGen/nrvo.c new file mode 100644 index 0000000000000..39796ce2144e0 --- /dev/null +++ b/clang/test/CodeGen/nrvo.c @@ -0,0 +1,75 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 5 +// RUN: %clang_cc1 -triple i386-unknown-unknown -emit-llvm -disable-llvm-passes -o - %s | FileCheck --check-prefixes=C %s +// RUN: %clang_cc1 -triple i386-unknown-unknown -emit-llvm -disable-llvm-passes -o - -x c++ %s | FileCheck --check-prefixes=CXX %s + +// NRVO is not allowed in C as it is in C++, so validate that this results in +// use of NRVO in C++, but not in C. +typedef struct { + int i, j; + double a, b; +} S; + +int result; + +// C-LABEL: define dso_local void @test( +// C-SAME: ptr dead_on_unwind noalias writable sret([[STRUCT_S:%.*]]) align 4 [[AGG_RESULT:%.*]], ptr noundef [[Q:%.*]]) #[[ATTR0:[0-9]+]] { +// C-NEXT: [[ENTRY:.*:]] +// C-NEXT: [[RESULT_PTR:%.*]] = alloca ptr, align 4 +// C-NEXT: [[Q_ADDR:%.*]] = alloca ptr, align 4 +// C-NEXT: [[S:%.*]] = alloca [[STRUCT_S]], align 4 +// C-NEXT: store ptr [[AGG_RESULT]], ptr [[RESULT_PTR]], align 4 +// C-NEXT: store ptr [[Q]], ptr [[Q_ADDR]], align 4 +// C-NEXT: call void @llvm.memset.p0.i32(ptr align 4 [[S]], i8 0, i32 24, i1 false) +// C-NEXT: [[TMP0:%.*]] = load ptr, ptr [[Q_ADDR]], align 4 +// C-NEXT: [[CMP:%.*]] = icmp eq ptr [[S]], [[TMP0]] +// C-NEXT: [[CONV:%.*]] = zext i1 [[CMP]] to i32 +// C-NEXT: store i32 [[CONV]], ptr @result, align 4 +// C-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[AGG_RESULT]], ptr align 4 [[S]], i32 24, i1 false) +// C-NEXT: ret void +// +// CXX-LABEL: define dso_local void @_Z4testP1S( +// CXX-SAME: ptr dead_on_unwind noalias writable sret([[STRUCT_S:%.*]]) align 4 [[AGG_RESULT:%.*]], ptr noundef [[Q:%.*]]) #[[ATTR0:[0-9]+]] { +// CXX-NEXT: [[ENTRY:.*:]] +// CXX-NEXT: [[RESULT_PTR:%.*]] = alloca ptr, align 4 +// CXX-NEXT: [[Q_ADDR:%.*]] = alloca ptr, align 4 +// CXX-NEXT: store ptr [[AGG_RESULT]], ptr [[RESULT_PTR]], align 4 +// CXX-NEXT: store ptr [[Q]], ptr [[Q_ADDR]], align 4 +// CXX-NEXT: call void @llvm.memset.p0.i32(ptr align 4 [[AGG_RESULT]], i8 0, i32 24, i1 false) +// CXX-NEXT: [[TMP0:%.*]] = load ptr, ptr [[Q_ADDR]], align 4 +// CXX-NEXT: [[CMP:%.*]] = icmp eq ptr [[AGG_RESULT]], [[TMP0]] +// CXX-NEXT: [[CONV:%.*]] = zext i1 [[CMP]] to i32 +// CXX-NEXT: store i32 [[CONV]], ptr @result, align 4 +// CXX-NEXT: ret void +// +S test(S* q) { + S s = {0, 0, 0, 0}; + result = &s == q; + return s; +} + +// C-LABEL: define dso_local i32 @main( +// C-SAME: ) #[[ATTR0]] { +// C-NEXT: [[ENTRY:.*:]] +// C-NEXT: [[RETVAL:%.*]] = alloca i32, align 4 +// C-NEXT: [[T:%.*]] = alloca [[STRUCT_S:%.*]], align 4 +// C-NEXT: store i32 0, ptr [[RETVAL]], align 4 +// C-NEXT: call void @test(ptr dead_on_unwind writable sret([[STRUCT_S]]) align 4 [[T]], ptr noundef [[T]]) +// C-NEXT: [[TMP0:%.*]] = load i32, ptr @result, align 4 +// C-NEXT: ret i32 [[TMP0]] +// +// CXX-LABEL: define dso_local noundef i32 @main( +// CXX-SAME: ) #[[ATTR2:[0-9]+]] { +// CXX-NEXT: [[ENTRY:.*:]] +// CXX-NEXT: [[RETVAL:%.*]] = alloca i32, align 4 +// CXX-NEXT: [[T:%.*]] = alloca [[STRUCT_S:%.*]], align 4 +// CXX-NEXT: store i32 0, ptr [[RETVAL]], align 4 +// CXX-NEXT: call void @_Z4testP1S(ptr dead_on_unwind writable sret([[STRUCT_S]]) align 4 [[T]], ptr noundef [[T]]) +// CXX-NEXT: [[TMP0:%.*]] = load i32, ptr @result, align 4 +// CXX-NEXT: ret i32 [[TMP0]] +// +int main(void) +{ + S t = test(&t); + return result; +} +