https://github.com/erichkeane updated https://github.com/llvm/llvm-project/pull/197265
>From dea6fc94de4fbbcc2fcf0dd8f4601c00b1d88ca0 Mon Sep 17 00:00:00 2001 From: erichkeane <[email protected]> Date: Mon, 11 May 2026 14:04:08 -0700 Subject: [PATCH 1/3] [CIR] Global-TLS Wrapper/init func lowering- This patch does most of the rest of the Global-TLS lowering prepare feature. It makes sure the individual 'init' functions are generated, and properly generates the 'wrapper' to call the 'init'. It is missing(to come in the followup patch): 1- Generation of the actual 'call all inits' function. 2- Emitting the 'guard' functionality in unordered inits. --- .../Dialect/Transforms/LoweringPrepare.cpp | 202 ++++++++++++++---- .../test/CIR/CodeGen/global-tls-dyn-init.cpp | 114 ++++++++-- .../CIR/CodeGen/global-tls-simple-init.cpp | 145 +++++++++---- .../test/CIR/CodeGen/global-tls-templates.cpp | 108 ++++++++-- 4 files changed, 442 insertions(+), 127 deletions(-) diff --git a/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp b/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp index dc1a872bd8c8a..e041ad984c995 100644 --- a/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp +++ b/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp @@ -123,10 +123,12 @@ struct LoweringPreparePass /// When looking at the 'global' op, create the wrapper function. void defineGlobalThreadLocalWrapper(cir::GlobalOp op, cir::FuncOp initAlias, bool isVarDefinition); + /// Create an initialization alias for a thread-local variable. + cir::FuncOp defineGlobalThreadLocalInitAlias(cir::GlobalOp op, + cir::FuncOp aliasee); /// Get the declaration for the 'wrapper' function for a global-TLS variable. cir::FuncOp getOrCreateThreadLocalWrapper(CIRBaseBuilderTy &builder, cir::GlobalOp op); - /// Handle the dtor region by registering destructor with __cxa_atexit cir::FuncOp getOrCreateDtorFunc(CIRBaseBuilderTy &builder, cir::GlobalOp op, mlir::Region &dtorRegion, @@ -134,6 +136,9 @@ struct LoweringPreparePass /// Build a module init function that calls all the dynamic initializers. void buildCXXGlobalInitFunc(); + // Build a init function for all of the ordered global thread local storage + // variables. + void buildCXXGlobalTlsFunc(); /// Materialize global ctor/dtor list void buildGlobalCtorDtorList(); @@ -173,6 +178,9 @@ struct LoweringPreparePass /// Get or create __cxa_guard_release function. cir::FuncOp getGuardReleaseFn(cir::PointerType guardPtrTy); + /// Get or create the __init_tls function. + cir::FuncOp getTlsInitFn(); + /// Create a guard global variable for a static local. cir::GlobalOp createGuardGlobalOp(CIRBaseBuilderTy &builder, mlir::Location loc, llvm::StringRef name, @@ -195,16 +203,35 @@ struct LoweringPreparePass /// Get or create the guard variable for a static local declaration. cir::GlobalOp getOrCreateStaticLocalDeclGuardAddress( - CIRBaseBuilderTy &builder, cir::GlobalOp globalOp, - cir::ASTVarDeclInterface varDecl, cir::IntType guardTy, - clang::CharUnits guardAlignment) { + CIRBaseBuilderTy &builder, cir::GlobalOp globalOp, StringRef guardName, + bool isLocalVarDecl, bool useInt8GuardVariable) { + + cir::CIRDataLayout dataLayout(mlirModule); + cir::IntType guardTy; + clang::CharUnits guardAlignment; + // Guard variables are 64 bits in the generic ABI and size width on ARM + // (i.e. 32-bit on AArch32, 64-bit on AArch64). + if (useInt8GuardVariable) { + guardTy = cir::IntType::get(&getContext(), 8, /*isSigned=*/true); + guardAlignment = clang::CharUnits::One(); + } else if (useARMGuardVarABI()) { + // Guard variables are size width on ARM (32-bit AArch32, 64-bit AArch64). + const unsigned sizeTypeSize = + astCtx->getTypeSize(astCtx->getSignedSizeType()); + guardTy = + cir::IntType::get(&getContext(), sizeTypeSize, /*isSigned=*/true); + guardAlignment = + clang::CharUnits::fromQuantity(dataLayout.getABITypeAlign(guardTy)); + } else { + guardTy = cir::IntType::get(&getContext(), 64, /*isSigned=*/true); + guardAlignment = + clang::CharUnits::fromQuantity(dataLayout.getABITypeAlign(guardTy)); + } + assert(guardTy && guardAlignment.getQuantity() != 0); + llvm::StringRef globalSymName = globalOp.getSymName(); cir::GlobalOp guard = getStaticLocalDeclGuardAddress(globalSymName); if (!guard) { - // Get the guard name from the static_local attribute. - llvm::StringRef guardName = - globalOp.getStaticLocalGuard()->getName().getValue(); - // Create the guard variable with a zero-initializer. guard = createGuardGlobalOp(builder, globalOp->getLoc(), guardName, guardTy, globalOp.getLinkage()); @@ -219,10 +246,12 @@ struct LoweringPreparePass // Wasm. bool hasComdat = globalOp.getComdat(); const llvm::Triple &triple = astCtx->getTargetInfo().getTriple(); - if (!varDecl.isLocalVarDecl() && hasComdat && + // TODO(cir): for now, we're just setting comdat to true, but it should + // contain a comdat reference name here instead. + if (!isLocalVarDecl && hasComdat && (triple.isOSBinFormatELF() || triple.isOSBinFormatWasm())) { - globalOp->emitError("NYI: guard COMDAT for non-local variables"); - return {}; + // This should be a comdat for the variable. + guard.setComdat(true); } else if (hasComdat && globalOp.isWeakForLinker()) { guard.setComdat(true); } @@ -264,7 +293,9 @@ struct LoweringPreparePass /// Tracks existing dynamic initializers. llvm::StringMap<uint32_t> dynamicInitializerNames; llvm::SmallVector<cir::FuncOp> dynamicInitializers; + llvm::SmallVector<cir::FuncOp> globalThreadLocalInitializers; llvm::StringMap<cir::FuncOp> threadLocalWrappers; + llvm::StringMap<cir::FuncOp> threadLocalInitAliases; /// Tracks guard variables for static locals (keyed by global symbol name). llvm::StringMap<cir::GlobalOp> staticLocalDeclGuardMap; @@ -988,7 +1019,6 @@ cir::FuncOp LoweringPreparePass::getOrCreateDtorFunc(CIRBaseBuilderTy &builder, cir::CallOp &dtorCall) { mlir::OpBuilder::InsertionGuard guard(builder); assert(!cir::MissingFeatures::astVarDeclInterface()); - assert(!cir::MissingFeatures::opGlobalThreadLocal()); cir::VoidType voidTy = builder.getVoidTy(); auto voidPtrTy = cir::PointerType::get(voidTy); @@ -1170,6 +1200,18 @@ LoweringPreparePass::getGuardReleaseFn(cir::PointerType guardPtrTy) { return buildRuntimeFunction(builder, "__cxa_guard_release", loc, fnType); } +cir::FuncOp LoweringPreparePass::getTlsInitFn() { + // void __tls_init(void); + CIRBaseBuilderTy builder(getContext()); + mlir::OpBuilder::InsertionGuard ipGuard{builder}; + builder.setInsertionPointToStart(mlirModule.getBody()); + mlir::Location loc = mlirModule.getLoc(); + cir::VoidType voidTy = cir::VoidType::get(&getContext()); + auto fnType = cir::FuncType::get({}, voidTy); + return buildRuntimeFunction(builder, "__tls_init", loc, fnType, + cir::GlobalLinkageKind::InternalLinkage); +} + cir::GlobalOp LoweringPreparePass::createGuardGlobalOp( CIRBaseBuilderTy &builder, mlir::Location loc, llvm::StringRef name, cir::IntType guardTy, cir::GlobalLinkageKind linkage) { @@ -1225,33 +1267,11 @@ void LoweringPreparePass::handleStaticLocal(cir::GlobalOp globalOp, // If we have a global variable with internal linkage and thread-safe statics // are disabled, we can just let the guard variable be of type i8. bool useInt8GuardVariable = !threadsafe && globalOp.hasInternalLinkage(); - cir::CIRDataLayout dataLayout(mlirModule); - cir::IntType guardTy; - clang::CharUnits guardAlignment; - // Guard variables are 64 bits in the generic ABI and size width on ARM - // (i.e. 32-bit on AArch32, 64-bit on AArch64). - if (useInt8GuardVariable) { - guardTy = cir::IntType::get(&getContext(), 8, /*isSigned=*/true); - guardAlignment = clang::CharUnits::One(); - } else if (useARMGuardVarABI()) { - // Guard variables are size width on ARM (32-bit AArch32, 64-bit AArch64). - const unsigned sizeTypeSize = - astCtx->getTypeSize(astCtx->getSignedSizeType()); - guardTy = cir::IntType::get(&getContext(), sizeTypeSize, /*isSigned=*/true); - guardAlignment = - clang::CharUnits::fromQuantity(dataLayout.getABITypeAlign(guardTy)); - } else { - guardTy = cir::IntType::get(&getContext(), 64, /*isSigned=*/true); - guardAlignment = - clang::CharUnits::fromQuantity(dataLayout.getABITypeAlign(guardTy)); - } - assert(guardTy && guardAlignment.getQuantity() != 0); - - auto guardPtrTy = cir::PointerType::get(guardTy); // Create the guard variable if we don't already have it. cir::GlobalOp guard = getOrCreateStaticLocalDeclGuardAddress( - builder, globalOp, varDecl, guardTy, guardAlignment); + builder, globalOp, globalOp.getStaticLocalGuard()->getName().getValue(), + varDecl.isLocalVarDecl(), useInt8GuardVariable); if (!guard) { // Error was already emitted, just restore the terminator and return. localInitBlock->push_back(ret); @@ -1288,7 +1308,7 @@ void LoweringPreparePass::handleStaticLocal(cir::GlobalOp globalOp, auto bytePtrTy = cir::PointerType::get(builder.getSIntNTy(8)); mlir::Value bytePtr = builder.createBitcast(guardPtr, bytePtrTy); mlir::Value guardLoad = builder.createAlignedLoad( - localInitOp.getLoc(), bytePtr, guardAlignment.getAsAlign().value()); + localInitOp.getLoc(), bytePtr, *guard.getAlignment()); // Itanium ABI: // An implementation supporting thread-safety on multiprocessor @@ -1341,7 +1361,8 @@ void LoweringPreparePass::handleStaticLocal(cir::GlobalOp globalOp, /*withElseRegion=*/false, [&](mlir::OpBuilder &, mlir::Location) { emitCXXGuardedInitIf(builder, globalOp, localInitOp.getCtorRegion(), localInitOp.getDtorRegion(), varDecl, guardPtr, - guardPtrTy, threadsafe); + builder.getPointerTo(guard.getSymType()), + threadsafe); }); } else { // Threadsafe statics without inline atomics - call __cxa_guard_acquire @@ -1447,12 +1468,64 @@ void LoweringPreparePass::defineGlobalThreadLocalWrapper(cir::GlobalOp op, // If we are a situation where we have/need one, emit a call to the init // function. if (initAlias) { - op->emitError("not yet implemented, wrapper with an init alias"); + if (!isVarDefinition) { + // If this isn't a definition, we have to check that the alias exists. + mlir::Value funcLoad = cir::GetGlobalOp::create( + builder, initAlias.getLoc(), + cir::PointerType::get(initAlias.getFunctionType()), + initAlias.getSymName()); + mlir::Value nullCheck = + builder.getNullValue(funcLoad.getType(), initAlias.getLoc()); + mlir::Value cmp = cir::CmpOp::create( + builder, initAlias.getLoc(), cir::CmpOpKind::ne, funcLoad, nullCheck); + cir::IfOp::create( + builder, initAlias.getLoc(), cmp, /*withElseRegion=*/false, + [&](mlir::OpBuilder &, mlir::Location loc) { + builder.createCallOp(initAlias.getLoc(), initAlias, {}); + cir::YieldOp::create(builder, initAlias.getLoc()); + }); + } else { + // If this IS a definition, we know the alias exists, so we can just emit + // a call to it. + builder.createCallOp(initAlias.getLoc(), initAlias, {}); + } } auto get = builder.createGetGlobal(op, /*tls=*/true); cir::ReturnOp::create(builder, op.getLoc(), {get}); } +cir::FuncOp +LoweringPreparePass::defineGlobalThreadLocalInitAlias(cir::GlobalOp op, + cir::FuncOp aliasee) { + CIRBaseBuilderTy builder(getContext()); + mlir::OpBuilder::InsertionGuard insertGuard(builder); + builder.setInsertionPointToStart(&mlirModule.getBodyRegion().front()); + mlir::StringAttr aliasName = op.getDynTlsRefs()->getInitName(); + auto existingAliasIter = threadLocalInitAliases.find(aliasName.getValue()); + + if (existingAliasIter != threadLocalInitAliases.end()) + return existingAliasIter->second; + + auto funcType = cir::FuncType::get({}, builder.getVoidTy()); + cir::FuncOp alias = + cir::FuncOp::create(builder, op.getLoc(), aliasName, funcType); + alias.setLinkageAttr(op.getLinkageAttr()); + + if (aliasee) { + alias.setAliasee(aliasee.getSymName()); + } else { + // If we don't have anything to alias (because this isn't a variable + // definition!), we set this as just a function definition with no alias, + // and extern-weak. + alias.setLinkage(cir::GlobalLinkageKind::ExternalWeakLinkage); + mlir::SymbolTable::setSymbolVisibility( + alias, mlir::SymbolTable::Visibility::Private); + } + + threadLocalInitAliases.insert({aliasName.getValue(), alias}); + return alias; +} + void LoweringPreparePass::lowerGlobalOp(GlobalOp op) { // Static locals are handled separately via guard variables. if (op.getStaticLocalGuard()) @@ -1460,7 +1533,6 @@ void LoweringPreparePass::lowerGlobalOp(GlobalOp op) { mlir::Region &ctorRegion = op.getCtorRegion(); mlir::Region &dtorRegion = op.getDtorRegion(); - // TODO(cir): Implement the initialization of this. cir::FuncOp initAlias; if (!ctorRegion.empty() || !dtorRegion.empty()) { @@ -1473,8 +1545,39 @@ void LoweringPreparePass::lowerGlobalOp(GlobalOp op) { dtorRegion.getBlocks().clear(); assert(!cir::MissingFeatures::astVarDeclInterface()); - dynamicInitializers.push_back(f); - } + if (op.getTlsModel() == TLS_Model::GeneralDynamic && + !op.getStaticLocalGuard().has_value()) { + // There are two types of global TLS variables: 'ordered' and 'unordered'. + // 'ordered' are the common case. A call to any of them causes all of the + // initializers for all other 'ordered' ones to be called, via a + // `__tls_init` function. So the 'init alias' that gets called in the + // wrapper for these goes directly to `__tls_init`. + + // 'Unordered' values are the case for variable templates. In this case, + // their init alias goes directly to their init function. The FE generates + // a guard variable for them (since they cannot use the global guard), so + // we differentiate them that way. + + if (op.getDynTlsRefs()->getGuardName()) { + // Unordered: the alias is the function we just generated. + initAlias = defineGlobalThreadLocalInitAlias(op, f); + } else { + // Ordered: Get the __tls_init, and make the alias to that. + initAlias = defineGlobalThreadLocalInitAlias(op, getTlsInitFn()); + // Ordered inits also need to get called from the __tls_init function, + // so we add the init function to the list, so that we can add them to + // it later. + globalThreadLocalInitializers.push_back(f); + } + } else { + dynamicInitializers.push_back(f); + } + } else if (op.getTlsModel() == TLS_Model::GeneralDynamic && + op.getDynTlsRefs() && op.isDeclaration()) { + // If this is a declaration and has no init function, we probably DO have to + // create an alias that needs checking, so create it as extern-weak. + initAlias = defineGlobalThreadLocalInitAlias(op, {}); + } // We need a wrapper for TLS globals that MIGHT have a non-constant // initialization. The FE will have generated the DynTlsRefs for any with @@ -1609,6 +1712,20 @@ void LoweringPreparePass::buildGlobalCtorDtorList() { } } +void LoweringPreparePass::buildCXXGlobalTlsFunc() { + if (globalThreadLocalInitializers.empty()) + return; + // The global-ordered-init function for TLS variables just calls each of the + // init-functions in order after doing a guard. + + cir::FuncOp tlsInit = getTlsInitFn(); + mlir::Location loc = tlsInit.getLoc(); + CIRBaseBuilderTy builder(getContext()); + mlir::Block *entryBB = tlsInit.addEntryBlock(); + builder.setInsertionPointToStart(entryBB); + // Note: a followup patch will emit the body here correctly. + cir::ReturnOp::create(builder, loc); +} void LoweringPreparePass::buildCXXGlobalInitFunc() { if (dynamicInitializers.empty()) return; @@ -2584,6 +2701,7 @@ void LoweringPreparePass::runOnOperation() { runOnOp(o); buildCXXGlobalInitFunc(); + buildCXXGlobalTlsFunc(); if (astCtx->getLangOpts().CUDA && !astCtx->getLangOpts().CUDAIsDevice) buildCUDAModuleCtor(); diff --git a/clang/test/CIR/CodeGen/global-tls-dyn-init.cpp b/clang/test/CIR/CodeGen/global-tls-dyn-init.cpp index b974f61131353..f471d586aa850 100644 --- a/clang/test/CIR/CodeGen/global-tls-dyn-init.cpp +++ b/clang/test/CIR/CodeGen/global-tls-dyn-init.cpp @@ -10,51 +10,70 @@ struct CtorDtor { int i; }; -// Wrappers: +// LLVM-BOTH-DAG: @__dso_handle = external hidden global i8 +// LLVM-BOTH-DAG: @tls_cd = thread_local global %struct.CtorDtor { i32 5 }, align 4 +// LLVM-BOTH-DAG: @tls_cd_dyn = thread_local global %struct.CtorDtor zeroinitializer, align 4 +// LLVM-BOTH-DAG: @tls_cd_ref = thread_local global ptr null, align 8 +// LLVM-BOTH-DAG: @tls_cd_dyn_not_used = thread_local global %struct.CtorDtor zeroinitializer, align 4 +// +// LLVM-BOTH-DAG: @_ZTH19tls_cd_dyn_not_used = alias void (), ptr @__tls_init +// LLVM-BOTH-DAG: @_ZTH10tls_cd_ref = alias void (), ptr @__tls_init +// LLVM-BOTH-DAG: @_ZTH10tls_cd_dyn = alias void (), ptr @__tls_init +// LLVM-BOTH-DAG: @_ZTH6tls_cd = alias void (), ptr @__tls_init + +// Wrappers & aliases. // CIR-LABEL: cir.func comdat weak_odr private hidden @_ZTW19tls_cd_dyn_not_used() -> !cir.ptr<!rec_CtorDtor> { -// CIR-NOT: cir.call @_ZTH19tls_cd_dyn_not_used() : () -> () +// CIR: cir.call @_ZTH19tls_cd_dyn_not_used() : () -> () // CIR: %[[GET_GLOB:.*]] = cir.get_global thread_local @tls_cd_dyn_not_used : !cir.ptr<!rec_CtorDtor> // CIR: cir.return %[[GET_GLOB]] : !cir.ptr<!rec_CtorDtor> +// CIR: cir.func @_ZTH19tls_cd_dyn_not_used() alias(@__tls_init) // CIR-LABEL: cir.func comdat weak_odr private hidden @_ZTW10tls_cd_ref() -> !cir.ptr<!cir.ptr<!rec_CtorDtor>> { -// CIR-NOT: cir.call @_ZTH10tls_cd_ref() : () -> () +// CIR: cir.call @_ZTH10tls_cd_ref() : () -> () // CIR: %[[GET_GLOB:.*]] = cir.get_global thread_local @tls_cd_ref : !cir.ptr<!cir.ptr<!rec_CtorDtor>> // CIR: cir.return %[[GET_GLOB]] : !cir.ptr<!cir.ptr<!rec_CtorDtor>> +// CIR: cir.func @_ZTH10tls_cd_ref() alias(@__tls_init) // CIR-LABEL: cir.func comdat weak_odr private hidden @_ZTW10tls_cd_dyn() -> !cir.ptr<!rec_CtorDtor> { -// CIR-NOT: cir.call @_ZTH10tls_cd_dyn() : () -> () +// CIR: cir.call @_ZTH10tls_cd_dyn() : () -> () // CIR: %[[GET_GLOB:.*]] = cir.get_global thread_local @tls_cd_dyn : !cir.ptr<!rec_CtorDtor> // CIR: cir.return %[[GET_GLOB]] : !cir.ptr<!rec_CtorDtor> +// CIR: cir.func @_ZTH10tls_cd_dyn() alias(@__tls_init) // CIR-LABEL: cir.func comdat weak_odr private hidden @_ZTW6tls_cd() -> !cir.ptr<!rec_CtorDtor> { -// CIR-NOT: cir.call @_ZTH6tls_cd() : () -> () +// CIR: cir.call @_ZTH6tls_cd() : () -> () // CIR: %[[GET_GLOB:.*]] = cir.get_global thread_local @tls_cd : !cir.ptr<!rec_CtorDtor> // CIR: cir.return %[[GET_GLOB]] : !cir.ptr<!rec_CtorDtor> +// CIR: cir.func @_ZTH6tls_cd() alias(@__tls_init) + +// CIR-LABEL: cir.func internal private @__tls_init() { +// CIR: cir.return // LLVM: define weak_odr hidden ptr @_ZTW19tls_cd_dyn_not_used() { -// LLVM-NOT: call void @_ZTH19tls_cd_dyn_not_used() +// LLVM: call void @_ZTH19tls_cd_dyn_not_used() // LLVM: %[[GET_GLOB:.*]] = call ptr @llvm.threadlocal.address.p0(ptr @tls_cd_dyn_not_used) // LLVM: ret ptr %[[GET_GLOB]] // LLVM: } // // LLVM: define weak_odr hidden ptr @_ZTW10tls_cd_ref() { -// LLVM-NOT: call void @_ZTH10tls_cd_ref() +// LLVM: call void @_ZTH10tls_cd_ref() // LLVM: %[[GET_GLOB:.*]] = call ptr @llvm.threadlocal.address.p0(ptr @tls_cd_ref) // LLVM: ret ptr %[[GET_GLOB]] // LLVM: } // // LLVM: define weak_odr hidden ptr @_ZTW10tls_cd_dyn() { -// LLVM-NOT: call void @_ZTH10tls_cd_dyn() +// LLVM: call void @_ZTH10tls_cd_dyn() // LLVM: %[[GET_GLOB:.*]] = call ptr @llvm.threadlocal.address.p0(ptr @tls_cd_dyn) // LLVM: ret ptr %[[GET_GLOB]] // LLVM: } // // LLVM: define weak_odr hidden ptr @_ZTW6tls_cd() { -// LLVM-NOT: call void @_ZTH6tls_cd() +// LLVM: call void @_ZTH6tls_cd() // LLVM: %[[GET_GLOB:.*]] = call ptr @llvm.threadlocal.address.p0(ptr @tls_cd) // LLVM: ret ptr %[[GET_GLOB]] // LLVM: } // +// LLVM: define internal void @__tls_init() { thread_local CtorDtor tls_cd = 5; // CIR-BEFORE-LPP: cir.global external tls_dyn dyn_tls_refs = <"_ZTW6tls_cd", "_ZTH6tls_cd"> @tls_cd = #cir.const_record<{#cir.int<5> : !s32i}> : !rec_CtorDtor dtor { @@ -62,10 +81,21 @@ thread_local CtorDtor tls_cd = 5; // CIR-BEFORE-LPP: cir.call @_ZN8CtorDtorD1Ev(%[[GET_GLOB]]) : (!cir.ptr<!rec_CtorDtor>) -> () // CIR-BEFORE-LPP: } // CIR: cir.global external tls_dyn dyn_tls_refs = <"_ZTW6tls_cd", "_ZTH6tls_cd"> @tls_cd = #cir.const_record<{#cir.int<5> : !s32i}> : !rec_CtorDtor - +// CIR: cir.func internal private @[[TLS_CD_INIT:.*]]() { +// CIR: %[[GET_GLOB:.*]] = cir.get_global thread_local @tls_cd : !cir.ptr<!rec_CtorDtor> +// CIR: %[[GET_DTOR:.*]] = cir.get_global @_ZN8CtorDtorD1Ev : !cir.ptr<!cir.func<(!cir.ptr<!rec_CtorDtor>)>> +// CIR: %[[DTOR_DECAY:.*]] = cir.cast bitcast %[[GET_DTOR]] : !cir.ptr<!cir.func<(!cir.ptr<!rec_CtorDtor>)>> -> !cir.ptr<!cir.func<(!cir.ptr<!void>)>> +// CIR: %[[GLOB_DECAY:.*]] = cir.cast bitcast %[[GET_GLOB]] : !cir.ptr<!rec_CtorDtor> -> !cir.ptr<!void> +// CIR: %[[DSOHANDLE:.*]] = cir.get_global @__dso_handle : !cir.ptr<i8> +// CIR: cir.call @__cxa_thread_atexit(%[[DTOR_DECAY]], %[[GLOB_DECAY]], %[[DSOHANDLE]]) : (!cir.ptr<!cir.func<(!cir.ptr<!void>)>>, !cir.ptr<!void>, !cir.ptr<i8>) -> () +// CIR: cir.return +// +// LLVM: define internal void @[[TLS_CD_INIT:.*]]() { // OGCG: define internal void @[[TLS_CD_INIT:.*]]() {{.*}}{ +// LLVM: %[[GET_GLOB:.*]] = call ptr @llvm.threadlocal.address.p0(ptr @tls_cd) +// LLVM: call void @__cxa_thread_atexit(ptr @_ZN8CtorDtorD1Ev, ptr %[[GET_GLOB]], ptr @__dso_handle) // OGCG: call i32 @__cxa_thread_atexit(ptr @_ZN8CtorDtorD1Ev, ptr @tls_cd, ptr @__dso_handle) -// OGCG: ret void +// LLVM-BOTH: ret void thread_local CtorDtor tls_cd_dyn = get_i(); // CIR-BEFORE-LPP: cir.global external tls_dyn dyn_tls_refs = <"_ZTW10tls_cd_dyn", "_ZTH10tls_cd_dyn"> @tls_cd_dyn = ctor : !rec_CtorDtor { @@ -77,12 +107,28 @@ thread_local CtorDtor tls_cd_dyn = get_i(); // CIR-BEFORE-LPP: cir.call @_ZN8CtorDtorD1Ev(%[[GET_GLOB]]) : (!cir.ptr<!rec_CtorDtor>) -> () // CIR-BEFORE-LPP: } // CIR: cir.global external tls_dyn dyn_tls_refs = <"_ZTW10tls_cd_dyn", "_ZTH10tls_cd_dyn"> @tls_cd_dyn = #cir.zero : !rec_CtorDtor - +// CIR: cir.func internal private @[[TLS_CD_DYN_INIT:.*]]() { +// CIR: %[[GET_GLOB:.*]] = cir.get_global thread_local @tls_cd_dyn : !cir.ptr<!rec_CtorDtor> +// CIR: %[[CALL:.*]] = cir.call @_Z5get_iv() : () -> (!s32i {llvm.noundef}) +// CIR: cir.call @_ZN8CtorDtorC1Ei(%[[GET_GLOB]], %[[CALL]]) +// CIR: %[[GET_GLOB:.*]] = cir.get_global thread_local @tls_cd_dyn : !cir.ptr<!rec_CtorDtor> +// CIR: %[[GET_DTOR:.*]] = cir.get_global @_ZN8CtorDtorD1Ev : !cir.ptr<!cir.func<(!cir.ptr<!rec_CtorDtor>)>> +// CIR: %[[DTOR_DECAY:.*]] = cir.cast bitcast %[[GET_DTOR]] : !cir.ptr<!cir.func<(!cir.ptr<!rec_CtorDtor>)>> -> !cir.ptr<!cir.func<(!cir.ptr<!void>)>> +// CIR: %[[GLOB_DECAY:.*]] = cir.cast bitcast %[[GET_GLOB]] : !cir.ptr<!rec_CtorDtor> -> !cir.ptr<!void> +// CIR: %[[DSOHANDLE:.*]] = cir.get_global @__dso_handle : !cir.ptr<i8> +// CIR: cir.call @__cxa_thread_atexit(%[[DTOR_DECAY]], %[[GLOB_DECAY]], %[[DSOHANDLE]]) : (!cir.ptr<!cir.func<(!cir.ptr<!void>)>>, !cir.ptr<!void>, !cir.ptr<i8>) -> () +// CIR: cir.return +// +// LLVM: define internal void @[[TLS_CD_DYN_INIT:.*]]() { // OGCG: define internal void @[[TLS_CD_DYN_INIT:.*]]() {{.*}} { -// OGCG: %[[CALL:.*]] = call noundef i32 @_Z5get_iv() +// LLVM: %[[GET_GLOB:.*]] = call ptr @llvm.threadlocal.address.p0(ptr @tls_cd_dyn) +// LLVM-BOTH: %[[CALL:.*]] = call noundef i32 @_Z5get_iv() +// LLVM: call void @_ZN8CtorDtorC1Ei(ptr {{.*}}%[[GET_GLOB]], i32 {{.*}}%[[CALL]]) // OGCG: call void @_ZN8CtorDtorC1Ei(ptr {{.*}}@tls_cd_dyn, i32 {{.*}}%[[CALL]]) +// LLVM: %[[GET_GLOB:.*]] = call ptr @llvm.threadlocal.address.p0(ptr @tls_cd_dyn) +// LLVM: call void @__cxa_thread_atexit(ptr @_ZN8CtorDtorD1Ev, ptr %[[GET_GLOB]], ptr @__dso_handle) // OGCG: call i32 @__cxa_thread_atexit(ptr @_ZN8CtorDtorD1Ev, ptr @tls_cd_dyn, ptr @__dso_handle) -// OGCG: ret void +// LLVM-BOTH: ret void thread_local CtorDtor &tls_cd_ref = tls_cd_dyn; // CIR-BEFORE-LPP: cir.global external tls_dyn dyn_tls_refs = <"_ZTW10tls_cd_ref", "_ZTH10tls_cd_ref"> @tls_cd_ref = ctor : !cir.ptr<!rec_CtorDtor> { @@ -91,13 +137,20 @@ thread_local CtorDtor &tls_cd_ref = tls_cd_dyn; // CIR-BEFORE-LPP: cir.store {{.*}}%[[CALL]], %[[GET_GLOB]] : !cir.ptr<!rec_CtorDtor>, !cir.ptr<!cir.ptr<!rec_CtorDtor>> // CIR-BEFORE-LPP: } // CIR: cir.global external tls_dyn dyn_tls_refs = <"_ZTW10tls_cd_ref", "_ZTH10tls_cd_ref"> @tls_cd_ref = #cir.ptr<null> : !cir.ptr<!rec_CtorDtor> - +// CIR: cir.func internal private @[[TLS_CD_REF_INIT:.*]]() { +// CIR: %[[GET_GLOB:.*]] = cir.get_global thread_local @tls_cd_ref : !cir.ptr<!cir.ptr<!rec_CtorDtor>> +// CIR: %[[GET_DYN:.*]] = cir.call @_ZTW10tls_cd_dyn() : () -> !cir.ptr<!rec_CtorDtor> +// CIR: cir.store align(8) %[[GET_DYN]], %[[GET_GLOB]] : !cir.ptr<!rec_CtorDtor>, !cir.ptr<!cir.ptr<!rec_CtorDtor>> +// CIR: cir.return +// +// LLVM: define internal void @[[TLS_CD_REF_INIT:.*]]() { // OGCG: define internal void @[[TLS_CD_REF_INIT:.*]]() {{.*}} { -// OGCG: %[[CALL:.*]] = call ptr @_ZTW10tls_cd_dyn() +// LLVM: %[[GET_GLOB:.*]] = call ptr @llvm.threadlocal.address.p0(ptr @tls_cd_ref) +// LLVM-BOTH: %[[CALL:.*]] = call ptr @_ZTW10tls_cd_dyn() // OGCG: %[[GET_GLOB:.*]] = call {{.*}}ptr @llvm.threadlocal.address.p0(ptr {{.*}}@tls_cd_ref) -// OGCG: store ptr %[[CALL]], ptr %[[GET_GLOB]], align 8 -// OGCG: ret void - +// LLVM-BOTH: store ptr %[[CALL]], ptr %[[GET_GLOB]], align 8 +// LLVM-BOTH: ret void +// // OGCG: define weak_odr hidden noundef ptr @_ZTW10tls_cd_dyn() {{.*}} comdat { // OGCG: call void @_ZTH10tls_cd_dyn() // OGCG: %[[GET_GLOB:.*]] = call {{.*}}ptr @llvm.threadlocal.address.p0(ptr {{.*}}@tls_cd_dyn) @@ -114,12 +167,28 @@ thread_local CtorDtor tls_cd_dyn_not_used = get_i(); // CIR-BEFORE-LPP: cir.call @_ZN8CtorDtorD1Ev(%[[GET_GLOB]]) : (!cir.ptr<!rec_CtorDtor>) -> () // CIR-BEFORE-LPP: } // CIR: cir.global external tls_dyn dyn_tls_refs = <"_ZTW19tls_cd_dyn_not_used", "_ZTH19tls_cd_dyn_not_used"> @tls_cd_dyn_not_used = #cir.zero : !rec_CtorDtor - +// CIR: cir.func internal private @[[TLS_CD_DYN_NOT_USED_INIT:.*]]() { +// CIR: %[[GET_GLOB:.*]] = cir.get_global thread_local @tls_cd_dyn_not_used : !cir.ptr<!rec_CtorDtor> +// CIR: %[[CALL:.*]] = cir.call @_Z5get_iv() : () -> (!s32i {llvm.noundef}) +// CIR: cir.call @_ZN8CtorDtorC1Ei(%[[GET_GLOB]], %[[CALL]]) +// CIR: %[[GET_GLOB:.*]] = cir.get_global thread_local @tls_cd_dyn_not_used : !cir.ptr<!rec_CtorDtor> +// CIR: %[[GET_DTOR:.*]] = cir.get_global @_ZN8CtorDtorD1Ev : !cir.ptr<!cir.func<(!cir.ptr<!rec_CtorDtor>)>> +// CIR: %[[DTOR_DECAY:.*]] = cir.cast bitcast %[[GET_DTOR]] : !cir.ptr<!cir.func<(!cir.ptr<!rec_CtorDtor>)>> -> !cir.ptr<!cir.func<(!cir.ptr<!void>)>> +// CIR: %[[GLOB_DECAY:.*]] = cir.cast bitcast %[[GET_GLOB]] : !cir.ptr<!rec_CtorDtor> -> !cir.ptr<!void> +// CIR: %[[DSOHANDLE:.*]] = cir.get_global @__dso_handle : !cir.ptr<i8> +// CIR: cir.call @__cxa_thread_atexit(%[[DTOR_DECAY]], %[[GLOB_DECAY]], %[[DSOHANDLE]]) : (!cir.ptr<!cir.func<(!cir.ptr<!void>)>>, !cir.ptr<!void>, !cir.ptr<i8>) -> () +// CIR: cir.return +// +// LLVM: define internal void @[[TLS_CD_DYN_NOT_USED_INIT:.*]]() { // OGCG: define internal void @[[TLS_CD_DYN_NOT_USED_INIT:.*]]() {{.*}} { -// OGCG: %[[CALL:.*]] = call noundef i32 @_Z5get_iv() +// LLVM: %[[GET_GLOB:.*]] = call ptr @llvm.threadlocal.address.p0(ptr @tls_cd_dyn_not_used) +// LLVM-BOTH: %[[CALL:.*]] = call noundef i32 @_Z5get_iv() +// LLVM: call void @_ZN8CtorDtorC1Ei(ptr {{.*}}%[[GET_GLOB]], i32 {{.*}}%[[CALL]]) // OGCG: call void @_ZN8CtorDtorC1Ei(ptr {{.*}}@tls_cd_dyn_not_used, i32 {{.*}}%[[CALL]]) +// LLVM: %[[GET_GLOB:.*]] = call ptr @llvm.threadlocal.address.p0(ptr @tls_cd_dyn_not_used) +// LLVM: call void @__cxa_thread_atexit(ptr @_ZN8CtorDtorD1Ev, ptr %[[GET_GLOB]], ptr @__dso_handle) // OGCG: call i32 @__cxa_thread_atexit(ptr @_ZN8CtorDtorD1Ev, ptr @tls_cd_dyn_not_used, ptr @__dso_handle) -// OGCG: ret void +// LLVM-BOTH: ret void void uses() { auto a = tls_cd; @@ -165,3 +234,4 @@ void uses() { // OGCG: %[[GET_GLOB:.*]] = call {{.*}} ptr @llvm.threadlocal.address.p0(ptr {{.*}} @tls_cd_dyn_not_used) // OGCG: ret ptr %[[GET_GLOB]] // OGCG: } +// diff --git a/clang/test/CIR/CodeGen/global-tls-simple-init.cpp b/clang/test/CIR/CodeGen/global-tls-simple-init.cpp index b9030d6518222..c14c21358ebff 100644 --- a/clang/test/CIR/CodeGen/global-tls-simple-init.cpp +++ b/clang/test/CIR/CodeGen/global-tls-simple-init.cpp @@ -9,58 +9,80 @@ struct CtorDtor { ~CtorDtor(){} int i; }; - -// Wrappers: +// Wrappers & Aliases: // CIR-LABEL: cir.func comdat linkonce_odr private hidden @_ZTW12maybe_inited() -> !cir.ptr<!s32i> { -// CIR-NOT: %[[GET_INIT_FUNC:.*]] = cir.get_global @_ZTH12maybe_inited : !cir.ptr<!cir.func<()>> -// Note: The following intentionally disabled since they have matchers in them. -// CIRX-NOT: %[[NULL:.*]] = cir.const #cir.ptr<null> : !cir.ptr<!cir.func<()>> -// CIRX-NOT: %[[IS_VALID:.*]] = cir.cmp ne %[[GET_INIT_FUNC]], %[[NULL]] : !cir.ptr<!cir.func<()>> -// CIRX-NOT: cir.if %[[IS_VALID]] { -// CIR-NOT: cir.call @_ZTH12maybe_inited() : () -> () -// CIRX-NOT: } +// CIR: %[[GET_INIT_FUNC:.*]] = cir.get_global @_ZTH12maybe_inited : !cir.ptr<!cir.func<()>> +// CIR: %[[NULL:.*]] = cir.const #cir.ptr<null> : !cir.ptr<!cir.func<()>> +// CIR: %[[IS_VALID:.*]] = cir.cmp ne %[[GET_INIT_FUNC]], %[[NULL]] : !cir.ptr<!cir.func<()>> +// CIR: cir.if %[[IS_VALID]] { +// CIR: cir.call @_ZTH12maybe_inited() : () -> () +// CIR: } // CIR: %[[GET_GLOB:.*]] = cir.get_global thread_local @maybe_inited : !cir.ptr<!s32i> // CIR: cir.return %[[GET_GLOB]] : !cir.ptr<!s32i> // CIR-LABEL: cir.func comdat weak_odr private hidden @_ZTW21definitely_inited_dyn() -> !cir.ptr<!s32i> { -// CIR-NOT: cir.call @_ZTH21definitely_inited_dyn() : () -> () +// CIR: cir.call @_ZTH21definitely_inited_dyn() : () -> () // CIR: %[[GET_GLOB:.*]] = cir.get_global thread_local @definitely_inited_dyn : !cir.ptr<!s32i> // CIR: cir.return %[[GET_GLOB]] : !cir.ptr<!s32i> +// CIR: cir.func @_ZTH21definitely_inited_dyn() alias(@__tls_init) + // CIR: cir.func comdat weak_odr private hidden @_ZTW17definitely_inited() -> !cir.ptr<!s32i> { -// CIR-NEXT: %[[GET_GLOB:.*]] = cir.get_global thread_local @definitely_inited : !cir.ptr<!s32i> +// CIR: %[[GET_GLOB:.*]] = cir.get_global thread_local @definitely_inited : !cir.ptr<!s32i> // CIR: cir.return %[[GET_GLOB]] : !cir.ptr<!s32i> +// CIR: cir.func @_ZTH17tls_int_self_init() alias(@__tls_init) + // CIR-LABEL: cir.func comdat weak_odr private hidden @_ZTW17tls_int_self_init() -> !cir.ptr<!s32i> { -// CIR-NOT: cir.call @_ZTH17tls_int_self_init() : () -> () +// CIR: cir.call @_ZTH17tls_int_self_init() : () -> () // CIR: %[[GET_GLOB:.*]] = cir.get_global thread_local @tls_int_self_init : !cir.ptr<!s32i> // CIR: cir.return %[[GET_GLOB]] : !cir.ptr<!s32i> // CIR-LABEL: cir.func comdat weak_odr private hidden @_ZTW11tls_int_ref() -> !cir.ptr<!cir.ptr<!s32i>> { -// CIR-NOT: cir.call @_ZTH11tls_int_ref() : () -> () +// CIR: cir.call @_ZTH11tls_int_ref() : () -> () // CIR: %[[GET_GLOB:.*]] = cir.get_global thread_local @tls_int_ref : !cir.ptr<!cir.ptr<!s32i>> // CIR: cir.return %0 : !cir.ptr<!cir.ptr<!s32i>> +// CIR: cir.func @_ZTH11tls_int_ref() alias(@__tls_init) + // CIR-LABEL: cir.func comdat weak_odr private hidden @_ZTW11tls_int_dyn() -> !cir.ptr<!s32i> { -// CIR-NOT: cir.call @_ZTH11tls_int_dyn() : () -> () +// CIR: cir.call @_ZTH11tls_int_dyn() : () -> () // CIR: %[[GET_GLOB:.*]] = cir.get_global thread_local @tls_int_dyn : !cir.ptr<!s32i> // CIR: cir.return %[[GET_GLOB]] : !cir.ptr<!s32i> +// CIR: cir.func @_ZTH11tls_int_dyn() alias(@__tls_init) + +// Full init of all variables (func names below). +// CIR-LABEL: cir.func internal private @__tls_init() { +// CIR: cir.return + // CIR-LABEL: cir.func comdat weak_odr private hidden @_ZTW7tls_int() -> !cir.ptr<!s32i> { // CIR: %[[GET_GLOB:.*]] = cir.get_global thread_local @tls_int : !cir.ptr<!s32i> // CIR: cir.return %[[GET_GLOB]] +// LLVM-BOTH-DAG: @tls_int = thread_local global i32 5, align 4 +// LLVM-BOTH-DAG: @tls_int_dyn = thread_local global i32 0, align 4 +// LLVM-BOTH-DAG: @tls_int_ref = thread_local global ptr null, align 8 +// LLVM-BOTH-DAG: @tls_int_self_init = thread_local global i32 0, align 4 +// LLVM-BOTH-DAG: @definitely_inited = thread_local global i32 5, align 4 +// LLVM-BOTH-DAG: @definitely_inited_dyn = thread_local global i32 0, align 4 +// LLVM-BOTH-DAG: @maybe_inited = external thread_local global i32, align 4 +// +// LLVM-BOTH-DAG: @_ZTH21definitely_inited_dyn = alias void (), ptr @__tls_init +// LLVM-BOTH-DAG: @_ZTH17tls_int_self_init = alias void (), ptr @__tls_init +// LLVM-BOTH-DAG: @_ZTH11tls_int_ref = alias void (), ptr @__tls_init +// LLVM-BOTH-DAG: @_ZTH11tls_int_dyn = alias void (), ptr @__tls_init + // Wrappers: // LLVM: define linkonce_odr hidden ptr @_ZTW12maybe_inited() { -// Intentionally disabled until we implement this. -// LLVMX: %[[HAS_INIT_FUNC:.*]] = icmp ne ptr @_ZTH12maybe_inited, null -// LLVMX: br i1 %[[HAS_INIT_FUNC]] -// LLVM-NOT: call void @_ZTH12maybe_inited() +// LLVM: %[[HAS_INIT_FUNC:.*]] = icmp ne ptr @_ZTH12maybe_inited, null +// LLVM: br i1 %[[HAS_INIT_FUNC]] +// LLVM: call void @_ZTH12maybe_inited() // LLVM: %[[GET_GLOB:.*]] = call ptr @llvm.threadlocal.address.p0(ptr @maybe_inited) // LLVM: ret ptr %[[GET_GLOB]] // // LLVM: define weak_odr hidden ptr @_ZTW21definitely_inited_dyn() { -// LLVM-NOT: call void @_ZTH21definitely_inited_dyn() +// LLVM: call void @_ZTH21definitely_inited_dyn() // LLVM: %[[GET_GLOB:.*]] = call ptr @llvm.threadlocal.address.p0(ptr @definitely_inited_dyn) // LLVM: ret ptr %[[GET_GLOB]] // @@ -70,25 +92,29 @@ struct CtorDtor { // LLVM: } // // LLVM: define weak_odr hidden ptr @_ZTW17tls_int_self_init() { -// LLVM-NOT: call void @_ZTH17tls_int_self_init() +// LLVM: call void @_ZTH17tls_int_self_init() // LLVM: %[[GET_GLOB:.*]] = call ptr @llvm.threadlocal.address.p0(ptr @tls_int_self_init) // LLVM: ret ptr %[[GET_GLOB]] // // LLVM: define weak_odr hidden ptr @_ZTW11tls_int_ref() { -// LLVM-NOT: call void @_ZTH11tls_int_ref() +// LLVM: call void @_ZTH11tls_int_ref() // LLVM: %[[GET_GLOB:.*]] = call ptr @llvm.threadlocal.address.p0(ptr @tls_int_ref) // LLVM: ret ptr %[[GET_GLOB]] // // LLVM: define weak_odr hidden ptr @_ZTW11tls_int_dyn() { -// LLVM-NOT: call void @_ZTH11tls_int_dyn() +// LLVM: call void @_ZTH11tls_int_dyn() // LLVM: %[[GET_GLOB:.*]] = call ptr @llvm.threadlocal.address.p0(ptr @tls_int_dyn) // LLVM: ret ptr %[[GET_GLOB]] +// LLVM: define internal void @__tls_init() { +// LLVM: ret void + // LLVM: define weak_odr hidden ptr @_ZTW7tls_int() { // LLVM: %[[GET_GLOB:.*]] = call ptr @llvm.threadlocal.address.p0(ptr @tls_int) // LLVM: ret ptr %[[GET_GLOB]] // LLVM: } + thread_local int tls_int = 5; // CIR-BEFORE-LPP: cir.global external tls_dyn dyn_tls_refs = <"_ZTW7tls_int", "_ZTH7tls_int"> @tls_int = #cir.int<5> : !s32i // CIR: cir.global external tls_dyn dyn_tls_refs = <"_ZTW7tls_int", "_ZTH7tls_int"> @tls_int = #cir.int<5> : !s32i @@ -100,12 +126,18 @@ thread_local int tls_int_dyn = get_i(); // CIR-BEFORE-LPP: cir.store {{.*}}%[[CALL]], %[[GET_GLOB]] : !s32i, !cir.ptr<!s32i> // CIR-BEFORE-LPP: } // CIR: cir.global external tls_dyn dyn_tls_refs = <"_ZTW11tls_int_dyn", "_ZTH11tls_int_dyn"> @tls_int_dyn = #cir.int<0> : !s32i - +// CIR: cir.func internal private @[[TLS_INT_DYN_INIT:.*]]() { +// CIR: %[[GET_GLOB:.*]] = cir.get_global thread_local @tls_int_dyn : !cir.ptr<!s32i> +// CIR: %[[CALL:.*]] = cir.call @_Z5get_iv() : () -> (!s32i {llvm.noundef}) +// CIR: cir.store {{.*}}%[[CALL]], %[[GET_GLOB]] : !s32i, !cir.ptr<!s32i> +// CIR: cir.return +// LLVM: define internal void @[[TLS_INT_DYN_INIT:.*]]() { // OGCG: define internal void @[[TLS_INT_DYN_INIT:.*]]() // OGCG: %[[CALL:.*]] = call noundef i32 @_Z5get_iv() -// OGCG: %[[GET_GLOB:.*]] = call {{.*}}ptr @llvm.threadlocal.address.p0(ptr {{.*}}@tls_int_dyn) -// OGCG: store i32 %[[CALL]], ptr %[[GET_GLOB]], align 4 -// OGCG: ret void +// LLVM-BOTH: %[[GET_GLOB:.*]] = call {{.*}}ptr @llvm.threadlocal.address.p0(ptr {{.*}}@tls_int_dyn) +// LLVM: %[[CALL:.*]] = call noundef i32 @_Z5get_iv() +// LLVM-BOTH: store i32 %[[CALL]], ptr %[[GET_GLOB]], align 4 +// LLVM-BOTH: ret void thread_local int &tls_int_ref = tls_int_dyn; // CIR-BEFORE-LPP: cir.global external tls_dyn dyn_tls_refs = <"_ZTW11tls_int_ref", "_ZTH11tls_int_ref"> @tls_int_ref = ctor : !cir.ptr<!s32i> { @@ -114,12 +146,18 @@ thread_local int &tls_int_ref = tls_int_dyn; // CIR-BEFORE-LPP: cir.store {{.*}}%[[GET_OTHER]], %[[GET_GLOB]] : !cir.ptr<!s32i>, !cir.ptr<!cir.ptr<!s32i>> // CIR-BEFORE-LPP: } // CIR: cir.global external tls_dyn dyn_tls_refs = <"_ZTW11tls_int_ref", "_ZTH11tls_int_ref"> @tls_int_ref = #cir.ptr<null> : !cir.ptr<!s32i> - +// CIR: cir.func internal private @[[TLS_INT_REF_INIT:.*]]() { +// CIR: %[[GET_GLOB:.*]] = cir.get_global thread_local @tls_int_ref : !cir.ptr<!cir.ptr<!s32i>> +// CIR: %[[GET_REF:.*]] = cir.call @_ZTW11tls_int_dyn() : () -> !cir.ptr<!s32i> +// CIR: cir.store {{.*}}%[[GET_REF]], %[[GET_GLOB]] : !cir.ptr<!s32i>, !cir.ptr<!cir.ptr<!s32i>> +// CIR: cir.return +// LLVM: define internal void @[[TLS_INT_REF_INIT:.*]]() { // OGCG: define internal void @[[TLS_INT_REF_INIT:.*]]() // OGCG: %[[GET_REF:.*]] = call ptr @_ZTW11tls_int_dyn() -// OGCG: %[[GET_GLOB:.*]] = call {{.*}}ptr @llvm.threadlocal.address.p0(ptr {{.*}}@tls_int_ref) -// OGCG: store ptr %[[GET_REF]], ptr %[[GET_GLOB]], align 8 -// OGCG: ret void +// LLVM-BOTH: %[[GET_GLOB:.*]] = call {{.*}}ptr @llvm.threadlocal.address.p0(ptr {{.*}}@tls_int_ref) +// LLVM: %[[GET_REF:.*]] = call ptr @_ZTW11tls_int_dyn() +// LLVM-BOTH: store ptr %[[GET_REF]], ptr %[[GET_GLOB]], align 8 +// LLVM-BOTH: ret void // OGCG: define weak_odr hidden noundef ptr @_ZTW11tls_int_dyn() {{.*}} comdat { // OGCG: call void @_ZTH11tls_int_dyn() @@ -136,16 +174,25 @@ thread_local int tls_int_self_init = tls_int_self_init + get_i(); // CIR-BEFORE-LPP: cir.store {{.*}}%[[ADD]], %[[GET_GLOB]] : !s32i, !cir.ptr<!s32i> // CIR-BEFORE-LPP: } // CIR: cir.global external tls_dyn dyn_tls_refs = <"_ZTW17tls_int_self_init", "_ZTH17tls_int_self_init"> @tls_int_self_init = #cir.int<0> : !s32i - +// CIR: cir.func internal private @[[TLS_INT_SELF_REF_INIT:.*]]() { +// CIR: %[[GET_GLOB:.*]] = cir.get_global thread_local @tls_int_self_init : !cir.ptr<!s32i> +// CIR: %[[GET_SELF_FROM_WRAPPER:.*]] = cir.call @_ZTW17tls_int_self_init() : () -> !cir.ptr<!s32i> +// CIR: %[[SELF_LOAD:.*]] = cir.load {{.*}}%[[GET_SELF_FROM_WRAPPER]] : !cir.ptr<!s32i>, !s32i +// CIR: %[[CALL:.*]] = cir.call @_Z5get_iv() : () -> (!s32i {llvm.noundef}) +// CIR: %[[ADD:.*]] = cir.add nsw %[[SELF_LOAD]], %[[CALL]] : !s32i +// CIR: cir.store{{.*}} %[[ADD]], %[[GET_GLOB]] : !s32i, !cir.ptr<!s32i> +// CIR: cir.return +// LLVM: define internal void @[[TLS_INT_SELF_REF_INIT:.*]]() { // OGCG: define internal void @[[TLS_INT_SELF_REF_INIT:.*]]() -// OGCG: %[[GET_SELF_FROM_WRAPPER:.*]] = call ptr @_ZTW17tls_int_self_init() -// OGCG: %[[SELF_LOAD:.*]] = load i32, ptr %[[GET_SELF_FROM_WRAPPER]], align 4 -// OGCG: %[[CALL:.*]] = call noundef i32 @_Z5get_iv() -// OGCG: %[[ADD:.*]] = add nsw i32 %[[SELF_LOAD]], %[[CALL]] +// LLVM: %[[GET_GLOB:.*]] = call {{.*}}ptr @llvm.threadlocal.address.p0(ptr {{.*}}@tls_int_self_init) +// LLVM-BOTH: %[[GET_SELF_FROM_WRAPPER:.*]] = call ptr @_ZTW17tls_int_self_init() +// LLVM-BOTH: %[[SELF_LOAD:.*]] = load i32, ptr %[[GET_SELF_FROM_WRAPPER]], align 4 +// LLVM-BOTH: %[[CALL:.*]] = call noundef i32 @_Z5get_iv() +// LLVM-BOTH: %[[ADD:.*]] = add nsw i32 %[[SELF_LOAD]], %[[CALL]] // OGCG: %[[GET_GLOB:.*]] = call {{.*}}ptr @llvm.threadlocal.address.p0(ptr {{.*}}@tls_int_self_init) -// OGCG: store i32 %[[ADD]], ptr %[[GET_GLOB]], align 4 -// OGCG: ret void - +// LLVM-BOTH: store i32 %[[ADD]], ptr %[[GET_GLOB]], align 4 +// LLVM-BOTH: ret void +// // OGCG: define weak_odr hidden noundef ptr @_ZTW17tls_int_self_init() {{.*}} comdat { // OGCG: call void @_ZTH17tls_int_self_init() // OGCG: %[[GET_GLOB:.*]] = call {{.*}}ptr @llvm.threadlocal.address.p0(ptr {{.*}}@tls_int_self_init) @@ -155,12 +202,6 @@ extern thread_local int definitely_inited = 5; // CIR-BEFORE-LPP: cir.global external tls_dyn dyn_tls_refs = <"_ZTW17definitely_inited", "_ZTH17definitely_inited"> @definitely_inited = #cir.int<5> : !s32i // CIR: cir.global external tls_dyn dyn_tls_refs = <"_ZTW17definitely_inited", "_ZTH17definitely_inited"> @definitely_inited = #cir.int<5> : !s32i -// OGCG: define internal void @[[DEF_INITED_DYN:.*]]() -// OGCG: %[[CALL:.*]] = call noundef i32 @_Z5get_iv() -// OGCG: %[[GET_GLOB:.*]] = call {{.*}}ptr @llvm.threadlocal.address.p0(ptr {{.*}}@definitely_inited_dyn) -// OGCG: store i32 %[[CALL]], ptr %[[GET_GLOB]], align 4 -// OGCG: ret void - extern thread_local int definitely_inited_dyn = get_i(); // CIR-BEFORE-LPP: cir.global external tls_dyn dyn_tls_refs = <"_ZTW21definitely_inited_dyn", "_ZTH21definitely_inited_dyn"> @definitely_inited_dyn = ctor : !s32i { // CIR-BEFORE-LPP: %[[GET_GLOB:.*]] = cir.get_global thread_local @definitely_inited_dyn : !cir.ptr<!s32i> @@ -168,9 +209,22 @@ extern thread_local int definitely_inited_dyn = get_i(); // CIR-BEFORE-LPP: cir.store {{.*}}%[[CALL]], %[[GET_GLOB]] : !s32i, !cir.ptr<!s32i> // CIR-BEFORE-LPP: } // CIR: cir.global external tls_dyn dyn_tls_refs = <"_ZTW21definitely_inited_dyn", "_ZTH21definitely_inited_dyn"> @definitely_inited_dyn = #cir.int<0> : !s32i +// CIR: cir.func internal private @[[DEF_INITED_DYN:.*]]() { +// CIR: %[[GET_GLOB:.*]] = cir.get_global thread_local @definitely_inited_dyn : !cir.ptr<!s32i> +// CIR: %[[CALL:.*]] = cir.call @_Z5get_iv() : () -> (!s32i {llvm.noundef}) +// CIR: cir.store align(4) %[[CALL]], %[[GET_GLOB]] : !s32i, !cir.ptr<!s32i> +// CIR: cir.return +// LLVM: define internal void @[[DEF_INITED_DYN:.*]]() { +// OGCG: define internal void @[[DEF_INITED_DYN:.*]]() +// LLVM: %[[GET_GLOB:.*]] = call ptr @llvm.threadlocal.address.p0(ptr @definitely_inited_dyn) +// LLVM-BOTH: %[[CALL:.*]] = call noundef i32 @_Z5get_iv() +// OGCG: %[[GET_GLOB:.*]] = call {{.*}}ptr @llvm.threadlocal.address.p0(ptr {{.*}}@definitely_inited_dyn) +// LLVM-BOTH: store i32 %[[CALL]], ptr %[[GET_GLOB]], align 4 +// LLVM-BOTH: ret void extern thread_local int maybe_inited; // CIR-BEFORE-LPP: cir.global "private" external tls_dyn dyn_tls_refs = <"_ZTW12maybe_inited", "_ZTH12maybe_inited"> @maybe_inited : !s32i +// CIR: cir.global "private" external tls_dyn dyn_tls_refs = <"_ZTW12maybe_inited", "_ZTH12maybe_inited"> @maybe_inited : !s32i void uses() { auto a = tls_int; @@ -189,7 +243,6 @@ void uses() { // CIR-BEFORE-LPP: cir.get_global thread_local @tls_int_ref : !cir.ptr<!cir.ptr<!s32i>> // CIR: cir.call @_ZTW11tls_int_ref() : () -> !cir.ptr<!cir.ptr<!s32i>> // LLVM-BOTH: call ptr @_ZTW11tls_int_ref() - auto d = tls_int_self_init; // CIR-BEFORE-LPP: cir.get_global thread_local @tls_int_self_init : !cir.ptr<!s32i> // CIR: cir.call @_ZTW17tls_int_self_init() : () -> !cir.ptr<!s32i> @@ -198,7 +251,6 @@ void uses() { // CIR-BEFORE-LPP: cir.get_global thread_local @maybe_inited : !cir.ptr<!s32i> // CIR: cir.call @_ZTW12maybe_inited() : () -> !cir.ptr<!s32i> // LLVM-BOTH: call ptr @_ZTW12maybe_inited() - auto f = definitely_inited; // CIR-BEFORE-LPP: cir.get_global thread_local @definitely_inited : !cir.ptr<!s32i> // CIR: cir.call @_ZTW17definitely_inited() : () -> !cir.ptr<!s32i> @@ -212,6 +264,7 @@ void uses() { // CIR: cir.call @_ZTW21definitely_inited_dyn() : () -> !cir.ptr<!s32i> // LLVM-BOTH: call ptr @_ZTW21definitely_inited_dyn() } + // OGCG Wrappers: For some reason this puts them at the end, otherwise they are // basically identical (return val has a noundef?). Note some are above because // they are referenced up there. diff --git a/clang/test/CIR/CodeGen/global-tls-templates.cpp b/clang/test/CIR/CodeGen/global-tls-templates.cpp index 36086f45e6543..95cf1a26069e9 100644 --- a/clang/test/CIR/CodeGen/global-tls-templates.cpp +++ b/clang/test/CIR/CodeGen/global-tls-templates.cpp @@ -30,60 +30,134 @@ thread_local T tls_templ = {get_i()}; // Wrapper: Ctor/Dtor // CIR-LABEL: cir.func comdat weak_odr private hidden @_ZTW9tls_templI8CtorDtorE() -> !cir.ptr<!rec_CtorDtor> { -// CIR-NOT: cir.call @_ZTH9tls_templI8CtorDtorE() : () -> () +// CIR: cir.call @_ZTH9tls_templI8CtorDtorE() : () -> () // CIR: %[[GET_GLOB:.*]] = cir.get_global thread_local @_Z9tls_templI8CtorDtorE : !cir.ptr<!rec_CtorDtor> // CIR: cir.return %[[GET_GLOB]] : !cir.ptr<!rec_CtorDtor> // CIR:} +// Alias: Ctor/Dtor: +// CIR: cir.func linkonce_odr @_ZTH9tls_templI8CtorDtorE() alias(@[[CTOR_DTOR_INIT:[^)]*]]) +// TLS Guard: Ctor/Dtor: + // Wrapper: int // CIR-LABEL: cir.func comdat weak_odr private hidden @_ZTW9tls_templIiE() -> !cir.ptr<!s32i> -// CIR-NOT: cir.call @_ZTH9tls_templIiE() : () -> () +// CIR: cir.call @_ZTH9tls_templIiE() : () -> () // CIR: %[[GET_GLOB:.*]] = cir.get_global thread_local @_Z9tls_templIiE : !cir.ptr<!s32i> // CIR: cir.return %[[GET_GLOB]] : !cir.ptr<!s32i> // CIR: } +// Alias: int +// CIR: cir.func linkonce_odr @_ZTH9tls_templIiE() alias(@[[INT_INIT:[^)]*]]) + // Global: int // CIR: cir.global linkonce_odr comdat tls_dyn dyn_tls_refs = <"_ZTW9tls_templIiE", "_ZTH9tls_templIiE", "_ZGV9tls_templIiE"> @_Z9tls_templIiE = #cir.int<0> : !s32i + +// Init Func: int +// CIR: cir.func internal private @[[INT_INIT]]() { +// CIR: %[[GET_GLOB:.*]] = cir.get_global thread_local @_Z9tls_templIiE : !cir.ptr<!s32i> +// CIR: %[[CALL:.*]] = cir.call @_Z5get_iv() : () -> (!s32i {llvm.noundef}) +// CIR: cir.store {{.*}}%[[CALL]], %[[GET_GLOB]] : !s32i, !cir.ptr<!s32i> + + // Global: Ctor/Dotr: // CIR: cir.global linkonce_odr comdat tls_dyn dyn_tls_refs = <"_ZTW9tls_templI8CtorDtorE", "_ZTH9tls_templI8CtorDtorE", "_ZGV9tls_templI8CtorDtorE"> @_Z9tls_templI8CtorDtorE = #cir.zero : !rec_CtorDtor +// Init Func: Ctor/Dtor: +// CIR: cir.func internal private @[[CTOR_DTOR_INIT]]() { +// CIR: %[[GET_GLOB:.*]] = cir.get_global thread_local @_Z9tls_templI8CtorDtorE : !cir.ptr<!rec_CtorDtor> +// CIR: %[[CALL:.*]] = cir.call @_Z5get_iv() : () -> (!s32i {llvm.noundef}) +// CIR: cir.call @_ZN8CtorDtorC1Ei(%[[GET_GLOB]], %[[CALL]]) : (!cir.ptr<!rec_CtorDtor> {{.*}}, !s32i {llvm.noundef}) -> () +// CIR: %[[GET_GLOB:.*]] = cir.get_global thread_local @_Z9tls_templI8CtorDtorE : !cir.ptr<!rec_CtorDtor> +// CIR: %[[GET_DTOR:.*]] = cir.get_global @_ZN8CtorDtorD1Ev : !cir.ptr<!cir.func<(!cir.ptr<!rec_CtorDtor>)>> +// CIR: %[[DTOR_FPTR:.*]] = cir.cast bitcast %[[GET_DTOR]] : !cir.ptr<!cir.func<(!cir.ptr<!rec_CtorDtor>)>> -> !cir.ptr<!cir.func<(!cir.ptr<!void>)>> +// CIR: %[[GLOB_DECAY:.*]] = cir.cast bitcast %[[GET_GLOB:.*]] : !cir.ptr<!rec_CtorDtor> -> !cir.ptr<!void> +// CIR: %[[DSO_HANDLE:.*]] = cir.get_global @__dso_handle : !cir.ptr<i8> +// CIR: cir.call @__cxa_thread_atexit(%[[DTOR_FPTR]], %[[GLOB_DECAY]], %[[DSO_HANDLE]]) : (!cir.ptr<!cir.func<(!cir.ptr<!void>)>>, !cir.ptr<!void>, !cir.ptr<i8>) -> () + +// FIXME: These have inconsistent COMDAT with classic codegen, but we don't +// currently specify 'comdat' with a name. // Globals: // LLVM-BOTH-DAG: @_Z9tls_templIiE = linkonce_odr thread_local global i32 0, comdat, align 4 // LLVM-BOTH-DAG: @_Z9tls_templI8CtorDtorE = linkonce_odr thread_local global %struct.CtorDtor zeroinitializer, comdat, align 4 +// Aliases: +// LLVM-BOTH-DAG: @_ZTH9tls_templI8CtorDtorE = linkonce_odr alias void (), ptr @[[CTOR_DTOR_INIT:.*]] +// LLVM-BOTH-DAG: @_ZTH9tls_templIiE = linkonce_odr alias void (), ptr @[[INT_INIT:.*]] + +// OGCG Has this first, same check lines as LLVM. +// OGCG-LABEL: define dso_local void @_Z4usesv() +// OGCG: call ptr @_ZTW9tls_templIiE() +// OGCG: call ptr @_ZTW9tls_templI8CtorDtorE() + // Wrappers: Just opposite ordering, same check lines as LLVM. // FIXME: OGCG has these set as 'comdat'. However, CIR doesn't lower comdat to // LLVM, so it doesn't show up in the IR here. // LLVM-LABEL: define weak_odr hidden {{.*}}ptr @_ZTW9tls_templI8CtorDtorE() { -// LLVM-NOT: call void @_ZTH9tls_templI8CtorDtorE() +// LLVM: call void @_ZTH9tls_templI8CtorDtorE() // LLVM: call {{.*}}ptr @llvm.threadlocal.address.p0(ptr {{.*}}@_Z9tls_templI8CtorDtorE) // LLVM: } // LLVM-LABEL: define weak_odr hidden {{.*}}ptr @_ZTW9tls_templIiE() { -// LLVM-NOT: call void @_ZTH9tls_templIiE() +// LLVM: call void @_ZTH9tls_templIiE() // LLVM: call {{.*}}ptr @llvm.threadlocal.address.p0(ptr {{.*}}@_Z9tls_templIiE) // LLVM: } +// OGCG-LABEL: define weak_odr hidden {{.*}}ptr @_ZTW9tls_templIiE() {{.*}} comdat { +// OGCG: call void @_ZTH9tls_templIiE() +// OGCG: call {{.*}}ptr @llvm.threadlocal.address.p0(ptr {{.*}}@_Z9tls_templIiE) +// OGCG: } + +// OGCG-LABEL: define weak_odr hidden {{.*}}ptr @_ZTW9tls_templI8CtorDtorE(){{.*}} comdat { +// OGCG: call void @_ZTH9tls_templI8CtorDtorE() +// OGCG: call {{.*}}ptr @llvm.threadlocal.address.p0(ptr {{.*}}@_Z9tls_templI8CtorDtorE) +// OGCG: } + + +// Inits: +// Note: the differences here are mostly ordering, however there are a few diferences: +// 1- OGCG skipps the llvm.threadlocal call. We've seen this elsewhere with thread local, +// so I don't think it is problematic, as it just seems like an early opt. +// 2- For some reason OGCG generates guards as i64/i8 depending on platform (like with static-local), +// but ALWAYS treats the load/stores as i8. This is likely a 'bug' in OGCG, but one that +// doesn't really matter at all. +// LLVM-BOTH: define internal void @[[INT_INIT]]() +// OGCG: %[[LOAD_GUARD:.*]] = load i8, ptr @_ZGV9tls_templIiE, align 8 +// OGCG: %[[ISUNINIT:.*]] = icmp eq i{{.*}} %[[LOAD_GUARD]], 0 +// OGCG: br i1 %[[ISUNINIT]] +// +// OGCG: store i8 1, ptr @_ZGV9tls_templIiE, align 8 +// LLVM: %[[GET_GLOB:.*]] = call {{.*}}ptr @llvm.threadlocal.address.p0(ptr {{.*}}@_Z9tls_templIiE) +// LLVM: %[[CALL:.*]] = call noundef i32 @_Z5get_iv() +// OGCG: %[[CALL:.*]] = call noundef i32 @_Z5get_iv() +// OGCG: %[[GET_GLOB:.*]] = call {{.*}}ptr @llvm.threadlocal.address.p0(ptr {{.*}}@_Z9tls_templIiE) +// LLVM-BOTH: store i32 %[[CALL]], ptr %[[GET_GLOB]] + +// LLVM-BOTH: define internal void @[[CTOR_DTOR_INIT]]() +// OGCG: %[[LOAD_GUARD:.*]] = load i8, ptr @_ZGV9tls_templI8CtorDtorE, align 8 +// OGCG: %[[ISUNINIT:.*]] = icmp eq i{{.*}} %[[LOAD_GUARD]], 0 +// OGCG: br i1 %[[ISUNINIT]] +// +// OGCG: store i8 1, ptr @_ZGV9tls_templI8CtorDtorE, align 8 +// +// LLVM: %[[GET_GLOB:.*]] = call {{.*}}ptr @llvm.threadlocal.address.p0(ptr {{.*}}@_Z9tls_templI8CtorDtorE) +// LLVM-BOTH: %[[CALL:.*]] = call noundef i32 @_Z5get_iv() +// LLVM: call void @_ZN8CtorDtorC1Ei(ptr {{.*}}%[[GET_GLOB]], i32 {{.*}}%[[CALL]]) +// OGCG: call void @_ZN8CtorDtorC1Ei(ptr {{.*}}@_Z9tls_templI8CtorDtorE, i32 {{.*}}%[[CALL]]) +// +// LLVM: %[[GET_GLOB:.*]] = call ptr @llvm.threadlocal.address.p0(ptr @_Z9tls_templI8CtorDtorE) +// LLVM: call void @__cxa_thread_atexit(ptr @_ZN8CtorDtorD1Ev, ptr %[[GET_GLOB]], ptr @__dso_handle) +// OGCG: call i32 @__cxa_thread_atexit(ptr @_ZN8CtorDtorD1Ev, ptr @_Z9tls_templI8CtorDtorE, ptr @__dso_handle) + // CIR-BEFORE-LPP-LABEL: cir.func{{.*}}@_Z4usesv // CIR-LABEL: cir.func{{.*}}@_Z4usesv -// LLVM-BOTH-LABEL: define dso_local void @_Z4usesv() +// LLVM-LABEL: define dso_local void @_Z4usesv() void uses() { auto x = tls_templ<int>; // CIR-BEFORE-LPP: cir.get_global thread_local @_Z9tls_templIiE : !cir.ptr<!s32i> // CIR: cir.call @_ZTW9tls_templIiE() : () -> !cir.ptr<!s32i> -// LLVM-BOTH: call ptr @_ZTW9tls_templIiE() +// LLVM: call ptr @_ZTW9tls_templIiE() auto y = tls_templ<CtorDtor>; // CIR-BEFORE-LPP: cir.get_global thread_local @_Z9tls_templI8CtorDtorE : !cir.ptr<!rec_CtorDtor> // CIR: cir.call @_ZTW9tls_templI8CtorDtorE() : () -> !cir.ptr<!rec_CtorDtor> -// LLVM-BOTH: call ptr @_ZTW9tls_templI8CtorDtorE() +// LLVM: call ptr @_ZTW9tls_templI8CtorDtorE() } - -// OGCG-LABEL: define weak_odr hidden {{.*}}ptr @_ZTW9tls_templIiE() {{.*}} comdat { -// OGCG: call void @_ZTH9tls_templIiE() -// OGCG: call {{.*}}ptr @llvm.threadlocal.address.p0(ptr {{.*}}@_Z9tls_templIiE) -// OGCG: } - -// OGCG-LABEL: define weak_odr hidden {{.*}}ptr @_ZTW9tls_templI8CtorDtorE(){{.*}} comdat { -// OGCG: call void @_ZTH9tls_templI8CtorDtorE() -// OGCG: call {{.*}}ptr @llvm.threadlocal.address.p0(ptr {{.*}}@_Z9tls_templI8CtorDtorE) -// OGCG: } >From e4dbb6902dda076d6b1833c235b035b5e90f4e9f Mon Sep 17 00:00:00 2001 From: erichkeane <[email protected]> Date: Tue, 12 May 2026 11:14:01 -0700 Subject: [PATCH 2/3] clang-format --- clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp b/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp index e041ad984c995..18eaa920682e8 100644 --- a/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp +++ b/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp @@ -138,7 +138,7 @@ struct LoweringPreparePass void buildCXXGlobalInitFunc(); // Build a init function for all of the ordered global thread local storage // variables. - void buildCXXGlobalTlsFunc(); + void buildCXXGlobalTlsFunc(); /// Materialize global ctor/dtor list void buildGlobalCtorDtorList(); @@ -1577,7 +1577,7 @@ void LoweringPreparePass::lowerGlobalOp(GlobalOp op) { // If this is a declaration and has no init function, we probably DO have to // create an alias that needs checking, so create it as extern-weak. initAlias = defineGlobalThreadLocalInitAlias(op, {}); - } + } // We need a wrapper for TLS globals that MIGHT have a non-constant // initialization. The FE will have generated the DynTlsRefs for any with >From 7e8c89c8aa4b5310e49c46c2e14fded6df4138bb Mon Sep 17 00:00:00 2001 From: erichkeane <[email protected]> Date: Tue, 12 May 2026 11:39:49 -0700 Subject: [PATCH 3/3] Do the suggestions from xluanko --- .../Dialect/Transforms/LoweringPrepare.cpp | 31 +++++++++---------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp b/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp index 18eaa920682e8..bd710b52c643a 100644 --- a/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp +++ b/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp @@ -1203,11 +1203,11 @@ LoweringPreparePass::getGuardReleaseFn(cir::PointerType guardPtrTy) { cir::FuncOp LoweringPreparePass::getTlsInitFn() { // void __tls_init(void); CIRBaseBuilderTy builder(getContext()); - mlir::OpBuilder::InsertionGuard ipGuard{builder}; + mlir::OpBuilder::InsertionGuard _{builder}; builder.setInsertionPointToStart(mlirModule.getBody()); mlir::Location loc = mlirModule.getLoc(); cir::VoidType voidTy = cir::VoidType::get(&getContext()); - auto fnType = cir::FuncType::get({}, voidTy); + auto fnType = builder.getVoidFnTy(); return buildRuntimeFunction(builder, "__tls_init", loc, fnType, cir::GlobalLinkageKind::InternalLinkage); } @@ -1468,29 +1468,28 @@ void LoweringPreparePass::defineGlobalThreadLocalWrapper(cir::GlobalOp op, // If we are a situation where we have/need one, emit a call to the init // function. if (initAlias) { + mlir::Location aliasLoc = initAlias.getLoc(); if (!isVarDefinition) { // If this isn't a definition, we have to check that the alias exists. mlir::Value funcLoad = cir::GetGlobalOp::create( - builder, initAlias.getLoc(), - cir::PointerType::get(initAlias.getFunctionType()), + builder, aliasLoc, cir::PointerType::get(initAlias.getFunctionType()), initAlias.getSymName()); mlir::Value nullCheck = - builder.getNullValue(funcLoad.getType(), initAlias.getLoc()); + builder.getNullValue(funcLoad.getType(), aliasLoc); mlir::Value cmp = cir::CmpOp::create( - builder, initAlias.getLoc(), cir::CmpOpKind::ne, funcLoad, nullCheck); - cir::IfOp::create( - builder, initAlias.getLoc(), cmp, /*withElseRegion=*/false, - [&](mlir::OpBuilder &, mlir::Location loc) { - builder.createCallOp(initAlias.getLoc(), initAlias, {}); - cir::YieldOp::create(builder, initAlias.getLoc()); - }); + builder, aliasLoc, cir::CmpOpKind::ne, funcLoad, nullCheck); + cir::IfOp::create(builder, aliasLoc, cmp, /*withElseRegion=*/false, + [&](mlir::OpBuilder &, mlir::Location loc) { + builder.createCallOp(aliasLoc, initAlias, {}); + cir::YieldOp::create(builder, aliasLoc); + }); } else { // If this IS a definition, we know the alias exists, so we can just emit // a call to it. - builder.createCallOp(initAlias.getLoc(), initAlias, {}); + builder.createCallOp(aliasLoc, initAlias, {}); } } - auto get = builder.createGetGlobal(op, /*tls=*/true); + cir::GetGlobalOp get = builder.createGetGlobal(op, /*tls=*/true); cir::ReturnOp::create(builder, op.getLoc(), {get}); } @@ -1506,10 +1505,10 @@ LoweringPreparePass::defineGlobalThreadLocalInitAlias(cir::GlobalOp op, if (existingAliasIter != threadLocalInitAliases.end()) return existingAliasIter->second; - auto funcType = cir::FuncType::get({}, builder.getVoidTy()); + auto funcType = builder.getVoidFnTy(); cir::FuncOp alias = cir::FuncOp::create(builder, op.getLoc(), aliasName, funcType); - alias.setLinkageAttr(op.getLinkageAttr()); + alias.setLinkage(op.getLinkage()); if (aliasee) { alias.setAliasee(aliasee.getSymName()); _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
