From bffed79107efcde84a3f15305a84f2f7d27a0971 Mon Sep 17 00:00:00 2001 From: Nathan Lanza Date: Tue, 10 Dec 2024 23:43:29 -0500 Subject: [PATCH 01/10] cleanup Created using spr 1.3.5 --- clang/lib/CIR/CodeGen/CIRGenCXXABI.h | 2 +- clang/lib/CIR/CodeGen/CIRGenDecl.cpp | 2 +- clang/lib/CIR/CodeGen/CIRGenDeclCXX.cpp | 4 +- clang/lib/CIR/CodeGen/CIRGenFunction.cpp | 2 +- clang/lib/CIR/CodeGen/CIRGenFunction.h | 4 +- clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp | 234 +----------------- clang/lib/CIR/CodeGen/CIRGenModule.h | 2 +- clang/lib/CIR/CodeGen/LoweringPrepare.cpp | 2 +- clang/test/CIR/CodeGen/static-local.cpp | 25 ++ 9 files changed, 36 insertions(+), 241 deletions(-) diff --git a/clang/lib/CIR/CodeGen/CIRGenCXXABI.h b/clang/lib/CIR/CodeGen/CIRGenCXXABI.h index b6f0fc398a2c..839447cb7232 100644 --- a/clang/lib/CIR/CodeGen/CIRGenCXXABI.h +++ b/clang/lib/CIR/CodeGen/CIRGenCXXABI.h @@ -182,7 +182,7 @@ class CIRGenCXXABI { /// The variable may be: /// - a static local variable /// - a static data member of a class template instantiation - virtual void buildGuardedInit(CIRGenFunction &cgf, const VarDecl &varDecl, + virtual void emitGuardedInit(CIRGenFunction &cgf, const VarDecl &varDecl, cir::GlobalOp globalOp, bool performInit) = 0; /// Emit code to force the execution of a destructor during global diff --git a/clang/lib/CIR/CodeGen/CIRGenDecl.cpp b/clang/lib/CIR/CodeGen/CIRGenDecl.cpp index 6dd3eefbf2e3..cdc1fde1348f 100644 --- a/clang/lib/CIR/CodeGen/CIRGenDecl.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenDecl.cpp @@ -558,7 +558,7 @@ CIRGenFunction::addInitializerToStaticVarDecl(const VarDecl &varDecl, // be constant. globalOp.setConstant(false); - buildCXXGuardedInit(varDecl, globalOp, /*performInit*/ true); + emitCXXGuardedInit(varDecl, globalOp, /*performInit*/ true); getGlobalOp.setStaticLocal(true); } return globalOp; diff --git a/clang/lib/CIR/CodeGen/CIRGenDeclCXX.cpp b/clang/lib/CIR/CodeGen/CIRGenDeclCXX.cpp index 12b412ff2c8f..da200ca40b69 100644 --- a/clang/lib/CIR/CodeGen/CIRGenDeclCXX.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenDeclCXX.cpp @@ -53,7 +53,7 @@ void CIRGenModule::emitCXXGlobalVarDeclInitFunc(const VarDecl *D, emitCXXGlobalVarDeclInit(D, Addr, PerformInit); } -void CIRGenFunction::buildCXXGuardedInit(const VarDecl &varDecl, +void CIRGenFunction::emitCXXGuardedInit(const VarDecl &varDecl, cir::GlobalOp globalOp, bool performInit) { // If we've been asked to forbid guard variables, emit an error now. This @@ -62,7 +62,7 @@ void CIRGenFunction::buildCXXGuardedInit(const VarDecl &varDecl, if (CGM.getCodeGenOpts().ForbidGuardVariables) llvm_unreachable("NYI"); - CGM.getCXXABI().buildGuardedInit(*this, varDecl, globalOp, performInit); + CGM.getCXXABI().emitGuardedInit(*this, varDecl, globalOp, performInit); } void CIRGenFunction::buildCXXGlobalVarDeclInit(const VarDecl &varDecl, diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp index adc4893c8771..47b07c3749c8 100644 --- a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp @@ -757,7 +757,7 @@ mlir::Value CIRGenFunction::createLoad(const VarDecl *VD, const char *Name) { addr.getElementType(), addr.getPointer()); } -void CIRGenFunction::buildCXXGuardedInitBranch(mlir::Value needsInit, +void CIRGenFunction::emitCXXGuardedInitBranch(mlir::Value needsInit, mlir::Block *initBlock, mlir::Block *noInitBlock, GuardKind kind, diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.h b/clang/lib/CIR/CodeGen/CIRGenFunction.h index dba5fd602a3b..e43e984c28ee 100644 --- a/clang/lib/CIR/CodeGen/CIRGenFunction.h +++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h @@ -944,13 +944,13 @@ class CIRGenFunction : public CIRGenTypeCache { /// Guarded initializations are used when it's not possible to prove that /// initialization will be done exactly once, e.g. with a static local /// variable or a static data member of a class template. - void buildCXXGuardedInit(const VarDecl &varDecl, cir::GlobalOp globalOp, + void emitCXXGuardedInit(const VarDecl &varDecl, cir::GlobalOp globalOp, bool performInit); enum class GuardKind { variableGuard, tlsGuard }; /// Emit a branch to select whether or not to perform guarded initialization. - void buildCXXGuardedInitBranch(mlir::Value needsInit, mlir::Block *initBlock, + void emitCXXGuardedInitBranch(mlir::Value needsInit, mlir::Block *initBlock, mlir::Block *noInitBlock, GuardKind kind, const VarDecl *varDecl); diff --git a/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp b/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp index f166dff593f8..b3db4573542a 100644 --- a/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp @@ -195,7 +195,7 @@ class CIRGenItaniumCXXABI : public CIRGenCXXABI { CXXDtorType Type, bool ForVirtualBase, bool Delegating, Address This, QualType ThisTy) override; - void buildGuardedInit(CIRGenFunction &cgf, const VarDecl &varDecl, + void emitGuardedInit(CIRGenFunction &cgf, const VarDecl &varDecl, cir::GlobalOp globalOp, bool performInit) override; void registerGlobalDtor(CIRGenFunction &CGF, const VarDecl *D, cir::FuncOp dtor, mlir::Value Addr) override; @@ -2696,17 +2696,11 @@ bool CIRGenItaniumCXXABI::isZeroInitializable(const MemberPointerType *MPT) { /// The ARM code here follows the Itanium code closely enough that we just /// special-case it at particular places. -void CIRGenItaniumCXXABI::buildGuardedInit(CIRGenFunction &cgf, +void CIRGenItaniumCXXABI::emitGuardedInit(CIRGenFunction &cgf, const VarDecl &varDecl, cir::GlobalOp globalOp, bool performInit) { - // TODO(CIR): Map this to the globalOp as it needs to be consumed during - // lowering prepare. This is hardcoded to `true` in - // `LoweringPreparePass::handleStaticLocal` and must be fixed there at the - // same time. - assert(performInit && "NYI"); - // Emit the initializer and add a global destructor if appropriate. cgf.CGM.emitCXXGlobalVarDeclInit(&varDecl, globalOp, performInit); @@ -2714,228 +2708,4 @@ void CIRGenItaniumCXXABI::buildGuardedInit(CIRGenFunction &cgf, // marking the global as static local. The emission of the guard/acquire walk // is done during LoweringPrepare. globalOp.setStaticLocal(true); - - return; - - CIRGenBuilderTy &builder = cgf.getBuilder(); - - // Inline variables that weren't instantiated from variable templates have - // partially-ordered initialization within their translation unit. - bool nonTemplateInline = - varDecl.isInline() && - !isTemplateInstantiation(varDecl.getTemplateSpecializationKind()); - - // We only need to use thread-safe statics for local non-TLS variables and - // inline variables; other global initialization is always single-threaded - // or (through lazy dynamic loading in multiple threads) unsequenced. - bool threadsafe = getContext().getLangOpts().ThreadsafeStatics && - (varDecl.isLocalVarDecl() || nonTemplateInline) && - !varDecl.getTLSKind(); - - // 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::IntType guardTy; - CharUnits guardAlignment; - if (useInt8GuardVariable) { - guardTy = cgf.SInt8Ty; - guardAlignment = CharUnits::One(); - } else { - // 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 (UseARMGuardVarABI) { - llvm_unreachable("NYI"); - } else { - guardTy = cgf.SInt64Ty; - guardAlignment = - CharUnits::fromQuantity(CGM.getDataLayout().getABITypeAlign(guardTy)); - } - } - auto guardPtrTy = cir::PointerType::get(guardTy); - - // Create the guard variable if we don't already have it (as we might if - // we're double-emitting this function body). - cir::GlobalOp guard = CGM.getStaticLocalDeclGuardAddress(&varDecl); - if (!guard) { - // Mangle the name for the guard. - SmallString<256> guardName; - { - llvm::raw_svector_ostream out(guardName); - getMangleContext().mangleStaticGuardVariable(&varDecl, out); - } - - // Create the guard variable with a zero-initializer. - // Just absorb linkage, visibility and dll storage class from the guarded - // variable. - mlir::Location loc = CGM.getLoc(varDecl.getLocation()); - guard = CGM.createGlobalOp(CGM, loc, guardName, guardTy, - /*isConstant=*/false, /*addrSpace=*/{}, - /*insertPoint=*/nullptr, globalOp.getLinkage()); - guard.setInitialValueAttr(cir::IntAttr::get(guardTy, 0)); - guard.setDSOLocal(globalOp.isDSOLocal()); - guard.setVisibility(globalOp.getVisibility()); - assert(!cir::MissingFeatures::setDLLStorageClass()); - // guard.setDLLStorageClass(globalOp.getDLLStorageClass()); - // If the variable is thread-local, so is its guard variable. - assert(!cir::MissingFeatures::threadLocal()); - // guard.setThreadLocalMode(globalOp.getThreadLocalMode()); - guard.setAlignment(guardAlignment.getAsAlign().value()); - - // The ABI says: "It is suggested that it be emitted in the same COMDAT - // group as the associated data object." In practice, this doesn't work - // for non-ELF and non-Wasm object formats, so only do it for ELF and - // Wasm. - assert(!cir::MissingFeatures::setComdat()); - - CGM.setStaticLocalDeclGuardAddress(&varDecl, guard); - } - - mlir::Value getGuard = - CGM.getBuilder().createGetGlobal(guard, /*threadLocal*/ false); - Address guardAddr = Address(getGuard, guard.getSymType(), guardAlignment); - - // Test whether the variable has completed initialization. - // - // Itanium C++ ABI 3.3.2: - // The following is pseudo-code showing how these functions can be used: - // if (obj_guard.first_byte == 0) { - // if ( __cxa_guard_acquire (&obj_guard) ) { - // try { - // ... initialize the object ...; - // } catch (...) { - // __cxa_guard_abort (&obj_guard); - // throw; - // } - // ... queue object destructor with __cxa_atexit() ...; - // __cxa_guard_release (&obj_guard); - // } - // } - // - // If threadsafe statics are enabled, but we don't have inline atomics, just - // call __cxa_guard_acquire unconditionally. The "inline" check isn't - // actually inline, and the user might not expect calls to __atomic - // libcalls. - - unsigned maxInlineWidthInbits = cgf.getTarget().getMaxAtomicInlineWidth(); - // "init.end" block - mlir::Block *endBlock = nullptr; - { - mlir::OpBuilder::InsertionGuard guard(builder); - endBlock = builder.createBlock(builder.getBlock()->getParent()); - } - if (!threadsafe || maxInlineWidthInbits) { - // Load the first byte of the guard variable. - mlir::Value load = - cgf.getBuilder().createLoad(cgf.getLoc(varDecl.getLocation()), - guardAddr.withElementType(CGM.SInt8Ty)); - - // Itanium ABI: - // An implementation supporting thread-safety on multiprocesor systems - // must also guarantee that references to the initialized object do not - // occur before the load of the initialization flag. - // - // In LLVM, we do this by marking the load Acquire. - if (threadsafe) - cast(load.getDefiningOp()).setAtomic(cir::MemOrder::Acquire); - - // For ARM, we should only check the first bit, rather than the entire - // byte: - // - // ARM C++ ABI 3.2.3.1: - // To support the potential use of initialization guard variables - // as semaphores that are the target of ARM SWP and LDREX/STREX - // synchronizing instructions we define a static initialization - // guard variable to be a 4-byte aligned, 4-byte word with the - // following inline access protocol. - // #define INITIALIZED 1 - // if ((obj_guard & INITIALIZED) != INITIALIZED) { - // if (__cxa_guard_acquire(&obj_guard)) - // ... - // } - // - // and similarly for ARM64: - // - // ARM64 C++ ABI 3.2.2: - // This ABI instead only specifies the value bit 0 of the static guard - // variable; all other bits are platform defined. Bit 0 shall be 0 when - // the variable is not initialized and 1 when it is. - if (UseARMGuardVarABI && !useInt8GuardVariable) - llvm_unreachable("NYI"); - mlir::Value value = load; - mlir::Value needsInit = builder.createIsNull( - cgf.getLoc(varDecl.getLocation()), value, "guard.uninitialized"); - - mlir::Block *initCheckBlock = nullptr; - { - mlir::OpBuilder::InsertionGuard guard(builder); - // "init.check" block - initCheckBlock = builder.createBlock(builder.getBlock()->getParent()); - } - - // Check if the first byte of the guard variable is zero. - cgf.buildCXXGuardedInitBranch(needsInit, initCheckBlock, endBlock, - CIRGenFunction::GuardKind::variableGuard, - &varDecl); - - cgf.buildBlock(initCheckBlock); - } - - // The semantics of dynamic initialization of variables with static or - // thread storage duration depends on whether they are declared at - // block-scope. The initialization of such variables at block-scope can be - // aborted with an exception and later retried (per C++20 [stmt.dcl]p4), and - // recursive entry to their initialization has undefined behavior (also per - // C++20 [stmt.dcl]p4). For such variables declared at non-block scope, - // exceptions lead to termination (per C++20 [except.terminate]p1), and - // recursive references to the variables are governed only by the lifetime - // rules (per C++20 [class.cdtor]p2), which means such references are - // perfectly fine as long as they avoid touching memory. As a result, - // block-scope variables must not be marked as initialized until after - // initialization completes (unless the mark is reverted following an - // exception), but non-block-scope variables must be marked prior to - // initialization so that recursive accesses during initialization do not - // restart initialization. - - // Variables used when coping with thread-safe statics and exceptions. - if (threadsafe) { - auto loc = cgf.getLoc(varDecl.getLocation()); - // Call __cxa_guard_acquire. - mlir::Value value = cgf.emitNounwindRuntimeCall( - cgf.getLoc(varDecl.getLocation()), getGuardAcquireFn(CGM, guardPtrTy), - guardAddr.emitRawPointer()); - - mlir::Block *initBlock = nullptr; - { - mlir::OpBuilder::InsertionGuard guard(builder); - initBlock = builder.createBlock(builder.getBlock()->getParent()); - } - - builder.createCondBr(loc, builder.createIsNotNull(loc, value), initBlock, - endBlock); - - // Call __cxa_guard_abort along the exceptional edge. - cgf.EHStack.pushCleanup(EHCleanup, guard); - - cgf.buildBlock(initBlock); - } else if (!varDecl.isLocalVarDecl()) { - llvm_unreachable("NYI"); - } - - // Emit the initializer and add a global destructor if appropriate. - cgf.CGM.emitCXXGlobalVarDeclInit(&varDecl, globalOp, performInit); - - if (threadsafe) { - // Pop the guard-abort cleanup if we pushed one. - cgf.PopCleanupBlock(); - - // Call __cxa_guard_release. This cannot throw. - cgf.emitNounwindRuntimeCall(cgf.getLoc(varDecl.getLocation()), - getGuardReleaseFn(CGM, guardPtrTy), - guardAddr.emitRawPointer()); - } else if (varDecl.isLocalVarDecl()) { - llvm_unreachable("NYI"); - } - - cgf.buildBlock(endBlock); } diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.h b/clang/lib/CIR/CodeGen/CIRGenModule.h index e141397d90b4..78c5737571d8 100644 --- a/clang/lib/CIR/CodeGen/CIRGenModule.h +++ b/clang/lib/CIR/CodeGen/CIRGenModule.h @@ -165,7 +165,7 @@ class CIRGenModule : public CIRGenTypeCache { CIRGenTypes &getTypes() { return genTypes; } const clang::LangOptions &getLangOpts() const { return langOpts; } CIRGenFunction *getCurrCIRGenFun() const { return CurCGF; } - void setCurrentCIRGenFn(CIRGenFunction* cgf) { CurCGF = cgf; } + void setCurrentCIRGenFn(CIRGenFunction *cgf) { CurCGF = cgf; } const cir::CIRDataLayout getDataLayout() const { // FIXME(cir): instead of creating a CIRDataLayout every time, set it as an // attribute for the CIRModule class. diff --git a/clang/lib/CIR/CodeGen/LoweringPrepare.cpp b/clang/lib/CIR/CodeGen/LoweringPrepare.cpp index 236c1d7790a5..a2fc79d2c2d5 100644 --- a/clang/lib/CIR/CodeGen/LoweringPrepare.cpp +++ b/clang/lib/CIR/CodeGen/LoweringPrepare.cpp @@ -1140,7 +1140,7 @@ void LoweringPreparePass::handleStaticLocal(GlobalOp globalOp, } // Check if the first byte of the guard variable is zero. - cgf.buildCXXGuardedInitBranch( + cgf.emitCXXGuardedInitBranch( needsInit, initCheckBlock, endBlock, clang::CIRGen::CIRGenFunction::GuardKind::variableGuard, varDecl.getRawDecl()); diff --git a/clang/test/CIR/CodeGen/static-local.cpp b/clang/test/CIR/CodeGen/static-local.cpp index 49831c9adc12..db41f2fa85cf 100644 --- a/clang/test/CIR/CodeGen/static-local.cpp +++ b/clang/test/CIR/CodeGen/static-local.cpp @@ -1,6 +1,31 @@ +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o %t.cir +// RUN: FileCheck --input-file=%t.cir %s + int fnA(); void foo() { static int val = fnA(); } +// CHECK: @_ZZ3foovE3val = internal global i32 0, align 4 +// CHECK: @_ZGVZ3foovE3val = internal global i64 0, align 8 +// CHECK: declare i32 @_Z3fnAv() +// CHECK: declare i32 @__cxa_guard_acquire(ptr) +// CHECK: declare void @__cxa_guard_release(ptr) + +// CHECK: define dso_local void @_Z3foov() +// CHECK-NEXT: %1 = load atomic i8, ptr @_ZGVZ3foovE3val acquire, align 1 +// CHECK-NEXT: %2 = icmp eq i8 %1, 0 +// CHECK-NEXT: br i1 %2, label %3, label %8 +// CHECK-DAG: 3: +// CHECK-NEXT: %4 = call i32 @__cxa_guard_acquire(ptr @_ZGVZ3foovE3val) +// CHECK-NEXT: %5 = icmp ne i32 %4, 0 +// CHECK-NEXT: br i1 %5, label %6, label %8 +// CHECK-DAG: 6: +// CHECK-NEXT: %7 = call i32 @_Z3fnAv() +// CHECK-NEXT: store i32 %7, ptr @_ZZ3foovE3val, align 4 +// CHECK-NEXT: call void @__cxa_guard_release(ptr @_ZGVZ3foovE3val) +// CHECK-NEXT: br label %8 +// CHECK-DAG: 8: +// CHECK-NEXT: ret void +// CHECK-NEXT: } From 08297bfe585e7d1ad1b6ca9d2e266441e49a51af Mon Sep 17 00:00:00 2001 From: Nathan Lanza Date: Wed, 11 Dec 2024 00:23:58 -0500 Subject: [PATCH 02/10] cleanup Created using spr 1.3.5 --- clang/lib/CIR/CodeGen/LoweringPrepare.cpp | 79 +++++++++++------------ clang/test/CIR/CodeGen/static-local.cpp | 39 +++++------ 2 files changed, 59 insertions(+), 59 deletions(-) diff --git a/clang/lib/CIR/CodeGen/LoweringPrepare.cpp b/clang/lib/CIR/CodeGen/LoweringPrepare.cpp index a2fc79d2c2d5..bead6d667ce3 100644 --- a/clang/lib/CIR/CodeGen/LoweringPrepare.cpp +++ b/clang/lib/CIR/CodeGen/LoweringPrepare.cpp @@ -893,6 +893,45 @@ void LoweringPreparePass::lowerThreeWayCmpOp(CmpThreeWayOp op) { op.erase(); } +void LoweringPreparePass::handleGlobalOpCtorDtor(GlobalOp globalOp) { + auto &ctorRegion = globalOp.getCtorRegion(); + auto &dtorRegion = globalOp.getDtorRegion(); + + if (!ctorRegion.empty() || !dtorRegion.empty()) { + // Build a variable initialization function and move the initialzation code + // in the ctor region over. + auto f = buildCXXGlobalVarDeclInitFunc(globalOp); + + // Clear the ctor and dtor region + ctorRegion.getBlocks().clear(); + dtorRegion.getBlocks().clear(); + + // Add a function call to the variable initialization function. + assert(!hasAttr( + mlir::cast(*globalOp.getAst())) && + "custom initialization priority NYI"); + dynamicInitializers.push_back(f); + } + + std::optional annotations = globalOp.getAnnotations(); + if (annotations) + addGlobalAnnotations(globalOp, annotations.value()); +} + +void LoweringPreparePass::lowerGetGlobalOp(GetGlobalOp getGlobalOp) { + if (!getGlobalOp.getStaticLocal()) + return; + + namesToStaticLocalGetGlobalOps.insert({getGlobalOp.getName(), getGlobalOp}); +} + +void LoweringPreparePass::lowerGlobalOp(GlobalOp globalOp) { + if (!globalOp.getStaticLocal()) + handleGlobalOpCtorDtor(globalOp); + + namesToStaticLocalGlobalOps.insert({globalOp.getName(), globalOp}); +} + static cir::GlobalOp createGuardGlobalOp(::cir::CIRBaseBuilderTy &builder, mlir::Location loc, StringRef name, mlir::Type type, bool isConstant, @@ -1190,7 +1229,6 @@ void LoweringPreparePass::handleStaticLocal(GlobalOp globalOp, } // Emit the initializer and add a global destructor if appropriate. - bool performInit = true; auto &ctorRegion = globalOp.getCtorRegion(); assert(!ctorRegion.empty() && "This should never be empty here."); if (!ctorRegion.hasOneBlock()) @@ -1220,45 +1258,6 @@ void LoweringPreparePass::handleStaticLocal(GlobalOp globalOp, cgm.setCurrentCIRGenFn(nullptr); } -void LoweringPreparePass::handleGlobalOpCtorDtor(GlobalOp globalOp) { - auto &ctorRegion = globalOp.getCtorRegion(); - auto &dtorRegion = globalOp.getDtorRegion(); - - if (!ctorRegion.empty() || !dtorRegion.empty()) { - // Build a variable initialization function and move the initialzation code - // in the ctor region over. - auto f = buildCXXGlobalVarDeclInitFunc(globalOp); - - // Clear the ctor and dtor region - ctorRegion.getBlocks().clear(); - dtorRegion.getBlocks().clear(); - - // Add a function call to the variable initialization function. - assert(!hasAttr( - mlir::cast(*globalOp.getAst())) && - "custom initialization priority NYI"); - dynamicInitializers.push_back(f); - } - - std::optional annotations = globalOp.getAnnotations(); - if (annotations) - addGlobalAnnotations(globalOp, annotations.value()); -} - -void LoweringPreparePass::lowerGetGlobalOp(GetGlobalOp getGlobalOp) { - if (!getGlobalOp.getStaticLocal()) - return; - - namesToStaticLocalGetGlobalOps.insert({getGlobalOp.getName(), getGlobalOp}); -} - -void LoweringPreparePass::lowerGlobalOp(GlobalOp globalOp) { - if (!globalOp.getStaticLocal()) - handleGlobalOpCtorDtor(globalOp); - - namesToStaticLocalGlobalOps.insert({globalOp.getName(), globalOp}); -} - void LoweringPreparePass::buildGlobalCtorDtorList() { if (!globalCtorList.empty()) { theModule->setAttr(cir::CIRDialect::getGlobalCtorsAttrName(), diff --git a/clang/test/CIR/CodeGen/static-local.cpp b/clang/test/CIR/CodeGen/static-local.cpp index ca1b0c643407..3e40f0b4a45a 100644 --- a/clang/test/CIR/CodeGen/static-local.cpp +++ b/clang/test/CIR/CodeGen/static-local.cpp @@ -1,8 +1,9 @@ -// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o %t.cir -// RUN: FileCheck --input-file=%t.cir %s // RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o %t.cir // RUN: FileCheck --input-file=%t.cir %s --check-prefix=CIR -// RUN: %clang_cc1 -triple aarch64-none-linux-android21 -fclangir -emit-cir -mmlir --mlir-print-ir-before=cir-lowering-prepare %s -o %t.cir 2>&1 | FileCheck %s -check-prefix=CIRGEN +// RUN: %clang_cc1 -triple aarch64-none-linux-android21 -fclangir -emit-cir -clangir-disable-passes %s -o %t.cir +// RUN: FileCheck --input-file=%t.cir %s --check-prefix=CIRGEN +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o %t.cir +// RUN: FileCheck --input-file=%t.cir %s --check-prefix=LLVM int fnA(); @@ -56,19 +57,19 @@ void foo() { // CHECK: declare i32 @__cxa_guard_acquire(ptr) // CHECK: declare void @__cxa_guard_release(ptr) -// CHECK: define dso_local void @_Z3foov() -// CHECK-NEXT: %1 = load atomic i8, ptr @_ZGVZ3foovE3val acquire, align 1 -// CHECK-NEXT: %2 = icmp eq i8 %1, 0 -// CHECK-NEXT: br i1 %2, label %3, label %8 -// CHECK-DAG: 3: -// CHECK-NEXT: %4 = call i32 @__cxa_guard_acquire(ptr @_ZGVZ3foovE3val) -// CHECK-NEXT: %5 = icmp ne i32 %4, 0 -// CHECK-NEXT: br i1 %5, label %6, label %8 -// CHECK-DAG: 6: -// CHECK-NEXT: %7 = call i32 @_Z3fnAv() -// CHECK-NEXT: store i32 %7, ptr @_ZZ3foovE3val, align 4 -// CHECK-NEXT: call void @__cxa_guard_release(ptr @_ZGVZ3foovE3val) -// CHECK-NEXT: br label %8 -// CHECK-DAG: 8: -// CHECK-NEXT: ret void -// CHECK-NEXT: } +// LLVM: define dso_local void @_Z3foov() +// LLVM-NEXT: %1 = load atomic i8, ptr @_ZGVZ3foovE3val acquire, align 1 +// LLVM-NEXT: %2 = icmp eq i8 %1, 0 +// LLVM-NEXT: br i1 %2, label %3, label %8 +// LLVM-DAG: 3: +// LLVM-NEXT: %4 = call i32 @__cxa_guard_acquire(ptr @_ZGVZ3foovE3val) +// LLVM-NEXT: %5 = icmp ne i32 %4, 0 +// LLVM-NEXT: br i1 %5, label %6, label %8 +// LLVM-DAG: 6: +// LLVM-NEXT: %7 = call i32 @_Z3fnAv() +// LLVM-NEXT: store i32 %7, ptr @_ZZ3foovE3val, align 4 +// LLVM-NEXT: call void @__cxa_guard_release(ptr @_ZGVZ3foovE3val) +// LLVM-NEXT: br label %8 +// LLVM-DAG: 8: +// LLVM-NEXT: ret void +// LLVM-NEXT: } From 38dc33a7f703734035129926e2e3a4ba4768149e Mon Sep 17 00:00:00 2001 From: Nathan Lanza Date: Wed, 11 Dec 2024 00:33:44 -0500 Subject: [PATCH 03/10] add comments Created using spr 1.3.5 --- clang/lib/CIR/CodeGen/LoweringPrepare.cpp | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/clang/lib/CIR/CodeGen/LoweringPrepare.cpp b/clang/lib/CIR/CodeGen/LoweringPrepare.cpp index bead6d667ce3..ff1d4098bfcf 100644 --- a/clang/lib/CIR/CodeGen/LoweringPrepare.cpp +++ b/clang/lib/CIR/CodeGen/LoweringPrepare.cpp @@ -1013,11 +1013,17 @@ void LoweringPreparePass::handleStaticLocal(GlobalOp globalOp, clang::CIRGen::CIRGenModule &cgm = *this->cgm; clang::CIRGen::CIRGenCXXABI &cxxABI = cgm.getCXXABI(); clang::CIRGen::CIRGenBuilderTy &builder = cgm.getBuilder(); + builder.setInsertionPointAfter(getGlobalOp); Block *getGlobalOpBlock = builder.getInsertionBlock(); + // TODO(CIR): This is too simple at the moment. This is only tested on a + // simple test case with only the static local var decl and thus we only have + // the return. For less trivial examples we'll have to handle shuffling the + // contents of this block more carefully. Operation *ret = getGlobalOpBlock->getTerminator(); ret->remove(); builder.setInsertionPointAfter(getGlobalOp); + clang::CIRGen::CIRGenFunction cgf{cgm, builder, true}; cgf.CurFn = getGlobalOp->getParentOfType(); cgm.setCurrentCIRGenFn(&cgf); @@ -1228,7 +1234,10 @@ void LoweringPreparePass::handleStaticLocal(GlobalOp globalOp, llvm_unreachable("NYI"); } - // Emit the initializer and add a global destructor if appropriate. + // CIR: Move the initializer from the globalOp's ctor region into the current + // block. + // TODO(CIR): Once we support exceptions we'll need to walk the ctor region to + // change calls to invokes. auto &ctorRegion = globalOp.getCtorRegion(); assert(!ctorRegion.empty() && "This should never be empty here."); if (!ctorRegion.hasOneBlock()) @@ -1255,6 +1264,7 @@ void LoweringPreparePass::handleStaticLocal(GlobalOp globalOp, cgf.buildBlock(endBlock); builder.insert(ret); + cgm.setCurrentCIRGenFn(nullptr); } From 912e55bedd693fc2fbae3b0a5fdfc4672a79aad4 Mon Sep 17 00:00:00 2001 From: Nathan Lanza Date: Wed, 11 Dec 2024 21:40:32 -0500 Subject: [PATCH 04/10] remove cgm/cgf usages Created using spr 1.3.5 --- .../clang/CIR/Interfaces/ASTAttrInterfaces.td | 2 +- clang/lib/CIR/CodeGen/CIRGenModule.h | 2 +- clang/lib/CIR/CodeGen/LoweringPrepare.cpp | 440 ++++++++++++------ clang/test/CIR/CodeGen/static-local.cpp | 48 +- 4 files changed, 329 insertions(+), 163 deletions(-) diff --git a/clang/include/clang/CIR/Interfaces/ASTAttrInterfaces.td b/clang/include/clang/CIR/Interfaces/ASTAttrInterfaces.td index 769c13123ee4..b1d3dbe6c853 100644 --- a/clang/include/clang/CIR/Interfaces/ASTAttrInterfaces.td +++ b/clang/include/clang/CIR/Interfaces/ASTAttrInterfaces.td @@ -68,7 +68,7 @@ let cppNamespace = "::cir" in { /*defaultImplementation=*/ [{ std::unique_ptr mangleCtx( $_attr.getAst()->getASTContext().createMangleContext()); - mangleCtx->mangleDynamicInitializer($_attr.getAst(), Out); + mangleCtx->mangleStaticGuardVariable($_attr.getAst(), Out); }] >, InterfaceMethod<"", "clang::VarDecl::TLSKind", "getTLSKind", (ins), [{}], diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.h b/clang/lib/CIR/CodeGen/CIRGenModule.h index 3f8f522a2dc9..30458e45d39f 100644 --- a/clang/lib/CIR/CodeGen/CIRGenModule.h +++ b/clang/lib/CIR/CodeGen/CIRGenModule.h @@ -132,7 +132,7 @@ class CIRGenModule : public CIRGenTypeCache { llvm::DenseSet DiagnosedConflictingDefinitions; CIRGenModule *createMinimalCGM() { - auto cgm = new CIRGenModule(getMLIRContext(), getASTContext(), + auto *cgm = new CIRGenModule(getMLIRContext(), getASTContext(), getCodeGenOpts(), getDiags()); } diff --git a/clang/lib/CIR/CodeGen/LoweringPrepare.cpp b/clang/lib/CIR/CodeGen/LoweringPrepare.cpp index ff1d4098bfcf..44296d6cd1c0 100644 --- a/clang/lib/CIR/CodeGen/LoweringPrepare.cpp +++ b/clang/lib/CIR/CodeGen/LoweringPrepare.cpp @@ -384,7 +384,6 @@ void LoweringPreparePass::lowerVAArgOp(VAArgOp op) { op.replaceAllUsesWith(res); op.erase(); } - return; } void LoweringPreparePass::lowerUnaryOp(UnaryOp op) { @@ -957,51 +956,232 @@ static cir::GlobalOp createGuardGlobalOp(::cir::CIRBaseBuilderTy &builder, return g; } -static cir::FuncOp getGuardAbortFn(clang::CIRGen::CIRGenModule &cgm, +static mlir::Operation *getGlobalValue(mlir::ModuleOp theModule, + StringRef name) { + auto *global = mlir::SymbolTable::lookupSymbolIn(theModule, name); + if (!global) + return {}; + return global; +} + +static cir::FuncOp createCIRFunction(clang::CIRGen::CIRGenBuilderTy &builder, + mlir::MLIRContext &mlirContext, + mlir::ModuleOp theModule, + mlir::Location loc, StringRef name, + cir::FuncType type) { + // At the point we need to create the function, the insertion point + // could be anywhere (e.g. callsite). Do not rely on whatever it might + // be, properly save, find the appropriate place and restore. + FuncOp f; + { + mlir::OpBuilder::InsertionGuard guard(builder); + + // Get the first function in the module as the location to insert the new + // function. + Operation *firstFn = &theModule->getRegion(0).getBlocks().front().front(); + builder.setInsertionPoint(firstFn); + + f = builder.create(loc, name, type); + + assert(f.isDeclaration() && "expected empty body"); + + // A declaration gets private visibility by default, but external linkage + // as the default linkage. + f.setLinkageAttr(cir::GlobalLinkageKindAttr::get( + &mlirContext, cir::GlobalLinkageKind::ExternalLinkage)); + mlir::SymbolTable::setSymbolVisibility( + f, mlir::SymbolTable::Visibility::Private); + + // Initialize with empty dict of extra attributes. + f.setExtraAttrsAttr(cir::ExtraFuncAttributesAttr::get( + &mlirContext, builder.getDictionaryAttr({}))); + } + return f; +} + +/// If the specified mangled name is not in the module, +/// create and return a CIR Function with the specified type. If there is +/// something in the module with the specified name, return it potentially +/// bitcasted to the right type. +/// +/// If D is non-null, it specifies a decl that corresponded to this. This is +/// used to set the attributes on the function when it is first created. +static cir::FuncOp +getOrCreateCIRFunction(clang::ASTContext &astContext, + mlir::MLIRContext &mlirContext, mlir::ModuleOp theModule, + clang::CIRGen::CIRGenBuilderTy &builder, + StringRef mangledName, mlir::Type type) { + mlir::ArrayAttr extraAttrs = {}; + clang::CIRGen::ForDefinition_t isForDefinition = + clang::CIRGen::NotForDefinition; + clang::GlobalDecl globalDecl = clang::GlobalDecl(); + bool dontDefer = false; + bool forVTable = false; + + const auto *decl = globalDecl.getDecl(); + + // Lookup the entry, lazily creating it if necessary. + mlir::Operation *entry = getGlobalValue(theModule, mangledName); + if (entry) { + assert(isa(entry) && + "not implemented, only supports FuncOp for now"); + + // If there are two attempts to define the same mangled name, issue an + // error. + auto fn = cast(entry); + + if (fn && fn.getFunctionType() == type) { + return fn; + } + + if (!isForDefinition) { + return fn; + } + + // TODO: clang checks here if this is a llvm::GlobalAlias... how will we + // support this? + } + + // This function doesn't have a complete type (for example, the return type is + // an incomplete struct). Use a fake type instead, and make sure not to try to + // set attributes. + bool isIncompleteFunction = false; + + cir::FuncType fTy; + if (mlir::isa(type)) { + fTy = mlir::cast(type); + } else { + assert(false && "NYI"); + // FTy = mlir::FunctionType::get(VoidTy, false); + isIncompleteFunction = true; + } + + // TODO: CodeGen includeds the linkage (ExternalLinkage) and only passes the + // mangledname if Entry is nullptr + auto func = createCIRFunction(builder, mlirContext, theModule, + theModule.getLoc(), mangledName, fTy); + + // If we already created a function with the same mangled name (but different + // type) before, take its name and add it to the list of functions to be + // replaced with F at the end of CodeGen. + // + // This happens if there is a prototype for a function (e.g. "int f()") and + // then a definition of a different type (e.g. "int f(int x)"). + if (entry) { + // Fetch a generic symbol-defining operation and its uses. + auto symbolOp = dyn_cast(entry); + assert(symbolOp && "Expected a symbol-defining operation"); + + // TODO(cir): When can this symbol be something other than a function? + assert(isa(entry) && "NYI"); + + // Obliterate no-proto declaration. + entry->erase(); + } + + if (!isIncompleteFunction) { + assert(func.getFunctionType() == type); + return func; + } + + // TODO(cir): Might need bitcast to different address space. + assert(!cir::MissingFeatures::addressSpace()); + return func; +} + +static cir::FuncOp createRuntimeFunction( + clang::ASTContext &astContext, mlir::MLIRContext &mlirContext, + mlir::ModuleOp theModule, clang::CIRGen::CIRGenBuilderTy &builder, + + cir::FuncType type, StringRef name, mlir::ArrayAttr = {}, + [[maybe_unused]] bool local = false, bool assumeConvergent = false) { + if (assumeConvergent) { + llvm_unreachable("NYI"); + } + if (local) + llvm_unreachable("NYI"); + + auto entry = getOrCreateCIRFunction(astContext, mlirContext, theModule, + builder, name, type); + + // Traditional codegen checks for a valid dyn_cast llvm::Function for `entry`, + // no testcase that cover this path just yet though. + if (!entry) { + // Setup runtime CC, DLL support for windows and set dso local. + llvm_unreachable("NYI"); + } + + return entry; +} + +static cir::FuncOp getGuardAbortFn(clang::ASTContext &astContext, + mlir::MLIRContext &mlirContext, + mlir::ModuleOp theModule, + clang::CIRGen::CIRGenBuilderTy &builder, cir::PointerType guardPtrTy) { // void __cxa_guard_abort(__guard *guard_object); - cir::FuncType fTy = cgm.getBuilder().getFuncType(guardPtrTy, {cgm.VoidTy}, - /*isVarArg=*/false); + cir::FuncType fTy = builder.getFuncType(guardPtrTy, {builder.getVoidTy()}, + /*isVarArg=*/false); assert(!cir::MissingFeatures::functionIndexAttribute()); assert(!cir::MissingFeatures::noUnwindAttribute()); - return cgm.createRuntimeFunction(fTy, "__cxa_guard_abort"); + return createRuntimeFunction(astContext, mlirContext, theModule, builder, fTy, + "__cxa_guard_abort"); } -static cir::FuncOp getGuardAcquireFn(clang::CIRGen::CIRGenModule &cgm, +static cir::FuncOp getGuardAcquireFn(clang::ASTContext &astContext, + mlir::MLIRContext &mlirContext, + mlir::ModuleOp theModule, + clang::CIRGen::CIRGenBuilderTy &builder, cir::PointerType guardPtrTy) { // int __cxa_guard_acquire(__guard *guard_object); - cir::FuncType fTy = cgm.getBuilder().getFuncType( - guardPtrTy, {cgm.getTypes().ConvertType(cgm.getASTContext().IntTy)}, - /*isVarArg=*/false); + // TODO(CIR): The hardcoded getSInt32Ty is wrong here. CodeGen uses + // CodeGenTypes.convertType but we don't have access to the CGM. + cir::FuncType fTy = builder.getFuncType(guardPtrTy, {builder.getSInt32Ty()}, + /*isVarArg=*/false); assert(!cir::MissingFeatures::functionIndexAttribute()); assert(!cir::MissingFeatures::noUnwindAttribute()); - return cgm.createRuntimeFunction(fTy, "__cxa_guard_acquire"); + return createRuntimeFunction(astContext, mlirContext, theModule, builder, fTy, + "__cxa_guard_acquire"); } -static cir::FuncOp getGuardReleaseFn(clang::CIRGen::CIRGenModule &cgm, +static cir::FuncOp getGuardReleaseFn(clang::ASTContext &astContext, + mlir::MLIRContext &mlirContext, + mlir::ModuleOp theModule, + clang::CIRGen::CIRGenBuilderTy &builder, cir::PointerType guardPtrTy) { // void __cxa_guard_release(__guard *guard_object); - cir::FuncType fTy = cgm.getBuilder().getFuncType(guardPtrTy, {cgm.VoidTy}, - /*isVarArg=*/false); + cir::FuncType fTy = builder.getFuncType(guardPtrTy, {builder.getVoidTy()}, + /*isVarArg=*/false); assert(!cir::MissingFeatures::functionIndexAttribute()); assert(!cir::MissingFeatures::noUnwindAttribute()); - return cgm.createRuntimeFunction(fTy, "__cxa_guard_release"); + return createRuntimeFunction(astContext, mlirContext, theModule, builder, fTy, + "__cxa_guard_release"); } -namespace { -struct CallGuardAbort final : clang::CIRGen::EHScopeStack::Cleanup { - cir::GlobalOp guard; - CallGuardAbort(cir::GlobalOp guard) : guard{guard} {} - - void Emit(clang::CIRGen::CIRGenFunction &cgf, Flags flags) override { - auto guardPtrTy = cir::PointerType::get(guard.getSymType()); - mlir::Value getGuard = - cgf.CGM.getBuilder().createGetGlobal(guard, /*threadLocal*/ false); - cgf.emitNounwindRuntimeCall(guard->getLoc(), - getGuardAbortFn(cgf.CGM, guardPtrTy), getGuard); - } -}; -} // namespace +static mlir::Value emitRuntimeCall(clang::CIRGen::CIRGenBuilderTy &builder, + mlir::Location loc, cir::FuncOp callee, + ArrayRef args) { + // TODO(cir): set the calling convention to this runtime call. + assert(!cir::MissingFeatures::setCallingConv()); + + auto call = builder.createCallOp(loc, callee, args); + assert(call->getNumResults() <= 1 && + "runtime functions have at most 1 result"); + + if (call->getNumResults() == 0) + return nullptr; + + return call->getResult(0); +} + +static mlir::Value +emitNounwindRuntimeCall(clang::CIRGen::CIRGenBuilderTy &builder, + mlir::Location loc, cir::FuncOp callee, + ArrayRef args) { + mlir::Value call = emitRuntimeCall(builder, loc, callee, args); + assert(!cir::MissingFeatures::noUnwindAttribute()); + return call; +} void LoweringPreparePass::handleStaticLocal(GlobalOp globalOp, GetGlobalOp getGlobalOp) { @@ -1011,7 +1191,6 @@ void LoweringPreparePass::handleStaticLocal(GlobalOp globalOp, cir::ASTVarDeclInterface varDecl = astOption.value(); clang::CIRGen::CIRGenModule &cgm = *this->cgm; - clang::CIRGen::CIRGenCXXABI &cxxABI = cgm.getCXXABI(); clang::CIRGen::CIRGenBuilderTy &builder = cgm.getBuilder(); builder.setInsertionPointAfter(getGlobalOp); @@ -1024,10 +1203,6 @@ void LoweringPreparePass::handleStaticLocal(GlobalOp globalOp, ret->remove(); builder.setInsertionPointAfter(getGlobalOp); - clang::CIRGen::CIRGenFunction cgf{cgm, builder, true}; - cgf.CurFn = getGlobalOp->getParentOfType(); - cgm.setCurrentCIRGenFn(&cgf); - // Inline variables that weren't instantiated from variable templates have // partially-ordered initialization within their translation unit. bool nonTemplateInline = @@ -1057,9 +1232,9 @@ void LoweringPreparePass::handleStaticLocal(GlobalOp globalOp, llvm_unreachable("NYI"); } else { guardTy = cir::IntType::get(&getContext(), 64, /*isSigned=*/true); - ::cir::CIRDataLayout layout{globalOp->getParentOfType()}; + cir::CIRDataLayout dataLayout(theModule); guardAlignment = - clang::CharUnits::fromQuantity(layout.getABITypeAlign(guardTy)); + clang::CharUnits::fromQuantity(dataLayout.getABITypeAlign(guardTy)); } } auto guardPtrTy = cir::PointerType::get(guardTy); @@ -1072,8 +1247,7 @@ void LoweringPreparePass::handleStaticLocal(GlobalOp globalOp, SmallString<256> guardName; { llvm::raw_svector_ostream out(guardName); - cxxABI.getMangleContext().mangleStaticGuardVariable(varDecl.getRawDecl(), - out); + varDecl.mangleStaticGuardVariable(out); } // Create the guard variable with a zero-initializer. @@ -1102,8 +1276,7 @@ void LoweringPreparePass::handleStaticLocal(GlobalOp globalOp, setStaticLocalDeclGuardAddress(varDecl, guard); } - mlir::Value getGuard = - cgm.getBuilder().createGetGlobal(guard, /*threadLocal*/ false); + mlir::Value getGuard = builder.createGetGlobal(guard, /*threadLocal*/ false); clang::CIRGen::Address guardAddr = clang::CIRGen::Address(getGuard, guard.getSymType(), guardAlignment); @@ -1128,18 +1301,89 @@ void LoweringPreparePass::handleStaticLocal(GlobalOp globalOp, // call __cxa_guard_acquire unconditionally. The "inline" check isn't // actually inline, and the user might not expect calls to __atomic // libcalls. - unsigned maxInlineWidthInbits = cgm.getTarget().getMaxAtomicInlineWidth(); + unsigned maxInlineWidthInbits = + astCtx->getTargetInfo().getMaxAtomicInlineWidth(); // "init.end" block - mlir::Block *endBlock = nullptr; - { - mlir::OpBuilder::InsertionGuard guard(builder); - endBlock = builder.createBlock(builder.getBlock()->getParent()); - } + + auto initBlock = [&]() { + // CIR: Move the initializer from the globalOp's ctor region into the + // current block. + // TODO(CIR): Once we support exceptions we'll need to walk the ctor region + // to change calls to invokes. + auto &ctorRegion = globalOp.getCtorRegion(); + assert(!ctorRegion.empty() && "This should never be empty here."); + if (!ctorRegion.hasOneBlock()) + llvm_unreachable("Multiple blocks NYI"); + Block &block = ctorRegion.front(); + Block *insertBlock = builder.getInsertionBlock(); + insertBlock->getOperations().splice(insertBlock->end(), + block.getOperations(), block.begin(), + std::prev(block.end())); + builder.setInsertionPointToEnd(insertBlock); + + ctorRegion.getBlocks().clear(); + + if (threadsafe) { + // NOTE(CIR): CodeGen clears the above pushed CallGuardAbort here. + + // Call __cxa_guard_release. This cannot throw. + emitNounwindRuntimeCall(builder, globalOp->getLoc(), + getGuardReleaseFn(*astCtx, getContext(), + theModule, builder, guardPtrTy), + guardAddr.emitRawPointer()); + } else if (varDecl.isLocalVarDecl()) { + llvm_unreachable("NYI"); + } + }; + + // The semantics of dynamic initialization of variables with static or + // thread storage duration depends on whether they are declared at + // block-scope. The initialization of such variables at block-scope can be + // aborted with an exception and later retried (per C++20 [stmt.dcl]p4), and + // recursive entry to their initialization has undefined behavior (also per + // C++20 [stmt.dcl]p4). For such variables declared at non-block scope, + // exceptions lead to termination (per C++20 [except.terminate]p1), and + // recursive references to the variables are governed only by the lifetime + // rules (per C++20 [class.cdtor]p2), which means such references are + // perfectly fine as long as they avoid touching memory. As a result, + // block-scope variables must not be marked as initialized until after + // initialization completes (unless the mark is reverted following an + // exception), but non-block-scope variables must be marked prior to + // initialization so that recursive accesses during initialization do not + // restart initialization. + + // Variables used when coping with thread-safe statics and exceptions. + auto thenStmt = [&]() { + if (threadsafe) { + auto loc = globalOp->getLoc(); + // Call __cxa_guard_acquire. + mlir::Value value = emitNounwindRuntimeCall( + builder, loc, + getGuardAcquireFn(*astCtx, getContext(), theModule, builder, + guardPtrTy), + guardAddr.emitRawPointer()); + + auto isNotNull = builder.createIsNotNull(loc, value); + builder.create(globalOp.getLoc(), isNotNull, + /*=withElseRegion*/ false, + [&](mlir::OpBuilder &, mlir::Location) { + initBlock(); + builder.createYield(getGlobalOp->getLoc()); + }); + + // NOTE(CIR): CodeGen pushes a CallGuardAbort cleanup here, but we are + // synthesizing the outcome via walking the CIR in the ctor region and + // changing calls to invokes. + + } else if (!varDecl.isLocalVarDecl()) { + llvm_unreachable("NYI"); + } + }; + if (!threadsafe || maxInlineWidthInbits) { // Load the first byte of the guard variable. - mlir::Value load = - builder.createLoad(cgm.getLoc(varDecl.getLocation()), - guardAddr.withElementType(cgm.SInt8Ty)); + mlir::Value load = builder.createLoad( + globalOp.getLoc(), guardAddr.withElementType(builder.getSInt8Ty())); // Itanium ABI: // An implementation supporting thread-safety on multiprocesor systems @@ -1174,98 +1418,20 @@ void LoweringPreparePass::handleStaticLocal(GlobalOp globalOp, if (MissingFeatures::useARMGuardVarABI() && !useInt8GuardVariable) llvm_unreachable("NYI"); mlir::Value value = load; - mlir::Value needsInit = builder.createIsNull( - cgm.getLoc(varDecl.getLocation()), value, "guard.uninitialized"); - - mlir::Block *initCheckBlock = nullptr; - { - mlir::OpBuilder::InsertionGuard guard(builder); - // "init.check" block - initCheckBlock = builder.createBlock(builder.getBlock()->getParent()); - } - - // Check if the first byte of the guard variable is zero. - cgf.emitCXXGuardedInitBranch( - needsInit, initCheckBlock, endBlock, - clang::CIRGen::CIRGenFunction::GuardKind::variableGuard, - varDecl.getRawDecl()); - - cgf.buildBlock(initCheckBlock); - } - - // The semantics of dynamic initialization of variables with static or - // thread storage duration depends on whether they are declared at - // block-scope. The initialization of such variables at block-scope can be - // aborted with an exception and later retried (per C++20 [stmt.dcl]p4), and - // recursive entry to their initialization has undefined behavior (also per - // C++20 [stmt.dcl]p4). For such variables declared at non-block scope, - // exceptions lead to termination (per C++20 [except.terminate]p1), and - // recursive references to the variables are governed only by the lifetime - // rules (per C++20 [class.cdtor]p2), which means such references are - // perfectly fine as long as they avoid touching memory. As a result, - // block-scope variables must not be marked as initialized until after - // initialization completes (unless the mark is reverted following an - // exception), but non-block-scope variables must be marked prior to - // initialization so that recursive accesses during initialization do not - // restart initialization. - - // Variables used when coping with thread-safe statics and exceptions. - if (threadsafe) { - auto loc = cgf.getLoc(varDecl.getLocation()); - // Call __cxa_guard_acquire. - mlir::Value value = cgf.emitNounwindRuntimeCall( - cgf.getLoc(varDecl.getLocation()), getGuardAcquireFn(cgm, guardPtrTy), - guardAddr.emitRawPointer()); - - mlir::Block *initBlock = nullptr; - { - mlir::OpBuilder::InsertionGuard guard(builder); - initBlock = builder.createBlock(builder.getBlock()->getParent()); - } - - builder.createCondBr(loc, builder.createIsNotNull(loc, value), initBlock, - endBlock); - - // Call __cxa_guard_abort along the exceptional edge. - cgf.EHStack.pushCleanup(clang::CIRGen::EHCleanup, guard); - - cgf.buildBlock(initBlock); - } else if (!varDecl.isLocalVarDecl()) { - llvm_unreachable("NYI"); - } - - // CIR: Move the initializer from the globalOp's ctor region into the current - // block. - // TODO(CIR): Once we support exceptions we'll need to walk the ctor region to - // change calls to invokes. - auto &ctorRegion = globalOp.getCtorRegion(); - assert(!ctorRegion.empty() && "This should never be empty here."); - if (!ctorRegion.hasOneBlock()) - llvm_unreachable("Multiple blocks NYI"); - Block &block = ctorRegion.front(); - Block *insertBlock = builder.getInsertionBlock(); - insertBlock->getOperations().splice(insertBlock->end(), block.getOperations(), - block.begin(), std::prev(block.end())); - builder.setInsertionPointToEnd(insertBlock); - - ctorRegion.getBlocks().clear(); - - if (threadsafe) { - // Pop the guard-abort cleanup if we pushed one. - cgf.PopCleanupBlock(); - - // Call __cxa_guard_release. This cannot throw. - cgf.emitNounwindRuntimeCall(cgf.getLoc(varDecl.getLocation()), - getGuardReleaseFn(cgm, guardPtrTy), - guardAddr.emitRawPointer()); - } else if (varDecl.isLocalVarDecl()) { - llvm_unreachable("NYI"); + mlir::Value needsInit = + builder.createIsNull(globalOp.getLoc(), value, "guard.uninitialized"); + + builder.create(globalOp.getLoc(), needsInit, + /*=withElseRegion*/ false, + [&](mlir::OpBuilder &, mlir::Location) { + if (MissingFeatures::metaDataNode()) + llvm_unreachable("NYI"); + thenStmt(); + builder.createYield(getGlobalOp->getLoc()); + }); } - cgf.buildBlock(endBlock); builder.insert(ret); - - cgm.setCurrentCIRGenFn(nullptr); } void LoweringPreparePass::buildGlobalCtorDtorList() { diff --git a/clang/test/CIR/CodeGen/static-local.cpp b/clang/test/CIR/CodeGen/static-local.cpp index 3e40f0b4a45a..92bbf04b9f11 100644 --- a/clang/test/CIR/CodeGen/static-local.cpp +++ b/clang/test/CIR/CodeGen/static-local.cpp @@ -23,44 +23,42 @@ void foo() { // CIRGEN-NEXT: cir.return // CIRGEN-NEXT: } +// CIR: cir.func private @__cxa_guard_release(!cir.ptr) +// CIR: cir.func private @__cxa_guard_acquire(!cir.ptr) -> !s32i // CIR: cir.func private @_Z3fnAv() -> !s32i -// CIR-NEXT: cir.global "private" internal dsolocal @_ZZ3foovE3val = #cir.int<0> : !s32i {alignment = 4 : i64, ast = #cir.var.decl.ast, static_local} -// CIR-NEXT: cir.global "private" internal dsolocal @_ZGVZ3foovE3val = #cir.int<0> : !s64i {alignment = 8 : i64} -// CIR-NEXT: cir.func private @__cxa_guard_acquire(!cir.ptr) -> !s32i -// CIR-NEXT: cir.func private @__cxa_guard_release(!cir.ptr) -// CIR-NEXT: cir.func @_Z3foov() extra(#fn_attr) { +// CIR: cir.global "private" internal dsolocal @_ZZ3foovE3val = #cir.int<0> : !s32i {alignment = 4 : i64, ast = #cir.var.decl.ast, static_local} +// CIR: cir.global "private" internal dsolocal @_ZGVZ3foovE3val = #cir.int<0> : !s64i {alignment = 8 : i64} +// CIR: cir.func @_Z3foov() extra(#fn_attr) { // CIR-NEXT: %0 = cir.get_global @_ZZ3foovE3val : !cir.ptr {static_local} // CIR-NEXT: %1 = cir.get_global @_ZGVZ3foovE3val : !cir.ptr // CIR-NEXT: %2 = cir.cast(bitcast, %1 : !cir.ptr), !cir.ptr // CIR-NEXT: %3 = cir.load atomic(acquire) %2 : !cir.ptr, !s8i // CIR-NEXT: %4 = cir.const #cir.int<0> : !s8i // CIR-NEXT: %5 = cir.cmp(eq, %3, %4) : !s8i, !cir.bool -// CIR-NEXT: cir.brcond %5 ^bb1, ^bb3 -// CIR-NEXT: ^bb1: // pred: ^bb0 -// CIR-NEXT: %6 = cir.call @__cxa_guard_acquire(%1) : (!cir.ptr) -> !s32i -// CIR-NEXT: %7 = cir.const #cir.int<0> : !s32i -// CIR-NEXT: %8 = cir.cmp(ne, %6, %7) : !s32i, !cir.bool -// CIR-NEXT: cir.brcond %8 ^bb2, ^bb3 -// CIR-NEXT: ^bb2: // pred: ^bb1 -// CIR-NEXT: %9 = cir.get_global @_ZZ3foovE3val : !cir.ptr -// CIR-NEXT: %10 = cir.call @_Z3fnAv() : () -> !s32i -// CIR-NEXT: cir.store %10, %9 : !s32i, !cir.ptr -// CIR-NEXT: cir.call @__cxa_guard_release(%1) : (!cir.ptr) -> () -// CIR-NEXT: cir.br ^bb3 -// CIR-NEXT: ^bb3: // 3 preds: ^bb0, ^bb1, ^bb2 +// CIR-NEXT: cir.if %5 { +// CIR-NEXT: %6 = cir.call @__cxa_guard_acquire(%1) : (!cir.ptr) -> !s32i +// CIR-NEXT: %7 = cir.const #cir.int<0> : !s32i +// CIR-NEXT: %8 = cir.cmp(ne, %6, %7) : !s32i, !cir.bool +// CIR-NEXT: cir.if %8 { +// CIR-NEXT: %9 = cir.get_global @_ZZ3foovE3val : !cir.ptr +// CIR-NEXT: %10 = cir.call @_Z3fnAv() : () -> !s32i +// CIR-NEXT: cir.store %10, %9 : !s32i, !cir.ptr +// CIR-NEXT: cir.call @__cxa_guard_release(%1) : (!cir.ptr) -> () +// CIR-NEXT: } +// CIR-NEXT: } // CIR-NEXT: cir.return // CIR-NEXT: } -// CHECK: @_ZZ3foovE3val = internal global i32 0, align 4 -// CHECK: @_ZGVZ3foovE3val = internal global i64 0, align 8 -// CHECK: declare i32 @_Z3fnAv() -// CHECK: declare i32 @__cxa_guard_acquire(ptr) -// CHECK: declare void @__cxa_guard_release(ptr) +// LLVM: @_ZZ3foovE3val = internal global i32 0, align 4 +// LLVM: @_ZGVZ3foovE3val = internal global i64 0, align 8 +// LLVM: declare void @__cxa_guard_release(ptr) +// LLVM: declare i32 @__cxa_guard_acquire(ptr) +// LLVM: declare i32 @_Z3fnAv() // LLVM: define dso_local void @_Z3foov() // LLVM-NEXT: %1 = load atomic i8, ptr @_ZGVZ3foovE3val acquire, align 1 // LLVM-NEXT: %2 = icmp eq i8 %1, 0 -// LLVM-NEXT: br i1 %2, label %3, label %8 +// LLVM-NEXT: br i1 %2, label %3, label %9 // LLVM-DAG: 3: // LLVM-NEXT: %4 = call i32 @__cxa_guard_acquire(ptr @_ZGVZ3foovE3val) // LLVM-NEXT: %5 = icmp ne i32 %4, 0 @@ -71,5 +69,7 @@ void foo() { // LLVM-NEXT: call void @__cxa_guard_release(ptr @_ZGVZ3foovE3val) // LLVM-NEXT: br label %8 // LLVM-DAG: 8: +// LLVM-NEXT: br label %9 +// LLVM-DAG: 9: // LLVM-NEXT: ret void // LLVM-NEXT: } From 680e5f48d21ed2bddc7de24c9fee57799f0c19dd Mon Sep 17 00:00:00 2001 From: Nathan Lanza Date: Wed, 11 Dec 2024 21:48:18 -0500 Subject: [PATCH 05/10] remove cgm Created using spr 1.3.5 --- clang/lib/CIR/CodeGen/CIRGenBuilder.h | 30 ------------- clang/lib/CIR/CodeGen/CIRGenCXXABI.h | 2 +- clang/lib/CIR/CodeGen/CIRGenCall.cpp | 5 ++- clang/lib/CIR/CodeGen/CIRGenDeclCXX.cpp | 8 ++-- clang/lib/CIR/CodeGen/CIRGenFunction.cpp | 14 ------- clang/lib/CIR/CodeGen/CIRGenFunction.h | 36 +++------------- clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp | 8 ++-- clang/lib/CIR/CodeGen/CIRGenModule.h | 2 +- clang/lib/CIR/CodeGen/CIRGenStmt.cpp | 42 ------------------- 9 files changed, 18 insertions(+), 129 deletions(-) diff --git a/clang/lib/CIR/CodeGen/CIRGenBuilder.h b/clang/lib/CIR/CodeGen/CIRGenBuilder.h index 0c40c15e6c08..a2273c3b2668 100644 --- a/clang/lib/CIR/CodeGen/CIRGenBuilder.h +++ b/clang/lib/CIR/CodeGen/CIRGenBuilder.h @@ -1074,36 +1074,6 @@ class CIRGenBuilderTy : public cir::CIRBaseBuilderTy { /// pointed to by arrayPtr. mlir::Value maybeBuildArrayDecay(mlir::Location loc, mlir::Value arrayPtr, mlir::Type eltTy); - - /// Create an unconditional branch op. - cir::BrOp createBr(mlir::Location loc, mlir::Block *dest) { - assert(!cir::MissingFeatures::metaDataNode()); - return create(loc, dest); - } - - /// Create a conditional branch operation - cir::BrCondOp createCondBr(mlir::Location loc, mlir::Value condition, - mlir::Block *trueBlock, mlir::Block *falseBlock) { - if (cir::MissingFeatures::metaDataNode()) - llvm_unreachable("NYI"); - return create(loc, condition, trueBlock, falseBlock); - } - - /// createBasicBlock - Create an MLIR block - mlir::Block *createBasicBlock(cir::FuncOp parent = nullptr, - mlir::Block *before = nullptr) { - auto *b = new mlir::Block(); - if (parent) { - - if (before == nullptr) - before = &*parent.end(); - - parent.getFunctionBody().getBlocks().insert( - mlir::Region::iterator(before), b); - } - - return b; - } }; } // namespace clang::CIRGen diff --git a/clang/lib/CIR/CodeGen/CIRGenCXXABI.h b/clang/lib/CIR/CodeGen/CIRGenCXXABI.h index 839447cb7232..47129ce4e3d0 100644 --- a/clang/lib/CIR/CodeGen/CIRGenCXXABI.h +++ b/clang/lib/CIR/CodeGen/CIRGenCXXABI.h @@ -183,7 +183,7 @@ class CIRGenCXXABI { /// - a static local variable /// - a static data member of a class template instantiation virtual void emitGuardedInit(CIRGenFunction &cgf, const VarDecl &varDecl, - cir::GlobalOp globalOp, bool performInit) = 0; + cir::GlobalOp globalOp, bool performInit) = 0; /// Emit code to force the execution of a destructor during global /// teardown. The default implementation of this uses atexit. diff --git a/clang/lib/CIR/CodeGen/CIRGenCall.cpp b/clang/lib/CIR/CodeGen/CIRGenCall.cpp index bbb0b8ad7a54..9d416e87e1a4 100644 --- a/clang/lib/CIR/CodeGen/CIRGenCall.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenCall.cpp @@ -942,8 +942,9 @@ mlir::Value CIRGenFunction::emitRuntimeCall(mlir::Location loc, return call->getResult(0); } -mlir::Value CIRGenFunction::emitNounwindRuntimeCall( - mlir::Location loc, cir::FuncOp callee, ArrayRef args) { +mlir::Value +CIRGenFunction::emitNounwindRuntimeCall(mlir::Location loc, cir::FuncOp callee, + ArrayRef args) { mlir::Value call = emitRuntimeCall(loc, callee, args); assert(!cir::MissingFeatures::noUnwindAttribute()); return call; diff --git a/clang/lib/CIR/CodeGen/CIRGenDeclCXX.cpp b/clang/lib/CIR/CodeGen/CIRGenDeclCXX.cpp index a36f19311316..430550979aa3 100644 --- a/clang/lib/CIR/CodeGen/CIRGenDeclCXX.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenDeclCXX.cpp @@ -54,8 +54,8 @@ void CIRGenModule::emitCXXGlobalVarDeclInitFunc(const VarDecl *D, } void CIRGenFunction::emitCXXGuardedInit(const VarDecl &varDecl, - cir::GlobalOp globalOp, - bool performInit) { + cir::GlobalOp globalOp, + bool performInit) { // If we've been asked to forbid guard variables, emit an error now. This // diagnostic is hard-coded for Darwin's use case; we can find better phrasing // if someone else needs it. @@ -66,8 +66,8 @@ void CIRGenFunction::emitCXXGuardedInit(const VarDecl &varDecl, } void CIRGenFunction::emitCXXGlobalVarDeclInit(const VarDecl &varDecl, - cir::GlobalOp globalOp, - bool performInit) { + cir::GlobalOp globalOp, + bool performInit) { // TODO(CIR): We diverge from CodeGen here via having this in CIRGenModule // instead. This is necessary due to the way we are constructing global inits // at the moment. With LoweringPrepare being moved to CIRGen we should diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp index 47b07c3749c8..6f2ba4248802 100644 --- a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp @@ -757,20 +757,6 @@ mlir::Value CIRGenFunction::createLoad(const VarDecl *VD, const char *Name) { addr.getElementType(), addr.getPointer()); } -void CIRGenFunction::emitCXXGuardedInitBranch(mlir::Value needsInit, - mlir::Block *initBlock, - mlir::Block *noInitBlock, - GuardKind kind, - const VarDecl *varDecl) { - assert((kind == GuardKind::tlsGuard || varDecl) && "no guarded variable"); - - if (MissingFeatures::metaDataNode()) - llvm_unreachable("NYI"); - - builder.createCondBr(getLoc(varDecl->getLocation()), needsInit, initBlock, - noInitBlock); -} - void CIRGenFunction::emitConstructorBody(FunctionArgList &Args) { assert(!cir::MissingFeatures::emitAsanPrologueOrEpilogue()); const auto *Ctor = cast(CurGD.getDecl()); diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.h b/clang/lib/CIR/CodeGen/CIRGenFunction.h index 195d1477170f..c2470fc07204 100644 --- a/clang/lib/CIR/CodeGen/CIRGenFunction.h +++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h @@ -622,25 +622,6 @@ class CIRGenFunction : public CIRGenTypeCache { symbolTable.insert(VD, Addr.getPointer()); } - // buildBlock - Emit the given block \arg bb and set it as the insert point, - // adding a fall-through branch from the current insert block if necessary. It - // is legal to call this function even if there is no current insertion point. - // - // isFinished - If true, indicates that the caller has finished emitting - // branches to the given block and does not expect to emit code into it. This - // means the block can be ignored if it is unreachable. - void buildBlock(mlir::Block *bb, bool isFinished = false); - - /// buildBranch - Emit a branch to the specified basic block from the current - /// insert block, taking care to avoid creation of branches from dummy - /// blocks. It is legal to call this function even if there is no current - /// insertion point. - /// - /// This function clears the current insertion point. The caller should follow - /// calls to this function with calls to Emit*Block prior to generation new - /// code. - void buildBranch(mlir::Location loc, mlir::Block *block); - /// True if an insertion point is defined. If not, this indicates that the /// current code being emitted is unreachable. /// FIXME(cir): we need to inspect this and perhaps use a cleaner mechanism @@ -945,15 +926,10 @@ class CIRGenFunction : public CIRGenTypeCache { /// initialization will be done exactly once, e.g. with a static local /// variable or a static data member of a class template. void emitCXXGuardedInit(const VarDecl &varDecl, cir::GlobalOp globalOp, - bool performInit); + bool performInit); enum class GuardKind { variableGuard, tlsGuard }; - /// Emit a branch to select whether or not to perform guarded initialization. - void emitCXXGuardedInitBranch(mlir::Value needsInit, mlir::Block *initBlock, - mlir::Block *noInitBlock, GuardKind kind, - const VarDecl *varDecl); - /// TODO: Add TBAAAccessInfo Address emitCXXMemberDataPointerAddress( const Expr *E, Address base, mlir::Value memberPtr, @@ -994,18 +970,16 @@ class CIRGenFunction : public CIRGenTypeCache { mlir::Value emitRuntimeCall(mlir::Location loc, cir::FuncOp callee, llvm::ArrayRef args = {}); - mlir::Value emitNounwindRuntimeCall(mlir::Location loc, - cir::FuncOp callee, - ArrayRef args); + mlir::Value emitNounwindRuntimeCall(mlir::Location loc, cir::FuncOp callee, + ArrayRef args); // Emit an invariant.start call for the given memory region. void emitInvariantStart(CharUnits Size); /// emitCXXGlobalVarDeclInit - Create the initializer for a C++ variable with /// global storage. - void emitCXXGlobalVarDeclInit(const VarDecl &varDecl, - cir::GlobalOp globalOp, - bool performInit); + void emitCXXGlobalVarDeclInit(const VarDecl &varDecl, cir::GlobalOp globalOp, + bool performInit); /// Create a check for a function parameter that may potentially be /// declared as non-null. diff --git a/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp b/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp index a1c8e32cd723..2a6d6ebbd028 100644 --- a/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp @@ -196,7 +196,7 @@ class CIRGenItaniumCXXABI : public CIRGenCXXABI { bool Delegating, Address This, QualType ThisTy) override; void emitGuardedInit(CIRGenFunction &cgf, const VarDecl &varDecl, - cir::GlobalOp globalOp, bool performInit) override; + cir::GlobalOp globalOp, bool performInit) override; void registerGlobalDtor(CIRGenFunction &CGF, const VarDecl *D, cir::FuncOp dtor, mlir::Value Addr) override; virtual void emitRethrow(CIRGenFunction &CGF, bool isNoReturn) override; @@ -2651,9 +2651,9 @@ bool CIRGenItaniumCXXABI::isZeroInitializable(const MemberPointerType *MPT) { /// The ARM code here follows the Itanium code closely enough that we just /// special-case it at particular places. void CIRGenItaniumCXXABI::emitGuardedInit(CIRGenFunction &cgf, - const VarDecl &varDecl, - cir::GlobalOp globalOp, - bool performInit) { + const VarDecl &varDecl, + cir::GlobalOp globalOp, + bool performInit) { // Emit the initializer and add a global destructor if appropriate. cgf.CGM.emitCXXGlobalVarDeclInit(&varDecl, globalOp, performInit); diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.h b/clang/lib/CIR/CodeGen/CIRGenModule.h index 30458e45d39f..693b59931c5d 100644 --- a/clang/lib/CIR/CodeGen/CIRGenModule.h +++ b/clang/lib/CIR/CodeGen/CIRGenModule.h @@ -133,7 +133,7 @@ class CIRGenModule : public CIRGenTypeCache { CIRGenModule *createMinimalCGM() { auto *cgm = new CIRGenModule(getMLIRContext(), getASTContext(), - getCodeGenOpts(), getDiags()); + getCodeGenOpts(), getDiags()); } /// ------- diff --git a/clang/lib/CIR/CodeGen/CIRGenStmt.cpp b/clang/lib/CIR/CodeGen/CIRGenStmt.cpp index 7ed6e50041cd..8708eeecb7e5 100644 --- a/clang/lib/CIR/CodeGen/CIRGenStmt.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenStmt.cpp @@ -1157,45 +1157,3 @@ void CIRGenFunction::emitReturnOfRValue(mlir::Location loc, RValue RV, } emitBranchThroughCleanup(loc, ReturnBlock()); } - -void CIRGenFunction::buildBlock(mlir::Block *bb, bool isFinished) { - mlir::Block *curBB = getBuilder().getInsertionBlock(); - - // Fall out of the current block (if necessary). - buildBranch(mlir::UnknownLoc::get(&CGM.getMLIRContext()), bb); - - if (isFinished && bb->use_empty()) { - llvm_unreachable("NYI"); - } - - // MLIR blocks are always added to a Region by default, but based on the use - // case here it might have been the wrong region. - auto curFn = cast(CurFn); - curFn.getBlocks().remove(bb); - - // Place the block after the current block, if possible, or else at the end of - // the function. - if (curBB && curBB->getParent()) - curFn.getBlocks().insert(std::next(curBB->getIterator()), bb); - else - curFn.push_back(bb); - - getBuilder().setInsertionPoint(bb, bb->end()); -} - -void CIRGenFunction::buildBranch(mlir::Location loc, mlir::Block *target) { - // Emit a branch from the current block to the target one if this was a real - // block. If this was just a fall-through block after a terminator, don't emit - // it. - mlir::Block *curBB = getBuilder().getInsertionBlock(); - assert(curBB && "ClangIR should always have an insert block"); - - if (!curBB || (curBB->mightHaveTerminator() && curBB->getTerminator())) { - // If there is no insert point or the previous block is already terminated, - // don't touch it. - } else { - builder.createBr(loc, target); - } - - getBuilder().clearInsertionPoint(); -} From a562267ee1fa1849bf2892a05e86a7679b56ed1b Mon Sep 17 00:00:00 2001 From: Nathan Lanza Date: Wed, 11 Dec 2024 23:44:45 -0500 Subject: [PATCH 06/10] cleanup Created using spr 1.3.5 --- clang/lib/CIR/CodeGen/CIRGenCall.cpp | 8 ----- clang/lib/CIR/CodeGen/CIRGenFunction.h | 3 -- clang/lib/CIR/CodeGen/LoweringPrepare.cpp | 40 +++++++---------------- 3 files changed, 11 insertions(+), 40 deletions(-) diff --git a/clang/lib/CIR/CodeGen/CIRGenCall.cpp b/clang/lib/CIR/CodeGen/CIRGenCall.cpp index 9d416e87e1a4..0c02a5b2ce95 100644 --- a/clang/lib/CIR/CodeGen/CIRGenCall.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenCall.cpp @@ -942,14 +942,6 @@ mlir::Value CIRGenFunction::emitRuntimeCall(mlir::Location loc, return call->getResult(0); } -mlir::Value -CIRGenFunction::emitNounwindRuntimeCall(mlir::Location loc, cir::FuncOp callee, - ArrayRef args) { - mlir::Value call = emitRuntimeCall(loc, callee, args); - assert(!cir::MissingFeatures::noUnwindAttribute()); - return call; -} - void CIRGenFunction::emitCallArg(CallArgList &args, const Expr *E, QualType type) { // TODO: Add the DisableDebugLocationUpdates helper diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.h b/clang/lib/CIR/CodeGen/CIRGenFunction.h index c2470fc07204..bc1a6f2b0990 100644 --- a/clang/lib/CIR/CodeGen/CIRGenFunction.h +++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h @@ -970,9 +970,6 @@ class CIRGenFunction : public CIRGenTypeCache { mlir::Value emitRuntimeCall(mlir::Location loc, cir::FuncOp callee, llvm::ArrayRef args = {}); - mlir::Value emitNounwindRuntimeCall(mlir::Location loc, cir::FuncOp callee, - ArrayRef args); - // Emit an invariant.start call for the given memory region. void emitInvariantStart(CharUnits Size); diff --git a/clang/lib/CIR/CodeGen/LoweringPrepare.cpp b/clang/lib/CIR/CodeGen/LoweringPrepare.cpp index 42ff8a5976d0..70987fea80af 100644 --- a/clang/lib/CIR/CodeGen/LoweringPrepare.cpp +++ b/clang/lib/CIR/CodeGen/LoweringPrepare.cpp @@ -8,9 +8,7 @@ #include "Address.h" #include "CIRGenBuilder.h" -#include "CIRGenCXXABI.h" #include "CIRGenModule.h" -#include "EHScopeStack.h" #include "mlir/IR/BuiltinAttributes.h" #include "mlir/IR/Region.h" @@ -1008,20 +1006,10 @@ static cir::FuncOp createCIRFunction(clang::CIRGen::CIRGenBuilderTy &builder, /// /// If D is non-null, it specifies a decl that corresponded to this. This is /// used to set the attributes on the function when it is first created. -static cir::FuncOp -getOrCreateCIRFunction(clang::ASTContext &astContext, - mlir::MLIRContext &mlirContext, mlir::ModuleOp theModule, - clang::CIRGen::CIRGenBuilderTy &builder, - StringRef mangledName, mlir::Type type) { - mlir::ArrayAttr extraAttrs = {}; - clang::CIRGen::ForDefinition_t isForDefinition = - clang::CIRGen::NotForDefinition; - clang::GlobalDecl globalDecl = clang::GlobalDecl(); - bool dontDefer = false; - bool forVTable = false; - - const auto *decl = globalDecl.getDecl(); - +static cir::FuncOp getOrCreateCIRFunctionForRuntimeFunction( + clang::ASTContext &astContext, mlir::MLIRContext &mlirContext, + mlir::ModuleOp theModule, clang::CIRGen::CIRGenBuilderTy &builder, + StringRef mangledName, mlir::Type type) { // Lookup the entry, lazily creating it if necessary. mlir::Operation *entry = getGlobalValue(theModule, mangledName); if (entry) { @@ -1035,13 +1023,6 @@ getOrCreateCIRFunction(clang::ASTContext &astContext, if (fn && fn.getFunctionType() == type) { return fn; } - - if (!isForDefinition) { - return fn; - } - - // TODO: clang checks here if this is a llvm::GlobalAlias... how will we - // support this? } // This function doesn't have a complete type (for example, the return type is @@ -1103,8 +1084,8 @@ static cir::FuncOp createRuntimeFunction( if (local) llvm_unreachable("NYI"); - auto entry = getOrCreateCIRFunction(astContext, mlirContext, theModule, - builder, name, type); + auto entry = getOrCreateCIRFunctionForRuntimeFunction( + astContext, mlirContext, theModule, builder, name, type); // Traditional codegen checks for a valid dyn_cast llvm::Function for `entry`, // no testcase that cover this path just yet though. @@ -1302,7 +1283,6 @@ void LoweringPreparePass::handleStaticLocal(GlobalOp globalOp, // libcalls. unsigned maxInlineWidthInbits = astCtx->getTargetInfo().getMaxAtomicInlineWidth(); - // "init.end" block auto initBlock = [&]() { // CIR: Move the initializer from the globalOp's ctor region into the @@ -1323,7 +1303,9 @@ void LoweringPreparePass::handleStaticLocal(GlobalOp globalOp, ctorRegion.getBlocks().clear(); if (threadsafe) { - // NOTE(CIR): CodeGen clears the above pushed CallGuardAbort here. + // NOTE(CIR): CodeGen clears the above pushed CallGuardAbort here and thus + // the __guard_abort gets inserted. We'll have to figure out how to + // properly handle this when supporting static locals with exceptions. // Call __cxa_guard_release. This cannot throw. emitNounwindRuntimeCall(*builder, globalOp->getLoc(), @@ -1353,7 +1335,7 @@ void LoweringPreparePass::handleStaticLocal(GlobalOp globalOp, // restart initialization. // Variables used when coping with thread-safe statics and exceptions. - auto thenStmt = [&]() { + auto guardAcquireBlock = [&]() { if (threadsafe) { auto loc = globalOp->getLoc(); // Call __cxa_guard_acquire. @@ -1426,7 +1408,7 @@ void LoweringPreparePass::handleStaticLocal(GlobalOp globalOp, [&](mlir::OpBuilder &, mlir::Location) { if (MissingFeatures::metaDataNode()) llvm_unreachable("NYI"); - thenStmt(); + guardAcquireBlock(); builder->createYield(getGlobalOp->getLoc()); }); } From a04387a68dd75935512eb77199e1654ad313f0ec Mon Sep 17 00:00:00 2001 From: Nathan Lanza Date: Wed, 11 Dec 2024 23:52:09 -0500 Subject: [PATCH 07/10] simplify getGlobal walk Created using spr 1.3.5 --- clang/lib/CIR/CodeGen/LoweringPrepare.cpp | 19 ++++--------------- 1 file changed, 4 insertions(+), 15 deletions(-) diff --git a/clang/lib/CIR/CodeGen/LoweringPrepare.cpp b/clang/lib/CIR/CodeGen/LoweringPrepare.cpp index 70987fea80af..a74ab4aa06ab 100644 --- a/clang/lib/CIR/CodeGen/LoweringPrepare.cpp +++ b/clang/lib/CIR/CodeGen/LoweringPrepare.cpp @@ -197,9 +197,6 @@ struct LoweringPreparePass : public LoweringPrepareBase { llvm::DenseMap staticLocalDeclGuardMap; - llvm::DenseMap - namesToStaticLocalGetGlobalOps; - llvm::DenseMap namesToStaticLocalGlobalOps; }; } // namespace @@ -921,14 +918,15 @@ void LoweringPreparePass::lowerGetGlobalOp(GetGlobalOp getGlobalOp) { if (!getGlobalOp.getStaticLocal()) return; - namesToStaticLocalGetGlobalOps.insert({getGlobalOp.getName(), getGlobalOp}); + auto globalOp = mlir::cast( + mlir::SymbolTable::lookupSymbolIn(theModule, getGlobalOp.getName())); + + handleStaticLocal(globalOp, getGlobalOp); } void LoweringPreparePass::lowerGlobalOp(GlobalOp globalOp) { if (!globalOp.getStaticLocal()) handleGlobalOpCtorDtor(globalOp); - - namesToStaticLocalGlobalOps.insert({globalOp.getName(), globalOp}); } static cir::GlobalOp createGuardGlobalOp(::cir::CIRBaseBuilderTy &builder, @@ -1671,14 +1669,6 @@ void LoweringPreparePass::buildGlobalAnnotationValues() { theModule.getContext(), annotationValueArray)); } -void LoweringPreparePass::buildStaticLocals() { - for (auto &[name, getGlobalOp] : namesToStaticLocalGetGlobalOps) { - cir::GlobalOp globalOp = namesToStaticLocalGlobalOps[name]; - - handleStaticLocal(globalOp, getGlobalOp); - } -} - void LoweringPreparePass::runOnOp(Operation *op) { if (auto unary = dyn_cast(op)) { lowerUnaryOp(unary); @@ -1742,7 +1732,6 @@ void LoweringPreparePass::runOnOperation() { for (auto *o : opsToTransform) runOnOp(o); - buildStaticLocals(); buildCXXGlobalInitFunc(); buildGlobalCtorDtorList(); buildGlobalAnnotationValues(); From 8bc8a54df247a07010e19af7ac25c171e57b2499 Mon Sep 17 00:00:00 2001 From: Nathan Lanza Date: Thu, 12 Dec 2024 00:51:01 -0500 Subject: [PATCH 08/10] fix alignment and and Created using spr 1.3.5 --- clang/lib/CIR/CodeGen/LoweringPrepare.cpp | 17 ++++---- clang/test/CIR/CodeGen/static-local.cpp | 49 ++++++++++++----------- 2 files changed, 36 insertions(+), 30 deletions(-) diff --git a/clang/lib/CIR/CodeGen/LoweringPrepare.cpp b/clang/lib/CIR/CodeGen/LoweringPrepare.cpp index a74ab4aa06ab..5206ce340e9b 100644 --- a/clang/lib/CIR/CodeGen/LoweringPrepare.cpp +++ b/clang/lib/CIR/CodeGen/LoweringPrepare.cpp @@ -8,7 +8,6 @@ #include "Address.h" #include "CIRGenBuilder.h" -#include "CIRGenModule.h" #include "mlir/IR/BuiltinAttributes.h" #include "mlir/IR/Region.h" @@ -110,9 +109,6 @@ struct LoweringPreparePass : public LoweringPrepareBase { /// Build attribute of global annotation values void buildGlobalAnnotationValues(); - /// fix this - void buildStaticLocals(); - cir::GlobalOp getStaticLocalDeclGuardAddress(cir::ASTVarDeclInterface varDecl) { return staticLocalDeclGuardMap[varDecl]; @@ -1362,8 +1358,9 @@ void LoweringPreparePass::handleStaticLocal(GlobalOp globalOp, if (!threadsafe || maxInlineWidthInbits) { // Load the first byte of the guard variable. - mlir::Value load = builder->createLoad( - globalOp.getLoc(), guardAddr.withElementType(builder->getSInt8Ty())); + mlir::Value load = builder->createAlignedLoad( + getGlobalOp.getLoc(), builder->getSInt8Ty(), guardAddr.getPointer(), + guardAddr.getAlignment()); // Itanium ABI: // An implementation supporting thread-safety on multiprocesor systems @@ -1397,7 +1394,13 @@ void LoweringPreparePass::handleStaticLocal(GlobalOp globalOp, // the variable is not initialized and 1 when it is. if (MissingFeatures::useARMGuardVarABI() && !useInt8GuardVariable) llvm_unreachable("NYI"); - mlir::Value value = load; + mlir::Value constOne = builder->getConstInt( + getGlobalOp->getLoc(), llvm::APSInt(llvm::APInt(8, 1), + /*isUnsigned=*/false)); + mlir::Value value = + (!cir::MissingFeatures::useARMGuardVarABI() && !useInt8GuardVariable) + ? builder->createAnd(load, constOne) + : load; mlir::Value needsInit = builder->createIsNull(globalOp.getLoc(), value, "guard.uninitialized"); diff --git a/clang/test/CIR/CodeGen/static-local.cpp b/clang/test/CIR/CodeGen/static-local.cpp index 92bbf04b9f11..4d2005098dec 100644 --- a/clang/test/CIR/CodeGen/static-local.cpp +++ b/clang/test/CIR/CodeGen/static-local.cpp @@ -32,17 +32,19 @@ void foo() { // CIR-NEXT: %0 = cir.get_global @_ZZ3foovE3val : !cir.ptr {static_local} // CIR-NEXT: %1 = cir.get_global @_ZGVZ3foovE3val : !cir.ptr // CIR-NEXT: %2 = cir.cast(bitcast, %1 : !cir.ptr), !cir.ptr -// CIR-NEXT: %3 = cir.load atomic(acquire) %2 : !cir.ptr, !s8i -// CIR-NEXT: %4 = cir.const #cir.int<0> : !s8i -// CIR-NEXT: %5 = cir.cmp(eq, %3, %4) : !s8i, !cir.bool -// CIR-NEXT: cir.if %5 { -// CIR-NEXT: %6 = cir.call @__cxa_guard_acquire(%1) : (!cir.ptr) -> !s32i -// CIR-NEXT: %7 = cir.const #cir.int<0> : !s32i -// CIR-NEXT: %8 = cir.cmp(ne, %6, %7) : !s32i, !cir.bool -// CIR-NEXT: cir.if %8 { -// CIR-NEXT: %9 = cir.get_global @_ZZ3foovE3val : !cir.ptr -// CIR-NEXT: %10 = cir.call @_Z3fnAv() : () -> !s32i -// CIR-NEXT: cir.store %10, %9 : !s32i, !cir.ptr +// CIR-NEXT: %3 = cir.load align(8) atomic(acquire) %2 : !cir.ptr, !s8i +// CIR-NEXT: %4 = cir.const #cir.int<1> : !s8i +// CIR-NEXT: %5 = cir.binop(and, %3, %4) : !s8i +// CIR-NEXT: %6 = cir.const #cir.int<0> : !s8i +// CIR-NEXT: %7 = cir.cmp(eq, %5, %6) : !s8i, !cir.bool +// CIR-NEXT: cir.if %7 { +// CIR-NEXT: %8 = cir.call @__cxa_guard_acquire(%1) : (!cir.ptr) -> !s32i +// CIR-NEXT: %9 = cir.const #cir.int<0> : !s32i +// CIR-NEXT: %10 = cir.cmp(ne, %8, %9) : !s32i, !cir.bool +// CIR-NEXT: cir.if %10 { +// CIR-NEXT: %11 = cir.get_global @_ZZ3foovE3val : !cir.ptr +// CIR-NEXT: %12 = cir.call @_Z3fnAv() : () -> !s32i +// CIR-NEXT: cir.store %12, %11 : !s32i, !cir.ptr // CIR-NEXT: cir.call @__cxa_guard_release(%1) : (!cir.ptr) -> () // CIR-NEXT: } // CIR-NEXT: } @@ -56,20 +58,21 @@ void foo() { // LLVM: declare i32 @_Z3fnAv() // LLVM: define dso_local void @_Z3foov() -// LLVM-NEXT: %1 = load atomic i8, ptr @_ZGVZ3foovE3val acquire, align 1 -// LLVM-NEXT: %2 = icmp eq i8 %1, 0 -// LLVM-NEXT: br i1 %2, label %3, label %9 -// LLVM-DAG: 3: -// LLVM-NEXT: %4 = call i32 @__cxa_guard_acquire(ptr @_ZGVZ3foovE3val) -// LLVM-NEXT: %5 = icmp ne i32 %4, 0 -// LLVM-NEXT: br i1 %5, label %6, label %8 -// LLVM-DAG: 6: -// LLVM-NEXT: %7 = call i32 @_Z3fnAv() -// LLVM-NEXT: store i32 %7, ptr @_ZZ3foovE3val, align 4 +// LLVM-NEXT: %1 = load atomic i8, ptr @_ZGVZ3foovE3val acquire, align 8 +// LLVM-NEXT: %2 = and i8 %1, 1 +// LLVM-NEXT: %3 = icmp eq i8 %2, 0 +// LLVM-NEXT: br i1 %3, label %4, label %10 +// LLVM-DAG: 4: +// LLVM-NEXT: %5 = call i32 @__cxa_guard_acquire(ptr @_ZGVZ3foovE3val) +// LLVM-NEXT: %6 = icmp ne i32 %5, 0 +// LLVM-NEXT: br i1 %6, label %7, label %9 +// LLVM-DAG: 7: +// LLVM-NEXT: %8 = call i32 @_Z3fnAv() +// LLVM-NEXT: store i32 %8, ptr @_ZZ3foovE3val, align 4 // LLVM-NEXT: call void @__cxa_guard_release(ptr @_ZGVZ3foovE3val) -// LLVM-NEXT: br label %8 -// LLVM-DAG: 8: // LLVM-NEXT: br label %9 // LLVM-DAG: 9: +// LLVM-NEXT: br label %10 +// LLVM-DAG: 10: // LLVM-NEXT: ret void // LLVM-NEXT: } From 9cebca915d2abb04ac2daae44fad3d7ff84e03bc Mon Sep 17 00:00:00 2001 From: Nathan Lanza Date: Thu, 12 Dec 2024 00:57:02 -0500 Subject: [PATCH 09/10] remove bad test Created using spr 1.3.5 --- clang/test/CIR/idk.cpp | 17 ----------------- 1 file changed, 17 deletions(-) delete mode 100644 clang/test/CIR/idk.cpp diff --git a/clang/test/CIR/idk.cpp b/clang/test/CIR/idk.cpp deleted file mode 100644 index a8a49a4df93c..000000000000 --- a/clang/test/CIR/idk.cpp +++ /dev/null @@ -1,17 +0,0 @@ -class a { -}; -struct b; -template class ref {}; -class c { - a mA(b *); - ref mB(b *, long); -}; -typedef struct d *tdA; - - -tdA fnA(b *, a, char *, char *); - -ref c::mB(b *arg, long) { - static tdA val = fnA(arg, mA(arg), "", ""); -} - From c78abce5afe3222a2e6c55a0cd2562f05e5b214c Mon Sep 17 00:00:00 2001 From: Nathan Lanza Date: Thu, 12 Dec 2024 01:10:34 -0500 Subject: [PATCH 10/10] fix non-trivials and update commit message Created using spr 1.3.5 --- clang/lib/CIR/CodeGen/LoweringPrepare.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/clang/lib/CIR/CodeGen/LoweringPrepare.cpp b/clang/lib/CIR/CodeGen/LoweringPrepare.cpp index 5206ce340e9b..7d3f7f1f0200 100644 --- a/clang/lib/CIR/CodeGen/LoweringPrepare.cpp +++ b/clang/lib/CIR/CodeGen/LoweringPrepare.cpp @@ -1414,6 +1414,7 @@ void LoweringPreparePass::handleStaticLocal(GlobalOp globalOp, }); } + builder->setInsertionPointToEnd(getGlobalOpBlock); builder->insert(ret); }