https://github.com/akash-manna-sky updated https://github.com/llvm/llvm-project/pull/218262
>From 615dfb8496b61dbdd80cd32254d46c320beaac52 Mon Sep 17 00:00:00 2001 From: Akash Manna <[email protected]> Date: Sun, 23 Aug 2026 22:04:43 +0530 Subject: [PATCH 1/5] [clang][CodeGen] Fix assertion failure with #embed in array new-expression initializers An EmbedExpr in a semantic initializer list can represent many array elements, but EmitNewArrayInitializer counted it as one and emitted it through the scalar path, hitting assert(E->getDataElementCount() == 1). Emit one store per embed data element instead, count initializers with getNumInitsWithEmbedExpanded() (also for the minimum-allocation check), and make that helper look through implicit casts the way the other embed consumers already do. Fixes #128985 --- clang/docs/ReleaseNotes.md | 4 ++ clang/include/clang/AST/Expr.h | 2 +- clang/lib/CodeGen/CGExprCXX.cpp | 34 +++++++++--- clang/test/CodeGenCXX/GH128985.cpp | 89 ++++++++++++++++++++++++++++++ 4 files changed, 120 insertions(+), 9 deletions(-) create mode 100644 clang/test/CodeGenCXX/GH128985.cpp diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md index 3c6694f510952..a973b143e6e2e 100644 --- a/clang/docs/ReleaseNotes.md +++ b/clang/docs/ReleaseNotes.md @@ -531,6 +531,10 @@ features cannot lower the translation-unit ABI level; parameter that follows a parameter pack (e.g. `template <typename... T> S::S(T..., int = 10) {}`). (#GH216211) +- Fixed an assertion failure when `#embed` was used in the braced initializer + of an array new-expression; codegen now expands the embedded data into the + individual array elements. (#GH128985) + #### Bug Fixes to AST Handling - Fixed a non-deterministic ordering of unused local typedefs that made diff --git a/clang/include/clang/AST/Expr.h b/clang/include/clang/AST/Expr.h index 72762c668f26a..93b5a8aea6614 100644 --- a/clang/include/clang/AST/Expr.h +++ b/clang/include/clang/AST/Expr.h @@ -5365,7 +5365,7 @@ class InitListExpr : public Expr { unsigned getNumInitsWithEmbedExpanded() const { unsigned Sum = InitExprs.size(); for (auto *IE : InitExprs) - if (auto *EE = dyn_cast<EmbedExpr>(IE)) + if (auto *EE = dyn_cast<EmbedExpr>(cast<Expr>(IE)->IgnoreParenImpCasts())) Sum += EE->getDataElementCount() - 1; return Sum; } diff --git a/clang/lib/CodeGen/CGExprCXX.cpp b/clang/lib/CodeGen/CGExprCXX.cpp index e400a5c5a49c5..39c0e94b488f4 100644 --- a/clang/lib/CodeGen/CGExprCXX.cpp +++ b/clang/lib/CodeGen/CGExprCXX.cpp @@ -1103,7 +1103,8 @@ void CodeGenFunction::EmitNewArrayInitializer( ArrayRef<const Expr *> InitExprs = ILE ? ILE->inits() : CPLIE->getInitExprs(); - InitListElements = InitExprs.size(); + InitListElements = + ILE ? ILE->getNumInitsWithEmbedExpanded() : InitExprs.size(); // If this is a multi-dimensional array new, we will initialize multiple // elements with each init list element. @@ -1138,6 +1139,14 @@ void CodeGenFunction::EmitNewArrayInitializer( CharUnits StartAlign = CurPtr.getAlignment(); unsigned i = 0; + auto AdvanceToNextElement = [&]() { + CurPtr = Address(Builder.CreateInBoundsGEP(CurPtr.getElementType(), + CurPtr.emitRawPointer(*this), + Builder.getSize(1), + "array.exp.next"), + CurPtr.getElementType(), + StartAlign.alignmentAtOffset((++i) * ElementSize)); + }; for (const Expr *IE : InitExprs) { // Tell the cleanup that it needs to destroy up to this // element. TODO: some of these stores can be trivially @@ -1145,17 +1154,25 @@ void CodeGenFunction::EmitNewArrayInitializer( if (EndOfInit.isValid()) { Builder.CreateStore(CurPtr.emitRawPointer(*this), EndOfInit); } + // An EmbedExpr can initialize more than one array element. + if (const auto *EmbedS = dyn_cast<EmbedExpr>(IE->IgnoreParenImpCasts())) { + for (const IntegerLiteral *DataElement : + EmbedS->underlying_data_elements()) { + llvm::Value *Val = EmitScalarConversion( + Builder.getInt(DataElement->getValue()), DataElement->getType(), + ElementType, DataElement->getExprLoc()); + EmitStoreOfScalar(Val, MakeAddrLValue(CurPtr, ElementType), + /*isInit=*/true); + AdvanceToNextElement(); + } + continue; + } // FIXME: If the last initializer is an incomplete initializer list for // an array, and we have an array filler, we can fold together the two // initialization loops. StoreAnyExprIntoOneUnit(*this, IE, IE->getType(), CurPtr, AggValueSlot::DoesNotOverlap); - CurPtr = Address(Builder.CreateInBoundsGEP(CurPtr.getElementType(), - CurPtr.emitRawPointer(*this), - Builder.getSize(1), - "array.exp.next"), - CurPtr.getElementType(), - StartAlign.alignmentAtOffset((++i) * ElementSize)); + AdvanceToNextElement(); } // The remaining elements are filled with the array filler expression. @@ -1591,7 +1608,8 @@ llvm::Value *CodeGenFunction::EmitCXXNewExpr(const CXXNewExpr *E) { cast<ConstantArrayType>(Init->getType()->getAsArrayTypeUnsafe()) ->getZExtSize(); } else if (ILE || CPLIE) { - minElements = ILE ? ILE->getNumInits() : CPLIE->getInitExprs().size(); + minElements = ILE ? ILE->getNumInitsWithEmbedExpanded() + : CPLIE->getInitExprs().size(); } } diff --git a/clang/test/CodeGenCXX/GH128985.cpp b/clang/test/CodeGenCXX/GH128985.cpp new file mode 100644 index 0000000000000..7b703af005687 --- /dev/null +++ b/clang/test/CodeGenCXX/GH128985.cpp @@ -0,0 +1,89 @@ +// RUN: %clang_cc1 %s -triple x86_64 -emit-llvm -o - | FileCheck %s + +// GH128985: #embed in the braced initializer of an array new-expression +// asserted in codegen. +// The first four bytes of this file are '/', '/', ' ', 'R' (47, 47, 32, 82). + +// CHECK-LABEL: define {{.*}}void @_Z2f1i( +// CHECK: icmp ult i64 %{{.*}}, 4 +// CHECK: %[[A1:.*]] = call {{.*}}ptr @_Znam(i64 {{.*}}) +// CHECK: store i32 47, ptr %[[A1]] +// CHECK: %[[F1E1:.*]] = getelementptr inbounds i32, ptr %[[A1]], i64 1 +// CHECK: store i32 47, ptr %[[F1E1]] +// CHECK: %[[F1E2:.*]] = getelementptr inbounds i32, ptr %[[F1E1]], i64 1 +// CHECK: store i32 32, ptr %[[F1E2]] +// CHECK: %[[F1E3:.*]] = getelementptr inbounds i32, ptr %[[F1E2]], i64 1 +// CHECK: store i32 82, ptr %[[F1E3]] +// CHECK: %[[F1REST:.*]] = sub i64 %{{.*}}, 16 +// CHECK: call void @llvm.memset.p0.i64(ptr align 4 %{{.*}}, i8 0, i64 %[[F1REST]], i1 false) +void f1(int x) { + int *p = new int[x]{ +#embed __FILE__ limit(4) + }; +} + +// CHECK-LABEL: define {{.*}}void @_Z2f2i( +// CHECK: icmp ult i64 %{{.*}}, 4 +// CHECK: %[[A2:.*]] = call {{.*}}ptr @_Znam(i64 {{.*}}) +// CHECK: store i32 500, ptr %[[A2]] +// CHECK: %[[F2E1:.*]] = getelementptr inbounds i32, ptr %[[A2]], i64 1 +// CHECK: store i32 47, ptr %[[F2E1]] +// CHECK: %[[F2E2:.*]] = getelementptr inbounds i32, ptr %[[F2E1]], i64 1 +// CHECK: store i32 47, ptr %[[F2E2]] +// CHECK: %[[F2E3:.*]] = getelementptr inbounds i32, ptr %[[F2E2]], i64 1 +// CHECK: store i32 600, ptr %[[F2E3]] +// CHECK: %[[F2REST:.*]] = sub i64 %{{.*}}, 16 +// CHECK: call void @llvm.memset.p0.i64(ptr align 4 %{{.*}}, i8 0, i64 %[[F2REST]], i1 false) +void f2(int x) { + int *p = new int[x]{ + 500, +#embed __FILE__ limit(2) suffix(, 600) + }; +} + +// char arrays are initialized from the embed data via the string literal +// initialization path. +// CHECK-LABEL: define {{.*}}void @_Z2f3i( +// CHECK: icmp ult i64 %{{.*}}, 4 +// CHECK: %[[A3:.*]] = call {{.*}}ptr @_Znam(i64 {{.*}}) +// CHECK: call void @llvm.memcpy.p0.p0.i64(ptr align 1 %[[A3]], ptr align 1 @{{.*}}, i64 4, i1 false) +// CHECK: %[[F3END:.*]] = getelementptr inbounds i8, ptr %[[A3]], i64 4 +// CHECK: %[[F3REST:.*]] = sub i64 %{{.*}}, 4 +// CHECK: call void @llvm.memset.p0.i64(ptr align 1 %[[F3END]], i8 0, i64 %[[F3REST]], i1 false) +void f3(int x) { + char *p = new char[x]{ +#embed __FILE__ limit(4) + }; +} + +// CHECK-LABEL: define {{.*}}void @_Z2f4i( +// CHECK: icmp ult i64 %{{.*}}, 2 +// CHECK: %[[A4:.*]] = call {{.*}}ptr @_Znam(i64 {{.*}}) +// CHECK: store i32 900, ptr %[[A4]] +// CHECK: %[[F4E1:.*]] = getelementptr inbounds i32, ptr %[[A4]], i64 1 +// CHECK: store i32 47, ptr %[[F4E1]] +// CHECK: %[[F4REST:.*]] = sub i64 %{{.*}}, 8 +// CHECK: call void @llvm.memset.p0.i64(ptr align 4 %{{.*}}, i8 0, i64 %[[F4REST]], i1 false) +void f4(int x) { + int *p = new int[x]{ +#embed __FILE__ limit(1) prefix(900, ) + }; +} + +// Constant size fully covered by the embed data: no trailing fill. +// CHECK-LABEL: define {{.*}}void @_Z2f5v( +// CHECK: %[[A5:.*]] = call {{.*}}ptr @_Znam(i64 {{.*}}) +// CHECK: store i32 47, ptr %[[A5]] +// CHECK: %[[F5E1:.*]] = getelementptr inbounds i32, ptr %[[A5]], i64 1 +// CHECK: store i32 47, ptr %[[F5E1]] +// CHECK: %[[F5E2:.*]] = getelementptr inbounds i32, ptr %[[F5E1]], i64 1 +// CHECK: store i32 32, ptr %[[F5E2]] +// CHECK: %[[F5E3:.*]] = getelementptr inbounds i32, ptr %[[F5E2]], i64 1 +// CHECK: store i32 82, ptr %[[F5E3]] +// CHECK-NOT: call void @llvm.memset +// CHECK: ret void +void f5() { + int *p = new int[4]{ +#embed __FILE__ limit(4) + }; +} >From df02caddd25573b2ce95f071453d00069c096607 Mon Sep 17 00:00:00 2001 From: Akash Manna <[email protected]> Date: Mon, 24 Aug 2026 23:31:48 +0530 Subject: [PATCH 2/5] [clang][CodeGen] Address review comments for #embed in array new Use a dedicated input file instead of __FILE__, add codegen coverage for non-int element types, multidimensional arrays, arrays of structs and deduced array bounds, and add a Sema test for too many/too few embed elements with a constant bound. --- clang/test/CodeGenCXX/GH128985.cpp | 130 ++++++++++++++++---- clang/test/CodeGenCXX/Inputs/embed-data.txt | 1 + clang/test/SemaCXX/GH128985.cpp | 38 ++++++ 3 files changed, 147 insertions(+), 22 deletions(-) create mode 100644 clang/test/CodeGenCXX/Inputs/embed-data.txt create mode 100644 clang/test/SemaCXX/GH128985.cpp diff --git a/clang/test/CodeGenCXX/GH128985.cpp b/clang/test/CodeGenCXX/GH128985.cpp index 7b703af005687..87ea50661b09c 100644 --- a/clang/test/CodeGenCXX/GH128985.cpp +++ b/clang/test/CodeGenCXX/GH128985.cpp @@ -1,24 +1,26 @@ -// RUN: %clang_cc1 %s -triple x86_64 -emit-llvm -o - | FileCheck %s +// RUN: %clang_cc1 %s -triple x86_64 --embed-dir=%S/Inputs -emit-llvm -o - | FileCheck %s -// GH128985: #embed in the braced initializer of an array new-expression -// asserted in codegen. -// The first four bytes of this file are '/', '/', ' ', 'R' (47, 47, 32, 82). +// embed-data.txt contains "0123456789" (48 ... 57) without a trailing newline. + +struct S { + int a, b; +}; // CHECK-LABEL: define {{.*}}void @_Z2f1i( // CHECK: icmp ult i64 %{{.*}}, 4 // CHECK: %[[A1:.*]] = call {{.*}}ptr @_Znam(i64 {{.*}}) -// CHECK: store i32 47, ptr %[[A1]] +// CHECK: store i32 48, ptr %[[A1]] // CHECK: %[[F1E1:.*]] = getelementptr inbounds i32, ptr %[[A1]], i64 1 -// CHECK: store i32 47, ptr %[[F1E1]] +// CHECK: store i32 49, ptr %[[F1E1]] // CHECK: %[[F1E2:.*]] = getelementptr inbounds i32, ptr %[[F1E1]], i64 1 -// CHECK: store i32 32, ptr %[[F1E2]] +// CHECK: store i32 50, ptr %[[F1E2]] // CHECK: %[[F1E3:.*]] = getelementptr inbounds i32, ptr %[[F1E2]], i64 1 -// CHECK: store i32 82, ptr %[[F1E3]] +// CHECK: store i32 51, ptr %[[F1E3]] // CHECK: %[[F1REST:.*]] = sub i64 %{{.*}}, 16 // CHECK: call void @llvm.memset.p0.i64(ptr align 4 %{{.*}}, i8 0, i64 %[[F1REST]], i1 false) void f1(int x) { int *p = new int[x]{ -#embed __FILE__ limit(4) +#embed <embed-data.txt> limit(4) }; } @@ -27,9 +29,9 @@ void f1(int x) { // CHECK: %[[A2:.*]] = call {{.*}}ptr @_Znam(i64 {{.*}}) // CHECK: store i32 500, ptr %[[A2]] // CHECK: %[[F2E1:.*]] = getelementptr inbounds i32, ptr %[[A2]], i64 1 -// CHECK: store i32 47, ptr %[[F2E1]] +// CHECK: store i32 48, ptr %[[F2E1]] // CHECK: %[[F2E2:.*]] = getelementptr inbounds i32, ptr %[[F2E1]], i64 1 -// CHECK: store i32 47, ptr %[[F2E2]] +// CHECK: store i32 49, ptr %[[F2E2]] // CHECK: %[[F2E3:.*]] = getelementptr inbounds i32, ptr %[[F2E2]], i64 1 // CHECK: store i32 600, ptr %[[F2E3]] // CHECK: %[[F2REST:.*]] = sub i64 %{{.*}}, 16 @@ -37,12 +39,11 @@ void f1(int x) { void f2(int x) { int *p = new int[x]{ 500, -#embed __FILE__ limit(2) suffix(, 600) +#embed <embed-data.txt> limit(2) suffix(, 600) }; } -// char arrays are initialized from the embed data via the string literal -// initialization path. +// char arrays go through the string literal initialization path. // CHECK-LABEL: define {{.*}}void @_Z2f3i( // CHECK: icmp ult i64 %{{.*}}, 4 // CHECK: %[[A3:.*]] = call {{.*}}ptr @_Znam(i64 {{.*}}) @@ -52,7 +53,7 @@ void f2(int x) { // CHECK: call void @llvm.memset.p0.i64(ptr align 1 %[[F3END]], i8 0, i64 %[[F3REST]], i1 false) void f3(int x) { char *p = new char[x]{ -#embed __FILE__ limit(4) +#embed <embed-data.txt> limit(4) }; } @@ -61,29 +62,114 @@ void f3(int x) { // CHECK: %[[A4:.*]] = call {{.*}}ptr @_Znam(i64 {{.*}}) // CHECK: store i32 900, ptr %[[A4]] // CHECK: %[[F4E1:.*]] = getelementptr inbounds i32, ptr %[[A4]], i64 1 -// CHECK: store i32 47, ptr %[[F4E1]] +// CHECK: store i32 48, ptr %[[F4E1]] // CHECK: %[[F4REST:.*]] = sub i64 %{{.*}}, 8 // CHECK: call void @llvm.memset.p0.i64(ptr align 4 %{{.*}}, i8 0, i64 %[[F4REST]], i1 false) void f4(int x) { int *p = new int[x]{ -#embed __FILE__ limit(1) prefix(900, ) +#embed <embed-data.txt> limit(1) prefix(900, ) }; } // Constant size fully covered by the embed data: no trailing fill. // CHECK-LABEL: define {{.*}}void @_Z2f5v( // CHECK: %[[A5:.*]] = call {{.*}}ptr @_Znam(i64 {{.*}}) -// CHECK: store i32 47, ptr %[[A5]] +// CHECK: store i32 48, ptr %[[A5]] // CHECK: %[[F5E1:.*]] = getelementptr inbounds i32, ptr %[[A5]], i64 1 -// CHECK: store i32 47, ptr %[[F5E1]] +// CHECK: store i32 49, ptr %[[F5E1]] // CHECK: %[[F5E2:.*]] = getelementptr inbounds i32, ptr %[[F5E1]], i64 1 -// CHECK: store i32 32, ptr %[[F5E2]] +// CHECK: store i32 50, ptr %[[F5E2]] // CHECK: %[[F5E3:.*]] = getelementptr inbounds i32, ptr %[[F5E2]], i64 1 -// CHECK: store i32 82, ptr %[[F5E3]] +// CHECK: store i32 51, ptr %[[F5E3]] // CHECK-NOT: call void @llvm.memset // CHECK: ret void void f5() { int *p = new int[4]{ -#embed __FILE__ limit(4) +#embed <embed-data.txt> limit(4) + }; +} + +// Sema wraps the EmbedExpr in an implicit conversion to the element type. +// CHECK-LABEL: define {{.*}}void @_Z2f6i( +// CHECK: icmp ult i64 %{{.*}}, 4 +// CHECK: %[[A6:.*]] = call {{.*}}ptr @_Znam(i64 {{.*}}) +// CHECK: store i64 48, ptr %[[A6]] +// CHECK: %[[F6E1:.*]] = getelementptr inbounds i64, ptr %[[A6]], i64 1 +// CHECK: store i64 49, ptr %[[F6E1]] +// CHECK: %[[F6E2:.*]] = getelementptr inbounds i64, ptr %[[F6E1]], i64 1 +// CHECK: store i64 50, ptr %[[F6E2]] +// CHECK: %[[F6E3:.*]] = getelementptr inbounds i64, ptr %[[F6E2]], i64 1 +// CHECK: store i64 51, ptr %[[F6E3]] +// CHECK: %[[F6REST:.*]] = sub i64 %{{.*}}, 32 +// CHECK: call void @llvm.memset.p0.i64(ptr align 8 %{{.*}}, i8 0, i64 %[[F6REST]], i1 false) +void f6(int x) { + long long *p = new long long[x]{ +#embed <embed-data.txt> limit(4) + }; +} + +// CHECK-LABEL: define {{.*}}void @_Z2f7i( +// CHECK: icmp ult i64 %{{.*}}, 2 +// CHECK: call {{.*}}ptr @_Znam(i64 {{.*}}) +// CHECK: store i32 48, ptr +// CHECK: store i32 49, ptr +// CHECK: store i32 50, ptr +// CHECK: store i32 51, ptr +// CHECK: %[[F7REST:.*]] = sub i64 %{{.*}}, 16 +// CHECK: call void @llvm.memset.p0.i64(ptr align {{[0-9]+}} %{{.*}}, i8 0, i64 %[[F7REST]], i1 false) +void f7(int x) { + int (*p)[2] = new int[x][2]{ +#embed <embed-data.txt> limit(4) + }; +} + +// CHECK-LABEL: define {{.*}}void @_Z2f8i( +// CHECK: icmp ult i64 %{{.*}}, 2 +// CHECK: call {{.*}}ptr @_Znam(i64 {{.*}}) +// CHECK: store i32 48, ptr +// CHECK: store i32 49, ptr +// CHECK: store i32 50, ptr +// CHECK: store i32 51, ptr +// CHECK: %[[F8REST:.*]] = sub i64 %{{.*}}, 16 +// CHECK: call void @llvm.memset.p0.i64(ptr align {{[0-9]+}} %{{.*}}, i8 0, i64 %[[F8REST]], i1 false) +void f8(int x) { + S *p = new S[x]{ +#embed <embed-data.txt> limit(4) + }; +} + +// CHECK-LABEL: define {{.*}}void @_Z2f9i( +// CHECK: icmp ult i64 %{{.*}}, 2 +// CHECK: call {{.*}}ptr @_Znam(i64 {{.*}}) +// CHECK: store i32 48, ptr +// CHECK: store i32 49, ptr +// CHECK: store i32 50, ptr +// CHECK: store i32 51, ptr +// CHECK: store i32 52, ptr +// CHECK: store i32 53, ptr +// CHECK: store i32 54, ptr +// CHECK: store i32 55, ptr +// CHECK: %[[F9REST:.*]] = sub i64 %{{.*}}, 32 +// CHECK: call void @llvm.memset.p0.i64(ptr align {{[0-9]+}} %{{.*}}, i8 0, i64 %[[F9REST]], i1 false) +void f9(int x) { + S (*p)[2] = new S[x][2]{ +#embed <embed-data.txt> limit(8) + }; +} + +// CHECK-LABEL: define {{.*}}void @_Z3f10v( +// CHECK: %[[A10:.*]] = call {{.*}}ptr @_Znam(i64 noundef 16) +// CHECK: store i32 48, ptr %[[A10]] +// CHECK: %[[F10E1:.*]] = getelementptr inbounds i32, ptr %[[A10]], i64 1 +// CHECK: store i32 49, ptr %[[F10E1]] +// CHECK: %[[F10E2:.*]] = getelementptr inbounds i32, ptr %[[F10E1]], i64 1 +// CHECK: store i32 50, ptr %[[F10E2]] +// CHECK: %[[F10E3:.*]] = getelementptr inbounds i32, ptr %[[F10E2]], i64 1 +// CHECK: store i32 51, ptr %[[F10E3]] +// CHECK-NOT: call void @llvm.memset +// CHECK: ret void +void f10() { + int *p = new int[]{ +#embed <embed-data.txt> limit(4) }; } diff --git a/clang/test/CodeGenCXX/Inputs/embed-data.txt b/clang/test/CodeGenCXX/Inputs/embed-data.txt new file mode 100644 index 0000000000000..ad471007bd7f5 --- /dev/null +++ b/clang/test/CodeGenCXX/Inputs/embed-data.txt @@ -0,0 +1 @@ +0123456789 \ No newline at end of file diff --git a/clang/test/SemaCXX/GH128985.cpp b/clang/test/SemaCXX/GH128985.cpp new file mode 100644 index 0000000000000..adb0d8a1fc93f --- /dev/null +++ b/clang/test/SemaCXX/GH128985.cpp @@ -0,0 +1,38 @@ +// RUN: %clang_cc1 -fsyntax-only -verify -Wno-c23-extensions %s + +struct S { + int a, b; +}; + +void f(int x) { + int *a = new int[2]{ +#embed __FILE__ limit(4) + // expected-error@-1 {{excess elements in array initializer}} + }; + + int *b = new int[4]{ +#embed __FILE__ limit(4) + }; + + int *c = new int[8]{ +#embed __FILE__ limit(4) + }; + + int *d = new int[x]{ +#embed __FILE__ limit(4) + }; + + int (*e)[2] = new int[2][2]{ +#embed __FILE__ limit(5) + // expected-error@-1 {{excess elements in array initializer}} + }; + + S *s = new S[1]{ +#embed __FILE__ limit(3) + // expected-error@-1 {{excess elements in array initializer}} + }; + + S *t = new S[x]{ +#embed __FILE__ limit(3) + }; +} >From 6dbf0263885b2814f41d8699e95a3c97604ffbe6 Mon Sep 17 00:00:00 2001 From: Akash Manna <[email protected]> Date: Tue, 25 Aug 2026 21:35:55 +0530 Subject: [PATCH 3/5] [clang][CodeGen] Read embed data directly when initializing array new Emit the elements of an EmbedExpr from its underlying string literal instead of iterating the fake integer literals, and add a float array test. --- clang/lib/CodeGen/CGExprCXX.cpp | 11 +++++++---- clang/test/CodeGenCXX/GH128985.cpp | 18 ++++++++++++++++++ 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/clang/lib/CodeGen/CGExprCXX.cpp b/clang/lib/CodeGen/CGExprCXX.cpp index 39c0e94b488f4..3c141990dc887 100644 --- a/clang/lib/CodeGen/CGExprCXX.cpp +++ b/clang/lib/CodeGen/CGExprCXX.cpp @@ -1156,11 +1156,14 @@ void CodeGenFunction::EmitNewArrayInitializer( } // An EmbedExpr can initialize more than one array element. if (const auto *EmbedS = dyn_cast<EmbedExpr>(IE->IgnoreParenImpCasts())) { - for (const IntegerLiteral *DataElement : - EmbedS->underlying_data_elements()) { + const StringLiteral *SL = EmbedS->getDataStringLiteral(); + llvm::Type *DataTy = ConvertType(EmbedS->getType()); + for (unsigned I = EmbedS->getStartingElementPos(), + End = I + EmbedS->getDataElementCount(); + I != End; ++I) { llvm::Value *Val = EmitScalarConversion( - Builder.getInt(DataElement->getValue()), DataElement->getType(), - ElementType, DataElement->getExprLoc()); + llvm::ConstantInt::get(DataTy, SL->getCodeUnit(I)), + EmbedS->getType(), ElementType, EmbedS->getLocation()); EmitStoreOfScalar(Val, MakeAddrLValue(CurPtr, ElementType), /*isInit=*/true); AdvanceToNextElement(); diff --git a/clang/test/CodeGenCXX/GH128985.cpp b/clang/test/CodeGenCXX/GH128985.cpp index 87ea50661b09c..37660d47b29b8 100644 --- a/clang/test/CodeGenCXX/GH128985.cpp +++ b/clang/test/CodeGenCXX/GH128985.cpp @@ -173,3 +173,21 @@ void f10() { #embed <embed-data.txt> limit(4) }; } + +// CHECK-LABEL: define {{.*}}void @_Z3f11i( +// CHECK: icmp ult i64 %{{.*}}, 4 +// CHECK: %[[A11:.*]] = call {{.*}}ptr @_Znam(i64 {{.*}}) +// CHECK: store float 4.800000e+01, ptr %[[A11]] +// CHECK: %[[F11E1:.*]] = getelementptr inbounds float, ptr %[[A11]], i64 1 +// CHECK: store float 4.900000e+01, ptr %[[F11E1]] +// CHECK: %[[F11E2:.*]] = getelementptr inbounds float, ptr %[[F11E1]], i64 1 +// CHECK: store float 5.000000e+01, ptr %[[F11E2]] +// CHECK: %[[F11E3:.*]] = getelementptr inbounds float, ptr %[[F11E2]], i64 1 +// CHECK: store float 5.100000e+01, ptr %[[F11E3]] +// CHECK: %[[F11REST:.*]] = sub i64 %{{.*}}, 16 +// CHECK: call void @llvm.memset.p0.i64(ptr align 4 %{{.*}}, i8 0, i64 %[[F11REST]], i1 false) +void f11(int x) { + float *p = new float[x]{ +#embed <embed-data.txt> limit(4) + }; +} >From 606fdb8a57dc7e13b1afdfad046dabdeb606a15d Mon Sep 17 00:00:00 2001 From: Akash Manna <[email protected]> Date: Tue, 25 Aug 2026 22:31:02 +0530 Subject: [PATCH 4/5] [clang][Sema] Only expand #embed over multiple elements for scalar array elements HandleEmbed returned a multi-element EmbedExpr for any array element, so for a class element with a converting constructor the whole embed became a single constructor argument. Restrict it to scalar element types so class elements are constructed from one data element each. --- clang/lib/Sema/SemaInit.cpp | 3 ++- clang/test/CodeGenCXX/GH128985.cpp | 16 ++++++++++++++++ clang/test/SemaCXX/GH128985.cpp | 12 ++++++++++++ 3 files changed, 30 insertions(+), 1 deletion(-) diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp index 9b10ac1735c81..ef259bbb0e98c 100644 --- a/clang/lib/Sema/SemaInit.cpp +++ b/clang/lib/Sema/SemaInit.cpp @@ -563,7 +563,8 @@ class InitListChecker { // Reference just one if we're initializing a single scalar. uint64_t ElsCount = 1; // Otherwise try to fill whole array with embed data. - if (Entity.getKind() == InitializedEntity::EK_ArrayElement) { + if (Entity.getKind() == InitializedEntity::EK_ArrayElement && + Entity.getType()->isScalarType()) { unsigned ArrIndex = Entity.getElementIndex(); auto *AType = SemaRef.Context.getAsArrayType(Entity.getParent()->getType()); diff --git a/clang/test/CodeGenCXX/GH128985.cpp b/clang/test/CodeGenCXX/GH128985.cpp index 37660d47b29b8..741d1f7ea1d80 100644 --- a/clang/test/CodeGenCXX/GH128985.cpp +++ b/clang/test/CodeGenCXX/GH128985.cpp @@ -6,6 +6,10 @@ struct S { int a, b; }; +struct A { + A(char); +}; + // CHECK-LABEL: define {{.*}}void @_Z2f1i( // CHECK: icmp ult i64 %{{.*}}, 4 // CHECK: %[[A1:.*]] = call {{.*}}ptr @_Znam(i64 {{.*}}) @@ -191,3 +195,15 @@ void f11(int x) { #embed <embed-data.txt> limit(4) }; } + +// Class elements are constructed from one data element each. +// CHECK-LABEL: define {{.*}}void @_Z3f12v( +// CHECK: %[[A12:.*]] = call {{.*}}ptr @_Znam(i64 noundef 2) +// CHECK: call void @_ZN1AC1Ec(ptr {{.*}}%[[A12]], i8 noundef signext 48) +// CHECK: %[[F12E1:.*]] = getelementptr inbounds %struct.A, ptr %[[A12]], i64 1 +// CHECK: call void @_ZN1AC1Ec(ptr {{.*}}%[[F12E1]], i8 noundef signext 49) +void f12() { + A *p = new A[]{ +#embed <embed-data.txt> limit(2) + }; +} diff --git a/clang/test/SemaCXX/GH128985.cpp b/clang/test/SemaCXX/GH128985.cpp index adb0d8a1fc93f..faa6eeab3a253 100644 --- a/clang/test/SemaCXX/GH128985.cpp +++ b/clang/test/SemaCXX/GH128985.cpp @@ -4,6 +4,10 @@ struct S { int a, b; }; +struct A { + A(char); +}; + void f(int x) { int *a = new int[2]{ #embed __FILE__ limit(4) @@ -35,4 +39,12 @@ void f(int x) { S *t = new S[x]{ #embed __FILE__ limit(3) }; + + A *u = new A[]{1, 2, 3, +#embed __FILE__ limit(10) + }; + + A v[] = {1, 2, 3, +#embed __FILE__ limit(10) + }; } >From f391dc9cd3fffcf9e834f4f63724640e245ed688 Mon Sep 17 00:00:00 2001 From: Akash Manna <[email protected]> Date: Tue, 25 Aug 2026 23:28:33 +0530 Subject: [PATCH 5/5] [clang][Sema] Narrow multi-element #embed expansion to integer and floating-point arrays Match the element types the constant evaluator and constant emitter handle, and only take the multi-element path in array new codegen when the embed actually covers more than one element, so single-element embeds under other conversions (e.g. to _Complex) go through the normal initializer path. --- clang/docs/ReleaseNotes.md | 4 ++-- clang/include/clang/AST/Expr.h | 2 +- clang/lib/CodeGen/CGExprCXX.cpp | 3 ++- clang/lib/Sema/SemaInit.cpp | 3 ++- clang/test/CodeGenCXX/GH128985.cpp | 13 +++++++++++++ clang/test/SemaCXX/GH128985.cpp | 4 ++++ 6 files changed, 24 insertions(+), 5 deletions(-) diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md index a973b143e6e2e..c719a8c86d0cd 100644 --- a/clang/docs/ReleaseNotes.md +++ b/clang/docs/ReleaseNotes.md @@ -532,8 +532,8 @@ features cannot lower the translation-unit ABI level; `template <typename... T> S::S(T..., int = 10) {}`). (#GH216211) - Fixed an assertion failure when `#embed` was used in the braced initializer - of an array new-expression; codegen now expands the embedded data into the - individual array elements. (#GH128985) + of an array new-expression, or of an array whose elements are of class type. + (#GH128985) #### Bug Fixes to AST Handling diff --git a/clang/include/clang/AST/Expr.h b/clang/include/clang/AST/Expr.h index 93b5a8aea6614..737ec4134a449 100644 --- a/clang/include/clang/AST/Expr.h +++ b/clang/include/clang/AST/Expr.h @@ -5151,7 +5151,7 @@ struct EmbedDataStorage { /// { {EE(9th and 10th element), { zeroinitializer }}} /// /// EmbedExpr inside of a semantic initializer list and referencing more than -/// one element can only appear for arrays of scalars. +/// one element can only appear for arrays of integer or floating-point type. class EmbedExpr final : public Expr { SourceLocation EmbedKeywordLoc; IntegerLiteral *FakeChildNode = nullptr; diff --git a/clang/lib/CodeGen/CGExprCXX.cpp b/clang/lib/CodeGen/CGExprCXX.cpp index 3c141990dc887..7969aec916122 100644 --- a/clang/lib/CodeGen/CGExprCXX.cpp +++ b/clang/lib/CodeGen/CGExprCXX.cpp @@ -1155,7 +1155,8 @@ void CodeGenFunction::EmitNewArrayInitializer( Builder.CreateStore(CurPtr.emitRawPointer(*this), EndOfInit); } // An EmbedExpr can initialize more than one array element. - if (const auto *EmbedS = dyn_cast<EmbedExpr>(IE->IgnoreParenImpCasts())) { + const auto *EmbedS = dyn_cast<EmbedExpr>(IE->IgnoreParenImpCasts()); + if (EmbedS && EmbedS->getDataElementCount() > 1) { const StringLiteral *SL = EmbedS->getDataStringLiteral(); llvm::Type *DataTy = ConvertType(EmbedS->getType()); for (unsigned I = EmbedS->getStartingElementPos(), diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp index ef259bbb0e98c..086aed2e30d85 100644 --- a/clang/lib/Sema/SemaInit.cpp +++ b/clang/lib/Sema/SemaInit.cpp @@ -564,7 +564,8 @@ class InitListChecker { uint64_t ElsCount = 1; // Otherwise try to fill whole array with embed data. if (Entity.getKind() == InitializedEntity::EK_ArrayElement && - Entity.getType()->isScalarType()) { + (Entity.getType()->isIntegerType() || + Entity.getType()->isRealFloatingType())) { unsigned ArrIndex = Entity.getElementIndex(); auto *AType = SemaRef.Context.getAsArrayType(Entity.getParent()->getType()); diff --git a/clang/test/CodeGenCXX/GH128985.cpp b/clang/test/CodeGenCXX/GH128985.cpp index 741d1f7ea1d80..1eeb1d17678de 100644 --- a/clang/test/CodeGenCXX/GH128985.cpp +++ b/clang/test/CodeGenCXX/GH128985.cpp @@ -207,3 +207,16 @@ void f12() { #embed <embed-data.txt> limit(2) }; } + +// Complex elements are converted from one data element each. +// CHECK-LABEL: define {{.*}}void @_Z3f13v( +// CHECK: call {{.*}}ptr @_Znam(i64 noundef 32) +// CHECK: store double 4.800000e+01, ptr +// CHECK: store double 0.000000e+00, ptr +// CHECK: store double 4.900000e+01, ptr +// CHECK: store double 0.000000e+00, ptr +void f13() { + _Complex double *p = new _Complex double[]{ +#embed <embed-data.txt> limit(2) + }; +} diff --git a/clang/test/SemaCXX/GH128985.cpp b/clang/test/SemaCXX/GH128985.cpp index faa6eeab3a253..0d51e3e53582f 100644 --- a/clang/test/SemaCXX/GH128985.cpp +++ b/clang/test/SemaCXX/GH128985.cpp @@ -45,6 +45,10 @@ void f(int x) { }; A v[] = {1, 2, 3, +#embed __FILE__ limit(10) + }; + + _Complex double *w = new _Complex double[]{ #embed __FILE__ limit(10) }; } _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
