[clang] [CIR] Support for static variables (PR #143980)
Andy Kaylor via cfe-commits
cfe-commits at lists.llvm.org
Thu Jun 12 17:17:38 PDT 2025
https://github.com/andykaylor updated https://github.com/llvm/llvm-project/pull/143980
>From 3041ad6f68f4097dadf0870177eed4e749e915cc Mon Sep 17 00:00:00 2001
From: Andy Kaylor <akaylor at nvidia.com>
Date: Wed, 4 Jun 2025 10:27:48 -0700
Subject: [PATCH 1/2] [CIR] Support for static variables
This adds support for emitting static variables and their initializers.
---
.../CIR/Dialect/Builder/CIRBaseBuilder.h | 12 +
clang/include/clang/CIR/MissingFeatures.h | 1 +
clang/lib/CIR/CodeGen/CIRGenBuilder.h | 18 ++
clang/lib/CIR/CodeGen/CIRGenDecl.cpp | 267 +++++++++++++++++-
clang/lib/CIR/CodeGen/CIRGenFunction.h | 6 +
clang/lib/CIR/CodeGen/CIRGenModule.h | 13 +
clang/test/CIR/CodeGen/static-vars.c | 37 +++
clang/test/CIR/CodeGen/static-vars.cpp | 49 ++++
8 files changed, 401 insertions(+), 2 deletions(-)
create mode 100644 clang/test/CIR/CodeGen/static-vars.c
create mode 100644 clang/test/CIR/CodeGen/static-vars.cpp
diff --git a/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h b/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h
index a3754f4de66b0..dfff2740c75b5 100644
--- a/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h
+++ b/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h
@@ -185,11 +185,23 @@ class CIRBaseBuilderTy : public mlir::OpBuilder {
global.getSymName());
}
+ mlir::Value createGetGlobal(cir::GlobalOp global) {
+ return createGetGlobal(global.getLoc(), global);
+ }
+
cir::StoreOp createStore(mlir::Location loc, mlir::Value val, mlir::Value dst,
mlir::IntegerAttr align = {}) {
return create<cir::StoreOp>(loc, val, dst, align);
}
+ [[nodiscard]] cir::GlobalOp
+ createGlobal(mlir::ModuleOp module, mlir::Location loc, mlir::StringRef name,
+ mlir::Type type, cir::GlobalLinkageKind linkage) {
+ mlir::OpBuilder::InsertionGuard guard(*this);
+ setInsertionPointToStart(module.getBody());
+ return create<cir::GlobalOp>(loc, name, type, linkage);
+ }
+
cir::GetMemberOp createGetMember(mlir::Location loc, mlir::Type resultTy,
mlir::Value base, llvm::StringRef name,
unsigned index) {
diff --git a/clang/include/clang/CIR/MissingFeatures.h b/clang/include/clang/CIR/MissingFeatures.h
index fbd15d5c886d2..570b1544a6964 100644
--- a/clang/include/clang/CIR/MissingFeatures.h
+++ b/clang/include/clang/CIR/MissingFeatures.h
@@ -230,6 +230,7 @@ struct MissingFeatures {
static bool emitCtorPrologue() { return false; }
static bool thunks() { return false; }
static bool runCleanupsScope() { return false; }
+ static bool dataLayoutAllocaSize() { return false; }
// Missing types
static bool dataMemberType() { return false; }
diff --git a/clang/lib/CIR/CodeGen/CIRGenBuilder.h b/clang/lib/CIR/CodeGen/CIRGenBuilder.h
index fb1a290c18fa2..c81301e9a6fe1 100644
--- a/clang/lib/CIR/CodeGen/CIRGenBuilder.h
+++ b/clang/lib/CIR/CodeGen/CIRGenBuilder.h
@@ -24,6 +24,7 @@ namespace clang::CIRGen {
class CIRGenBuilderTy : public cir::CIRBaseBuilderTy {
const CIRGenTypeCache &typeCache;
llvm::StringMap<unsigned> recordNames;
+ llvm::StringMap<unsigned> globalsVersioning;
public:
CIRGenBuilderTy(mlir::MLIRContext &mlirContext, const CIRGenTypeCache &tc)
@@ -358,6 +359,23 @@ class CIRGenBuilderTy : public cir::CIRBaseBuilderTy {
/// pointed to by \p arrayPtr.
mlir::Value maybeBuildArrayDecay(mlir::Location loc, mlir::Value arrayPtr,
mlir::Type eltTy);
+
+ /// Creates a versioned global variable. If the symbol is already taken, an ID
+ /// will be appended to the symbol. The returned global must always be queried
+ /// for its name so it can be referenced correctly.
+ [[nodiscard]] cir::GlobalOp
+ createVersionedGlobal(mlir::ModuleOp module, mlir::Location loc,
+ mlir::StringRef name, mlir::Type type,
+ cir::GlobalLinkageKind linkage) {
+ // Create a unique name if the given name is already taken.
+ std::string uniqueName;
+ if (unsigned version = globalsVersioning[name.str()]++)
+ uniqueName = name.str() + "." + std::to_string(version);
+ else
+ uniqueName = name.str();
+
+ return createGlobal(module, loc, uniqueName, type, linkage);
+ }
};
} // namespace clang::CIRGen
diff --git a/clang/lib/CIR/CodeGen/CIRGenDecl.cpp b/clang/lib/CIR/CodeGen/CIRGenDecl.cpp
index 80b0172090aa3..1d703d78c8bf3 100644
--- a/clang/lib/CIR/CodeGen/CIRGenDecl.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenDecl.cpp
@@ -208,8 +208,25 @@ void CIRGenFunction::emitVarDecl(const VarDecl &d) {
if (d.hasExternalStorage())
return;
- if (d.getStorageDuration() != SD_Automatic)
- cgm.errorNYI(d.getSourceRange(), "emitVarDecl automatic storage duration");
+ if (d.getStorageDuration() != SD_Automatic) {
+ // Static sampler variables translated to function calls.
+ if (d.getType()->isSamplerT()) {
+ // Nothing needs to be done here, but let's flag it as an error until we
+ // have a test. It requires OpenCL support.
+ cgm.errorNYI(d.getSourceRange(), "emitVarDecl static sampler type");
+ return;
+ }
+
+ cir::GlobalLinkageKind linkage =
+ cgm.getCIRLinkageVarDefinition(&d, /*IsConstant=*/false);
+
+ // FIXME: We need to force the emission/use of a guard variable for
+ // some variables even if we can constant-evaluate them because
+ // we can't guarantee every translation unit will constant-evaluate them.
+
+ return emitStaticVarDecl(d, linkage);
+ }
+
if (d.getType().getAddressSpace() == LangAS::opencl_local)
cgm.errorNYI(d.getSourceRange(), "emitVarDecl openCL address space");
@@ -219,6 +236,252 @@ void CIRGenFunction::emitVarDecl(const VarDecl &d) {
return emitAutoVarDecl(d);
}
+static std::string getStaticDeclName(CIRGenModule &cgm, const VarDecl &d) {
+ if (cgm.getLangOpts().CPlusPlus)
+ return cgm.getMangledName(&d).str();
+
+ // If this isn't C++, we don't need a mangled name, just a pretty one.
+ assert(!d.isExternallyVisible() && "name shouldn't matter");
+ std::string contextName;
+ const DeclContext *dc = d.getDeclContext();
+ if (auto *cd = dyn_cast<CapturedDecl>(dc))
+ dc = cast<DeclContext>(cd->getNonClosureContext());
+ if (const auto *fd = dyn_cast<FunctionDecl>(dc))
+ contextName = std::string(cgm.getMangledName(fd));
+ else if (isa<BlockDecl>(dc))
+ cgm.errorNYI(d.getSourceRange(), "block decl context for static var");
+ else if (isa<ObjCMethodDecl>(dc))
+ cgm.errorNYI(d.getSourceRange(), "ObjC decl context for static var");
+ else
+ cgm.errorNYI(d.getSourceRange(), "Unknown context for static var decl");
+
+ contextName += "." + d.getNameAsString();
+ return contextName;
+}
+
+// TODO(cir): LLVM uses a Constant base class. Maybe CIR could leverage an
+// interface for all constants?
+cir::GlobalOp
+CIRGenModule::getOrCreateStaticVarDecl(const VarDecl &d,
+ cir::GlobalLinkageKind linkage) {
+ // In general, we don't always emit static var decls once before we reference
+ // them. It is possible to reference them before emitting the function that
+ // contains them, and it is possible to emit the containing function multiple
+ // times.
+ if (cir::GlobalOp existingGV = getStaticLocalDeclAddress(&d))
+ return existingGV;
+
+ QualType ty = d.getType();
+ assert(ty->isConstantSizeType() && "VLAs can't be static");
+
+ // Use the label if the variable is renamed with the asm-label extension.
+ std::string name;
+ if (d.hasAttr<AsmLabelAttr>())
+ errorNYI(d.getSourceRange(), "getOrCreateStaticVarDecl: asm label");
+ else
+ name = getStaticDeclName(*this, d);
+
+ mlir::Type lty = getTypes().convertTypeForMem(ty);
+ assert(!cir::MissingFeatures::addressSpace());
+
+ // OpenCL variables in local address space and CUDA shared
+ // variables cannot have an initializer.
+ mlir::Attribute init = nullptr;
+ if (d.hasAttr<LoaderUninitializedAttr>())
+ errorNYI(d.getSourceRange(),
+ "getOrCreateStaticVarDecl: LoaderUninitializedAttr");
+
+ init = builder.getZeroInitAttr(convertType(ty));
+
+ cir::GlobalOp gv = builder.createVersionedGlobal(
+ getModule(), getLoc(d.getLocation()), name, lty, linkage);
+ // TODO(cir): infer visibility from linkage in global op builder.
+ gv.setVisibility(getMLIRVisibilityFromCIRLinkage(linkage));
+ gv.setInitialValueAttr(init);
+ gv.setAlignment(getASTContext().getDeclAlign(&d).getAsAlign().value());
+
+ if (supportsCOMDAT() && gv.isWeakForLinker())
+ gv.setComdat(true);
+
+ assert(!cir::MissingFeatures::opGlobalThreadLocal());
+
+ setGVProperties(gv, &d);
+
+ // OG checks if the expected address space, denoted by the type, is the
+ // same as the actual address space indicated by attributes. If they aren't
+ // the same, an addrspacecast is emitted when this variable is accessed.
+ // In CIR however, cir.get_global already carries that information in
+ // !cir.ptr type - if this global is in OpenCL local address space, then its
+ // type would be !cir.ptr<..., addrspace(offload_local)>. Therefore we don't
+ // need an explicit address space cast in CIR: they will get emitted when
+ // lowering to LLVM IR.
+
+ // Ensure that the static local gets initialized by making sure the parent
+ // function gets emitted eventually.
+ const Decl *dc = cast<Decl>(d.getDeclContext());
+
+ // We can't name blocks or captured statements directly, so try to emit their
+ // parents.
+ if (isa<BlockDecl>(dc) || isa<CapturedDecl>(dc)) {
+ dc = dc->getNonClosureContext();
+ // FIXME: Ensure that global blocks get emitted.
+ if (!dc)
+ errorNYI(d.getSourceRange(), "non-closure context");
+ }
+
+ GlobalDecl gd;
+ if (isa<CXXConstructorDecl>(dc))
+ errorNYI(d.getSourceRange(), "C++ constructors static var context");
+ else if (isa<CXXDestructorDecl>(dc))
+ errorNYI(d.getSourceRange(), "C++ destructors static var context");
+ else if (const auto *fd = dyn_cast<FunctionDecl>(dc))
+ gd = GlobalDecl(fd);
+ else {
+ // Don't do anything for Obj-C method decls or global closures. We should
+ // never defer them.
+ assert(isa<ObjCMethodDecl>(dc) && "unexpected parent code decl");
+ }
+ if (gd.getDecl() && cir::MissingFeatures::openMP()) {
+ // Disable emission of the parent function for the OpenMP device codegen.
+ errorNYI(d.getSourceRange(), "OpenMP");
+ }
+
+ return gv;
+}
+
+/// Add the initializer for 'd' to the global variable that has already been
+/// created for it. If the initializer has a different type than gv does, this
+/// may free gv and return a different one. Otherwise it just returns gv.
+cir::GlobalOp CIRGenFunction::addInitializerToStaticVarDecl(
+ const VarDecl &d, cir::GlobalOp gv, cir::GetGlobalOp gvAddr) {
+ ConstantEmitter emitter(*this);
+ mlir::TypedAttr init =
+ mlir::cast<mlir::TypedAttr>(emitter.tryEmitForInitializer(d));
+
+ // If constant emission failed, then this should be a C++ static
+ // initializer.
+ if (!init) {
+ cgm.errorNYI(d.getSourceRange(), "static var without initializer");
+ return gv;
+ }
+
+ // TODO(cir): There should be debug code here to assert that the decl size
+ // matches the CIR data layout alloca size, but the code for calculating the
+ // alloca size is not implemented yet.
+ assert(!cir::MissingFeatures::dataLayoutAllocaSize());
+
+ // The initializer may differ in type from the global. Rewrite
+ // the global to match the initializer. (We have to do this
+ // because some types, like unions, can't be completely represented
+ // in the LLVM type system.)
+ if (gv.getSymType() != init.getType()) {
+ gv.setSymType(init.getType());
+
+ // Normally this should be done with a call to cgm.replaceGlobal(oldGV, gv),
+ // but since at this point the current block hasn't been really attached,
+ // there's no visibility into the GetGlobalOp corresponding to this Global.
+ // Given those constraints, thread in the GetGlobalOp and update it
+ // directly.
+ assert(!cir::MissingFeatures::addressSpace());
+ gvAddr.getAddr().setType(builder.getPointerTo(init.getType()));
+ }
+
+ bool needsDtor =
+ d.needsDestruction(getContext()) == QualType::DK_cxx_destructor;
+
+ assert(!cir::MissingFeatures::opGlobalConstant());
+ gv.setInitialValueAttr(init);
+
+ emitter.finalize(gv);
+
+ if (needsDtor) {
+ // We have a constant initializer, but a nontrivial destructor. We still
+ // need to perform a guarded "initialization" in order to register the
+ // destructor.
+ cgm.errorNYI(d.getSourceRange(), "C++ guarded init");
+ }
+
+ return gv;
+}
+
+void CIRGenFunction::emitStaticVarDecl(const VarDecl &d,
+ cir::GlobalLinkageKind linkage) {
+ // Check to see if we already have a global variable for this
+ // declaration. This can happen when double-emitting function
+ // bodies, e.g. with complete and base constructors.
+ cir::GlobalOp globalOp = cgm.getOrCreateStaticVarDecl(d, linkage);
+ // TODO(cir): we should have a way to represent global ops as values without
+ // having to emit a get global op. Sometimes these emissions are not used.
+ mlir::Value addr = builder.createGetGlobal(globalOp);
+ auto getAddrOp = mlir::cast<cir::GetGlobalOp>(addr.getDefiningOp());
+
+ CharUnits alignment = getContext().getDeclAlign(&d);
+
+ // Store into LocalDeclMap before generating initializer to handle
+ // circular references.
+ mlir::Type elemTy = convertTypeForMem(d.getType());
+ setAddrOfLocalVar(&d, Address(addr, elemTy, alignment));
+
+ // We can't have a VLA here, but we can have a pointer to a VLA,
+ // even though that doesn't really make any sense.
+ // Make sure to evaluate VLA bounds now so that we have them for later.
+ if (d.getType()->isVariablyModifiedType()) {
+ cgm.errorNYI(d.getSourceRange(),
+ "emitStaticVarDecl: variably modified type");
+ }
+
+ // Save the type in case adding the initializer forces a type change.
+ mlir::Type expectedType = addr.getType();
+
+ cir::GlobalOp var = globalOp;
+
+ assert(!cir::MissingFeatures::cudaSupport());
+
+ // If this value has an initializer, emit it.
+ if (d.getInit())
+ var = addInitializerToStaticVarDecl(d, var, getAddrOp);
+
+ var.setAlignment(alignment.getAsAlign().value());
+
+ if (d.hasAttr<AnnotateAttr>())
+ cgm.errorNYI(d.getSourceRange(), "static var annotation");
+
+ if (d.getAttr<PragmaClangBSSSectionAttr>())
+ cgm.errorNYI(d.getSourceRange(), "static var BSS section attribute");
+ if (d.getAttr<PragmaClangDataSectionAttr>())
+ cgm.errorNYI(d.getSourceRange(), "static var Data section attribute");
+ if (d.getAttr<PragmaClangRodataSectionAttr>())
+ cgm.errorNYI(d.getSourceRange(), "static var Rodata section attribute");
+ if (d.getAttr<PragmaClangRelroSectionAttr>())
+ cgm.errorNYI(d.getSourceRange(), "static var Relro section attribute");
+
+ if (d.getAttr<SectionAttr>())
+ cgm.errorNYI(d.getSourceRange(),
+ "static var object file section attribute");
+
+ if (d.hasAttr<RetainAttr>())
+ cgm.errorNYI(d.getSourceRange(), "static var retain attribute");
+ else if (d.hasAttr<UsedAttr>())
+ cgm.errorNYI(d.getSourceRange(), "static var used attribute");
+
+ if (cgm.getCodeGenOpts().KeepPersistentStorageVariables)
+ cgm.errorNYI(d.getSourceRange(), "static var keep persistent storage");
+
+ // From traditional codegen:
+ // We may have to cast the constant because of the initializer
+ // mismatch above.
+ //
+ // FIXME: It is really dangerous to store this in the map; if anyone
+ // RAUW's the GV uses of this constant will be invalid.
+ mlir::Value castedAddr =
+ builder.createBitcast(getAddrOp.getAddr(), expectedType);
+ localDeclMap.find(&d)->second = Address(castedAddr, elemTy, alignment);
+ cgm.setStaticLocalDeclAddress(&d, var);
+
+ assert(!cir::MissingFeatures::sanitizers());
+ assert(!cir::MissingFeatures::generateDebugInfo());
+}
+
void CIRGenFunction::emitScalarInit(const Expr *init, mlir::Location loc,
LValue lvalue, bool capturedByInit) {
assert(!cir::MissingFeatures::objCLifetime());
diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.h b/clang/lib/CIR/CodeGen/CIRGenFunction.h
index 361dcd5ef1c31..56ecee3f45367 100644
--- a/clang/lib/CIR/CodeGen/CIRGenFunction.h
+++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h
@@ -465,6 +465,10 @@ class CIRGenFunction : public CIRGenTypeCache {
/// compare the result against zero, returning an Int1Ty value.
mlir::Value evaluateExprAsBool(const clang::Expr *e);
+ cir::GlobalOp addInitializerToStaticVarDecl(const VarDecl &d,
+ cir::GlobalOp gv,
+ cir::GetGlobalOp gvAddr);
+
/// Set the address of a local variable.
void setAddrOfLocalVar(const clang::VarDecl *vd, Address addr) {
assert(!localDeclMap.count(vd) && "Decl already exists in LocalDeclMap!");
@@ -935,6 +939,8 @@ class CIRGenFunction : public CIRGenTypeCache {
void emitScalarInit(const clang::Expr *init, mlir::Location loc,
LValue lvalue, bool capturedByInit = false);
+ void emitStaticVarDecl(const VarDecl &d, cir::GlobalLinkageKind linkage);
+
void emitStoreOfScalar(mlir::Value value, Address addr, bool isVolatile,
clang::QualType ty, bool isInit = false,
bool isNontemporal = false);
diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.h b/clang/lib/CIR/CodeGen/CIRGenModule.h
index f76fd8e733642..03606dba200fd 100644
--- a/clang/lib/CIR/CodeGen/CIRGenModule.h
+++ b/clang/lib/CIR/CodeGen/CIRGenModule.h
@@ -113,8 +113,21 @@ class CIRGenModule : public CIRGenTypeCache {
mlir::Operation *lastGlobalOp = nullptr;
+ llvm::DenseMap<const Decl *, cir::GlobalOp> staticLocalDeclMap;
+
mlir::Operation *getGlobalValue(llvm::StringRef ref);
+ cir::GlobalOp getStaticLocalDeclAddress(const VarDecl *d) {
+ return staticLocalDeclMap[d];
+ }
+
+ void setStaticLocalDeclAddress(const VarDecl *d, cir::GlobalOp c) {
+ staticLocalDeclMap[d] = c;
+ }
+
+ cir::GlobalOp getOrCreateStaticVarDecl(const VarDecl &d,
+ cir::GlobalLinkageKind linkage);
+
/// If the specified mangled name is not in the module, create and return an
/// mlir::GlobalOp value
cir::GlobalOp getOrCreateCIRGlobal(llvm::StringRef mangledName, mlir::Type ty,
diff --git a/clang/test/CIR/CodeGen/static-vars.c b/clang/test/CIR/CodeGen/static-vars.c
new file mode 100644
index 0000000000000..f45a41d9a00fc
--- /dev/null
+++ b/clang/test/CIR/CodeGen/static-vars.c
@@ -0,0 +1,37 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o %t.cir
+// RUN: FileCheck --input-file=%t.cir %s
+
+void func1(void) {
+ // Should lower default-initialized static vars.
+ static int i;
+ // CHECK-DAG: cir.global "private" internal dsolocal @func1.i = #cir.int<0> : !s32i
+
+ // Should lower constant-initialized static vars.
+ static int j = 1;
+ // CHECK-DAG: cir.global "private" internal dsolocal @func1.j = #cir.int<1> : !s32i
+
+ // Should properly shadow static vars in nested scopes.
+ {
+ static int j = 2;
+ // CHECK-DAG: cir.global "private" internal dsolocal @func1.j.1 = #cir.int<2> : !s32i
+ }
+ {
+ static int j = 3;
+ // CHECK-DAG: cir.global "private" internal dsolocal @func1.j.2 = #cir.int<3> : !s32i
+ }
+
+ // Should lower basic static vars arithmetics.
+ j++;
+ // CHECK-DAG: %[[#V2:]] = cir.get_global @func1.j : !cir.ptr<!s32i>
+ // CHECK-DAG: %[[#V3:]] = cir.load{{.*}} %[[#V2]] : !cir.ptr<!s32i>, !s32i
+ // CHECK-DAG: %[[#V4:]] = cir.unary(inc, %[[#V3]]) nsw : !s32i, !s32i
+ // CHECK-DAG: cir.store{{.*}} %[[#V4]], %[[#V2]] : !s32i, !cir.ptr<!s32i>
+}
+
+// Should shadow static vars on different functions.
+void func2(void) {
+ static char i;
+ // CHECK-DAG: cir.global "private" internal dsolocal @func2.i = #cir.int<0> : !s8i
+ static float j;
+ // CHECK-DAG: cir.global "private" internal dsolocal @func2.j = #cir.fp<0.000000e+00> : !cir.float
+}
diff --git a/clang/test/CIR/CodeGen/static-vars.cpp b/clang/test/CIR/CodeGen/static-vars.cpp
new file mode 100644
index 0000000000000..9b892c69a6fed
--- /dev/null
+++ b/clang/test/CIR/CodeGen/static-vars.cpp
@@ -0,0 +1,49 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o %t.cir
+// RUN: FileCheck --input-file=%t.cir %s
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o %t1.ll
+// RUN: FileCheck --check-prefix=LLVM --input-file=%t1.ll %s
+
+void func1(void) {
+ // Should lower default-initialized static vars.
+ static int i;
+ // CHECK-DAG: cir.global "private" internal dsolocal @_ZZ5func1vE1i = #cir.int<0> : !s32i
+
+ // Should lower constant-initialized static vars.
+ static int j = 1;
+ // CHECK-DAG: cir.global "private" internal dsolocal @_ZZ5func1vE1j = #cir.int<1> : !s32i
+
+ // Should properly shadow static vars in nested scopes.
+ {
+ static int j = 2;
+ // CHECK-DAG: cir.global "private" internal dsolocal @_ZZ5func1vE1j_0 = #cir.int<2> : !s32i
+ }
+ {
+ static int j = 3;
+ // CHECK-DAG: cir.global "private" internal dsolocal @_ZZ5func1vE1j_1 = #cir.int<3> : !s32i
+ }
+
+ // Should lower basic static vars arithmetics.
+ j++;
+ // CHECK-DAG: %[[#V2:]] = cir.get_global @_ZZ5func1vE1j : !cir.ptr<!s32i>
+ // CHECK-DAG: %[[#V3:]] = cir.load{{.*}} %[[#V2]] : !cir.ptr<!s32i>, !s32i
+ // CHECK-DAG: %[[#V4:]] = cir.unary(inc, %[[#V3]]) nsw : !s32i, !s32i
+ // CHECK-DAG: cir.store{{.*}} %[[#V4]], %[[#V2]] : !s32i, !cir.ptr<!s32i>
+}
+
+// Should shadow static vars on different functions.
+void func2(void) {
+ static char i;
+ // CHECK-DAG: cir.global "private" internal dsolocal @_ZZ5func2vE1i = #cir.int<0> : !s8i
+ static float j;
+ // CHECK-DAG: cir.global "private" internal dsolocal @_ZZ5func2vE1j = #cir.fp<0.000000e+00> : !cir.float
+}
+
+// CHECK-DAG: cir.global linkonce_odr comdat @_ZZ4testvE1c = #cir.int<0> : !s32i
+
+// LLVM-DAG: $_ZZ4testvE1c = comdat any
+// LLVM-DAG: @_ZZ4testvE1c = linkonce_odr global i32 0, comdat, align 4
+
+inline void test() { static int c; }
+// CHECK-LABEL: @_Z4testv
+// CHECK: {{%.*}} = cir.get_global @_ZZ4testvE1c : !cir.ptr<!s32i>
+void foo() { test(); }
>From fcc90ef4f0e8816ac22b7fbc439cb2ef605a13fe Mon Sep 17 00:00:00 2001
From: Andy Kaylor <akaylor at nvidia.com>
Date: Thu, 12 Jun 2025 17:16:45 -0700
Subject: [PATCH 2/2] Address review feedback
---
.../CIR/Dialect/Builder/CIRBaseBuilder.h | 10 +++--
clang/lib/CIR/CodeGen/CIRGenDecl.cpp | 37 +++++--------------
2 files changed, 15 insertions(+), 32 deletions(-)
diff --git a/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h b/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h
index dfff2740c75b5..502d58d7db8b5 100644
--- a/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h
+++ b/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h
@@ -194,11 +194,13 @@ class CIRBaseBuilderTy : public mlir::OpBuilder {
return create<cir::StoreOp>(loc, val, dst, align);
}
- [[nodiscard]] cir::GlobalOp
- createGlobal(mlir::ModuleOp module, mlir::Location loc, mlir::StringRef name,
- mlir::Type type, cir::GlobalLinkageKind linkage) {
+ [[nodiscard]] cir::GlobalOp createGlobal(mlir::ModuleOp mlirModule,
+ mlir::Location loc,
+ mlir::StringRef name,
+ mlir::Type type,
+ cir::GlobalLinkageKind linkage) {
mlir::OpBuilder::InsertionGuard guard(*this);
- setInsertionPointToStart(module.getBody());
+ setInsertionPointToStart(mlirModule.getBody());
return create<cir::GlobalOp>(loc, name, type, linkage);
}
diff --git a/clang/lib/CIR/CodeGen/CIRGenDecl.cpp b/clang/lib/CIR/CodeGen/CIRGenDecl.cpp
index 1d703d78c8bf3..18988571d14ab 100644
--- a/clang/lib/CIR/CodeGen/CIRGenDecl.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenDecl.cpp
@@ -275,23 +275,20 @@ CIRGenModule::getOrCreateStaticVarDecl(const VarDecl &d,
assert(ty->isConstantSizeType() && "VLAs can't be static");
// Use the label if the variable is renamed with the asm-label extension.
- std::string name;
if (d.hasAttr<AsmLabelAttr>())
errorNYI(d.getSourceRange(), "getOrCreateStaticVarDecl: asm label");
- else
- name = getStaticDeclName(*this, d);
+
+ std::string name = getStaticDeclName(*this, d);
mlir::Type lty = getTypes().convertTypeForMem(ty);
assert(!cir::MissingFeatures::addressSpace());
- // OpenCL variables in local address space and CUDA shared
- // variables cannot have an initializer.
- mlir::Attribute init = nullptr;
- if (d.hasAttr<LoaderUninitializedAttr>())
+ if (d.hasAttr<LoaderUninitializedAttr>() || d.hasAttr<CUDASharedAttr>())
errorNYI(d.getSourceRange(),
"getOrCreateStaticVarDecl: LoaderUninitializedAttr");
+ assert(!cir::MissingFeatures::addressSpace());
- init = builder.getZeroInitAttr(convertType(ty));
+ mlir::Attribute init = builder.getZeroInitAttr(convertType(ty));
cir::GlobalOp gv = builder.createVersionedGlobal(
getModule(), getLoc(d.getLocation()), name, lty, linkage);
@@ -443,26 +440,10 @@ void CIRGenFunction::emitStaticVarDecl(const VarDecl &d,
var.setAlignment(alignment.getAsAlign().value());
- if (d.hasAttr<AnnotateAttr>())
- cgm.errorNYI(d.getSourceRange(), "static var annotation");
-
- if (d.getAttr<PragmaClangBSSSectionAttr>())
- cgm.errorNYI(d.getSourceRange(), "static var BSS section attribute");
- if (d.getAttr<PragmaClangDataSectionAttr>())
- cgm.errorNYI(d.getSourceRange(), "static var Data section attribute");
- if (d.getAttr<PragmaClangRodataSectionAttr>())
- cgm.errorNYI(d.getSourceRange(), "static var Rodata section attribute");
- if (d.getAttr<PragmaClangRelroSectionAttr>())
- cgm.errorNYI(d.getSourceRange(), "static var Relro section attribute");
-
- if (d.getAttr<SectionAttr>())
- cgm.errorNYI(d.getSourceRange(),
- "static var object file section attribute");
-
- if (d.hasAttr<RetainAttr>())
- cgm.errorNYI(d.getSourceRange(), "static var retain attribute");
- else if (d.hasAttr<UsedAttr>())
- cgm.errorNYI(d.getSourceRange(), "static var used attribute");
+ // There are a lot of attributes that need to be handled here. Until
+ // we start to support them, we just report an error if there are any.
+ if (d.hasAttrs())
+ cgm.errorNYI(d.getSourceRange(), "static var with attrs");
if (cgm.getCodeGenOpts().KeepPersistentStorageVariables)
cgm.errorNYI(d.getSourceRange(), "static var keep persistent storage");
More information about the cfe-commits
mailing list