diff --git a/clang/lib/CIR/CodeGen/CIRGenCXX.cpp b/clang/lib/CIR/CodeGen/CIRGenCXX.cpp index e81ff16fd659..543ba8b7cfda 100644 --- a/clang/lib/CIR/CodeGen/CIRGenCXX.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenCXX.cpp @@ -306,51 +306,120 @@ void CIRGenFunction::buildInvariantStart([[maybe_unused]] CharUnits Size) { assert(!MissingFeatures::createInvariantIntrinsic()); } -void CIRGenModule::codegenGlobalInitCxxStructor(const VarDecl *D, - mlir::cir::GlobalOp Addr, - bool NeedsCtor, bool NeedsDtor, - bool isCstStorage) { - assert(D && " Expected a global declaration!"); - CIRGenFunction CGF{*this, builder, true}; - CurCGF = &CGF; - CurCGF->CurFn = Addr; - Addr.setAstAttr(mlir::cir::ASTVarDeclAttr::get(builder.getContext(), D)); +void CIRGenModule::buildCXXGlobalVarDeclInit(const VarDecl *varDecl, + mlir::cir::GlobalOp addr, + bool performInit) { + const Expr *init = varDecl->getInit(); + 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(!MissingFeatures::addressSpace()); + + if (getLangOpts().OpenMP && !getLangOpts().OpenMPSimd && + varDecl->hasAttr()) { + llvm_unreachable("NYI"); + } - if (NeedsCtor) { - mlir::OpBuilder::InsertionGuard guard(builder); - auto block = builder.createBlock(&Addr.getCtorRegion()); - CIRGenFunction::LexicalScope lexScope{*CurCGF, Addr.getLoc(), - builder.getInsertionBlock()}; - lexScope.setAsGlobalInit(); + assert(varDecl && " Expected a global declaration!"); + CIRGenFunction cgf{*this, builder, true}; + CurCGF = &cgf; + CurCGF->CurFn = addr; - builder.setInsertionPointToStart(block); - Address DeclAddr(getAddrOfGlobalVar(D), getASTContext().getDeclAlign(D)); - buildDeclInit(CGF, D, DeclAddr); - builder.setInsertionPointToEnd(block); - builder.create(Addr->getLoc()); - } + CIRGenFunction::SourceLocRAIIObject fnLoc{cgf, + getLoc(varDecl->getLocation())}; - if (isCstStorage) { - // TODO: this leads to a missing feature in the moment, probably also need a - // LexicalScope to be inserted here. - buildDeclInvariant(CGF, D); - } else { - // If not constant storage we'll emit this regardless of NeedsDtor value. + addr.setAstAttr( + mlir::cir::ASTVarDeclAttr::get(builder.getContext(), varDecl)); + + if (ty->isReferenceType()) { mlir::OpBuilder::InsertionGuard guard(builder); - auto block = builder.createBlock(&Addr.getDtorRegion()); - CIRGenFunction::LexicalScope lexScope{*CurCGF, Addr.getLoc(), + auto *block = builder.createBlock(&addr.getCtorRegion()); + CIRGenFunction::LexicalScope lexScope{*CurCGF, addr.getLoc(), builder.getInsertionBlock()}; lexScope.setAsGlobalInit(); - builder.setInsertionPointToStart(block); - buildDeclDestroy(CGF, D); + auto getGlobal = builder.createGetGlobal(addr); + + Address declAddr(getGlobal, getGlobal.getType(), + getASTContext().getDeclAlign(varDecl)); + assert(performInit && "cannot have constant initializer which needs " + "destruction for reference"); + RValue rv = cgf.buildReferenceBindingToExpr(init); + { + mlir::OpBuilder::InsertionGuard guard(builder); + mlir::Operation *rvalueDefOp = rv.getScalarVal().getDefiningOp(); + if (rvalueDefOp && rvalueDefOp->getBlock()) { + mlir::Block *rvalSrcBlock = rvalueDefOp->getBlock(); + if (!rvalSrcBlock->empty() && + isa(rvalSrcBlock->back())) { + auto &front = rvalSrcBlock->front(); + getGlobal.getDefiningOp()->moveBefore(&front); + auto yield = cast(rvalSrcBlock->back()); + builder.setInsertionPoint(yield); + } + } + cgf.buildStoreOfScalar(rv.getScalarVal(), declAddr, false, ty); + } builder.setInsertionPointToEnd(block); - if (block->empty()) { - block->erase(); - // Don't confuse lexical cleanup. - builder.clearInsertionPoint(); - } else - builder.create(Addr->getLoc()); + builder.create(addr->getLoc()); + } else { + bool needsDtor = varDecl->needsDestruction(getASTContext()) == + QualType::DK_cxx_destructor; + // PerformInit, constant store invariant / destroy handled below. + bool isConstantStorage = + varDecl->getType().isConstantStorage(getASTContext(), true, !needsDtor); + 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)); + buildDeclInit(cgf, varDecl, declAddr); + builder.setInsertionPointToEnd(block); + builder.create(addr->getLoc()); + } + + if (isConstantStorage) { + // TODO: this leads to a missing feature in the moment, probably also need + // a LexicalScope to be inserted here. + buildDeclInvariant(cgf, varDecl); + } else { + // If not constant storage we'll emit this regardless of NeedsDtor value. + mlir::OpBuilder::InsertionGuard guard(builder); + auto *block = builder.createBlock(&addr.getDtorRegion()); + CIRGenFunction::LexicalScope lexScope{*CurCGF, addr.getLoc(), + builder.getInsertionBlock()}; + lexScope.setAsGlobalInit(); + + builder.setInsertionPointToStart(block); + buildDeclDestroy(cgf, varDecl); + builder.setInsertionPointToEnd(block); + if (block->empty()) { + block->erase(); + // Don't confuse lexical cleanup. + builder.clearInsertionPoint(); + } else + builder.create(addr->getLoc()); + } } CurCGF = nullptr; diff --git a/clang/lib/CIR/CodeGen/CIRGenCXXABI.h b/clang/lib/CIR/CodeGen/CIRGenCXXABI.h index 6c67e849a4c4..7b0acae564b0 100644 --- a/clang/lib/CIR/CodeGen/CIRGenCXXABI.h +++ b/clang/lib/CIR/CodeGen/CIRGenCXXABI.h @@ -170,8 +170,7 @@ class CIRGenCXXABI { /// \param Dtor - a function taking a single pointer argument /// \param Addr - a pointer to pass to the destructor function. virtual void registerGlobalDtor(CIRGenFunction &CGF, const VarDecl *D, - mlir::cir::FuncOp dtor, - mlir::Attribute Addr) = 0; + mlir::cir::FuncOp dtor, mlir::Value Addr) = 0; virtual size_t getSrcArgforCopyCtor(const CXXConstructorDecl *, FunctionArgList &Args) const = 0; diff --git a/clang/lib/CIR/CodeGen/CIRGenDeclCXX.cpp b/clang/lib/CIR/CodeGen/CIRGenDeclCXX.cpp index 682eddbe9581..d50866853377 100644 --- a/clang/lib/CIR/CodeGen/CIRGenDeclCXX.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenDeclCXX.cpp @@ -44,8 +44,6 @@ void CIRGenModule::buildCXXGlobalVarDeclInitFunc(const VarDecl *D, D->hasAttr())) return; - assert(!getLangOpts().OpenMP && "OpenMP global var init not implemented"); - // Check if we've already initialized this decl. auto I = DelayedCXXInitPosition.find(D); if (I != DelayedCXXInitPosition.end() && I->second == ~0U) @@ -53,46 +51,3 @@ void CIRGenModule::buildCXXGlobalVarDeclInitFunc(const VarDecl *D, buildCXXGlobalVarDeclInit(D, Addr, PerformInit); } - -void CIRGenModule::buildCXXGlobalVarDeclInit(const VarDecl *D, - mlir::cir::GlobalOp Addr, - bool PerformInit) { - QualType T = D->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(!MissingFeatures::addressSpace()); - - if (!T->isReferenceType()) { - if (getLangOpts().OpenMP && !getLangOpts().OpenMPSimd && - D->hasAttr()) { - llvm_unreachable("NYI"); - } - bool NeedsDtor = - D->needsDestruction(getASTContext()) == QualType::DK_cxx_destructor; - // PerformInit, constant store invariant / destroy handled below. - bool isCstStorage = - D->getType().isConstantStorage(getASTContext(), true, !NeedsDtor); - codegenGlobalInitCxxStructor(D, Addr, PerformInit, NeedsDtor, isCstStorage); - return; - } - - assert(PerformInit && "cannot have constant initializer which needs " - "destruction for reference"); - // TODO(cir): buildReferenceBindingToExpr - llvm_unreachable("NYI"); -} diff --git a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp index db2d82c00dfb..5cf2e27b0dde 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp @@ -20,6 +20,7 @@ #include "EHScopeStack.h" #include "TargetInfo.h" +#include "clang/AST/Decl.h" #include "clang/AST/ExprCXX.h" #include "clang/AST/GlobalDecl.h" #include "clang/Basic/Builtins.h" @@ -1952,6 +1953,9 @@ LValue CIRGenFunction::buildCastLValue(const CastExpr *E) { // CK_NoOp can model a qualification conversion, which can remove an array // bound and change the IR type. LValue LV = buildLValue(E->getSubExpr()); + // Propagate the volatile qualifier to LValue, if exists in E. + if (E->changesVolatileQualification()) + llvm_unreachable("NYI"); if (LV.isSimple()) { Address V = LV.getAddress(); if (V.isValid()) { @@ -2195,8 +2199,14 @@ static Address createReferenceTemporary(CIRGenFunction &CGF, CGF.getCounterRefTmpAsString(), Alloca, ip); } case SD_Thread: - case SD_Static: - assert(0 && "NYI"); + case SD_Static: { + auto a = mlir::cast( + CGF.CGM.getAddrOfGlobalTemporary(M, Inner)); + auto f = CGF.CGM.getBuilder().createGetGlobal(a); + assert(a.getAlignment().has_value() && + "This should always have an alignment"); + return Address(f, clang::CharUnits::fromQuantity(a.getAlignment().value())); + } case SD_Dynamic: llvm_unreachable("temporary can't have dynamic storage duration"); @@ -2232,12 +2242,20 @@ static void pushTemporaryCleanup(CIRGenFunction &CGF, switch (M->getStorageDuration()) { case SD_Static: case SD_Thread: { + mlir::cir::FuncOp cleanupFn; + mlir::Value cleanupArg; if (E->getType()->isArrayType()) { llvm_unreachable("SD_Static|SD_Thread + array types not implemented"); } else { - llvm_unreachable("SD_Static|SD_Thread for general types not implemented"); + cleanupFn = CGF.CGM + .getAddrAndTypeOfCXXStructor( + GlobalDecl(ReferenceTemporaryDtor, Dtor_Complete)) + .second; + cleanupArg = ReferenceTemporary.emitRawPointer(); } - llvm_unreachable("SD_Static|SD_Thread not implemented"); + CGF.CGM.getCXXABI().registerGlobalDtor( + CGF, cast(M->getExtendingDecl()), cleanupFn, cleanupArg); + break; } case SD_FullExpression: diff --git a/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp b/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp index 5d6274ad81e9..d83d827e6947 100644 --- a/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp @@ -173,8 +173,7 @@ class CIRGenItaniumCXXABI : public cir::CIRGenCXXABI { bool Delegating, Address This, QualType ThisTy) override; void registerGlobalDtor(CIRGenFunction &CGF, const VarDecl *D, - mlir::cir::FuncOp dtor, - mlir::Attribute Addr) override; + mlir::cir::FuncOp dtor, mlir::Value Addr) override; virtual void buildRethrow(CIRGenFunction &CGF, bool isNoReturn) override; virtual void buildThrow(CIRGenFunction &CGF, const CXXThrowExpr *E) override; CatchTypeInfo @@ -2144,7 +2143,7 @@ void CIRGenItaniumCXXABI::buildDestructorCall( void CIRGenItaniumCXXABI::registerGlobalDtor(CIRGenFunction &CGF, const VarDecl *D, mlir::cir::FuncOp dtor, - mlir::Attribute Addr) { + mlir::Value Addr) { if (D->isNoDestroy(CGM.getASTContext())) return; diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.cpp b/clang/lib/CIR/CodeGen/CIRGenModule.cpp index 68e8e93e5b77..31f6178d4a37 100644 --- a/clang/lib/CIR/CodeGen/CIRGenModule.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenModule.cpp @@ -1645,7 +1645,7 @@ CIRGenModule::getAddrOfGlobalTemporary(const MaterializeTemporaryExpr *expr, } else { // No initializer, the initialization will be provided when we initialize // the declaration which performed lifetime extension. - llvm_unreachable("else value"); + type = getTypes().convertTypeForMem(materializedType); } // Create a global variable for this lifetime-extended temporary. diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.h b/clang/lib/CIR/CodeGen/CIRGenModule.h index b652ec4f9ef7..dd2f2639e497 100644 --- a/clang/lib/CIR/CodeGen/CIRGenModule.h +++ b/clang/lib/CIR/CodeGen/CIRGenModule.h @@ -630,8 +630,8 @@ class CIRGenModule : public CIRGenTypeCache { bool IsTentative = false); /// Emit the function that initializes the specified global - void buildCXXGlobalVarDeclInit(const VarDecl *D, mlir::cir::GlobalOp Addr, - bool PerformInit); + void buildCXXGlobalVarDeclInit(const VarDecl *varDecl, + mlir::cir::GlobalOp addr, bool performInit); void buildCXXGlobalVarDeclInitFunc(const VarDecl *D, mlir::cir::GlobalOp Addr, bool PerformInit); @@ -673,11 +673,6 @@ class CIRGenModule : public CIRGenTypeCache { // or if they are alias to each other. mlir::cir::FuncOp codegenCXXStructor(clang::GlobalDecl GD); - // Produce code for this constructor/destructor for global initialzation. - void codegenGlobalInitCxxStructor(const clang::VarDecl *D, - mlir::cir::GlobalOp Addr, bool NeedsCtor, - bool NeedsDtor, bool isCstStorage); - bool lookupRepresentativeDecl(llvm::StringRef MangledName, clang::GlobalDecl &Result) const; diff --git a/clang/test/CIR/CodeGen/temporaries.cpp b/clang/test/CIR/CodeGen/temporaries.cpp index 23e0adb70b2d..85ff19c12942 100644 --- a/clang/test/CIR/CodeGen/temporaries.cpp +++ b/clang/test/CIR/CodeGen/temporaries.cpp @@ -19,7 +19,7 @@ void f() { // CHECK-NEXT: cir.scope { // CHECK-NEXT: %[[ONE:[0-9]+]] = cir.alloca !ty_E, !cir.ptr, ["agg.tmp.ensured"] {alignment = 1 : i64} // CHECK-NEXT: %[[TWO:[0-9]+]] = cir.alloca !ty_E, !cir.ptr, ["ref.tmp0"] {alignment = 1 : i64} -// CHECK-NEXT: cir.call @_ZN1EC1Ev(%1) : (!cir.ptr) -> () extra(#fn_attr) +// CHECK-NEXT: cir.call @_ZN1EC1Ev(%[[TWO]]) : (!cir.ptr) -> () extra(#fn_attr) // CHECK-NEXT: %[[THREE:[0-9]+]] = cir.call @_ZN1EntEv(%[[TWO]]) : (!cir.ptr) -> !ty_E // CHECK-NEXT: cir.store %[[THREE]], %[[ONE]] : !ty_E, !cir.ptr // CHECK-NEXT: cir.call @_ZN1ED1Ev(%[[ONE]]) : (!cir.ptr) -> () extra(#fn_attr) @@ -37,3 +37,4 @@ const int &r = (const int&)n; // LLVM: @_ZGR1r_ = internal constant i32 1234, align 4 // LLVM-NEXT: @r = constant ptr @_ZGR1r_, align 8 + diff --git a/clang/test/CIR/CodeGen/tempref.cpp b/clang/test/CIR/CodeGen/tempref.cpp new file mode 100644 index 000000000000..9c7ac0eccb86 --- /dev/null +++ b/clang/test/CIR/CodeGen/tempref.cpp @@ -0,0 +1,42 @@ +// 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 +// RUN: cir-translate %t.cir -cir-to-llvmir -o %t.ll +// RUN: FileCheck --input-file=%t.ll %s -check-prefix=LLVM + +struct A { ~A(); }; +A &&a = dynamic_cast(A{}); + +// CHECK: cir.func private @_ZN1AD1Ev(!cir.ptr) extra(#fn_attr) +// CHECK-NEXT: cir.global external @a = #cir.ptr : !cir.ptr {alignment = 8 : i64, ast = #cir.var.decl.ast} +// CHECK-NEXT: cir.func internal private @__cxx_global_var_init() { +// CHECK-NEXT: cir.scope { +// CHECK-NEXT: %[[SEVEN:[0-9]+]] = cir.get_global @a : !cir.ptr> +// CHECK-NEXT: %[[EIGHT:[0-9]+]] = cir.get_global @_ZGR1a_ : !cir.ptr +// CHECK-NEXT: cir.store %[[EIGHT]], %[[SEVEN]] : !cir.ptr, !cir.ptr> +// CHECK-NEXT: } +// CHECK-NEXT: cir.return +// CHECK-NEXT: } +// CHECK-NEXT: cir.func private @_GLOBAL__sub_I_tempref.cpp() { +// CHECK-NEXT: cir.call @__cxx_global_var_init() : () -> () +// CHECK-NEXT: cir.return +// CHECK-NEXT: } + +// LLVM: @_ZGR1a_ = internal global %struct.A undef +// LLVM-DAG: @a = global ptr null, align 8 +// LLVM-DAG: @llvm.global_ctors = appending constant [1 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 65536, ptr @__cxx_global_var_init, ptr null }] + +// LLVM-DAG: declare {{.*}} void @_ZN1AD1Ev(ptr) + +// LLVM-DAG: define internal void @__cxx_global_var_init() +// LLVM-DAG: br label %[[L1:[0-9]+]] +// LLVM-DAG: [[L1]]: +// LLVM-DAG: store ptr @_ZGR1a_, ptr @a, align 8 +// LLVM-DAG: br label %[[L2:[0-9]+]] +// LLVM-DAG: [[L2]]: +// LLVM-DAG: ret void +// LLVM-DAG: } + +// LLVM-DAG: define void @_GLOBAL__sub_I_tempref.cpp() +// LLVM-DAG: call void @__cxx_global_var_init() +// LLVM-DAG: ret void +// LLVM-DAG: }