Skip to content
Draft
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
1,887 changes: 845 additions & 1,042 deletions clang/include/clang/CIR/Dialect/IR/CIROps.td

Large diffs are not rendered by default.

24 changes: 24 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,30 @@ static void emitDeclDestroy(CIRGenFunction &CGF, const VarDecl *D) {
CGM.getCXXABI().registerGlobalDtor(CGF, D, fnOp, nullptr);
}

/// Emit the code to initialize a static local variable from within a function
/// context. This is called from guarded init for static locals.
void CIRGenFunction::emitCXXGlobalVarDeclInit(const VarDecl &D,
Address DeclAddr,
bool PerformInit) {
const Expr *Init = D.getInit();
QualType T = D.getType();

if (!T->isReferenceType()) {
bool NeedsDtor =
D.needsDestruction(getContext()) == QualType::DK_cxx_destructor;
if (PerformInit)
emitDeclInit(*this, &D, DeclAddr);
if (NeedsDtor)
emitDeclDestroy(*this, &D);
return;
}

assert(PerformInit && "cannot have constant initializer which needs "
"destruction for reference");
RValue RV = emitReferenceBindingToExpr(Init);
emitStoreThroughLValue(RV, makeAddrLValue(DeclAddr, T));
}

cir::FuncOp CIRGenModule::codegenCXXStructor(GlobalDecl GD) {
const auto &FnInfo = getTypes().arrangeCXXStructorDeclaration(GD);
auto Fn = getAddrOfCXXStructor(GD, &FnInfo, /*FnType=*/nullptr,
Expand Down
13 changes: 13 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenCXXABI.h
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,19 @@ class CIRGenCXXABI {
virtual void registerGlobalDtor(CIRGenFunction &CGF, const VarDecl *D,
cir::FuncOp dtor, mlir::Value Addr) = 0;

/// Emit the guarded initialization code for static local variables with
/// non-trivial initialization or destruction. This implements the C++
/// standard's requirements for thread-safe static initialization.
///
/// \param CGF - The code generation function context
/// \param D - The variable being initialized
/// \param DeclPtr - The global variable representing the static local
/// \param PerformInit - Whether to perform initialization (true) or just
/// register the destructor (false, for constant-init
/// variables with non-trivial destructors)
virtual void emitGuardedInit(CIRGenFunction &CGF, const VarDecl &D,
cir::GlobalOp DeclPtr, bool PerformInit) = 0;

virtual void emitVirtualObjectDelete(CIRGenFunction &CGF,
const CXXDeleteExpr *DE, Address Ptr,
QualType ElementType,
Expand Down
8 changes: 4 additions & 4 deletions clang/lib/CIR/CodeGen/CIRGenDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -556,8 +556,7 @@ cir::GlobalOp CIRGenFunction::addInitializerToStaticVarDecl(
const VarDecl &D, cir::GlobalOp GV, cir::GetGlobalOp GVAddr) {
ConstantEmitter emitter(*this);
mlir::TypedAttr Init =
mlir::dyn_cast<mlir::TypedAttr>(emitter.tryEmitForInitializer(D));
assert(Init && "Expected typed attribute");
mlir::dyn_cast_or_null<mlir::TypedAttr>(emitter.tryEmitForInitializer(D));

// If constant emission failed, then this should be a C++ static
// initializer.
Expand All @@ -570,7 +569,8 @@ cir::GlobalOp CIRGenFunction::addInitializerToStaticVarDecl(
// Since we have a static initializer, this global variable can't
// be constant.
GV.setConstant(false);
llvm_unreachable("C++ guarded init it NYI");

emitCXXGuardedInit(D, GV, /*PerformInit=*/true);
}
return GV;
}
Expand Down Expand Up @@ -613,7 +613,7 @@ cir::GlobalOp CIRGenFunction::addInitializerToStaticVarDecl(
// We have a constant initializer, but a nontrivial destructor. We still
// need to perform a guarded "initialization" in order to register the
// destructor.
llvm_unreachable("C++ guarded init is NYI");
emitCXXGuardedInit(D, GV, /*PerformInit=*/false);
}

return GV;
Expand Down
13 changes: 13 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenDeclCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
//
//===----------------------------------------------------------------------===//

#include "CIRGenCXXABI.h"
#include "CIRGenFunction.h"
#include "CIRGenModule.h"
#include "TargetInfo.h"
Expand Down Expand Up @@ -51,3 +52,15 @@ void CIRGenModule::emitCXXGlobalVarDeclInitFunc(const VarDecl *D,

emitCXXGlobalVarDeclInit(D, Addr, PerformInit);
}

void CIRGenFunction::emitCXXGuardedInit(const VarDecl &D, cir::GlobalOp DeclPtr,
bool PerformInit) {
// Darwin kernel configurations forbid guard variables to reduce overhead.
// See CodeGen: CGDeclCXX.cpp:390-396
if (CGM.getCodeGenOpts().ForbidGuardVariables)
CGM.Error(D.getLocation(),
"this initialization requires a guard variable, which "
"the kernel does not support");

CGM.getCXXABI().emitGuardedInit(*this, D, DeclPtr, PerformInit);
}
8 changes: 8 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenFunction.h
Original file line number Diff line number Diff line change
Expand Up @@ -2420,6 +2420,14 @@ class CIRGenFunction : public CIRGenTypeCache {

void emitStaticVarDecl(const VarDecl &D, cir::GlobalLinkageKind Linkage);

void emitCXXGuardedInit(const VarDecl &D, cir::GlobalOp DeclPtr,
bool PerformInit);

/// Emit code to perform initialization/destruction for a static local
/// variable from within a function. This is used by guarded init.
void emitCXXGlobalVarDeclInit(const VarDecl &D, Address DeclAddr,
bool PerformInit);

// Build CIR for a statement. useCurrentScope should be true if no
// new scopes need be created when finding a compound statement.
mlir::LogicalResult emitStmt(const clang::Stmt *S, bool useCurrentScope,
Expand Down
141 changes: 141 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,8 @@ class CIRGenItaniumCXXABI : public CIRGenCXXABI {
QualType ThisTy) override;
void registerGlobalDtor(CIRGenFunction &CGF, const VarDecl *D,
cir::FuncOp dtor, mlir::Value Addr) override;
void emitGuardedInit(CIRGenFunction &CGF, const VarDecl &D,
cir::GlobalOp DeclPtr, bool PerformInit) override;
void emitVirtualObjectDelete(CIRGenFunction &CGF, const CXXDeleteExpr *DE,
Address Ptr, QualType ElementType,
const CXXDestructorDecl *Dtor) override;
Expand Down Expand Up @@ -2375,6 +2377,145 @@ void CIRGenItaniumCXXABI::registerGlobalDtor(CIRGenFunction &CGF,
// prepare. Nothing to be done for CIR here.
}

void CIRGenItaniumCXXABI::emitGuardedInit(CIRGenFunction &CGF, const VarDecl &D,
cir::GlobalOp declPtr,
bool performInit) {
auto &builder = CGF.getBuilder();
mlir::Location loc = CGF.getLoc(D.getLocation());

// Inline variables that weren't instantiated from variable templates have
// partially-ordered initialization within their translation unit.
bool nonTemplateInline =
D.isInline() &&
!isTemplateInstantiation(D.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 &&
(D.isLocalVarDecl() || nonTemplateInline) &&
!D.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 &&
declPtr.getLinkage() == cir::GlobalLinkageKind::InternalLinkage;

// Determine the guard variable type.
mlir::Type guardTy;
if (useInt8GuardVariable) {
guardTy = builder.getUInt8Ty();
} 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: ARM guard variable ABI");
} else {
guardTy = builder.getUInt64Ty();
}
}

// Create the guard variable if we don't already have it (as we
// might if we're double-emitting this function body).
// See CodeGen: ItaniumCXXABI.cpp:2735-2772
cir::GlobalOp guardGlobal = CGM.getStaticLocalDeclGuardAddress(&D);
if (!guardGlobal) {
// Mangle the name for the guard variable.
SmallString<256> guardName;
{
llvm::raw_svector_ostream out(guardName);
getMangleContext().mangleStaticGuardVariable(&D, out);
}

// Create the guard variable with a zero-initializer.
// Just absorb linkage, visibility and dll storage class from the guarded
// variable.
guardGlobal = CIRGenModule::createGlobalOp(
CGM, loc, guardName.str(), guardTy, /*isConstant=*/false,
cir::AddressSpace::Default, /*insertPoint=*/nullptr,
declPtr.getLinkage());
guardGlobal.setVisibility(declPtr.getVisibility());
guardGlobal.setDSOLocal(declPtr.isDSOLocal());

// DLL storage class should match the guarded variable.
// See CodeGen: ItaniumCXXABI.cpp:2755
assert(!cir::MissingFeatures::setDLLStorageClass());

// Set alignment for the guard variable based on its type.
// See CodeGen: ItaniumCXXABI.cpp:2716-2728, 2758
CharUnits guardAlignment;
if (useInt8GuardVariable) {
guardAlignment = CharUnits::One();
} else {
if (UseARMGuardVarABI) {
llvm_unreachable("NYI: ARM guard variable ABI alignment");
} else {
guardAlignment = CharUnits::fromQuantity(
CGM.getDataLayout().getABITypeAlign(guardTy));
}
}
guardGlobal.setAlignment(guardAlignment.getQuantity());

// Set the initial value to zero.
guardGlobal.setInitialValueAttr(builder.getZeroInitAttr(guardTy));

// 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.
// See CodeGen: ItaniumCXXABI.cpp:2760-2770
bool declHasComdat = declPtr.getComdat();
if (!D.isLocalVarDecl() && declHasComdat &&
(CGM.getTarget().getTriple().isOSBinFormatELF() ||
CGM.getTarget().getTriple().isOSBinFormatWasm())) {
guardGlobal.setComdat(true);
} else if (CGM.supportsCOMDAT() && guardGlobal.isWeakForLinker()) {
guardGlobal.setComdat(true);
}

// Register the guard variable so we don't create it again.
CGM.setStaticLocalDeclGuardAddress(&D, guardGlobal);
}

// If the variable is thread-local, so is its guard variable.
if (D.getTLSKind())
llvm_unreachable("NYI: thread-local guard variables");

// Create a pointer to the guard variable for the GuardedInitOp.
auto guardAddr = builder.createGetGlobal(guardGlobal);

// Get a symbol reference to the static variable.
auto staticVarSymbol =
mlir::FlatSymbolRefAttr::get(builder.getContext(), declPtr.getSymName());

// Create the GuardedInitOp.
bool isLocalVar = D.isLocalVarDecl();
mlir::UnitAttr threadSafeAttr = threadSafe ? builder.getUnitAttr() : nullptr;
mlir::UnitAttr performInitAttr =
performInit ? builder.getUnitAttr() : nullptr;
mlir::UnitAttr isLocalVarAttr = isLocalVar ? builder.getUnitAttr() : nullptr;
auto guardedInitOp = builder.create<cir::GuardedInitOp>(
loc, guardAddr, staticVarSymbol, threadSafeAttr, performInitAttr,
isLocalVarAttr);

// Build the init region.
mlir::Region &initRegion = guardedInitOp.getInitRegion();
mlir::OpBuilder::InsertionGuard guard(builder);
mlir::Block *initBlock = builder.createBlock(&initRegion);
builder.setInsertionPointToStart(initBlock);

// Emit the initialization code if performInit is true.
if (performInit) {
// Get the address of the static variable for initialization.
Address declAddr(CGF.getBuilder().createGetGlobal(declPtr),
declPtr.getSymType(), getContext().getDeclAlign(&D));
CGF.emitCXXGlobalVarDeclInit(D, declAddr, /*performInit=*/true);
}

// Terminate the init region with a yield.
builder.create<cir::YieldOp>(loc);
}

mlir::Value CIRGenItaniumCXXABI::getCXXDestructorImplicitParam(
CIRGenFunction &CGF, const CXXDestructorDecl *DD, CXXDtorType Type,
bool ForVirtualBase, bool Delegating) {
Expand Down
9 changes: 9 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenModule.h
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,7 @@ class CIRGenModule : public CIRGenTypeCache {
void HandleCXXStaticMemberVarInstantiation(VarDecl *VD);

llvm::DenseMap<const Decl *, cir::GlobalOp> StaticLocalDeclMap;
llvm::DenseMap<const Decl *, cir::GlobalOp> StaticLocalDeclGuardMap;
llvm::DenseMap<llvm::StringRef, mlir::Value> Globals;
mlir::Operation *getGlobalValue(llvm::StringRef Ref);
mlir::Value getGlobalValue(const clang::Decl *D);
Expand All @@ -256,6 +257,14 @@ class CIRGenModule : public CIRGenTypeCache {
StaticLocalDeclMap[D] = C;
}

cir::GlobalOp getStaticLocalDeclGuardAddress(const VarDecl *D) {
return StaticLocalDeclGuardMap[D];
}

void setStaticLocalDeclGuardAddress(const VarDecl *D, cir::GlobalOp C) {
StaticLocalDeclGuardMap[D] = C;
}

cir::GlobalOp getOrCreateStaticVarDecl(const VarDecl &D,
cir::GlobalLinkageKind Linkage);

Expand Down
33 changes: 27 additions & 6 deletions clang/lib/CIR/Dialect/IR/CIRDialect.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -360,8 +360,8 @@ void cir::ConditionOp::getSuccessorRegions(
regions.emplace_back(&await.getSuspend(), await.getSuspend().getArguments());
}

MutableOperandRange cir::ConditionOp::getMutableSuccessorOperands(
RegionSuccessor /*successor*/) {
MutableOperandRange
cir::ConditionOp::getMutableSuccessorOperands(RegionSuccessor /*successor*/) {
// No values are yielded to the successor region.
return MutableOperandRange(getOperation(), 0, 0);
}
Expand Down Expand Up @@ -1525,8 +1525,7 @@ void cir::ScopeOp::getSuccessorRegions(
mlir::RegionBranchPoint point, SmallVectorImpl<RegionSuccessor> &regions) {
// The only region always branch back to the parent operation.
if (!point.isParent()) {
regions.push_back(
RegionSuccessor(getOperation(), this->getODSResults(0)));
regions.push_back(RegionSuccessor(getOperation(), this->getODSResults(0)));
return;
}

Expand Down Expand Up @@ -1787,8 +1786,8 @@ void cir::TernaryOp::build(
// YieldOp
//===----------------------------------------------------------------------===//

MutableOperandRange cir::YieldOp::getMutableSuccessorOperands(
RegionSuccessor successor) {
MutableOperandRange
cir::YieldOp::getMutableSuccessorOperands(RegionSuccessor successor) {
Operation *op = getOperation();
if (auto loop = dyn_cast<LoopOpInterface>(op->getParentOp())) {
if (op->getParentRegion() == &loop.getCond())
Expand Down Expand Up @@ -2506,6 +2505,28 @@ cir::GetGlobalOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
return success();
}

//===----------------------------------------------------------------------===//
// GuardedInitOp
//===----------------------------------------------------------------------===//

LogicalResult cir::GuardedInitOp::verify() {
// Verify that the guard variable is a pointer type.
auto guardPtrTy = mlir::dyn_cast<PointerType>(getGuardVar().getType());
if (!guardPtrTy)
return emitOpError("guard_var must be a pointer type");

// Verify that the init region has exactly one block.
if (getInitRegion().getBlocks().size() != 1)
return emitOpError("init_region must have exactly one block");

// Verify that the init region terminates with a yield.
auto &block = getInitRegion().front();
if (block.empty() || !isa<YieldOp>(block.back()))
return emitOpError("init_region must terminate with cir.yield");

return success();
}

//===----------------------------------------------------------------------===//
// VTableAddrPointOp
//===----------------------------------------------------------------------===//
Expand Down
Loading