Skip to content

[CIR][CodeGen] Support static references to temporaries #872

New issue

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

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

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
143 changes: 106 additions & 37 deletions clang/lib/CIR/CodeGen/CIRGenCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<OMPThreadPrivateDeclAttr>()) {
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<mlir::cir::YieldOp>(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<mlir::cir::YieldOp>(rvalSrcBlock->back())) {
auto &front = rvalSrcBlock->front();
getGlobal.getDefiningOp()->moveBefore(&front);
auto yield = cast<mlir::cir::YieldOp>(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<mlir::cir::YieldOp>(Addr->getLoc());
builder.create<mlir::cir::YieldOp>(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<mlir::cir::YieldOp>(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<mlir::cir::YieldOp>(addr->getLoc());
}
}

CurCGF = nullptr;
Expand Down
3 changes: 1 addition & 2 deletions clang/lib/CIR/CodeGen/CIRGenCXXABI.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
45 changes: 0 additions & 45 deletions clang/lib/CIR/CodeGen/CIRGenDeclCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,55 +44,10 @@ void CIRGenModule::buildCXXGlobalVarDeclInitFunc(const VarDecl *D,
D->hasAttr<CUDASharedAttr>()))
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)
return;

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<OMPThreadPrivateDeclAttr>()) {
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");
}
26 changes: 22 additions & 4 deletions clang/lib/CIR/CodeGen/CIRGenExpr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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()) {
Expand Down Expand Up @@ -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<mlir::cir::GlobalOp>(
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");
Expand Down Expand Up @@ -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<VarDecl>(M->getExtendingDecl()), cleanupFn, cleanupArg);
break;
}

case SD_FullExpression:
Expand Down
5 changes: 2 additions & 3 deletions clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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;

Expand Down
2 changes: 1 addition & 1 deletion clang/lib/CIR/CodeGen/CIRGenModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
9 changes: 2 additions & 7 deletions clang/lib/CIR/CodeGen/CIRGenModule.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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;

Expand Down
3 changes: 2 additions & 1 deletion clang/test/CIR/CodeGen/temporaries.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ void f() {
// CHECK-NEXT: cir.scope {
// CHECK-NEXT: %[[ONE:[0-9]+]] = cir.alloca !ty_E, !cir.ptr<!ty_E>, ["agg.tmp.ensured"] {alignment = 1 : i64}
// CHECK-NEXT: %[[TWO:[0-9]+]] = cir.alloca !ty_E, !cir.ptr<!ty_E>, ["ref.tmp0"] {alignment = 1 : i64}
// CHECK-NEXT: cir.call @_ZN1EC1Ev(%1) : (!cir.ptr<!ty_E>) -> () extra(#fn_attr)
// CHECK-NEXT: cir.call @_ZN1EC1Ev(%[[TWO]]) : (!cir.ptr<!ty_E>) -> () extra(#fn_attr)
// CHECK-NEXT: %[[THREE:[0-9]+]] = cir.call @_ZN1EntEv(%[[TWO]]) : (!cir.ptr<!ty_E>) -> !ty_E
// CHECK-NEXT: cir.store %[[THREE]], %[[ONE]] : !ty_E, !cir.ptr<!ty_E>
// CHECK-NEXT: cir.call @_ZN1ED1Ev(%[[ONE]]) : (!cir.ptr<!ty_E>) -> () extra(#fn_attr)
Expand All @@ -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


42 changes: 42 additions & 0 deletions clang/test/CIR/CodeGen/tempref.cpp
Original file line number Diff line number Diff line change
@@ -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&&>(A{});

// CHECK: cir.func private @_ZN1AD1Ev(!cir.ptr<!ty_A>) extra(#fn_attr)
// CHECK-NEXT: cir.global external @a = #cir.ptr<null> : !cir.ptr<!ty_A> {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<!cir.ptr<!ty_A>>
// CHECK-NEXT: %[[EIGHT:[0-9]+]] = cir.get_global @_ZGR1a_ : !cir.ptr<!ty_A>
// CHECK-NEXT: cir.store %[[EIGHT]], %[[SEVEN]] : !cir.ptr<!ty_A>, !cir.ptr<!cir.ptr<!ty_A>>
// 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: }