Skip to content
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
6 changes: 6 additions & 0 deletions clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -374,6 +374,12 @@ class CIRBaseBuilderTy : public mlir::OpBuilder {
resOperands, attrs);
}

cir::CallOp createCallOp(mlir::Location loc, mlir::SymbolRefAttr callee,
mlir::ValueRange operands = mlir::ValueRange(),
llvm::ArrayRef<mlir::NamedAttribute> attrs = {}) {
return createCallOp(loc, callee, cir::VoidType(), operands, attrs);
}

cir::CallOp createTryCallOp(
mlir::Location loc, mlir::SymbolRefAttr callee = mlir::SymbolRefAttr(),
mlir::Type returnType = cir::VoidType(),
Expand Down
3 changes: 1 addition & 2 deletions clang/include/clang/CIR/MissingFeatures.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,8 @@ struct MissingFeatures {
static bool opGlobalPartition() { return false; }
static bool opGlobalUsedOrCompilerUsed() { return false; }
static bool opGlobalAnnotations() { return false; }
static bool opGlobalDtorLowering() { return false; }
static bool opGlobalCtorPriority() { return false; }
static bool opGlobalCtorList() { return false; }
static bool opGlobalDtorList() { return false; }
static bool setDSOLocal() { return false; }
static bool setComdat() { return false; }

Expand Down
59 changes: 58 additions & 1 deletion clang/lib/CIR/CodeGen/CIRGenCXX.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"

Expand Down Expand Up @@ -95,7 +96,63 @@ static void emitDeclDestroy(CIRGenFunction &cgf, const VarDecl *vd,
return;
}

cgf.cgm.errorNYI(vd->getSourceRange(), "global with destructor");
// If not constant storage we'll emit this regardless of NeedsDtor value.
CIRGenBuilderTy &builder = cgf.getBuilder();

// Prepare the dtor region.
mlir::OpBuilder::InsertionGuard guard(builder);
mlir::Block *block = builder.createBlock(&addr.getDtorRegion());
CIRGenFunction::LexicalScope lexScope{cgf, addr.getLoc(),
builder.getInsertionBlock()};
lexScope.setAsGlobalInit();
builder.setInsertionPointToStart(block);

CIRGenModule &cgm = cgf.cgm;
QualType type = vd->getType();

// Special-case non-array C++ destructors, if they have the right signature.
// Under some ABIs, destructors return this instead of void, and cannot be
// passed directly to __cxa_atexit if the target does not allow this
// mismatch.
const CXXRecordDecl *record = type->getAsCXXRecordDecl();
bool canRegisterDestructor =
record && (!cgm.getCXXABI().hasThisReturn(
GlobalDecl(record->getDestructor(), Dtor_Complete)) ||
cgm.getCXXABI().canCallMismatchedFunctionType());

// If __cxa_atexit is disabled via a flag, a different helper function is
// generated elsewhere which uses atexit instead, and it takes the destructor
// directly.
cir::FuncOp fnOp;
if (record && (canRegisterDestructor || cgm.getCodeGenOpts().CXAAtExit)) {
if (vd->getTLSKind())
cgm.errorNYI(vd->getSourceRange(), "TLS destructor");
assert(!record->hasTrivialDestructor());
assert(!cir::MissingFeatures::openCL());
CXXDestructorDecl *dtor = record->getDestructor();
// In LLVM OG codegen this is done in registerGlobalDtor, but CIRGen
// relies on LoweringPrepare for further decoupling, so build the
// call right here.
auto gd = GlobalDecl(dtor, Dtor_Complete);
fnOp = cgm.getAddrAndTypeOfCXXStructor(gd).second;
cgf.getBuilder().createCallOp(
cgf.getLoc(vd->getSourceRange()),
mlir::FlatSymbolRefAttr::get(fnOp.getSymNameAttr()),
mlir::ValueRange{cgm.getAddrOfGlobalVar(vd)});
} else {
cgm.errorNYI(vd->getSourceRange(), "array destructor");
}
assert(fnOp && "expected cir.func");
cgm.getCXXABI().registerGlobalDtor(vd, fnOp, nullptr);

builder.setInsertionPointToEnd(block);
if (block->empty()) {
block->erase();
// Don't confuse lexical cleanup.
builder.clearInsertionPoint();
} else {
builder.create<cir::YieldOp>(addr.getLoc());
}
}

cir::FuncOp CIRGenModule::codegenCXXStructor(GlobalDecl gd) {
Expand Down
18 changes: 18 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenCXXABI.h
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,14 @@ class CIRGenCXXABI {
bool forVirtualBase, bool delegating,
Address thisAddr, QualType thisTy) = 0;

/// Emit code to force the execution of a destructor during global
/// teardown. The default implementation of this uses atexit.
///
/// \param dtor - a function taking a single pointer argument
/// \param addr - a pointer to pass to the destructor function.
virtual void registerGlobalDtor(const VarDecl *vd, cir::FuncOp dtor,
mlir::Value addr) = 0;

/// Checks if ABI requires extra virtual offset for vtable field.
virtual bool
isVirtualOffsetNeededForVTableField(CIRGenFunction &cgf,
Expand Down Expand Up @@ -233,6 +241,16 @@ class CIRGenCXXABI {
return false;
}

/// Returns true if the target allows calling a function through a pointer
/// with a different signature than the actual function (or equivalently,
/// bitcasting a function or function pointer to a different function type).
/// In principle in the most general case this could depend on the target, the
/// calling convention, and the actual types of the arguments and return
/// value. Here it just means whether the signature mismatch could *ever* be
/// allowed; in other words, does the target do strict checking of signatures
/// for all calls.
virtual bool canCallMismatchedFunctionType() const { return true; }

/// Gets the mangle context.
clang::MangleContext &getMangleContext() { return *mangleContext; }

Expand Down
23 changes: 23 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@ class CIRGenItaniumCXXABI : public CIRGenCXXABI {
CXXDtorType type, bool forVirtualBase,
bool delegating, Address thisAddr,
QualType thisTy) override;
void registerGlobalDtor(const VarDecl *vd, cir::FuncOp dtor,
mlir::Value addr) override;

void emitRethrow(CIRGenFunction &cgf, bool isNoReturn) override;
void emitThrow(CIRGenFunction &cgf, const CXXThrowExpr *e) override;
Expand Down Expand Up @@ -1507,6 +1509,27 @@ void CIRGenItaniumCXXABI::emitDestructorCall(
vttTy, nullptr);
}

void CIRGenItaniumCXXABI::registerGlobalDtor(const VarDecl *vd,
cir::FuncOp dtor,
mlir::Value addr) {
if (vd->isNoDestroy(cgm.getASTContext()))
return;

if (vd->getTLSKind()) {
cgm.errorNYI(vd->getSourceRange(), "registerGlobalDtor: TLS");
return;
}

// HLSL doesn't support atexit.
if (cgm.getLangOpts().HLSL) {
cgm.errorNYI(vd->getSourceRange(), "registerGlobalDtor: HLSL");
return;
}

// The default behavior is to use atexit. This is handled in lowering
// prepare. Nothing to be done for CIR here.
}

// The idea here is creating a separate block for the throw with an
// `UnreachableOp` as the terminator. So, we branch from the current block
// to the throw block and create a block for the remaining operations.
Expand Down
103 changes: 96 additions & 7 deletions clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,16 @@ static SmallString<128> getTransformedFileName(mlir::ModuleOp mlirModule) {
return fileName;
}

/// Return the FuncOp called by `callOp`.
static cir::FuncOp getCalledFunction(cir::CallOp callOp) {
mlir::SymbolRefAttr sym = llvm::dyn_cast_if_present<mlir::SymbolRefAttr>(
callOp.getCallableForCallee());
if (!sym)
return nullptr;
return dyn_cast_or_null<cir::FuncOp>(
mlir::SymbolTable::lookupNearestSymbolFrom(callOp, sym));
}

namespace {
struct LoweringPreparePass : public LoweringPrepareBase<LoweringPreparePass> {
LoweringPreparePass() = default;
Expand Down Expand Up @@ -69,6 +79,12 @@ struct LoweringPreparePass : public LoweringPrepareBase<LoweringPreparePass> {
cir::FuncType type,
cir::GlobalLinkageKind linkage = cir::GlobalLinkageKind::ExternalLinkage);

cir::GlobalOp buildRuntimeVariable(
mlir::OpBuilder &builder, llvm::StringRef name, mlir::Location loc,
mlir::Type type,
cir::GlobalLinkageKind linkage = cir::GlobalLinkageKind::ExternalLinkage,
cir::VisibilityKind visibility = cir::VisibilityKind::Default);

///
/// AST related
/// -----------
Expand All @@ -90,6 +106,25 @@ struct LoweringPreparePass : public LoweringPrepareBase<LoweringPreparePass> {

} // namespace

cir::GlobalOp LoweringPreparePass::buildRuntimeVariable(
mlir::OpBuilder &builder, llvm::StringRef name, mlir::Location loc,
mlir::Type type, cir::GlobalLinkageKind linkage,
cir::VisibilityKind visibility) {
cir::GlobalOp g = dyn_cast_or_null<cir::GlobalOp>(
mlir::SymbolTable::lookupNearestSymbolFrom(
mlirModule, mlir::StringAttr::get(mlirModule->getContext(), name)));
if (!g) {
g = cir::GlobalOp::create(builder, loc, name, type);
g.setLinkageAttr(
cir::GlobalLinkageKindAttr::get(builder.getContext(), linkage));
mlir::SymbolTable::setSymbolVisibility(
g, mlir::SymbolTable::Visibility::Private);
g.setGlobalVisibilityAttr(
cir::VisibilityAttr::get(builder.getContext(), visibility));
}
return g;
}

cir::FuncOp LoweringPreparePass::buildRuntimeFunction(
mlir::OpBuilder &builder, llvm::StringRef name, mlir::Location loc,
cir::FuncType type, cir::GlobalLinkageKind linkage) {
Expand Down Expand Up @@ -640,7 +675,8 @@ LoweringPreparePass::buildCXXGlobalVarDeclInitFunc(cir::GlobalOp op) {
// Create a variable initialization function.
CIRBaseBuilderTy builder(getContext());
builder.setInsertionPointAfter(op);
auto fnType = cir::FuncType::get({}, builder.getVoidTy());
cir::VoidType voidTy = builder.getVoidTy();
auto fnType = cir::FuncType::get({}, voidTy);
FuncOp f = buildRuntimeFunction(builder, fnName, op.getLoc(), fnType,
cir::GlobalLinkageKind::InternalLinkage);

Expand All @@ -655,8 +691,57 @@ LoweringPreparePass::buildCXXGlobalVarDeclInitFunc(cir::GlobalOp op) {
// Register the destructor call with __cxa_atexit
mlir::Region &dtorRegion = op.getDtorRegion();
if (!dtorRegion.empty()) {
assert(!cir::MissingFeatures::opGlobalDtorLowering());
llvm_unreachable("dtor region lowering is NYI");
assert(!cir::MissingFeatures::astVarDeclInterface());
assert(!cir::MissingFeatures::opGlobalThreadLocal());
// Create a variable that binds the atexit to this shared object.
builder.setInsertionPointToStart(&mlirModule.getBodyRegion().front());
cir::GlobalOp handle = buildRuntimeVariable(
builder, "__dso_handle", op.getLoc(), builder.getI8Type(),
cir::GlobalLinkageKind::ExternalLinkage, cir::VisibilityKind::Hidden);

// Look for the destructor call in dtorBlock
mlir::Block &dtorBlock = dtorRegion.front();
cir::CallOp dtorCall;
for (auto op : reverse(dtorBlock.getOps<cir::CallOp>())) {
dtorCall = op;
break;
}
assert(dtorCall && "Expected a dtor call");
cir::FuncOp dtorFunc = getCalledFunction(dtorCall);
assert(dtorFunc && "Expected a dtor call");

// Create a runtime helper function:
// extern "C" int __cxa_atexit(void (*f)(void *), void *p, void *d);
auto voidPtrTy = cir::PointerType::get(voidTy);
auto voidFnTy = cir::FuncType::get({voidPtrTy}, voidTy);
auto voidFnPtrTy = cir::PointerType::get(voidFnTy);
auto handlePtrTy = cir::PointerType::get(handle.getSymType());
auto fnAtExitType =
cir::FuncType::get({voidFnPtrTy, voidPtrTy, handlePtrTy}, voidTy);
const char *nameAtExit = "__cxa_atexit";
cir::FuncOp fnAtExit =
buildRuntimeFunction(builder, nameAtExit, op.getLoc(), fnAtExitType);

// Replace the dtor call with a call to __cxa_atexit(&dtor, &var,
// &__dso_handle)
builder.setInsertionPointAfter(dtorCall);
mlir::Value args[3];
auto dtorPtrTy = cir::PointerType::get(dtorFunc.getFunctionType());
// dtorPtrTy
args[0] = cir::GetGlobalOp::create(builder, dtorCall.getLoc(), dtorPtrTy,
dtorFunc.getSymName());
args[0] = cir::CastOp::create(builder, dtorCall.getLoc(), voidFnPtrTy,
cir::CastKind::bitcast, args[0]);
args[1] =
cir::CastOp::create(builder, dtorCall.getLoc(), voidPtrTy,
cir::CastKind::bitcast, dtorCall.getArgOperand(0));
args[2] = cir::GetGlobalOp::create(builder, handle.getLoc(), handlePtrTy,
handle.getSymName());
builder.createCallOp(dtorCall.getLoc(), fnAtExit, args);
dtorCall->erase();
entryBB->getOperations().splice(entryBB->end(), dtorBlock.getOperations(),
dtorBlock.begin(),
std::prev(dtorBlock.end()));
}

// Replace cir.yield with cir.return
Expand All @@ -666,11 +751,12 @@ LoweringPreparePass::buildCXXGlobalVarDeclInitFunc(cir::GlobalOp op) {
mlir::Block &block = op.getCtorRegion().front();
yieldOp = &block.getOperations().back();
} else {
assert(!cir::MissingFeatures::opGlobalDtorLowering());
llvm_unreachable("dtor region lowering is NYI");
assert(!dtorRegion.empty());
mlir::Block &block = dtorRegion.front();
yieldOp = &block.getOperations().back();
}

assert(isa<YieldOp>(*yieldOp));
assert(isa<cir::YieldOp>(*yieldOp));
cir::ReturnOp::create(builder, yieldOp->getLoc());
return f;
}
Expand Down Expand Up @@ -715,7 +801,10 @@ void LoweringPreparePass::buildGlobalCtorDtorList() {
mlir::ArrayAttr::get(&getContext(), globalCtors));
}

assert(!cir::MissingFeatures::opGlobalDtorLowering());
// We will eventual need to populate a global_dtor list, but that's not
// needed for globals with destructors. It will only be needed for functions
// that are marked as global destructors with an attribute.
assert(!cir::MissingFeatures::opGlobalDtorList());
}

void LoweringPreparePass::buildCXXGlobalInitFunc() {
Expand Down
7 changes: 6 additions & 1 deletion clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1771,9 +1771,13 @@ mlir::LogicalResult CIRToLLVMGlobalOpLowering::matchAndRewrite(
}

// Rewrite op.
rewriter.replaceOpWithNewOp<mlir::LLVM::GlobalOp>(
auto newOp = rewriter.replaceOpWithNewOp<mlir::LLVM::GlobalOp>(
op, llvmType, isConst, linkage, symbol, init.value_or(mlir::Attribute()),
alignment, addrSpace, isDsoLocal, isThreadLocal, comdatAttr, attributes);
newOp.setVisibility_Attr(mlir::LLVM::VisibilityAttr::get(
getContext(), lowerCIRVisibilityToLLVMVisibility(
op.getGlobalVisibilityAttr().getValue())));

return mlir::success();
}

Expand Down Expand Up @@ -2594,6 +2598,7 @@ void ConvertCIRToLLVMPass::runOnOperation() {
return std::make_pair(ctorAttr.getName(),
ctorAttr.getPriority());
});
assert(!cir::MissingFeatures::opGlobalDtorList());
}

mlir::LogicalResult CIRToLLVMBrOpLowering::matchAndRewrite(
Expand Down
Loading