-
Notifications
You must be signed in to change notification settings - Fork 15.1k
[CIR] Upstream support for generating global ctor regions #161298
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -15,10 +15,72 @@ | |||||
|
|
||||||
| #include "clang/AST/GlobalDecl.h" | ||||||
| #include "clang/CIR/MissingFeatures.h" | ||||||
| #include "llvm/Support/SaveAndRestore.h" | ||||||
|
|
||||||
| using namespace clang; | ||||||
| using namespace clang::CIRGen; | ||||||
|
|
||||||
| static void emitDeclInit(CIRGenFunction &cgf, const VarDecl *varDecl, | ||||||
| Address declPtr) { | ||||||
| assert((varDecl->hasGlobalStorage() || | ||||||
| (varDecl->hasLocalStorage() && | ||||||
| cgf.getContext().getLangOpts().OpenCLCPlusPlus)) && | ||||||
| "VarDecl must have global or local (in the case of OpenCL) storage!"); | ||||||
| assert(!varDecl->getType()->isReferenceType() && | ||||||
| "Should not call emitDeclInit on a reference!"); | ||||||
|
|
||||||
| QualType type = varDecl->getType(); | ||||||
| LValue lv = cgf.makeAddrLValue(declPtr, type); | ||||||
|
|
||||||
| const Expr *init = varDecl->getInit(); | ||||||
| switch (CIRGenFunction::getEvaluationKind(type)) { | ||||||
| case cir::TEK_Scalar: | ||||||
| assert(!cir::MissingFeatures::objCGC()); | ||||||
| cgf.emitScalarInit(init, cgf.getLoc(varDecl->getLocation()), lv, false); | ||||||
| return; | ||||||
| case cir::TEK_Complex: | ||||||
| cgf.cgm.errorNYI(varDecl->getSourceRange(), "complex global initializer"); | ||||||
| return; | ||||||
| case cir::TEK_Aggregate: | ||||||
| assert(!cir::MissingFeatures::aggValueSlotGC()); | ||||||
| cgf.emitAggExpr(init, | ||||||
| AggValueSlot::forLValue(lv, AggValueSlot::IsDestructed, | ||||||
| AggValueSlot::IsNotAliased, | ||||||
| AggValueSlot::DoesNotOverlap)); | ||||||
| return; | ||||||
| } | ||||||
| llvm_unreachable("bad evaluation kind"); | ||||||
| } | ||||||
|
|
||||||
| static void emitDeclDestroy(CIRGenFunction &cgf, const VarDecl *vd) { | ||||||
| // Honor __attribute__((no_destroy)) and bail instead of attempting | ||||||
| // to emit a reference to a possibly nonexistent destructor, which | ||||||
| // in turn can cause a crash. This will result in a global constructor | ||||||
| // that isn't balanced out by a destructor call as intended by the | ||||||
| // attribute. This also checks for -fno-c++-static-destructors and | ||||||
| // bails even if the attribute is not present. | ||||||
| QualType::DestructionKind dtorKind = vd->needsDestruction(cgf.getContext()); | ||||||
|
|
||||||
| // FIXME: __attribute__((cleanup)) ? | ||||||
|
|
||||||
| switch (dtorKind) { | ||||||
| case QualType::DK_none: | ||||||
| return; | ||||||
|
|
||||||
| case QualType::DK_cxx_destructor: | ||||||
| break; | ||||||
|
|
||||||
| case QualType::DK_objc_strong_lifetime: | ||||||
| case QualType::DK_objc_weak_lifetime: | ||||||
| case QualType::DK_nontrivial_c_struct: | ||||||
| // We don't care about releasing objects during process teardown. | ||||||
| assert(!vd->getTLSKind() && "should have rejected this"); | ||||||
| return; | ||||||
| } | ||||||
|
|
||||||
| cgf.cgm.errorNYI(vd->getSourceRange(), "global with destructor"); | ||||||
| } | ||||||
|
|
||||||
| cir::FuncOp CIRGenModule::codegenCXXStructor(GlobalDecl gd) { | ||||||
| const CIRGenFunctionInfo &fnInfo = | ||||||
| getTypes().arrangeCXXStructorDeclaration(gd); | ||||||
|
|
@@ -38,3 +100,93 @@ cir::FuncOp CIRGenModule::codegenCXXStructor(GlobalDecl gd) { | |||||
| assert(!cir::MissingFeatures::opFuncAttributesForDefinition()); | ||||||
| return fn; | ||||||
| } | ||||||
|
|
||||||
| // Global variables requiring non-trivial initialization are handled | ||||||
| // differently in CIR than in classic codegen. Classic codegen emits | ||||||
| // a global init function (__cxx_global_var_init) and inserts | ||||||
| // initialization for each global there. In CIR, we attach a ctor | ||||||
| // region to the global variable and insert the initialization code | ||||||
| // into the ctor region. This will be moved into the | ||||||
| // __cxx_global_var_init function during the LoweringPrepare pass. | ||||||
| void CIRGenModule::emitCXXGlobalVarDeclInit(const VarDecl *varDecl, | ||||||
| cir::GlobalOp addr, | ||||||
| bool performInit) { | ||||||
| QualType ty = varDecl->getType(); | ||||||
|
|
||||||
| // TODO: handle address space | ||||||
| // The address space of a static local variable (DeclPtr) may be different | ||||||
| // from the address space of the "this" argument of the constructor. In that | ||||||
| // case, we need an addrspacecast before calling the constructor. | ||||||
| // | ||||||
| // struct StructWithCtor { | ||||||
| // __device__ StructWithCtor() {...} | ||||||
| // }; | ||||||
| // __device__ void foo() { | ||||||
| // __shared__ StructWithCtor s; | ||||||
| // ... | ||||||
| // } | ||||||
| // | ||||||
| // For example, in the above CUDA code, the static local variable s has a | ||||||
| // "shared" address space qualifier, but the constructor of StructWithCtor | ||||||
| // expects "this" in the "generic" address space. | ||||||
| assert(!cir::MissingFeatures::addressSpace()); | ||||||
|
|
||||||
| // Create a CIRGenFunction to emit the initializer. While this isn't a true | ||||||
| // function, the handling works the same way. | ||||||
| CIRGenFunction cgf{*this, builder, true}; | ||||||
| llvm::SaveAndRestore<CIRGenFunction *> savedCGF(curCGF, &cgf); | ||||||
| curCGF->curFn = addr; | ||||||
|
|
||||||
| CIRGenFunction::SourceLocRAIIObject fnLoc{cgf, | ||||||
| getLoc(varDecl->getLocation())}; | ||||||
|
|
||||||
| assert(!cir::MissingFeatures::astVarDeclInterface()); | ||||||
|
|
||||||
| if (!ty->isReferenceType()) { | ||||||
| assert(!cir::MissingFeatures::openMP()); | ||||||
|
|
||||||
| bool needsDtor = varDecl->needsDestruction(getASTContext()) == | ||||||
| QualType::DK_cxx_destructor; | ||||||
| // PerformInit, constant store invariant / destroy handled below. | ||||||
| if (performInit) { | ||||||
| mlir::OpBuilder::InsertionGuard guard(builder); | ||||||
| auto *block = builder.createBlock(&addr.getCtorRegion()); | ||||||
| CIRGenFunction::LexicalScope lexScope{*curCGF, addr.getLoc(), | ||||||
| builder.getInsertionBlock()}; | ||||||
| lexScope.setAsGlobalInit(); | ||||||
|
|
||||||
| builder.setInsertionPointToStart(block); | ||||||
| Address declAddr(getAddrOfGlobalVar(varDecl), | ||||||
| getASTContext().getDeclAlign(varDecl)); | ||||||
|
||||||
| emitDeclInit(cgf, varDecl, declAddr); | ||||||
| builder.setInsertionPointToEnd(block); | ||||||
| builder.create<cir::YieldOp>(addr->getLoc()); | ||||||
|
||||||
| builder.create<cir::YieldOp>(addr->getLoc()); | |
| cir::YieldOp::create(builder, addr->getLoc()); |
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh I see now that it uses different region for insertion.
Maybe I would suggest to set insertion inside emitDeclDestroy and emitDeclInit as it semantically needs to always set the correct insertion point for the global?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I think that makes sense. Near as I can tell, if we don't emit any dtor code we'll be creating an entry block and then erasing it, and I guess our canonicalization pass erases the region. It would be better to just not create it in the first place, and as you say, having the insertion point set where it's used will be better.
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| builder.create<cir::YieldOp>(addr->getLoc()); | |
| cir::YieldOp::create(builder, addr->getLoc()); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,28 @@ | ||
| //===----------------------------------------------------------------------===// | ||
| // | ||
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | ||
| // See https://llvm.org/LICENSE.txt for license information. | ||
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | ||
| // | ||
| //===----------------------------------------------------------------------===// | ||
| // | ||
| // This contains code dealing with code generation of C++ declarations | ||
| // | ||
| //===----------------------------------------------------------------------===// | ||
|
|
||
| #include "CIRGenModule.h" | ||
| #include "clang/AST/Attr.h" | ||
| #include "clang/Basic/LangOptions.h" | ||
|
|
||
| using namespace clang; | ||
| using namespace clang::CIRGen; | ||
|
|
||
| void CIRGenModule::emitCXXGlobalVarDeclInitFunc(const VarDecl *vd, | ||
| cir::GlobalOp addr, | ||
| bool performInit) { | ||
| assert(!cir::MissingFeatures::cudaSupport()); | ||
|
|
||
| assert(!cir::MissingFeatures::deferredCXXGlobalInit()); | ||
|
|
||
| emitCXXGlobalVarDeclInit(vd, addr, performInit); | ||
| } |
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,17 @@ | ||||||
| // RUN: %clang_cc1 -std=c++17 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o %t.cir | ||||||
| // RUN: FileCheck --input-file=%t.cir %s --check-prefix=CIR | ||||||
|
|
||||||
| // Note: The CIR generated fro this test isn't ready for lowering to LLVM yet. | ||||||
|
||||||
| // Note: The CIR generated fro this test isn't ready for lowering to LLVM yet. | |
| // Note: The CIR generated from this test isn't ready for lowering to LLVM yet. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OGCG takes here
AggValueSlot::DoesNotNeedGCBarrieras well.Any reason it is missing?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We haven't upstreamed any of the support for GC yet. That's what the
aggValueSlotGCassertion above is tracking.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah sorry, I overlooked the assertion.