Skip to content

Conversation

@AmrDeveloper
Copy link
Member

Add support for the EmbedExpr for ScalarExpr

@llvmbot llvmbot added clang Clang issues not falling into any other category ClangIR Anything related to the ClangIR project labels Dec 12, 2025
@llvmbot
Copy link
Member

llvmbot commented Dec 12, 2025

@llvm/pr-subscribers-clangir

@llvm/pr-subscribers-clang

Author: Amr Hesham (AmrDeveloper)

Changes

Add support for the EmbedExpr for ScalarExpr


Full diff: https://github.com/llvm/llvm-project/pull/172088.diff

2 Files Affected:

  • (modified) clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp (+8-2)
  • (added) clang/test/CIR/CodeGen/embed-expr.c (+68)
diff --git a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
index e86c62bf7793f..4577b646ea00c 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
@@ -263,8 +263,14 @@ class ScalarExprEmitter : public StmtVisitor<ScalarExprEmitter, mlir::Value> {
     return {};
   }
   mlir::Value VisitEmbedExpr(EmbedExpr *e) {
-    cgf.cgm.errorNYI(e->getSourceRange(), "ScalarExprEmitter: embed");
-    return {};
+    assert(e->getDataElementCount() == 1);
+    auto it = e->begin();
+    QualType qualTy = e->getType();
+    mlir::Type ty = cgf.convertType(qualTy);
+    llvm::APInt value = (*it)->getValue();
+    uint64_t actualValue = qualTy->isSignedIntegerType() ? value.getSExtValue()
+                                                         : value.getZExtValue();
+    return builder.getConstInt(cgf.getLoc(e->getExprLoc()), ty, actualValue);
   }
   mlir::Value VisitOpaqueValueExpr(OpaqueValueExpr *e) {
     if (e->isGLValue())
diff --git a/clang/test/CIR/CodeGen/embed-expr.c b/clang/test/CIR/CodeGen/embed-expr.c
new file mode 100644
index 0000000000000..24009e3732736
--- /dev/null
+++ b/clang/test/CIR/CodeGen/embed-expr.c
@@ -0,0 +1,68 @@
+// RUN: %clang_cc1 -std=c23 -triple x86_64-unknown-linux-gnu -Wno-unused-value -fclangir -emit-cir %s -o %t.cir
+// RUN: FileCheck --input-file=%t.cir %s -check-prefix=CIR
+// RUN: %clang_cc1 -std=c23 -triple x86_64-unknown-linux-gnu -Wno-unused-value -fclangir -emit-llvm %s -o %t-cir.ll
+// RUN: FileCheck --input-file=%t-cir.ll %s -check-prefix=LLVM
+// RUN: %clang_cc1 -std=c23 -triple x86_64-unknown-linux-gnu -Wno-unused-value -emit-llvm %s -o %t.ll
+// RUN: FileCheck --input-file=%t.ll %s -check-prefix=OGCG
+
+void embed_expr_on_scalar_with_constants() {
+  int a[3] = {
+      1,
+      2,
+#embed __FILE__
+  };
+}
+
+// CIR: %[[A_ADDR:.*]] = cir.alloca !cir.array<!s32i x 3>, !cir.ptr<!cir.array<!s32i x 3>>, ["a", init]
+// CIR: %[[ARRAY:.*]] = cir.const #cir.const_array<[#cir.int<1> : !s32i, #cir.int<2> : !s32i, #cir.int<47> : !s32i]> : !cir.array<!s32i x 3>
+// CIR: cir.store {{.*}} %[[ARRAY]], %[[A_ADDR]] : !cir.array<!s32i x 3>, !cir.ptr<!cir.array<!s32i x 3>>
+
+// LLVM: %[[A_ADDR:.*]] = alloca [3 x i32], i64 1, align 4
+// LLVM: store [3 x i32] [i32 1, i32 2, i32 47], ptr %[[A_ADDR]], align 4
+
+// OGCG: %[[A_ADDR:.*]] = alloca [3 x i32], align 4
+// OGCG: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %[[A_ADDR]], ptr align 4 @__const.embed_expr_on_scalar_with_constants.a, i64 12, i1 false)
+
+void embed_expr_on_scalar_with_non_constants() {
+  int a;
+  int b[3] = {
+      a,
+      a,
+#embed __FILE__
+  };
+}
+
+// CIR: %[[A_ADDR:.*]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["a"]
+// CIR: %[[B_ADDR:.*]] = cir.alloca !cir.array<!s32i x 3>, !cir.ptr<!cir.array<!s32i x 3>>, ["b", init]
+// CIR: %[[B_PTR:.*]] = cir.cast array_to_ptrdecay %[[B_ADDR]] : !cir.ptr<!cir.array<!s32i x 3>> -> !cir.ptr<!s32i>
+// CIR: %[[TMP_A:.*]] = cir.load {{.*}} %[[A_ADDR]] : !cir.ptr<!s32i>, !s32i
+// CIR: cir.store {{.*}} %[[TMP_A]], %[[B_PTR]] : !s32i, !cir.ptr<!s32i>
+// CIR: %[[CONST_1:.*]] = cir.const #cir.int<1> : !s64i
+// CIR: %[[B_ELEM_1_PTR:.*]] = cir.ptr_stride %[[B_PTR]], %[[CONST_1]] : (!cir.ptr<!s32i>, !s64i) -> !cir.ptr<!s32i>
+// CIR: %[[TMP_A:.*]] = cir.load {{.*}} %[[A_ADDR]] : !cir.ptr<!s32i>, !s32i
+// CIR: cir.store {{.*}} %[[TMP_A]], %[[B_ELEM_1_PTR]] : !s32i, !cir.ptr<!s32i>
+// CIR: %[[CONST_2:.*]] = cir.const #cir.int<2> : !s64i
+// CIR: %[[B_ELEM_2_PTR:.*]] = cir.ptr_stride %[[B_PTR]], %[[CONST_2]] : (!cir.ptr<!s32i>, !s64i) -> !cir.ptr<!s32i>
+// CIR: %[[CONST_47:.*]] = cir.const #cir.int<47> : !s32i
+// CIR: cir.store {{.*}} %[[CONST_47]], %[[B_ELEM_2_PTR]] : !s32i, !cir.ptr<!s32i>
+
+// LLVM: %[[A_ADDR:.*]] = alloca i32, i64 1, align 4
+// LLVM: %[[B_ADDR:.*]] = alloca [3 x i32], i64 1, align 4
+// LLVM: %[[B_ELEM_0_PTR:.*]] = getelementptr i32, ptr %[[B_ADDR]], i32 0
+// LLVM: %[[TMP_A:.*]] = load i32, ptr %[[A_ADDR]], align 4
+// LLVM: store i32 %[[TMP_A]], ptr %[[B_ELEM_0_PTR]], align 4
+// LLVM: %[[B_ELEM_1_PTR:.*]] = getelementptr i32, ptr %[[B_ELEM_0_PTR]], i64 1
+// LLVM: %[[TMP_A:.*]] = load i32, ptr %[[A_ADDR]], align 4
+// LLVM: store i32 %[[TMP_A]], ptr %[[B_ELEM_1_PTR]], align 4
+// LLVM: %[[B_ELEM_2_PTR:.*]] = getelementptr i32, ptr %[[B_ELEM_0_PTR]], i64 2
+// LLVM: store i32 47, ptr %[[B_ELEM_2_PTR]], align 4
+
+// OGCG: %[[A_ADDR:.*]] = alloca i32, align 4
+// OGCG: %[[B_ADDR:.*]] = alloca [3 x i32], align 4
+// OGCG: %[[TMP_A:.*]] = load i32, ptr %[[A_ADDR]], align 4
+// OGCG: store i32 %[[TMP_A]], ptr %[[B_ADDR]], align 4
+// OGCG: %[[B_ELEM_1_PTR:.*]] = getelementptr inbounds i32, ptr %[[B_ADDR]], i64 1
+// OGCG: %[[TMP_A:.*]] = load i32, ptr %[[A_ADDR]], align 4
+// OGCG: store i32 %[[TMP_A]], ptr %[[B_ELEM_1_PTR]], align 4
+// OGCG: %[[B_ELEM_2_PTR:.*]] = getelementptr inbounds i32, ptr %[[B_ADDR]], i64 2
+// OGCG: store i32 47, ptr %[[B_ELEM_2_PTR]], align 4

Comment on lines 266 to 273
assert(e->getDataElementCount() == 1);
auto it = e->begin();
QualType qualTy = e->getType();
mlir::Type ty = cgf.convertType(qualTy);
llvm::APInt value = (*it)->getValue();
uint64_t actualValue = qualTy->isSignedIntegerType() ? value.getSExtValue()
: value.getZExtValue();
return builder.getConstInt(cgf.getLoc(e->getExprLoc()), ty, actualValue);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Identically, we should use getConstInt and pass the APInt, but in our builder, when we pass APInt, we just construct APSInt with unsigned = true, so maybe we should add another parameter to this helper to control singing

cir::ConstantOp CIRGenBuilderTy::getConstInt(mlir::Location loc,
llvm::APInt intVal) {
return getConstInt(loc, llvm::APSInt(intVal));
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I think return builder.getConstInt(cgf.getLoc(e->getExprLoc()), value, qualTy->isSignedIntegerType()); would make more sense here if the APInt overload for getConstInt took a isSigned argument. Then we could get rid of a lot of the type handling here.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In getConstInt, we pass llvm::APInt always by copy, but I will keep this change to NFC PR :D

Copy link
Contributor

@andykaylor andykaylor left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm

@AmrDeveloper AmrDeveloper merged commit 0009536 into llvm:main Dec 15, 2025
10 checks passed
mahesh-attarde pushed a commit to mahesh-attarde/llvm-project that referenced this pull request Dec 19, 2025
Add support for the EmbedExpr for ScalarExpr
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

clang Clang issues not falling into any other category ClangIR Anything related to the ClangIR project

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants