Skip to content

[CIR] Fix dso_local and comdat handling for global vars #142214

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jun 2, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions clang/include/clang/CIR/MissingFeatures.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,17 +34,18 @@ struct MissingFeatures {
static bool opGlobalThreadLocal() { return false; }
static bool opGlobalConstant() { return false; }
static bool opGlobalWeakRef() { return false; }
static bool opGlobalLinkage() { return false; }
static bool opGlobalUnnamedAddr() { return false; }
static bool opGlobalSection() { return false; }
static bool opGlobalVisibility() { return false; }
static bool opGlobalDLLImportExport() { return false; }
static bool opGlobalPartition() { return false; }
static bool opGlobalUsedOrCompilerUsed() { return false; }

static bool supportIFuncAttr() { return false; }
static bool supportVisibility() { return false; }
static bool hiddenVisibility() { return false; }
static bool protectedVisibility() { return false; }
static bool defaultVisibility() { return false; }

// Load/store attributes
static bool opLoadStoreThreadLocal() { return false; }
Expand Down Expand Up @@ -77,6 +78,9 @@ struct MissingFeatures {
static bool opFuncLinkage() { return false; }
static bool opFuncVisibility() { return false; }
static bool opFuncNoProto() { return false; }
static bool opFuncCPUAndFeaturesAttributes() { return false; }
static bool opFuncSection() { return false; }
static bool opFuncSetComdat() { return false; }

// CallOp handling
static bool opCallBuiltinFunc() { return false; }
Expand Down Expand Up @@ -137,7 +141,6 @@ struct MissingFeatures {
// Misc
static bool cxxABI() { return false; }
static bool cirgenABIInfo() { return false; }
static bool cirgenTargetInfo() { return false; }
static bool abiArgInfo() { return false; }
static bool tryEmitAsConstant() { return false; }
static bool constructABIArgDirectExtend() { return false; }
Expand Down Expand Up @@ -200,6 +203,7 @@ struct MissingFeatures {
static bool cleanupsToDeactivate() { return false; }
static bool stackBase() { return false; }
static bool deferredDecls() { return false; }
static bool setTargetAttributes() { return false; }

// Missing types
static bool dataMemberType() { return false; }
Expand Down
64 changes: 55 additions & 9 deletions clang/lib/CIR/CodeGen/CIRGenModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,36 @@ cir::GlobalOp CIRGenModule::createGlobalOp(CIRGenModule &cgm,
return g;
}

void CIRGenModule::setCommonAttributes(GlobalDecl gd, mlir::Operation *gv) {
const Decl *d = gd.getDecl();
if (isa_and_nonnull<NamedDecl>(d))
setGVProperties(gv, dyn_cast<NamedDecl>(d));
assert(!cir::MissingFeatures::defaultVisibility());
assert(!cir::MissingFeatures::opGlobalUsedOrCompilerUsed());
}

void CIRGenModule::setNonAliasAttributes(GlobalDecl gd, mlir::Operation *op) {
setCommonAttributes(gd, op);

assert(!cir::MissingFeatures::opGlobalUsedOrCompilerUsed());
assert(!cir::MissingFeatures::opGlobalSection());
assert(!cir::MissingFeatures::opFuncCPUAndFeaturesAttributes());
assert(!cir::MissingFeatures::opFuncSection());

assert(!cir::MissingFeatures::setTargetAttributes());
}

static void setLinkageForGV(cir::GlobalOp &gv, const NamedDecl *nd) {
// Set linkage and visibility in case we never see a definition.
LinkageInfo lv = nd->getLinkageAndVisibility();
// Don't set internal linkage on declarations.
// "extern_weak" is overloaded in LLVM; we probably should have
// separate linkage types for this.
if (isExternallyVisible(lv.getLinkage()) &&
(nd->hasAttr<WeakAttr>() || nd->isWeakImported()))
gv.setLinkage(cir::GlobalLinkageKind::ExternalWeakLinkage);
}

/// If the specified mangled name is not in the module,
/// create and return an mlir GlobalOp with the specified type (TODO(cir):
/// address space).
Expand Down Expand Up @@ -387,7 +417,8 @@ CIRGenModule::getOrCreateCIRGlobal(StringRef mangledName, mlir::Type ty,

gv.setAlignmentAttr(getSize(astContext.getDeclAlign(d)));
assert(!cir::MissingFeatures::opGlobalConstant());
assert(!cir::MissingFeatures::opGlobalLinkage());

setLinkageForGV(gv, d);

if (d->getTLSKind())
errorNYI(d->getSourceRange(), "thread local global variable");
Expand Down Expand Up @@ -555,8 +586,6 @@ void CIRGenModule::emitGlobalVarDefinition(const clang::VarDecl *vd,
errorNYI(vd->getSourceRange(), "annotate global variable");
}

assert(!cir::MissingFeatures::opGlobalLinkage());

if (langOpts.CUDA) {
errorNYI(vd->getSourceRange(), "CUDA global variable");
}
Expand All @@ -577,6 +606,12 @@ void CIRGenModule::emitGlobalVarDefinition(const clang::VarDecl *vd,
assert(!cir::MissingFeatures::opGlobalDLLImportExport());
if (linkage == cir::GlobalLinkageKind::CommonLinkage)
errorNYI(initExpr->getSourceRange(), "common linkage");

setNonAliasAttributes(vd, gv);

assert(!cir::MissingFeatures::opGlobalThreadLocal());

maybeSetTrivialComdat(*vd, gv);
}

void CIRGenModule::emitGlobalDefinition(clang::GlobalDecl gd,
Expand Down Expand Up @@ -668,6 +703,15 @@ static bool shouldBeInCOMDAT(CIRGenModule &cgm, const Decl &d) {
llvm_unreachable("No such linkage");
}

void CIRGenModule::maybeSetTrivialComdat(const Decl &d, mlir::Operation *op) {
if (!shouldBeInCOMDAT(*this, d))
return;
if (auto globalOp = dyn_cast_or_null<cir::GlobalOp>(op))
globalOp.setComdat(true);

assert(!cir::MissingFeatures::opFuncSetComdat());
}

// TODO(CIR): this could be a common method between LLVM codegen.
static bool isVarDeclStrongDefinition(const ASTContext &astContext,
CIRGenModule &cgm, const VarDecl *vd,
Expand Down Expand Up @@ -830,10 +874,10 @@ CIRGenModule::getCIRLinkageVarDefinition(const VarDecl *vd, bool isConstant) {
return getCIRLinkageForDeclarator(vd, linkage, isConstant);
}

static cir::GlobalOp generateStringLiteral(mlir::Location loc,
mlir::TypedAttr c, CIRGenModule &cgm,
StringRef globalName,
CharUnits alignment) {
static cir::GlobalOp
generateStringLiteral(mlir::Location loc, mlir::TypedAttr c,
cir::GlobalLinkageKind lt, CIRGenModule &cgm,
StringRef globalName, CharUnits alignment) {
assert(!cir::MissingFeatures::addressSpace());

// Create a global variable for this string
Expand All @@ -843,7 +887,8 @@ static cir::GlobalOp generateStringLiteral(mlir::Location loc,

// Set up extra information and add to the module
gv.setAlignmentAttr(cgm.getSize(alignment));
assert(!cir::MissingFeatures::opGlobalLinkage());
gv.setLinkageAttr(
cir::GlobalLinkageKindAttr::get(cgm.getBuilder().getContext(), lt));
assert(!cir::MissingFeatures::opGlobalThreadLocal());
assert(!cir::MissingFeatures::opGlobalUnnamedAddr());
CIRGenModule::setInitializer(gv, c);
Expand Down Expand Up @@ -907,7 +952,8 @@ cir::GlobalOp CIRGenModule::getGlobalForStringLiteral(const StringLiteral *s,
mlir::Location loc = getLoc(s->getSourceRange());
auto typedC = llvm::cast<mlir::TypedAttr>(c);
cir::GlobalOp gv =
generateStringLiteral(loc, typedC, *this, uniqueName, alignment);
generateStringLiteral(loc, typedC, cir::GlobalLinkageKind::PrivateLinkage,
*this, uniqueName, alignment);
setDSOLocal(static_cast<mlir::Operation *>(gv));

assert(!cir::MissingFeatures::sanitizers());
Expand Down
9 changes: 9 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenModule.h
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,12 @@ class CIRGenModule : public CIRGenTypeCache {
cir::GlobalOp getGlobalForStringLiteral(const StringLiteral *s,
llvm::StringRef name = ".str");

/// Set attributes which are common to any form of a global definition (alias,
/// Objective-C method, function, global variable).
///
/// NOTE: This should only be called for definitions.
void setCommonAttributes(GlobalDecl gd, mlir::Operation *op);

const TargetCIRGenInfo &getTargetCIRGenInfo();

/// Helpers to convert the presumed location of Clang's SourceLocation to an
Expand Down Expand Up @@ -209,6 +215,7 @@ class CIRGenModule : public CIRGenTypeCache {
void emitTentativeDefinition(const VarDecl *d);

bool supportsCOMDAT() const;
void maybeSetTrivialComdat(const clang::Decl &d, mlir::Operation *op);

static void setInitializer(cir::GlobalOp &op, mlir::Attribute value);

Expand Down Expand Up @@ -285,6 +292,8 @@ class CIRGenModule : public CIRGenTypeCache {
// An ordered map of canonical GlobalDecls to their mangled names.
llvm::MapVector<clang::GlobalDecl, llvm::StringRef> mangledDeclNames;
llvm::StringMap<clang::GlobalDecl, llvm::BumpPtrAllocator> manglings;

void setNonAliasAttributes(GlobalDecl gd, mlir::Operation *op);
};
} // namespace CIRGen

Expand Down
32 changes: 27 additions & 5 deletions clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -969,13 +969,13 @@ void CIRToLLVMGlobalOpLowering::setupRegionInitializedLLVMGlobalOp(
const uint64_t alignment = op.getAlignment().value_or(0);
const mlir::LLVM::Linkage linkage = convertLinkage(op.getLinkage());
const StringRef symbol = op.getSymName();
mlir::SymbolRefAttr comdatAttr = getComdatAttr(op, rewriter);

SmallVector<mlir::NamedAttribute> attributes;
mlir::LLVM::GlobalOp newGlobalOp =
rewriter.replaceOpWithNewOp<mlir::LLVM::GlobalOp>(
op, llvmType, isConst, linkage, symbol, nullptr, alignment, addrSpace,
isDsoLocal, isThreadLocal,
/*comdat=*/mlir::SymbolRefAttr(), attributes);
isDsoLocal, isThreadLocal, comdatAttr, attributes);
newGlobalOp.getRegion().emplaceBlock();
rewriter.setInsertionPointToEnd(newGlobalOp.getInitializerBlock());
}
Expand Down Expand Up @@ -1024,6 +1024,7 @@ mlir::LogicalResult CIRToLLVMGlobalOpLowering::matchAndRewrite(
const mlir::LLVM::Linkage linkage = convertLinkage(op.getLinkage());
const StringRef symbol = op.getSymName();
SmallVector<mlir::NamedAttribute> attributes;
mlir::SymbolRefAttr comdatAttr = getComdatAttr(op, rewriter);

if (init.has_value()) {
if (mlir::isa<cir::FPAttr, cir::IntAttr, cir::BoolAttr>(init.value())) {
Expand Down Expand Up @@ -1054,12 +1055,33 @@ mlir::LogicalResult CIRToLLVMGlobalOpLowering::matchAndRewrite(
// Rewrite op.
rewriter.replaceOpWithNewOp<mlir::LLVM::GlobalOp>(
op, llvmType, isConst, linkage, symbol, init.value_or(mlir::Attribute()),
alignment, addrSpace, isDsoLocal, isThreadLocal,
/*comdat=*/mlir::SymbolRefAttr(), attributes);

alignment, addrSpace, isDsoLocal, isThreadLocal, comdatAttr, attributes);
return mlir::success();
}

mlir::SymbolRefAttr
CIRToLLVMGlobalOpLowering::getComdatAttr(cir::GlobalOp &op,
mlir::OpBuilder &builder) const {
if (!op.getComdat())
return mlir::SymbolRefAttr{};

mlir::ModuleOp module = op->getParentOfType<mlir::ModuleOp>();
mlir::OpBuilder::InsertionGuard guard(builder);
StringRef comdatName("__llvm_comdat_globals");
if (!comdatOp) {
builder.setInsertionPointToStart(module.getBody());
comdatOp =
builder.create<mlir::LLVM::ComdatOp>(module.getLoc(), comdatName);
}

builder.setInsertionPointToStart(&comdatOp.getBody().back());
auto selectorOp = builder.create<mlir::LLVM::ComdatSelectorOp>(
comdatOp.getLoc(), op.getSymName(), mlir::LLVM::comdat::Comdat::Any);
return mlir::SymbolRefAttr::get(
builder.getContext(), comdatName,
mlir::FlatSymbolRefAttr::get(selectorOp.getSymNameAttr()));
}

mlir::LogicalResult CIRToLLVMSwitchFlatOpLowering::matchAndRewrite(
cir::SwitchFlatOp op, OpAdaptor adaptor,
mlir::ConversionPatternRewriter &rewriter) const {
Expand Down
5 changes: 5 additions & 0 deletions clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#define CLANG_CIR_LOWERTOLLVM_H

#include "mlir/Dialect/LLVMIR/LLVMAttrs.h"
#include "mlir/Dialect/LLVMIR/LLVMDialect.h"
#include "mlir/Transforms/DialectConversion.h"
#include "clang/CIR/Dialect/IR/CIRDialect.h"

Expand Down Expand Up @@ -192,6 +193,10 @@ class CIRToLLVMGlobalOpLowering

void setupRegionInitializedLLVMGlobalOp(
cir::GlobalOp op, mlir::ConversionPatternRewriter &rewriter) const;

mutable mlir::LLVM::ComdatOp comdatOp = nullptr;
mlir::SymbolRefAttr getComdatAttr(cir::GlobalOp &op,
mlir::OpBuilder &builder) const;
};

class CIRToLLVMUnaryOpLowering
Expand Down
76 changes: 76 additions & 0 deletions clang/test/CIR/CodeGen/dso-local.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
// This test is copied from clang/test/CodeGen/dso-local-executable.c with
// unsupported targets, thread_local variables, and function checks removed.

// These are here so we find this test when grepping for missing features.
// cir::MissingFeatures::opGlobalThreadLocal()
// cir::MissingFeatures::opFuncDsoLocal()

/// Static relocation model defaults to -fdirect-access-external-data and sets
/// dso_local on most global objects.
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm -mrelocation-model static %s -o - | FileCheck --check-prefix=STATIC %s
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm -mrelocation-model static -fdirect-access-external-data %s -o - | FileCheck --check-prefix=STATIC %s
// STATIC: @baz = dso_local global i32 42
// STATIC-NEXT: @import_var = external dso_local global i32
// STATIC-NEXT: @weak_bar = extern_weak dso_local global i32
// STATIC-NEXT: @bar = external dso_local global i32

/// If -fno-direct-access-external-data is set, drop dso_local from global variable
/// declarations.
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm -mrelocation-model static -fno-direct-access-external-data %s -o - | FileCheck --check-prefix=STATIC-INDIRECT %s
// STATIC-INDIRECT: @baz = dso_local global i32 42
// STATIC-INDIRECT-NEXT: @import_var = external global i32
// STATIC-INDIRECT-NEXT: @weak_bar = extern_weak global i32
// STATIC-INDIRECT-NEXT: @bar = external global i32

// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm -pic-level 1 -pic-is-pie %s -o - | FileCheck --check-prefix=PIE %s
// PIE: @baz = dso_local global i32 42
// PIE-NEXT: @import_var = external global i32
// PIE-NEXT: @weak_bar = extern_weak global i32
// PIE-NEXT: @bar = external global i32

// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm -pic-level 1 -pic-is-pie -fdirect-access-external-data %s -o - | FileCheck --check-prefix=PIE-DIRECT %s
// PIE-DIRECT: @baz = dso_local global i32 42
// PIE-DIRECT-NEXT: @import_var = external dso_local global i32
// PIE-DIRECT-NEXT: @weak_bar = extern_weak global i32
// PIE-DIRECT-NEXT: @bar = external dso_local global i32

// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm -mrelocation-model static -fno-plt %s -o - | FileCheck --check-prefix=NOPLT %s
// NOPLT: @baz = dso_local global i32 42
// NOPLT-NEXT: @import_var = external dso_local global i32
// NOPLT-NEXT: @weak_bar = extern_weak dso_local global i32
// NOPLT-NEXT: @bar = external dso_local global i32

// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm -fno-plt -pic-level 1 -pic-is-pie -fdirect-access-external-data %s -o - | FileCheck --check-prefix=PIE-DIRECT-NOPLT %s
// PIE-DIRECT-NOPLT: @baz = dso_local global i32 42
// PIE-DIRECT-NOPLT-NEXT: @import_var = external dso_local global i32
// PIE-DIRECT-NOPLT-NEXT: @weak_bar = extern_weak global i32
// PIE-DIRECT-NOPLT-NEXT: @bar = external dso_local global i32

// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm -pic-level 1 -pic-is-pie -fno-plt %s -o - | FileCheck --check-prefix=PIE-NO-PLT %s
// RUN: %clang_cc1 -triple powerpc64le -fclangir -emit-llvm -mrelocation-model static %s -o - | FileCheck --check-prefix=PIE-NO-PLT %s
// PIE-NO-PLT: @baz = dso_local global i32 42
// PIE-NO-PLT-NEXT: @import_var = external global i32
// PIE-NO-PLT-NEXT: @weak_bar = extern_weak global i32
// PIE-NO-PLT-NEXT: @bar = external global i32

/// -fdirect-access-external-data is currently ignored for -fPIC.
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm -pic-level 2 %s -o - | FileCheck --check-prefix=SHARED %s
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm -pic-level 2 -fdirect-access-external-data %s -o - | FileCheck --check-prefix=SHARED %s
// SHARED-DAG: @bar = external global i32
// SHARED-DAG: @weak_bar = extern_weak global i32
// SHARED-DAG: @baz ={{.*}} global i32 42

int baz = 42;
__attribute__((dllimport)) extern int import_var;
__attribute__((weak)) extern int weak_bar;
extern int bar;

int *use_import(void) {
return &import_var;
}

int *zed(void) {
if (baz)
return &weak_bar;
return &bar;
}
2 changes: 1 addition & 1 deletion clang/test/CIR/CodeGen/namespace.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ namespace test {
}
}

// CHECK-DAG: cir.global "private" internal @_ZN12_GLOBAL__N_12g1E = #cir.int<1> : !s32i
// CHECK-DAG: cir.global "private" internal dsolocal @_ZN12_GLOBAL__N_12g1E = #cir.int<1> : !s32i
// CHECK-DAG: cir.global external @_ZN4test2g2E = #cir.int<2> : !s32i
// CHECK-DAG: cir.global external @_ZN4test5test22g3E = #cir.int<3> : !s32i
// CHECK-DAG: cir.func @_ZN12_GLOBAL__N_12f1Ev()
Expand Down
12 changes: 6 additions & 6 deletions clang/test/CIR/CodeGen/string-literals.c
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@
// RUN: %clang_cc1 -triple aarch64-none-linux-android21 -emit-llvm %s -o %t.ll
// RUN: FileCheck --check-prefix=OGCG --input-file=%t.ll %s

// CIR: cir.global "private" external @[[STR1_GLOBAL:.*]] = #cir.const_array<"1\00" : !cir.array<!s8i x 2>> : !cir.array<!s8i x 2>
// CIR: cir.global "private" external @[[STR2_GLOBAL:.*]] = #cir.zero : !cir.array<!s8i x 1>
// CIR: cir.global "private" external @[[STR3_GLOBAL:.*]] = #cir.zero : !cir.array<!s8i x 2>
// CIR: cir.global "private" cir_private dsolocal @[[STR1_GLOBAL:.*]] = #cir.const_array<"1\00" : !cir.array<!s8i x 2>> : !cir.array<!s8i x 2>
// CIR: cir.global "private" cir_private dsolocal @[[STR2_GLOBAL:.*]] = #cir.zero : !cir.array<!s8i x 1>
// CIR: cir.global "private" cir_private dsolocal @[[STR3_GLOBAL:.*]] = #cir.zero : !cir.array<!s8i x 2>

// LLVM: @[[STR1_GLOBAL:.*]] = global [2 x i8] c"1\00"
// LLVM: @[[STR2_GLOBAL:.*]] = global [1 x i8] zeroinitializer
// LLVM: @[[STR3_GLOBAL:.*]] = global [2 x i8] zeroinitializer
// LLVM: @[[STR1_GLOBAL:.*]] = private global [2 x i8] c"1\00"
// LLVM: @[[STR2_GLOBAL:.*]] = private global [1 x i8] zeroinitializer
// LLVM: @[[STR3_GLOBAL:.*]] = private global [2 x i8] zeroinitializer

// OGCG: @[[STR1_GLOBAL:.*]] = private unnamed_addr constant [2 x i8] c"1\00"
// OGCG: @[[STR2_GLOBAL:.*]] = private unnamed_addr constant [1 x i8] zeroinitializer
Expand Down
Loading
Loading