[clang] [CIR] Add support for emitting predefined expressions (PR #161757)

Andy Kaylor via cfe-commits cfe-commits at lists.llvm.org
Thu Oct 2 18:02:03 PDT 2025


https://github.com/andykaylor created https://github.com/llvm/llvm-project/pull/161757

This adds support for emitting predefined expressions (such as __func__) as l-values.

>From 71dc79edb42e6fbb39af837ebbe0627b3a2167af Mon Sep 17 00:00:00 2001
From: Andy Kaylor <akaylor at nvidia.com>
Date: Thu, 2 Oct 2025 17:58:23 -0700
Subject: [PATCH] [CIR] Add support for emitting predefined expressions

This adds support for emitting predefined expressions (such as __func__)
as l-values.
---
 clang/lib/CIR/CodeGen/CIRGenExpr.cpp     | 20 ++++++-
 clang/lib/CIR/CodeGen/CIRGenFunction.cpp |  2 +
 clang/lib/CIR/CodeGen/CIRGenFunction.h   |  5 +-
 clang/lib/CIR/CodeGen/CIRGenModule.cpp   | 52 +++++++++--------
 clang/lib/CIR/CodeGen/CIRGenModule.h     |  2 +
 clang/test/CIR/CodeGen/predefined-expr.c | 71 ++++++++++++++++++++++++
 6 files changed, 125 insertions(+), 27 deletions(-)
 create mode 100644 clang/test/CIR/CodeGen/predefined-expr.c

diff --git a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
index fa68ad931ba74..3074e1a3c845d 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
@@ -1108,8 +1108,9 @@ CIRGenFunction::emitArraySubscriptExpr(const clang::ArraySubscriptExpr *e) {
   return lv;
 }
 
-LValue CIRGenFunction::emitStringLiteralLValue(const StringLiteral *e) {
-  cir::GlobalOp globalOp = cgm.getGlobalForStringLiteral(e);
+LValue CIRGenFunction::emitStringLiteralLValue(const StringLiteral *e,
+                                               llvm::StringRef name) {
+  cir::GlobalOp globalOp = cgm.getGlobalForStringLiteral(e, name);
   assert(globalOp.getAlignment() && "expected alignment for string literal");
   unsigned align = *(globalOp.getAlignment());
   mlir::Value addr =
@@ -2372,6 +2373,21 @@ mlir::Value CIRGenFunction::emitScalarConstant(
   return builder.getConstant(getLoc(e->getSourceRange()), constant.getValue());
 }
 
+LValue CIRGenFunction::emitPredefinedLValue(const PredefinedExpr *e) {
+  const StringLiteral *sl = e->getFunctionName();
+  assert(sl != nullptr && "No StringLiteral name in PredefinedExpr");
+  auto fn = cast<cir::FuncOp>(curFn);
+  StringRef fnName = fn.getName();
+  fnName.consume_front("\01");
+  StringRef nameItems[] = {PredefinedExpr::getIdentKindName(e->getIdentKind()),
+                           fnName};
+  std::string gvName = llvm::join(nameItems, nameItems + 2, ".");
+  if (isa_and_nonnull<BlockDecl>(curCodeDecl))
+    cgm.errorNYI(e->getSourceRange(), "predefined lvalue in block");
+
+  return emitStringLiteralLValue(sl, gvName);
+}
+
 /// An LValue is a candidate for having its loads and stores be made atomic if
 /// we are operating under /volatile:ms *and* the LValue itself is volatile and
 /// performing such an operation can be performed without a libcall.
diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
index b26b4f2500579..3e34f5ca541fe 100644
--- a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
@@ -815,6 +815,8 @@ LValue CIRGenFunction::emitLValue(const Expr *e) {
     return emitMemberExpr(cast<MemberExpr>(e));
   case Expr::CompoundLiteralExprClass:
     return emitCompoundLiteralLValue(cast<CompoundLiteralExpr>(e));
+  case Expr::PredefinedExprClass:
+    return emitPredefinedLValue(cast<PredefinedExpr>(e));
   case Expr::BinaryOperatorClass:
     return emitBinaryOperatorLValue(cast<BinaryOperator>(e));
   case Expr::CompoundAssignOperatorClass: {
diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.h b/clang/lib/CIR/CodeGen/CIRGenFunction.h
index cb7cf983006e9..3eba242550c2c 100644
--- a/clang/lib/CIR/CodeGen/CIRGenFunction.h
+++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h
@@ -1279,6 +1279,8 @@ class CIRGenFunction : public CIRGenTypeCache {
   void emitInitializerForField(clang::FieldDecl *field, LValue lhs,
                                clang::Expr *init);
 
+  LValue emitPredefinedLValue(const PredefinedExpr *e);
+
   mlir::Value emitPromotedComplexExpr(const Expr *e, QualType promotionType);
 
   mlir::Value emitPromotedScalarExpr(const Expr *e, QualType promotionType);
@@ -1473,7 +1475,8 @@ class CIRGenFunction : public CIRGenTypeCache {
 
   mlir::Value emitStoreThroughBitfieldLValue(RValue src, LValue dstresult);
 
-  LValue emitStringLiteralLValue(const StringLiteral *e);
+  LValue emitStringLiteralLValue(const StringLiteral *e,
+                                 llvm::StringRef name = ".str");
 
   mlir::LogicalResult emitSwitchBody(const clang::Stmt *s);
   mlir::LogicalResult emitSwitchCase(const clang::SwitchCase &s,
diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.cpp b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
index 2bd2729f0b0fb..beeabafaeb964 100644
--- a/clang/lib/CIR/CodeGen/CIRGenModule.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
@@ -1343,32 +1343,36 @@ cir::GlobalOp CIRGenModule::getGlobalForStringLiteral(const StringLiteral *s,
 
   mlir::Attribute c = getConstantArrayFromStringLiteral(s);
 
-  if (getLangOpts().WritableStrings) {
-    errorNYI(s->getSourceRange(),
-             "getGlobalForStringLiteral: Writable strings");
-  }
-
-  // Mangle the string literal if that's how the ABI merges duplicate strings.
-  // Don't do it if they are writable, since we don't want writes in one TU to
-  // affect strings in another.
-  if (getCXXABI().getMangleContext().shouldMangleStringLiteral(s) &&
-      !getLangOpts().WritableStrings) {
-    errorNYI(s->getSourceRange(),
-             "getGlobalForStringLiteral: mangle string literals");
-  }
-
-  // Unlike LLVM IR, CIR doesn't automatically unique names for globals, so
-  // we need to do that explicitly.
-  std::string uniqueName = getUniqueGlobalName(name.str());
-  mlir::Location loc = getLoc(s->getSourceRange());
-  auto typedC = llvm::cast<mlir::TypedAttr>(c);
-  cir::GlobalOp gv =
-      generateStringLiteral(loc, typedC, cir::GlobalLinkageKind::PrivateLinkage,
-                            *this, uniqueName, alignment);
-  setDSOLocal(static_cast<mlir::Operation *>(gv));
+  cir::GlobalOp gv;
+  if (!getLangOpts().WritableStrings && constantStringMap.count(c)) {
+    gv = constantStringMap[c];
+    // The bigger alignment always wins.
+    if (!gv.getAlignment() ||
+        uint64_t(alignment.getQuantity()) > *gv.getAlignment())
+      gv.setAlignmentAttr(getSize(alignment));
+  } else {
+    // Mangle the string literal if that's how the ABI merges duplicate strings.
+    // Don't do it if they are writable, since we don't want writes in one TU to
+    // affect strings in another.
+    if (getCXXABI().getMangleContext().shouldMangleStringLiteral(s) &&
+        !getLangOpts().WritableStrings) {
+      errorNYI(s->getSourceRange(),
+               "getGlobalForStringLiteral: mangle string literals");
+    }
 
-  assert(!cir::MissingFeatures::sanitizers());
+    // Unlike LLVM IR, CIR doesn't automatically unique names for globals, so
+    // we need to do that explicitly.
+    std::string uniqueName = getUniqueGlobalName(name.str());
+    mlir::Location loc = getLoc(s->getSourceRange());
+    auto typedC = llvm::cast<mlir::TypedAttr>(c);
+    gv = generateStringLiteral(loc, typedC,
+                               cir::GlobalLinkageKind::PrivateLinkage, *this,
+                               uniqueName, alignment);
+    setDSOLocal(static_cast<mlir::Operation *>(gv));
+    constantStringMap[c] = gv;
 
+    assert(!cir::MissingFeatures::sanitizers());
+  }
   return gv;
 }
 
diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.h b/clang/lib/CIR/CodeGen/CIRGenModule.h
index 2c4c6dd14e2ff..541432407fea5 100644
--- a/clang/lib/CIR/CodeGen/CIRGenModule.h
+++ b/clang/lib/CIR/CodeGen/CIRGenModule.h
@@ -274,6 +274,8 @@ class CIRGenModule : public CIRGenTypeCache {
     llvm_unreachable("unknown visibility!");
   }
 
+  llvm::DenseMap<mlir::Attribute, cir::GlobalOp> constantStringMap;
+
   /// Return a constant array for the given string.
   mlir::Attribute getConstantArrayFromStringLiteral(const StringLiteral *e);
 
diff --git a/clang/test/CIR/CodeGen/predefined-expr.c b/clang/test/CIR/CodeGen/predefined-expr.c
new file mode 100644
index 0000000000000..674c9bd02b5cf
--- /dev/null
+++ b/clang/test/CIR/CodeGen/predefined-expr.c
@@ -0,0 +1,71 @@
+// RUN: %clang_cc1 %s -triple x86_64-unknown-linux-gnu -fclangir -emit-cir -o %t.cir
+// RUN: FileCheck %s --input-file=%t.cir --check-prefix=CIR
+// RUN: %clang_cc1 %s -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm -o %t-cir.ll
+// RUN: FileCheck %s --input-file=%t-cir.ll --check-prefix=LLVM
+// RUN: %clang_cc1 %s -triple x86_64-unknown-linux-gnu -emit-llvm -o %t.ll
+// RUN: FileCheck %s --input-file=%t.ll --check-prefix=OGCG
+
+// CIR: cir.global "private" constant cir_private dso_local @__func__.plainFunction = #cir.const_array<"plainFunction\00" : !cir.array<!s8i x 14>>
+// CIR: cir.global "private" constant cir_private dso_local @__PRETTY_FUNCTION__.plainFunction = #cir.const_array<"void plainFunction(void)\00" : !cir.array<!s8i x 25>>
+// CIR: cir.global "private" constant cir_private dso_local @__func__.externFunction = #cir.const_array<"externFunction\00" : !cir.array<!s8i x 15>>
+// CIR: cir.global "private" constant cir_private dso_local @__PRETTY_FUNCTION__.externFunction = #cir.const_array<"void externFunction(void)\00" : !cir.array<!s8i x 26>>
+// CIR: cir.global "private" constant cir_private dso_local @__func__.privateExternFunction = #cir.const_array<"privateExternFunction\00" : !cir.array<!s8i x 22>>
+// CIR: cir.global "private" constant cir_private dso_local @__PRETTY_FUNCTION__.privateExternFunction = #cir.const_array<"void privateExternFunction(void)\00" : !cir.array<!s8i x 33>>
+// CIR: cir.global "private" constant cir_private dso_local @__func__.staticFunction = #cir.const_array<"staticFunction\00" : !cir.array<!s8i x 15>>
+// CIR: cir.global "private" constant cir_private dso_local @__PRETTY_FUNCTION__.staticFunction = #cir.const_array<"void staticFunction(void)\00" : !cir.array<!s8i x 26>>
+
+// TODO(cir): These should be unnamed_addr
+// LLVM: @__func__.plainFunction = private constant [14 x i8] c"plainFunction\00"
+// LLVM: @__PRETTY_FUNCTION__.plainFunction = private constant [25 x i8] c"void plainFunction(void)\00"
+// LLVM: @__func__.externFunction = private constant [15 x i8] c"externFunction\00"
+// LLVM: @__PRETTY_FUNCTION__.externFunction = private constant [26 x i8] c"void externFunction(void)\00"
+// LLVM: @__func__.privateExternFunction = private constant [22 x i8] c"privateExternFunction\00"
+// LLVM: @__PRETTY_FUNCTION__.privateExternFunction = private constant [33 x i8] c"void privateExternFunction(void)\00"
+// LLVM: @__func__.staticFunction = private constant [15 x i8] c"staticFunction\00"
+// LLVM: @__PRETTY_FUNCTION__.staticFunction = private constant [26 x i8] c"void staticFunction(void)\00"
+
+// OGCG: @__func__.plainFunction = private unnamed_addr constant [14 x i8] c"plainFunction\00"
+// OGCG: @__PRETTY_FUNCTION__.plainFunction = private unnamed_addr constant [25 x i8] c"void plainFunction(void)\00"
+// OGCG: @__func__.externFunction = private unnamed_addr constant [15 x i8] c"externFunction\00"
+// OGCG: @__PRETTY_FUNCTION__.externFunction = private unnamed_addr constant [26 x i8] c"void externFunction(void)\00"
+// OGCG: @__func__.privateExternFunction = private unnamed_addr constant [22 x i8] c"privateExternFunction\00"
+// OGCG: @__PRETTY_FUNCTION__.privateExternFunction = private unnamed_addr constant [33 x i8] c"void privateExternFunction(void)\00"
+// OGCG: @__func__.staticFunction = private unnamed_addr constant [15 x i8] c"staticFunction\00"
+// OGCG: @__PRETTY_FUNCTION__.staticFunction = private unnamed_addr constant [26 x i8] c"void staticFunction(void)\00"
+
+int printf(const char *, ...);
+
+void plainFunction(void) {
+  printf("__func__ %s\n", __func__);
+  printf("__FUNCTION__ %s\n", __FUNCTION__);
+  printf("__PRETTY_FUNCTION__ %s\n\n", __PRETTY_FUNCTION__);
+}
+
+extern void externFunction(void) {
+  printf("__func__ %s\n", __func__);
+  printf("__FUNCTION__ %s\n", __FUNCTION__);
+  printf("__PRETTY_FUNCTION__ %s\n\n", __PRETTY_FUNCTION__);
+}
+
+__private_extern__ void privateExternFunction(void) {
+  printf("__func__ %s\n", __func__);
+  printf("__FUNCTION__ %s\n", __FUNCTION__);
+  printf("__PRETTY_FUNCTION__ %s\n\n", __PRETTY_FUNCTION__);
+}
+
+// TODO(cir): Add support for __captured_stmt
+
+static void staticFunction(void) {
+  printf("__func__ %s\n", __func__);
+  printf("__FUNCTION__ %s\n", __FUNCTION__);
+  printf("__PRETTY_FUNCTION__ %s\n\n", __PRETTY_FUNCTION__);
+}
+
+int main(void) {
+  plainFunction();
+  externFunction();
+  privateExternFunction();
+  staticFunction();
+
+  return 0;
+}



More information about the cfe-commits mailing list