https://github.com/nickdesaulniers updated https://github.com/llvm/llvm-project/pull/219045
>From 83e4a08a86b398b6e3f766b2ee32b460e487859d Mon Sep 17 00:00:00 2001 From: Nick Desaulniers <[email protected]> Date: Wed, 26 Aug 2026 13:41:55 -0700 Subject: [PATCH] [clang][CodeGen] Emit lifetime markers for block-scoped compound literals in C MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In C (C99/C11/C23 ยง6.2.4.5), block-scoped compound literals have automatic storage duration extending to the end of the enclosing block scope. Previously, Clang emitted alloca storage without lifetime markers (@llvm.lifetime.start / @llvm.lifetime.end), preventing LLVM's StackColoring pass from merging stack slots across disjoint scopes and inflating stack frame size. Following John McCall's guidance in #68746: 1. In Sema, record block-scoped compound literals in ExprCleanupObjects so the enclosing full-expression is wrapped in an ExprWithCleanups. 2. In CodeGen, emit @llvm.lifetime.start before evaluating initializers and schedule CallLifetimeEnd as a cleanup. Checking a build of an x86_64 Linux kernel defconfig build, this shaves 448 bytes off the stack usage for azx_probe(), one of the larger stack users in the kernel. Assisted-by: Gemini Fixes: https://github.com/llvm/llvm-project/issues/68746 --- clang/lib/CodeGen/CGExpr.cpp | 7 + clang/lib/Sema/JumpDiagnostics.cpp | 2 + clang/lib/Sema/SemaExpr.cpp | 4 +- clang/test/AST/ast-dump-expr-json.c | 226 +++++++++++------- clang/test/C/C11/n1285_1.c | 2 + .../CodeGen/attr-counted-by-with-sanitizers.c | 10 +- .../attr-counted-by-without-sanitizers.c | 4 + .../test/CodeGen/compound-literal-lifetime.c | 93 +++++++ clang/test/CodeGenObjC/arc-ternary-op.m | 2 + clang/test/Sema/scope-check.c | 14 ++ 10 files changed, 274 insertions(+), 90 deletions(-) create mode 100644 clang/test/CodeGen/compound-literal-lifetime.c diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp index eff6a7de320d7..19700d2fcf665 100644 --- a/clang/lib/CodeGen/CGExpr.cpp +++ b/clang/lib/CodeGen/CGExpr.cpp @@ -6005,6 +6005,13 @@ LValue CodeGenFunction::EmitCompoundLiteralLValue(const CompoundLiteralExpr *E){ const Expr *InitExpr = E->getInitializer(); LValue Result = MakeAddrLValue(DeclPtr, E->getType(), AlignmentSource::Decl); + if (!getLangOpts().CPlusPlus) { + if (HaveInsertPoint() && !hasLabelBeenSeenInCurrentScope() && + EmitLifetimeStart(DeclPtr.getBasePointer())) + pushCleanupAfterFullExpr<CallLifetimeEnd>(NormalEHLifetimeMarker, + DeclPtr); + } + EmitAnyExprToMem(InitExpr, DeclPtr, E->getType().getQualifiers(), /*Init*/ true); diff --git a/clang/lib/Sema/JumpDiagnostics.cpp b/clang/lib/Sema/JumpDiagnostics.cpp index b4a9cc12029d2..bbe6f644609bf 100644 --- a/clang/lib/Sema/JumpDiagnostics.cpp +++ b/clang/lib/Sema/JumpDiagnostics.cpp @@ -293,6 +293,8 @@ void JumpScopeChecker::BuildScopeInformation(VarDecl *D, /// non-trivial to destruct. void JumpScopeChecker::BuildScopeInformation(CompoundLiteralExpr *CLE, unsigned &ParentScope) { + if (!CLE->getType().isDestructedType()) + return; unsigned InDiag = diag::note_enters_compound_literal_scope; unsigned OutDiag = diag::note_exits_compound_literal_scope; Scopes.push_back(GotoScope(ParentScope, InDiag, OutDiag, CLE->getExprLoc())); diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index 2b524a956ecc4..48d80f5fd7ae8 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -7578,9 +7578,9 @@ Sema::BuildCompoundLiteralExpr(SourceLocation LParenLoc, TypeSourceInfo *TInfo, NTCUK_Destruct); // Diagnose jumps that enter or exit the lifetime of the compound literal. + Cleanup.setExprNeedsCleanups(true); + ExprCleanupObjects.push_back(E); if (literalType.isDestructedType()) { - Cleanup.setExprNeedsCleanups(true); - ExprCleanupObjects.push_back(E); getCurFunction()->setHasBranchProtectedScope(); } } diff --git a/clang/test/AST/ast-dump-expr-json.c b/clang/test/AST/ast-dump-expr-json.c index ecb6191c52200..48a6f19b55371 100644 --- a/clang/test/AST/ast-dump-expr-json.c +++ b/clang/test/AST/ast-dump-expr-json.c @@ -4650,7 +4650,7 @@ void PrimaryExpressions(int a) { // CHECK-NEXT: }, // CHECK-NEXT: { // CHECK-NEXT: "id": "0x{{.*}}", -// CHECK-NEXT: "kind": "ImplicitCastExpr", +// CHECK-NEXT: "kind": "ExprWithCleanups", // CHECK-NEXT: "range": { // CHECK-NEXT: "begin": { // CHECK-NEXT: "offset": {{[0-9]+}}, @@ -4668,11 +4668,17 @@ void PrimaryExpressions(int a) { // CHECK-NEXT: "qualType": "int *" // CHECK-NEXT: }, // CHECK-NEXT: "valueCategory": "prvalue", -// CHECK-NEXT: "castKind": "ArrayToPointerDecay", +// CHECK-NEXT: "cleanupsHaveSideEffects": true, +// CHECK-NEXT: "cleanups": [ +// CHECK-NEXT: { +// CHECK-NEXT: "id": "0x{{.*}}", +// CHECK-NEXT: "kind": "CompoundLiteralExpr" +// CHECK-NEXT: } +// CHECK-NEXT: ], // CHECK-NEXT: "inner": [ // CHECK-NEXT: { // CHECK-NEXT: "id": "0x{{.*}}", -// CHECK-NEXT: "kind": "CompoundLiteralExpr", +// CHECK-NEXT: "kind": "ImplicitCastExpr", // CHECK-NEXT: "range": { // CHECK-NEXT: "begin": { // CHECK-NEXT: "offset": {{[0-9]+}}, @@ -4686,17 +4692,18 @@ void PrimaryExpressions(int a) { // CHECK-NEXT: } // CHECK-NEXT: }, // CHECK-NEXT: "type": { -// CHECK-NEXT: "qualType": "int[4]" +// CHECK-NEXT: "qualType": "int *" // CHECK-NEXT: }, -// CHECK-NEXT: "valueCategory": "lvalue", +// CHECK-NEXT: "valueCategory": "prvalue", +// CHECK-NEXT: "castKind": "ArrayToPointerDecay", // CHECK-NEXT: "inner": [ // CHECK-NEXT: { // CHECK-NEXT: "id": "0x{{.*}}", -// CHECK-NEXT: "kind": "InitListExpr", +// CHECK-NEXT: "kind": "CompoundLiteralExpr", // CHECK-NEXT: "range": { // CHECK-NEXT: "begin": { // CHECK-NEXT: "offset": {{[0-9]+}}, -// CHECK-NEXT: "col": 12, +// CHECK-NEXT: "col": 3, // CHECK-NEXT: "tokLen": 1 // CHECK-NEXT: }, // CHECK-NEXT: "end": { @@ -4708,91 +4715,113 @@ void PrimaryExpressions(int a) { // CHECK-NEXT: "type": { // CHECK-NEXT: "qualType": "int[4]" // CHECK-NEXT: }, -// CHECK-NEXT: "valueCategory": "prvalue", +// CHECK-NEXT: "valueCategory": "lvalue", // CHECK-NEXT: "inner": [ // CHECK-NEXT: { // CHECK-NEXT: "id": "0x{{.*}}", -// CHECK-NEXT: "kind": "IntegerLiteral", +// CHECK-NEXT: "kind": "InitListExpr", // CHECK-NEXT: "range": { // CHECK-NEXT: "begin": { // CHECK-NEXT: "offset": {{[0-9]+}}, -// CHECK-NEXT: "col": 13, +// CHECK-NEXT: "col": 12, // CHECK-NEXT: "tokLen": 1 // CHECK-NEXT: }, // CHECK-NEXT: "end": { // CHECK-NEXT: "offset": {{[0-9]+}}, -// CHECK-NEXT: "col": 13, +// CHECK-NEXT: "col": 25, // CHECK-NEXT: "tokLen": 1 // CHECK-NEXT: } // CHECK-NEXT: }, // CHECK-NEXT: "type": { -// CHECK-NEXT: "qualType": "int" +// CHECK-NEXT: "qualType": "int[4]" // CHECK-NEXT: }, // CHECK-NEXT: "valueCategory": "prvalue", -// CHECK-NEXT: "value": "1" -// CHECK-NEXT: }, -// CHECK-NEXT: { -// CHECK-NEXT: "id": "0x{{.*}}", -// CHECK-NEXT: "kind": "IntegerLiteral", -// CHECK-NEXT: "range": { -// CHECK-NEXT: "begin": { -// CHECK-NEXT: "offset": {{[0-9]+}}, -// CHECK-NEXT: "col": 16, -// CHECK-NEXT: "tokLen": 1 +// CHECK-NEXT: "inner": [ +// CHECK-NEXT: { +// CHECK-NEXT: "id": "0x{{.*}}", +// CHECK-NEXT: "kind": "IntegerLiteral", +// CHECK-NEXT: "range": { +// CHECK-NEXT: "begin": { +// CHECK-NEXT: "offset": {{[0-9]+}}, +// CHECK-NEXT: "col": 13, +// CHECK-NEXT: "tokLen": 1 +// CHECK-NEXT: }, +// CHECK-NEXT: "end": { +// CHECK-NEXT: "offset": {{[0-9]+}}, +// CHECK-NEXT: "col": 13, +// CHECK-NEXT: "tokLen": 1 +// CHECK-NEXT: } +// CHECK-NEXT: }, +// CHECK-NEXT: "type": { +// CHECK-NEXT: "qualType": "int" +// CHECK-NEXT: }, +// CHECK-NEXT: "valueCategory": "prvalue", +// CHECK-NEXT: "value": "1" // CHECK-NEXT: }, -// CHECK-NEXT: "end": { -// CHECK-NEXT: "offset": {{[0-9]+}}, -// CHECK-NEXT: "col": 16, -// CHECK-NEXT: "tokLen": 1 -// CHECK-NEXT: } -// CHECK-NEXT: }, -// CHECK-NEXT: "type": { -// CHECK-NEXT: "qualType": "int" -// CHECK-NEXT: }, -// CHECK-NEXT: "valueCategory": "prvalue", -// CHECK-NEXT: "value": "2" -// CHECK-NEXT: }, -// CHECK-NEXT: { -// CHECK-NEXT: "id": "0x{{.*}}", -// CHECK-NEXT: "kind": "IntegerLiteral", -// CHECK-NEXT: "range": { -// CHECK-NEXT: "begin": { -// CHECK-NEXT: "offset": {{[0-9]+}}, -// CHECK-NEXT: "col": 19, -// CHECK-NEXT: "tokLen": 1 +// CHECK-NEXT: { +// CHECK-NEXT: "id": "0x{{.*}}", +// CHECK-NEXT: "kind": "IntegerLiteral", +// CHECK-NEXT: "range": { +// CHECK-NEXT: "begin": { +// CHECK-NEXT: "offset": {{[0-9]+}}, +// CHECK-NEXT: "col": 16, +// CHECK-NEXT: "tokLen": 1 +// CHECK-NEXT: }, +// CHECK-NEXT: "end": { +// CHECK-NEXT: "offset": {{[0-9]+}}, +// CHECK-NEXT: "col": 16, +// CHECK-NEXT: "tokLen": 1 +// CHECK-NEXT: } +// CHECK-NEXT: }, +// CHECK-NEXT: "type": { +// CHECK-NEXT: "qualType": "int" +// CHECK-NEXT: }, +// CHECK-NEXT: "valueCategory": "prvalue", +// CHECK-NEXT: "value": "2" // CHECK-NEXT: }, -// CHECK-NEXT: "end": { -// CHECK-NEXT: "offset": {{[0-9]+}}, -// CHECK-NEXT: "col": 19, -// CHECK-NEXT: "tokLen": 1 -// CHECK-NEXT: } -// CHECK-NEXT: }, -// CHECK-NEXT: "type": { -// CHECK-NEXT: "qualType": "int" -// CHECK-NEXT: }, -// CHECK-NEXT: "valueCategory": "prvalue", -// CHECK-NEXT: "value": "3" -// CHECK-NEXT: }, -// CHECK-NEXT: { -// CHECK-NEXT: "id": "0x{{.*}}", -// CHECK-NEXT: "kind": "IntegerLiteral", -// CHECK-NEXT: "range": { -// CHECK-NEXT: "begin": { -// CHECK-NEXT: "offset": {{[0-9]+}}, -// CHECK-NEXT: "col": 22, -// CHECK-NEXT: "tokLen": 1 +// CHECK-NEXT: { +// CHECK-NEXT: "id": "0x{{.*}}", +// CHECK-NEXT: "kind": "IntegerLiteral", +// CHECK-NEXT: "range": { +// CHECK-NEXT: "begin": { +// CHECK-NEXT: "offset": {{[0-9]+}}, +// CHECK-NEXT: "col": 19, +// CHECK-NEXT: "tokLen": 1 +// CHECK-NEXT: }, +// CHECK-NEXT: "end": { +// CHECK-NEXT: "offset": {{[0-9]+}}, +// CHECK-NEXT: "col": 19, +// CHECK-NEXT: "tokLen": 1 +// CHECK-NEXT: } +// CHECK-NEXT: }, +// CHECK-NEXT: "type": { +// CHECK-NEXT: "qualType": "int" +// CHECK-NEXT: }, +// CHECK-NEXT: "valueCategory": "prvalue", +// CHECK-NEXT: "value": "3" // CHECK-NEXT: }, -// CHECK-NEXT: "end": { -// CHECK-NEXT: "offset": {{[0-9]+}}, -// CHECK-NEXT: "col": 22, -// CHECK-NEXT: "tokLen": 1 +// CHECK-NEXT: { +// CHECK-NEXT: "id": "0x{{.*}}", +// CHECK-NEXT: "kind": "IntegerLiteral", +// CHECK-NEXT: "range": { +// CHECK-NEXT: "begin": { +// CHECK-NEXT: "offset": {{[0-9]+}}, +// CHECK-NEXT: "col": 22, +// CHECK-NEXT: "tokLen": 1 +// CHECK-NEXT: }, +// CHECK-NEXT: "end": { +// CHECK-NEXT: "offset": {{[0-9]+}}, +// CHECK-NEXT: "col": 22, +// CHECK-NEXT: "tokLen": 1 +// CHECK-NEXT: } +// CHECK-NEXT: }, +// CHECK-NEXT: "type": { +// CHECK-NEXT: "qualType": "int" +// CHECK-NEXT: }, +// CHECK-NEXT: "valueCategory": "prvalue", +// CHECK-NEXT: "value": "4" // CHECK-NEXT: } -// CHECK-NEXT: }, -// CHECK-NEXT: "type": { -// CHECK-NEXT: "qualType": "int" -// CHECK-NEXT: }, -// CHECK-NEXT: "valueCategory": "prvalue", -// CHECK-NEXT: "value": "4" +// CHECK-NEXT: ] // CHECK-NEXT: } // CHECK-NEXT: ] // CHECK-NEXT: } @@ -4802,7 +4831,7 @@ void PrimaryExpressions(int a) { // CHECK-NEXT: }, // CHECK-NEXT: { // CHECK-NEXT: "id": "0x{{.*}}", -// CHECK-NEXT: "kind": "ImplicitCastExpr", +// CHECK-NEXT: "kind": "ExprWithCleanups", // CHECK-NEXT: "range": { // CHECK-NEXT: "begin": { // CHECK-NEXT: "offset": {{[0-9]+}}, @@ -4820,11 +4849,17 @@ void PrimaryExpressions(int a) { // CHECK-NEXT: "qualType": "struct S" // CHECK-NEXT: }, // CHECK-NEXT: "valueCategory": "prvalue", -// CHECK-NEXT: "castKind": "LValueToRValue", +// CHECK-NEXT: "cleanupsHaveSideEffects": true, +// CHECK-NEXT: "cleanups": [ +// CHECK-NEXT: { +// CHECK-NEXT: "id": "0x{{.*}}", +// CHECK-NEXT: "kind": "CompoundLiteralExpr" +// CHECK-NEXT: } +// CHECK-NEXT: ], // CHECK-NEXT: "inner": [ // CHECK-NEXT: { // CHECK-NEXT: "id": "0x{{.*}}", -// CHECK-NEXT: "kind": "CompoundLiteralExpr", +// CHECK-NEXT: "kind": "ImplicitCastExpr", // CHECK-NEXT: "range": { // CHECK-NEXT: "begin": { // CHECK-NEXT: "offset": {{[0-9]+}}, @@ -4840,15 +4875,16 @@ void PrimaryExpressions(int a) { // CHECK-NEXT: "type": { // CHECK-NEXT: "qualType": "struct S" // CHECK-NEXT: }, -// CHECK-NEXT: "valueCategory": "lvalue", +// CHECK-NEXT: "valueCategory": "prvalue", +// CHECK-NEXT: "castKind": "LValueToRValue", // CHECK-NEXT: "inner": [ // CHECK-NEXT: { // CHECK-NEXT: "id": "0x{{.*}}", -// CHECK-NEXT: "kind": "InitListExpr", +// CHECK-NEXT: "kind": "CompoundLiteralExpr", // CHECK-NEXT: "range": { // CHECK-NEXT: "begin": { // CHECK-NEXT: "offset": {{[0-9]+}}, -// CHECK-NEXT: "col": 13, +// CHECK-NEXT: "col": 3, // CHECK-NEXT: "tokLen": 1 // CHECK-NEXT: }, // CHECK-NEXT: "end": { @@ -4860,28 +4896,50 @@ void PrimaryExpressions(int a) { // CHECK-NEXT: "type": { // CHECK-NEXT: "qualType": "struct S" // CHECK-NEXT: }, -// CHECK-NEXT: "valueCategory": "prvalue", +// CHECK-NEXT: "valueCategory": "lvalue", // CHECK-NEXT: "inner": [ // CHECK-NEXT: { // CHECK-NEXT: "id": "0x{{.*}}", -// CHECK-NEXT: "kind": "IntegerLiteral", +// CHECK-NEXT: "kind": "InitListExpr", // CHECK-NEXT: "range": { // CHECK-NEXT: "begin": { // CHECK-NEXT: "offset": {{[0-9]+}}, -// CHECK-NEXT: "col": 14, +// CHECK-NEXT: "col": 13, // CHECK-NEXT: "tokLen": 1 // CHECK-NEXT: }, // CHECK-NEXT: "end": { // CHECK-NEXT: "offset": {{[0-9]+}}, -// CHECK-NEXT: "col": 14, +// CHECK-NEXT: "col": 15, // CHECK-NEXT: "tokLen": 1 // CHECK-NEXT: } // CHECK-NEXT: }, // CHECK-NEXT: "type": { -// CHECK-NEXT: "qualType": "int" +// CHECK-NEXT: "qualType": "struct S" // CHECK-NEXT: }, // CHECK-NEXT: "valueCategory": "prvalue", -// CHECK-NEXT: "value": "1" +// CHECK-NEXT: "inner": [ +// CHECK-NEXT: { +// CHECK-NEXT: "id": "0x{{.*}}", +// CHECK-NEXT: "kind": "IntegerLiteral", +// CHECK-NEXT: "range": { +// CHECK-NEXT: "begin": { +// CHECK-NEXT: "offset": {{[0-9]+}}, +// CHECK-NEXT: "col": 14, +// CHECK-NEXT: "tokLen": 1 +// CHECK-NEXT: }, +// CHECK-NEXT: "end": { +// CHECK-NEXT: "offset": {{[0-9]+}}, +// CHECK-NEXT: "col": 14, +// CHECK-NEXT: "tokLen": 1 +// CHECK-NEXT: } +// CHECK-NEXT: }, +// CHECK-NEXT: "type": { +// CHECK-NEXT: "qualType": "int" +// CHECK-NEXT: }, +// CHECK-NEXT: "valueCategory": "prvalue", +// CHECK-NEXT: "value": "1" +// CHECK-NEXT: } +// CHECK-NEXT: ] // CHECK-NEXT: } // CHECK-NEXT: ] // CHECK-NEXT: } diff --git a/clang/test/C/C11/n1285_1.c b/clang/test/C/C11/n1285_1.c index ddf00c99fda92..37044ca0f02d5 100644 --- a/clang/test/C/C11/n1285_1.c +++ b/clang/test/C/C11/n1285_1.c @@ -94,6 +94,7 @@ int func_return(void) { // C11-O2-NEXT: store ptr [[ARRAYDECAY]], ptr @p, align 8, !tbaa [[INTPTR_TBAA6]] // C11-O2-NEXT: call void @llvm.lifetime.end.p0(ptr [[REF_TMP]]) #[[ATTR4]] // C11-O2-NEXT: call void @llvm.lifetime.start.p0(ptr [[Q]]) #[[ATTR4]] +// C11-O2-NEXT: call void @llvm.lifetime.start.p0(ptr [[DOTCOMPOUNDLITERAL]]) #[[ATTR4]] // C11-O2-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[DOTCOMPOUNDLITERAL]], i8 0, i64 20, i1 false) // C11-O2-NEXT: [[A2:%.*]] = getelementptr inbounds nuw [[STRUCT_X]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 // C11-O2-NEXT: [[A3:%.*]] = getelementptr inbounds nuw [[STRUCT_X]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 @@ -104,6 +105,7 @@ int func_return(void) { // C11-O2-NEXT: [[TMP2:%.*]] = load ptr, ptr [[Q]], align 8, !tbaa [[INTPTR_TBAA6]] // C11-O2-NEXT: [[TMP3:%.*]] = load i32, ptr [[TMP2]], align 4, !tbaa [[INT_TBAA9]] // C11-O2-NEXT: [[ADD:%.*]] = add nsw i32 [[TMP1]], [[TMP3]] +// C11-O2-NEXT: call void @llvm.lifetime.end.p0(ptr [[DOTCOMPOUNDLITERAL]]) #[[ATTR4]] // C11-O2-NEXT: call void @llvm.lifetime.end.p0(ptr [[Q]]) #[[ATTR4]] // C11-O2-NEXT: ret i32 [[ADD]] // diff --git a/clang/test/CodeGen/attr-counted-by-with-sanitizers.c b/clang/test/CodeGen/attr-counted-by-with-sanitizers.c index e840db632957e..576a61171c253 100644 --- a/clang/test/CodeGen/attr-counted-by-with-sanitizers.c +++ b/clang/test/CodeGen/attr-counted-by-with-sanitizers.c @@ -529,12 +529,12 @@ size_t test_return_bdos_of_anon_struct(struct union_of_fams *p) { // SANITIZE-WITH-ATTR-NEXT: [[COUNTED_BY_LOAD:%.*]] = load i8, ptr [[TMP0]], align 4 // SANITIZE-WITH-ATTR-NEXT: [[TMP1:%.*]] = zext i8 [[COUNTED_BY_LOAD]] to i32, !nosanitize [[META6]] // SANITIZE-WITH-ATTR-NEXT: [[TMP2:%.*]] = icmp ult i32 [[INDEX]], [[TMP1]], !nosanitize [[META6]] -// SANITIZE-WITH-ATTR-NEXT: br i1 [[TMP2]], label %[[CONT14:.*]], label %[[HANDLER_OUT_OF_BOUNDS:.*]], !prof [[PROF7]], !nosanitize [[META6]] +// SANITIZE-WITH-ATTR-NEXT: br i1 [[TMP2]], label %[[CONT16:.*]], label %[[HANDLER_OUT_OF_BOUNDS:.*]], !prof [[PROF7]], !nosanitize [[META6]] // SANITIZE-WITH-ATTR: [[HANDLER_OUT_OF_BOUNDS]]: // SANITIZE-WITH-ATTR-NEXT: [[TMP3:%.*]] = zext i32 [[INDEX]] to i64, !nosanitize [[META6]] // SANITIZE-WITH-ATTR-NEXT: tail call void @__ubsan_handle_out_of_bounds_abort(ptr nonnull @[[GLOB16:[0-9]+]], i64 [[TMP3]]) #[[ATTR7]], !nosanitize [[META6]] // SANITIZE-WITH-ATTR-NEXT: unreachable, !nosanitize [[META6]] -// SANITIZE-WITH-ATTR: [[CONT14]]: +// SANITIZE-WITH-ATTR: [[CONT16]]: // SANITIZE-WITH-ATTR-NEXT: [[INTS:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 9 // SANITIZE-WITH-ATTR-NEXT: [[IDXPROM:%.*]] = zext nneg i32 [[INDEX]] to i64 // SANITIZE-WITH-ATTR-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds nuw i8, ptr [[INTS]], i64 [[IDXPROM]] @@ -612,12 +612,12 @@ void test_assign_bdos_of_struct_to_union_fam(struct union_of_fams *p, int index) // SANITIZE-WITH-ATTR-NEXT: [[TMP1:%.*]] = icmp ult i32 [[INDEX]], [[COUNTED_BY_LOAD]], !nosanitize [[META6]] // SANITIZE-WITH-ATTR-NEXT: [[TMP2:%.*]] = icmp sgt i32 [[COUNTED_BY_LOAD]], 0, !nosanitize [[META6]] // SANITIZE-WITH-ATTR-NEXT: [[TMP3:%.*]] = and i1 [[TMP2]], [[TMP1]], !nosanitize [[META6]] -// SANITIZE-WITH-ATTR-NEXT: br i1 [[TMP3]], label %[[CONT14:.*]], label %[[HANDLER_OUT_OF_BOUNDS:.*]], !prof [[PROF7]], !nosanitize [[META6]] +// SANITIZE-WITH-ATTR-NEXT: br i1 [[TMP3]], label %[[CONT16:.*]], label %[[HANDLER_OUT_OF_BOUNDS:.*]], !prof [[PROF7]], !nosanitize [[META6]] // SANITIZE-WITH-ATTR: [[HANDLER_OUT_OF_BOUNDS]]: // SANITIZE-WITH-ATTR-NEXT: [[TMP4:%.*]] = zext i32 [[INDEX]] to i64, !nosanitize [[META6]] // SANITIZE-WITH-ATTR-NEXT: tail call void @__ubsan_handle_out_of_bounds_abort(ptr nonnull @[[GLOB19:[0-9]+]], i64 [[TMP4]]) #[[ATTR7]], !nosanitize [[META6]] // SANITIZE-WITH-ATTR-NEXT: unreachable, !nosanitize [[META6]] -// SANITIZE-WITH-ATTR: [[CONT14]]: +// SANITIZE-WITH-ATTR: [[CONT16]]: // SANITIZE-WITH-ATTR-NEXT: [[BYTES:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 12 // SANITIZE-WITH-ATTR-NEXT: [[IDXPROM:%.*]] = sext i32 [[INDEX]] to i64 // SANITIZE-WITH-ATTR-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds i8, ptr [[BYTES]], i64 [[IDXPROM]] @@ -852,6 +852,7 @@ struct compound_literal { // SANITIZE-WITH-ATTR-SAME: i32 noundef [[IDX:%.*]]) local_unnamed_addr #[[ATTR3]] { // SANITIZE-WITH-ATTR-NEXT: [[ENTRY:.*:]] // SANITIZE-WITH-ATTR-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_COMPOUND_LITERAL:%.*]], align 4 +// SANITIZE-WITH-ATTR-NEXT: call void @llvm.lifetime.start.p0(ptr nonnull [[DOTCOMPOUNDLITERAL]]) #[[ATTR8]] // SANITIZE-WITH-ATTR-NEXT: store i32 1, ptr [[DOTCOMPOUNDLITERAL]], align 4, !tbaa [[INT_TBAA8]] // SANITIZE-WITH-ATTR-NEXT: [[Y:%.*]] = getelementptr inbounds nuw i8, ptr [[DOTCOMPOUNDLITERAL]], i64 4 // SANITIZE-WITH-ATTR-NEXT: store i32 2, ptr [[Y]], align 4, !tbaa [[INT_TBAA8]] @@ -871,6 +872,7 @@ struct compound_literal { // SANITIZE-WITHOUT-ATTR-SAME: i32 noundef [[IDX:%.*]]) local_unnamed_addr #[[ATTR3]] { // SANITIZE-WITHOUT-ATTR-NEXT: [[ENTRY:.*:]] // SANITIZE-WITHOUT-ATTR-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_COMPOUND_LITERAL:%.*]], align 4 +// SANITIZE-WITHOUT-ATTR-NEXT: call void @llvm.lifetime.start.p0(ptr nonnull [[DOTCOMPOUNDLITERAL]]) #[[ATTR6]] // SANITIZE-WITHOUT-ATTR-NEXT: store i32 1, ptr [[DOTCOMPOUNDLITERAL]], align 4, !tbaa [[INT_TBAA6]] // SANITIZE-WITHOUT-ATTR-NEXT: [[Y:%.*]] = getelementptr inbounds nuw i8, ptr [[DOTCOMPOUNDLITERAL]], i64 4 // SANITIZE-WITHOUT-ATTR-NEXT: store i32 2, ptr [[Y]], align 4, !tbaa [[INT_TBAA6]] diff --git a/clang/test/CodeGen/attr-counted-by-without-sanitizers.c b/clang/test/CodeGen/attr-counted-by-without-sanitizers.c index 7ceb51c8986bd..6efa8c9ccf5f1 100644 --- a/clang/test/CodeGen/attr-counted-by-without-sanitizers.c +++ b/clang/test/CodeGen/attr-counted-by-without-sanitizers.c @@ -690,6 +690,7 @@ struct compound_literal { // NO-SANITIZE-WITH-ATTR-SAME: i32 noundef [[IDX:%.*]]) local_unnamed_addr #[[ATTR3]] { // NO-SANITIZE-WITH-ATTR-NEXT: [[ENTRY:.*:]] // NO-SANITIZE-WITH-ATTR-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_COMPOUND_LITERAL:%.*]], align 4 +// NO-SANITIZE-WITH-ATTR-NEXT: call void @llvm.lifetime.start.p0(ptr nonnull [[DOTCOMPOUNDLITERAL]]) #[[ATTR11]] // NO-SANITIZE-WITH-ATTR-NEXT: store i32 1, ptr [[DOTCOMPOUNDLITERAL]], align 4, !tbaa [[INT_TBAA6]] // NO-SANITIZE-WITH-ATTR-NEXT: [[Y:%.*]] = getelementptr inbounds nuw i8, ptr [[DOTCOMPOUNDLITERAL]], i64 4 // NO-SANITIZE-WITH-ATTR-NEXT: store i32 2, ptr [[Y]], align 4, !tbaa [[INT_TBAA6]] @@ -697,12 +698,14 @@ struct compound_literal { // NO-SANITIZE-WITH-ATTR-NEXT: [[IDXPROM:%.*]] = sext i32 [[IDX]] to i64 // NO-SANITIZE-WITH-ATTR-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds [4 x i8], ptr [[BLAH]], i64 [[IDXPROM]] // NO-SANITIZE-WITH-ATTR-NEXT: [[TMP0:%.*]] = load i32, ptr [[ARRAYIDX]], align 4, !tbaa [[INT_TBAA6]] +// NO-SANITIZE-WITH-ATTR-NEXT: call void @llvm.lifetime.end.p0(ptr nonnull [[DOTCOMPOUNDLITERAL]]) #[[ATTR11]] // NO-SANITIZE-WITH-ATTR-NEXT: ret i32 [[TMP0]] // // NO-SANITIZE-WITHOUT-ATTR-LABEL: define dso_local i32 @test_compound_literal( // NO-SANITIZE-WITHOUT-ATTR-SAME: i32 noundef [[IDX:%.*]]) local_unnamed_addr #[[ATTR1]] { // NO-SANITIZE-WITHOUT-ATTR-NEXT: [[ENTRY:.*:]] // NO-SANITIZE-WITHOUT-ATTR-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_COMPOUND_LITERAL:%.*]], align 4 +// NO-SANITIZE-WITHOUT-ATTR-NEXT: call void @llvm.lifetime.start.p0(ptr nonnull [[DOTCOMPOUNDLITERAL]]) #[[ATTR10]] // NO-SANITIZE-WITHOUT-ATTR-NEXT: store i32 1, ptr [[DOTCOMPOUNDLITERAL]], align 4, !tbaa [[INT_TBAA6]] // NO-SANITIZE-WITHOUT-ATTR-NEXT: [[Y:%.*]] = getelementptr inbounds nuw i8, ptr [[DOTCOMPOUNDLITERAL]], i64 4 // NO-SANITIZE-WITHOUT-ATTR-NEXT: store i32 2, ptr [[Y]], align 4, !tbaa [[INT_TBAA6]] @@ -710,6 +713,7 @@ struct compound_literal { // NO-SANITIZE-WITHOUT-ATTR-NEXT: [[IDXPROM:%.*]] = sext i32 [[IDX]] to i64 // NO-SANITIZE-WITHOUT-ATTR-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds [4 x i8], ptr [[BLAH]], i64 [[IDXPROM]] // NO-SANITIZE-WITHOUT-ATTR-NEXT: [[TMP0:%.*]] = load i32, ptr [[ARRAYIDX]], align 4, !tbaa [[INT_TBAA6]] +// NO-SANITIZE-WITHOUT-ATTR-NEXT: call void @llvm.lifetime.end.p0(ptr nonnull [[DOTCOMPOUNDLITERAL]]) #[[ATTR10]] // NO-SANITIZE-WITHOUT-ATTR-NEXT: ret i32 [[TMP0]] // int test_compound_literal(int idx) { diff --git a/clang/test/CodeGen/compound-literal-lifetime.c b/clang/test/CodeGen/compound-literal-lifetime.c new file mode 100644 index 0000000000000..42186b019ef18 --- /dev/null +++ b/clang/test/CodeGen/compound-literal-lifetime.c @@ -0,0 +1,93 @@ +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm -O1 %s -o - | FileCheck %s + +struct foo { + int x; + int y; +}; + +void bar(struct foo *); + +// CHECK-LABEL: define dso_local void @baz() +// CHECK: call void @llvm.lifetime.start.p0(ptr nonnull %[[SLOT1:.*]]) +// CHECK: call void @bar(ptr noundef nonnull %[[SLOT1]]) +// CHECK: call void @llvm.lifetime.end.p0(ptr nonnull %[[SLOT1]]) +// CHECK: call void @llvm.lifetime.start.p0(ptr nonnull %[[SLOT2:.*]]) +// CHECK: call void @bar(ptr noundef nonnull %[[SLOT2]]) +// CHECK: call void @llvm.lifetime.end.p0(ptr nonnull %[[SLOT2]]) +void baz(void) { + { + bar(&(struct foo){.x = 42, .y = 25}); + } + { + bar(&(struct foo){.x = 77, .y = 99}); + } +} + +int side_effect_true(void); +int side_effect_false(void); + +// Verify that initializers with side effects in conditional expressions +// are evaluated at the point of expression evaluation (short-circuited), +// rather than hoisted to block entry. +// CHECK-LABEL: define dso_local void @test_conditional( +// CHECK: cond.true: +// CHECK: call void @llvm.lifetime.start.p0(ptr nonnull %[[CL1:.*]]) +// CHECK: call i32 @side_effect_true() +// CHECK: cond.false: +// CHECK: call void @llvm.lifetime.start.p0(ptr nonnull %[[CL2:.*]]) +// CHECK: call i32 @side_effect_false() +// CHECK: cond.end: +// CHECK: call void @bar( +// CHECK: call void @llvm.lifetime.end.p0(ptr nonnull %[[CL2]]) +// CHECK: call void @llvm.lifetime.end.p0(ptr nonnull %[[CL1]]) +void test_conditional(int cond) { + cond ? bar(&(struct foo){.x = side_effect_true()}) + : bar(&(struct foo){.x = side_effect_false()}); +} + +int side_effect(void); + +// Verify that initializers in logical short-circuiting operators are only +// evaluated when the RHS is executed. +// CHECK-LABEL: define dso_local void @test_short_circuit( +// CHECK: br i1 %{{.*}}, label %{{.*}}, label %[[LAND_RHS:.*]] +// CHECK: [[LAND_RHS]]: +// CHECK: call i32 @side_effect() +void test_short_circuit(int flag) { + if (flag && ((struct foo){.x = side_effect()}).x > 0) + bar(0); +} + +void side_effect1(void); +int side_effect2(void); +void side_effect3(void); + +// Verify side effects are sequenced in evaluation order, not hoisted to block entry. +// CHECK-LABEL: define dso_local void @test_order( +// CHECK: call void @side_effect1() +// CHECK: call void @llvm.lifetime.start.p0(ptr nonnull %[[CL:.*]]) +// CHECK: call i32 @side_effect2() +// CHECK: call void @bar(ptr noundef nonnull %[[CL]]) +// CHECK: call void @side_effect3() +// CHECK: call void @llvm.lifetime.end.p0(ptr nonnull %[[CL]]) +void test_order(void) { + side_effect1(); + bar(&(struct foo){.x = side_effect2()}); + side_effect3(); +} + +// Verify that lifetime markers are not emitted when a label has been seen in the +// current scope, matching VarDecl behavior to avoid miscompilation on backward jumps. +// CHECK-LABEL: define dso_local i32 @test_backward_goto() +// CHECK-NOT: call void @llvm.lifetime.start +// CHECK-NOT: call void @llvm.lifetime.end +// CHECK: ret i32 +int test_backward_goto(void) { + int *p = 0; +label: + if (p) + return *p; + p = &(int){10}; + goto label; +} + diff --git a/clang/test/CodeGenObjC/arc-ternary-op.m b/clang/test/CodeGenObjC/arc-ternary-op.m index 46529b411f113..fe885affc2284 100644 --- a/clang/test/CodeGenObjC/arc-ternary-op.m +++ b/clang/test/CodeGenObjC/arc-ternary-op.m @@ -149,8 +149,10 @@ void test3(int cond) { // CHECK: define{{.*}} void @test3( // CHECK: %[[P:.*]] = alloca ptr, align 8 // CHECK: %[[_COMPOUNDLITERAL:.*]] = alloca [2 x ptr], align 8 + // CHECK: %[[LIFETIME_COND:.*]] = alloca i1, align 1 // CHECK: %[[CLEANUP_COND:.*]] = alloca i1, align 1 // CHECK: %[[_COMPOUNDLITERAL1:.*]] = alloca [2 x ptr], align 8 + // CHECK: %[[LIFETIME_COND4:.*]] = alloca i1, align 1 // CHECK: %[[CLEANUP_COND4:.*]] = alloca i1, align 1 // CHECK: %[[V2:.*]] = load ptr, ptr @g0, align 8 diff --git a/clang/test/Sema/scope-check.c b/clang/test/Sema/scope-check.c index f0100759ca9b2..1a9009862d8b0 100644 --- a/clang/test/Sema/scope-check.c +++ b/clang/test/Sema/scope-check.c @@ -264,3 +264,17 @@ void gh175549(int b, void* c) { (void)a[0]; } +struct TrivialCL { int a; }; +void test_trivial_compound_literal(int cond) { + struct TrivialCL s; + switch (cond) { + case 0: + s = (struct TrivialCL){ .a = 1 }; + break; + case 1: + s = (struct TrivialCL){ .a = 2 }; + break; + default: + break; + } +} _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
