[clang] [CIR] Handle DeclRefExpr's to NTTP Objects (PR #194482)

via cfe-commits cfe-commits at lists.llvm.org
Mon Apr 27 15:50:17 PDT 2026


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clangir

@llvm/pr-subscribers-clang

Author: Erich Keane (erichkeane)

<details>
<summary>Changes</summary>

NTTP objects are represented as globals so that you can refer to them/address of them/etc, but most access to them should result in constant expressions.  This patch implements the creation of these globals, and allows compelation to continue.

This should fix up the last DeclRefExpr LValue that appears other than MSGuids and named global registers, both of which are specific to individual attributes.

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


5 Files Affected:

- (modified) clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h (+4-3) 
- (modified) clang/lib/CIR/CodeGen/CIRGenExpr.cpp (+15-2) 
- (modified) clang/lib/CIR/CodeGen/CIRGenModule.cpp (+43) 
- (modified) clang/lib/CIR/CodeGen/CIRGenModule.h (+4) 
- (added) clang/test/CIR/CodeGen/temp-param-obj-decl.cpp (+52) 


``````````diff
diff --git a/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h b/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h
index 7c42542d2babc..646fc7eb3c226 100644
--- a/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h
+++ b/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h
@@ -354,15 +354,16 @@ class CIRBaseBuilderTy : public mlir::OpBuilder {
     return getGlobalViewAttr(type, globalOp, arAttr);
   }
 
-  mlir::Value createGetGlobal(mlir::Location loc, cir::GlobalOp global,
-                              bool threadLocal = false) {
+  cir::GetGlobalOp createGetGlobal(mlir::Location loc, cir::GlobalOp global,
+                                   bool threadLocal = false) {
     assert(!cir::MissingFeatures::addressSpace());
     return cir::GetGlobalOp::create(*this, loc,
                                     getPointerTo(global.getSymType()),
                                     global.getSymNameAttr(), threadLocal);
   }
 
-  mlir::Value createGetGlobal(cir::GlobalOp global, bool threadLocal = false) {
+  cir::GetGlobalOp createGetGlobal(cir::GlobalOp global,
+                                   bool threadLocal = false) {
     return createGetGlobal(global.getLoc(), global, threadLocal);
   }
 
diff --git a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
index 613c804466073..dc1d5185189bf 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
@@ -1056,9 +1056,22 @@ LValue CIRGenFunction::emitDeclRefLValue(const DeclRefExpr *e) {
 
     return lv;
   }
+  if (isa<MSGuidDecl>(nd))
+    cgm.errorNYI(e->getSourceRange(),
+                 "emitDeclRefLValue: unhandled MS Guid Decl");
+
+  if (const auto *tpo = dyn_cast<TemplateParamObjectDecl>(nd)) {
+    CharUnits alignment = cgm.getNaturalTypeAlignment(tpo->getType());
+    cir::GetGlobalOp atpo = cgm.getAddrOfTemplateParamObject(tpo);
+    assert(!MissingFeatures::addressSpace() &&
+           "Do an address space conversion if necessary");
+
+    return makeAddrLValue(
+        Address(atpo, convertTypeForMem(tpo->getType()), alignment), ty,
+        AlignmentSource::Decl);
+  }
 
-  cgm.errorNYI(e->getSourceRange(), "emitDeclRefLValue: unhandled decl type");
-  return LValue();
+  llvm_unreachable("Unhandled DeclRefExpr");
 }
 
 mlir::Value CIRGenFunction::evaluateExprAsBool(const Expr *e) {
diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.cpp b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
index 891b345fc7169..1f35f55669ff7 100644
--- a/clang/lib/CIR/CodeGen/CIRGenModule.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
@@ -3570,6 +3570,49 @@ CIRGenModule::getAddrOfGlobalTemporary(const MaterializeTemporaryExpr *mte,
   return cv;
 }
 
+cir::GetGlobalOp
+CIRGenModule::getAddrOfTemplateParamObject(const TemplateParamObjectDecl *tpo) {
+  StringRef name = getMangledName(tpo);
+  CharUnits alignment = getNaturalTypeAlignment(tpo->getType());
+
+  if (auto globalOp =
+          mlir::dyn_cast_or_null<cir::GlobalOp>(getGlobalValue(name)))
+    return builder.createGetGlobal(globalOp);
+
+  ConstantEmitter emitter(*this);
+  assert(!cir::MissingFeatures::addressSpace() &&
+         "emitForInitializer should take tpo->getType().getAddressSpace()");
+  mlir::Attribute init =
+      emitter.emitForInitializer(tpo->getValue(), tpo->getType());
+
+  if (!init) {
+    errorUnsupported(tpo, "template parameter object");
+    return {};
+  }
+
+  mlir::TypedAttr typedInit = cast<mlir::TypedAttr>(init);
+
+  cir::GlobalLinkageKind linkage =
+      isExternallyVisible(tpo->getLinkageAndVisibility().getLinkage())
+          ? cir::GlobalLinkageKind::LinkOnceODRLinkage
+          : cir::GlobalLinkageKind::InternalLinkage;
+
+  assert(!cir::MissingFeatures::addressSpace());
+  auto globalOp = createGlobalOp(*this, builder.getUnknownLoc(), name,
+                                 typedInit.getType(), /*is_constant=*/true);
+  globalOp.setLinkage(linkage);
+  globalOp.setAlignment(alignment.getAsAlign().value());
+  globalOp.setComdat(supportsCOMDAT() &&
+                     linkage == cir::GlobalLinkageKind::LinkOnceODRLinkage);
+
+  CIRGenModule::setInitializer(globalOp, init);
+  emitter.finalize(globalOp);
+
+  insertGlobalSymbol(globalOp);
+
+  return builder.createGetGlobal(globalOp);
+}
+
 //===----------------------------------------------------------------------===//
 // Annotations
 //===----------------------------------------------------------------------===//
diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.h b/clang/lib/CIR/CodeGen/CIRGenModule.h
index f6493afeaed8e..299346dc2de3f 100644
--- a/clang/lib/CIR/CodeGen/CIRGenModule.h
+++ b/clang/lib/CIR/CodeGen/CIRGenModule.h
@@ -357,6 +357,10 @@ class CIRGenModule : public CIRGenTypeCache {
   /// Return the mlir::GlobalViewAttr for the address of the given global.
   cir::GlobalViewAttr getAddrOfGlobalVarAttr(const VarDecl *d);
 
+  /// Get the GlobalOp of a template parameter object.
+  cir::GetGlobalOp
+  getAddrOfTemplateParamObject(const TemplateParamObjectDecl *tpo);
+
   CharUnits computeNonVirtualBaseClassOffset(
       const CXXRecordDecl *derivedClass,
       llvm::iterator_range<CastExpr::path_const_iterator> path);
diff --git a/clang/test/CIR/CodeGen/temp-param-obj-decl.cpp b/clang/test/CIR/CodeGen/temp-param-obj-decl.cpp
new file mode 100644
index 0000000000000..8437af739268a
--- /dev/null
+++ b/clang/test/CIR/CodeGen/temp-param-obj-decl.cpp
@@ -0,0 +1,52 @@
+// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o %t.cir
+// RUN: FileCheck --check-prefix=CIR --input-file=%t.cir %s
+// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o %t-cir.ll
+// RUN: FileCheck --check-prefix=LLVM,LLVM-BOTH --input-file=%t-cir.ll %s
+// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -emit-llvm %s -o %t.ll
+// RUN: FileCheck --check-prefix=OGCG,LLVM-BOTH --input-file=%t.ll %s
+
+struct HasMem { int x;};
+
+// CIR: cir.global "private" constant linkonce_odr comdat @_ZTAXtl6HasMemLi1EEE = #cir.const_record<{#cir.int<1> : !s32i}> : !rec_HasMem
+// CIR: cir.global "private" constant linkonce_odr comdat @_ZTAXtl6HasMemLi2EEE = #cir.const_record<{#cir.int<2> : !s32i}> : !rec_HasMem
+
+// LLVM-BOTH: @_ZTAXtl6HasMemLi1EEE = linkonce_odr constant %struct.HasMem { i32 1 }, comdat
+// LLVM-BOTH: @_ZTAXtl6HasMemLi2EEE = linkonce_odr constant %struct.HasMem { i32 2 }, comdat
+template<HasMem m>
+int get_x() { return m.x; }
+// CIR-LABEL: cir.func {{.*}}@_Z5get_xIXtl6HasMemLi1EEEEiv()
+// CIR: %[[RET_ALLOCA:.*]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["__retval"]
+// Note: This is emitted as an 'ignored' expression, but we cannot omit it,
+// since it might be used in an example like below.
+// CIR: %[[GET_GLOB:.*]] = cir.get_global @_ZTAXtl6HasMemLi1EEE : !cir.ptr<!rec_HasMem>
+// CIR: %[[ONE:.*]] = cir.const #cir.int<1> : !s32i
+// CIR: cir.store %[[ONE]], %[[RET_ALLOCA]] : !s32i, !cir.ptr<!s32i>
+//
+// LLVM-BOTH-LABEL: define {{.*}}@_Z5get_xIXtl6HasMemLi1EEEEiv()
+// LLVM: %[[RET_ALLOCA:.*]] = alloca i32
+// LLVM: store i32 1, ptr %[[RET_ALLOCA]]
+// LLVM: %[[LOAD_RET:.*]] = load i32, ptr %[[RET_ALLOCA]]
+// LLVM: ret i32 %[[LOAD_RET]]
+// OGCG: ret i32 1
+
+template<HasMem m>
+HasMem get_m() { return m; }
+// CIR-LABEL: cir.func {{.*}}@_Z5get_mIXtl6HasMemLi2EEEES0_v()
+// CIR: %[[RET_ALLOCA:.*]] = cir.alloca !rec_HasMem, !cir.ptr<!rec_HasMem>, ["__retval"] {alignment = 4 : i64}
+// CIR: %[[GET_GLOB:.*]] = cir.get_global @_ZTAXtl6HasMemLi2EEE : !cir.ptr<!rec_HasMem>
+// CIR: cir.copy %[[GET_GLOB]] to %[[RET_ALLOCA]] : !cir.ptr<!rec_HasMem>
+// 
+// LLVM-BOTH-LABEL: define {{.*}}@_Z5get_mIXtl6HasMemLi2EEEES0_v()
+// LLVM-BOTH: %[[RET_ALLOCA:.*]] = alloca %struct.HasMem
+// LLVM-BOTH: call void @llvm.memcpy.p0.p0.i64(ptr {{.*}}%[[RET_ALLOCA]], ptr {{.*}}@_ZTAXtl6HasMemLi2EEE, i64 4, i1 false)
+// LLVM: %[[LOAD_RET:.*]] = load %struct.HasMem, ptr %[[RET_ALLOCA]]
+// LLVM: ret %struct.HasMem %[[LOAD_RET]]
+
+// OGCG: %[[COERCE:.*]] = getelementptr{{.*}} %struct.HasMem, ptr %[[RET_ALLOCA]], i32 0, i32 0
+// OGCG: %[[TO_RET:.*]] = load i32, ptr %[[COERCE]]
+// OGCG: ret i32 %[[TO_RET]]
+
+void caller() {
+  get_x<{1}>();
+  get_m<{2}>();
+}

``````````

</details>


https://github.com/llvm/llvm-project/pull/194482


More information about the cfe-commits mailing list