[clang] [CIR] __builtin_source_location lowering for constant (PR #194505)
Erich Keane via cfe-commits
cfe-commits at lists.llvm.org
Mon Apr 27 18:09:43 PDT 2026
https://github.com/erichkeane created https://github.com/llvm/llvm-project/pull/194505
This handles the UnnamedGlobalConstantDecl lowering, which is only used for the source-location object. This uses similar logic to the classic compiler, so we end up with roughly the same IR.
There is a slight difference in how we choose which strings to uniquify (preexisting) and how we name them/name duplicates, but it isn't really relevant to the text itself.
>From 9a8984a926d801e89281fcb1fd43bb148a0c4eec Mon Sep 17 00:00:00 2001
From: erichkeane <ekeane at nvidia.com>
Date: Mon, 27 Apr 2026 17:59:07 -0700
Subject: [PATCH] [CIR] __builtin_source_location lowering for constant
This handles the UnnamedGlobalConstantDecl lowering, which is only used
for the source-location object. This uses similar logic to the classic
compiler, so we end up with roughly the same IR.
There is a slight difference in how we choose which strings to uniquify
(preexisting) and how we name them/name duplicates, but it isn't really
relevant to the text itself.
---
clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp | 6 +-
clang/lib/CIR/CodeGen/CIRGenModule.cpp | 40 ++++++++++
clang/lib/CIR/CodeGen/CIRGenModule.h | 5 ++
clang/test/CIR/CodeGen/source-loc.cpp | 79 +++++++++++++++++---
4 files changed, 115 insertions(+), 15 deletions(-)
diff --git a/clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp b/clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp
index 438f99246306e..331d1c37281ca 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp
@@ -1414,9 +1414,9 @@ ConstantLValueEmitter::tryEmitBase(const APValue::LValueBase &base) {
if (isa<MSGuidDecl>(d))
cgm.errorNYI(d->getSourceRange(), "ConstantLValueEmitter: MSGuidDecl");
- if (isa<UnnamedGlobalConstantDecl>(d))
- cgm.errorNYI(d->getSourceRange(),
- "ConstantLValueEmitter: Unnamed global constant");
+ if (const auto *gcd = dyn_cast<UnnamedGlobalConstantDecl>(d))
+ return cgm.getBuilder().getGlobalViewAttr(
+ cgm.getAddrOfUnnamedGlobalConstantDecl(gcd));
if (const auto *tpo = dyn_cast<TemplateParamObjectDecl>(d))
return cgm.getBuilder().getGlobalViewAttr(
diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.cpp b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
index 7d719600a6025..0e147cd9a22c8 100644
--- a/clang/lib/CIR/CodeGen/CIRGenModule.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
@@ -3570,6 +3570,46 @@ CIRGenModule::getAddrOfGlobalTemporary(const MaterializeTemporaryExpr *mte,
return cv;
}
+cir::GlobalOp CIRGenModule::getAddrOfUnnamedGlobalConstantDecl(
+ const UnnamedGlobalConstantDecl *gcd) {
+ unsigned numEntries = unnamedGlobalConstantDeclMap.size();
+ cir::GlobalOp *globalOpEntry = &unnamedGlobalConstantDeclMap[gcd];
+
+ if (*globalOpEntry)
+ return *globalOpEntry;
+
+ ConstantEmitter emitter(*this);
+
+ const APValue &value = gcd->getValue();
+ assert(!value.isAbsent());
+ assert(!cir::MissingFeatures::addressSpace() &&
+ "emitForInitializer should take gcd->getType().getAddressSpace()");
+ mlir::Attribute init = emitter.emitForInitializer(value, gcd->getType());
+ auto typedInit = cast<mlir::TypedAttr>(init);
+
+ assert(!cir::MissingFeatures::addressSpace());
+
+ // Classic codegen always creates these with .constant, then counts on the
+ // auto-addition of '.#'. CIR global doesn't have this, so we'll just auto-add
+ // one if this isn't the first. We could probably choose a better name than
+ // .constant to be unique for this type of decl, but this is consistent with
+ // classic codegen.
+ std::string name = ".constant";
+ if (numEntries != 0)
+ name += '.' + std::to_string(numEntries);
+ auto globalOp = createGlobalOp(*this, builder.getUnknownLoc(), name,
+ typedInit.getType(), /*is_constant=*/true);
+ globalOp.setLinkage(cir::GlobalLinkageKind::PrivateLinkage);
+
+ CharUnits alignment = getASTContext().getTypeAlignInChars(gcd->getType());
+ globalOp.setAlignment(alignment.getAsAlign().value());
+ CIRGenModule::setInitializer(globalOp, init);
+
+ emitter.finalize(globalOp);
+ *globalOpEntry = globalOp;
+ return globalOp;
+}
+
cir::GlobalOp
CIRGenModule::getAddrOfTemplateParamObject(const TemplateParamObjectDecl *tpo) {
StringRef name = getMangledName(tpo);
diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.h b/clang/lib/CIR/CodeGen/CIRGenModule.h
index b2c630ca7defd..4d04351973cbb 100644
--- a/clang/lib/CIR/CodeGen/CIRGenModule.h
+++ b/clang/lib/CIR/CodeGen/CIRGenModule.h
@@ -360,6 +360,9 @@ class CIRGenModule : public CIRGenTypeCache {
/// Get the GlobalOp of a template parameter object.
cir::GlobalOp
getAddrOfTemplateParamObject(const TemplateParamObjectDecl *tpo);
+ // Get the GlobalOp of a source_location object.
+ cir::GlobalOp
+ getAddrOfUnnamedGlobalConstantDecl(const UnnamedGlobalConstantDecl *tpo);
CharUnits computeNonVirtualBaseClassOffset(
const CXXRecordDecl *derivedClass,
@@ -429,6 +432,8 @@ class CIRGenModule : public CIRGenTypeCache {
}
llvm::DenseMap<mlir::Attribute, cir::GlobalOp> constantStringMap;
+ llvm::DenseMap<const UnnamedGlobalConstantDecl *, cir::GlobalOp>
+ unnamedGlobalConstantDeclMap;
/// Return a constant array for the given string.
mlir::Attribute getConstantArrayFromStringLiteral(const StringLiteral *e);
diff --git a/clang/test/CIR/CodeGen/source-loc.cpp b/clang/test/CIR/CodeGen/source-loc.cpp
index fc8ab76a9bbb8..f6ebdebd98a6c 100644
--- a/clang/test/CIR/CodeGen/source-loc.cpp
+++ b/clang/test/CIR/CodeGen/source-loc.cpp
@@ -5,6 +5,61 @@
// RUN: %clang_cc1 -std=c++20 -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
+namespace std {
+ struct source_location {
+ struct __impl {
+ const char * _M_file_name;
+ const char * _M_function_name;
+ unsigned _M_line;
+ unsigned _M_column;
+ };
+
+ const __impl * _impl = nullptr;
+
+ static constexpr source_location
+ current(const __impl *p = __builtin_source_location()) {
+ source_location loc;
+ loc._impl = p;
+ return loc;
+ }
+ };
+}
+
+// CIR: cir.global "private" constant cir_private dso_local @".str" = #cir.const_array<
+// LLVM: @.str = private constant
+// OGCG: @.str = private unnamed_addr constant
+//
+// CIR: cir.global "private" constant cir_private dso_local @".str.1" = #cir.const_array<"void use1()" : !cir.array<!s8i x 11>, trailing_zeros> : !cir.array<!s8i x 12>
+// LLVM: @.str.1 = private constant [{{.*}} x i8] c"void use1
+// OGCG: @.str.1 = private unnamed_addr constant [{{.*}} x i8] c"void use1
+//
+// CIR: cir.global "private" constant cir_private @".constant" = #cir.const_record<{#cir.global_view<@".str"> : !cir.ptr<!s8i>, #cir.global_view<@".str.1"> : !cir.ptr<!s8i>, #cir.int<{{.*}}> : !u32i, #cir.int<{{.*}}> : !u32i}> : !rec_std3A3Asource_location3A3A__impl
+// LLVM: @.constant = private constant %"struct.std::source_location::__impl" { ptr @.str, ptr @.str.1, i32 {{.*}}, i32 {{.*}} }
+// OGCG: @.constant = private unnamed_addr constant %"struct.std::source_location::__impl" { ptr @.str, ptr @.str.1, i32 {{.*}}, i32 {{.*}} }
+//
+// CIR: cir.global "private" constant cir_private dso_local @".str.2" = #cir.const_array<"void use2()" : !cir.array<!s8i x 11>, trailing_zeros> : !cir.array<!s8i x 12> {alignment = 1 : i64} loc(#loc1)
+// LLVM: @.str.2 = private constant [{{.*}} x i8] c"void use2
+// OGCG: @.str.2 = private unnamed_addr constant [{{.*}} x i8] c"void use2
+//
+// CIR: cir.global "private" constant cir_private @".constant.1" = #cir.const_record<{#cir.global_view<@".str"> : !cir.ptr<!s8i>, #cir.global_view<@".str.2"> : !cir.ptr<!s8i>, #cir.int<{{.*}}> : !u32i, #cir.int<{{.*}}> : !u32i}> : !rec_std3A3Asource_location3A3A__impl
+// LLVM: @.constant.1 = private constant %"struct.std::source_location::__impl" { ptr @.str, ptr @.str.2, i32 {{.*}}, i32 {{.*}} }
+// OGCG: @.constant.3 = private unnamed_addr constant %"struct.std::source_location::__impl" { ptr @.str, ptr @.str.2, i32 {{.*}}, i32 {{.*}} }
+
+
+void has_sl(std::source_location loc = std::source_location::current());
+void use1() { has_sl(); }
+// CIR-LABEL: cir.func{{.*}} @_Z4use1v
+// CIR: cir.const #cir.global_view<@".constant"> : !cir.ptr<!rec_std3A3Asource_location3A3A__impl>
+// LLVM: define {{.*}}@_Z4use1v()
+// LLVM: call {{.*}}@_ZNSt15source_location7currentEPKNS_6__implE(ptr noundef @.constant)
+// OGCG: call {{.*}}@_ZNSt15source_location7currentEPKNS_6__implE(ptr noundef @.constant)
+void use2() { has_sl(); }
+// CIR-LABEL: cir.func{{.*}} @_Z4use2v
+// CIR: cir.const #cir.global_view<@".constant.1"> : !cir.ptr<!rec_std3A3Asource_location3A3A__impl>
+// LLVM: define {{.*}}@_Z4use2v()
+// LLVM: call {{.*}}@_ZNSt15source_location7currentEPKNS_6__implE(ptr noundef @.constant.1)
+// OGCG: call {{.*}}@_ZNSt15source_location7currentEPKNS_6__implE(ptr noundef @.constant.3)
+
void line_column() {
unsigned int a = __builtin_LINE();
unsigned int b = __builtin_COLUMN();
@@ -12,19 +67,19 @@ void line_column() {
// CIR: %[[A_ADDR:.*]] = cir.alloca !u32i, !cir.ptr<!u32i>, ["a", init]
// CIR: %[[B_ADDR:.*]] = cir.alloca !u32i, !cir.ptr<!u32i>, ["b", init]
-// CIR: %[[CONST_9:.*]] = cir.const #cir.int<9> : !u32i
+// CIR: %[[CONST_9:.*]] = cir.const #cir.int<64> : !u32i
// CIR: cir.store {{.*}} %[[CONST_9]], %[[A_ADDR]] : !u32i, !cir.ptr<!u32i>
// CIR: %[[CONST_20:.*]] = cir.const #cir.int<20> : !u32i
// CIR: cir.store {{.*}} %[[CONST_20]], %[[B_ADDR]] : !u32i, !cir.ptr<!u32i>
// LLVM: %[[A_ADDR:.*]] = alloca i32, i64 1, align 4
// LLVM: %[[B_ADDR:.*]] = alloca i32, i64 1, align 4
-// LLVM: store i32 9, ptr %[[A_ADDR]], align 4
+// LLVM: store i32 64, ptr %[[A_ADDR]], align 4
// LLVM: store i32 20, ptr %[[B_ADDR]], align 4
// OGCG: %[[A_ADDR:.*]] = alloca i32, align 4
// OGCG: %[[B_ADDR:.*]] = alloca i32, align 4
-// OGCG: store i32 9, ptr %[[A_ADDR]], align 4
+// OGCG: store i32 64, ptr %[[A_ADDR]], align 4
// OGCG: store i32 20, ptr %[[B_ADDR]], align 4
void function_file() {
@@ -36,23 +91,23 @@ void function_file() {
// CIR: %[[A_ADDR:.*]] = cir.alloca !cir.ptr<!s8i>, !cir.ptr<!cir.ptr<!s8i>>, ["a", init]
// CIR: %[[B_ADDR:.*]] = cir.alloca !cir.ptr<!s8i>, !cir.ptr<!cir.ptr<!s8i>>, ["b", init]
// CIR: %[[C_ADDR:.*]] = cir.alloca !cir.ptr<!s8i>, !cir.ptr<!cir.ptr<!s8i>>, ["c", init]
-// CIR: %[[FUNC__GV:.*]] = cir.const #cir.global_view<@".str"> : !cir.ptr<!s8i>
+// CIR: %[[FUNC__GV:.*]] = cir.const #cir.global_view<@".str.3"> : !cir.ptr<!s8i>
// CIR: cir.store {{.*}} %[[FUNC__GV]], %[[A_ADDR]] : !cir.ptr<!s8i>, !cir.ptr<!cir.ptr<!s8i>>
-// CIR: %[[FILE_PATH_GV:.*]] = cir.const #cir.global_view<@".str.1"> : !cir.ptr<!s8i>
+// CIR: %[[FILE_PATH_GV:.*]] = cir.const #cir.global_view<@".str"> : !cir.ptr<!s8i>
// CIR: cir.store {{.*}} %[[FILE_PATH_GV]], %[[B_ADDR]] : !cir.ptr<!s8i>, !cir.ptr<!cir.ptr<!s8i>>
-// CIR: %[[FILE_GV:.*]] = cir.const #cir.global_view<@".str.2"> : !cir.ptr<!s8i>
+// CIR: %[[FILE_GV:.*]] = cir.const #cir.global_view<@".str.4"> : !cir.ptr<!s8i>
// CIR: cir.store {{.*}} %[[FILE_GV]], %[[C_ADDR]] : !cir.ptr<!s8i>, !cir.ptr<!cir.ptr<!s8i>>
// LLVM: %[[A_ADDR:.*]] = alloca ptr, i64 1, align 8
// LLVM: %[[B_ADDR:.*]] = alloca ptr, i64 1, align 8
// LLVM: %[[C_ADDR:.*]] = alloca ptr, i64 1, align 8
-// LLVM: store ptr @.str, ptr %[[A_ADDR]], align 8
-// LLVM: store ptr @.str.1, ptr %[[B_ADDR]], align 8
-// LLVM: store ptr @.str.2, ptr %[[C_ADDR]], align 8
+// LLVM: store ptr @.str.3, ptr %[[A_ADDR]], align 8
+// LLVM: store ptr @.str, ptr %[[B_ADDR]], align 8
+// LLVM: store ptr @.str.4, ptr %[[C_ADDR]], align 8
// OGCG: %[[A_ADDR:.*]] = alloca ptr, align 8
// OGCG: %[[B_ADDR:.*]] = alloca ptr, align 8
// OGCG: %[[C_ADDR:.*]] = alloca ptr, align 8
-// OGCG: store ptr @.str, ptr %[[A_ADDR]], align 8
-// OGCG: store ptr @.str.1, ptr %[[B_ADDR]], align 8
-// OGCG: store ptr @.str.2, ptr %[[C_ADDR]], align 8
+// OGCG: store ptr @.str.4, ptr %[[A_ADDR]], align 8
+// OGCG: store ptr @.str, ptr %[[B_ADDR]], align 8
+// OGCG: store ptr @.str.5, ptr %[[C_ADDR]], align 8
More information about the cfe-commits
mailing list