@@ -41,6 +41,16 @@ static SmallString<128> getTransformedFileName(mlir::ModuleOp mlirModule) {
4141 return fileName;
4242}
4343
44+ // / Return the FuncOp called by `callOp`.
45+ static cir::FuncOp getCalledFunction (cir::CallOp callOp) {
46+ mlir::SymbolRefAttr sym = llvm::dyn_cast_if_present<mlir::SymbolRefAttr>(
47+ callOp.getCallableForCallee ());
48+ if (!sym)
49+ return nullptr ;
50+ return dyn_cast_or_null<cir::FuncOp>(
51+ mlir::SymbolTable::lookupNearestSymbolFrom (callOp, sym));
52+ }
53+
4454namespace {
4555struct LoweringPreparePass : public LoweringPrepareBase <LoweringPreparePass> {
4656 LoweringPreparePass () = default ;
@@ -69,6 +79,12 @@ struct LoweringPreparePass : public LoweringPrepareBase<LoweringPreparePass> {
6979 cir::FuncType type,
7080 cir::GlobalLinkageKind linkage = cir::GlobalLinkageKind::ExternalLinkage);
7181
82+ cir::GlobalOp buildRuntimeVariable (
83+ mlir::OpBuilder &builder, llvm::StringRef name, mlir::Location loc,
84+ mlir::Type type,
85+ cir::GlobalLinkageKind linkage = cir::GlobalLinkageKind::ExternalLinkage,
86+ cir::VisibilityKind visibility = cir::VisibilityKind::Default);
87+
7288 // /
7389 // / AST related
7490 // / -----------
@@ -90,6 +106,25 @@ struct LoweringPreparePass : public LoweringPrepareBase<LoweringPreparePass> {
90106
91107} // namespace
92108
109+ cir::GlobalOp LoweringPreparePass::buildRuntimeVariable (
110+ mlir::OpBuilder &builder, llvm::StringRef name, mlir::Location loc,
111+ mlir::Type type, cir::GlobalLinkageKind linkage,
112+ cir::VisibilityKind visibility) {
113+ cir::GlobalOp g = dyn_cast_or_null<cir::GlobalOp>(
114+ mlir::SymbolTable::lookupNearestSymbolFrom (
115+ mlirModule, mlir::StringAttr::get (mlirModule->getContext (), name)));
116+ if (!g) {
117+ g = cir::GlobalOp::create (builder, loc, name, type);
118+ g.setLinkageAttr (
119+ cir::GlobalLinkageKindAttr::get (builder.getContext (), linkage));
120+ mlir::SymbolTable::setSymbolVisibility (
121+ g, mlir::SymbolTable::Visibility::Private);
122+ g.setGlobalVisibilityAttr (
123+ cir::VisibilityAttr::get (builder.getContext (), visibility));
124+ }
125+ return g;
126+ }
127+
93128cir::FuncOp LoweringPreparePass::buildRuntimeFunction (
94129 mlir::OpBuilder &builder, llvm::StringRef name, mlir::Location loc,
95130 cir::FuncType type, cir::GlobalLinkageKind linkage) {
@@ -640,7 +675,8 @@ LoweringPreparePass::buildCXXGlobalVarDeclInitFunc(cir::GlobalOp op) {
640675 // Create a variable initialization function.
641676 CIRBaseBuilderTy builder (getContext ());
642677 builder.setInsertionPointAfter (op);
643- auto fnType = cir::FuncType::get ({}, builder.getVoidTy ());
678+ cir::VoidType voidTy = builder.getVoidTy ();
679+ auto fnType = cir::FuncType::get ({}, voidTy);
644680 FuncOp f = buildRuntimeFunction (builder, fnName, op.getLoc (), fnType,
645681 cir::GlobalLinkageKind::InternalLinkage);
646682
@@ -655,8 +691,57 @@ LoweringPreparePass::buildCXXGlobalVarDeclInitFunc(cir::GlobalOp op) {
655691 // Register the destructor call with __cxa_atexit
656692 mlir::Region &dtorRegion = op.getDtorRegion ();
657693 if (!dtorRegion.empty ()) {
658- assert (!cir::MissingFeatures::opGlobalDtorLowering ());
659- llvm_unreachable (" dtor region lowering is NYI" );
694+ assert (!cir::MissingFeatures::astVarDeclInterface ());
695+ assert (!cir::MissingFeatures::opGlobalThreadLocal ());
696+ // Create a variable that binds the atexit to this shared object.
697+ builder.setInsertionPointToStart (&mlirModule.getBodyRegion ().front ());
698+ cir::GlobalOp handle = buildRuntimeVariable (
699+ builder, " __dso_handle" , op.getLoc (), builder.getI8Type (),
700+ cir::GlobalLinkageKind::ExternalLinkage, cir::VisibilityKind::Hidden);
701+
702+ // Look for the destructor call in dtorBlock
703+ mlir::Block &dtorBlock = dtorRegion.front ();
704+ cir::CallOp dtorCall;
705+ for (auto op : reverse (dtorBlock.getOps <cir::CallOp>())) {
706+ dtorCall = op;
707+ break ;
708+ }
709+ assert (dtorCall && " Expected a dtor call" );
710+ cir::FuncOp dtorFunc = getCalledFunction (dtorCall);
711+ assert (dtorFunc && " Expected a dtor call" );
712+
713+ // Create a runtime helper function:
714+ // extern "C" int __cxa_atexit(void (*f)(void *), void *p, void *d);
715+ auto voidPtrTy = cir::PointerType::get (voidTy);
716+ auto voidFnTy = cir::FuncType::get ({voidPtrTy}, voidTy);
717+ auto voidFnPtrTy = cir::PointerType::get (voidFnTy);
718+ auto handlePtrTy = cir::PointerType::get (handle.getSymType ());
719+ auto fnAtExitType =
720+ cir::FuncType::get ({voidFnPtrTy, voidPtrTy, handlePtrTy}, voidTy);
721+ const char *nameAtExit = " __cxa_atexit" ;
722+ cir::FuncOp fnAtExit =
723+ buildRuntimeFunction (builder, nameAtExit, op.getLoc (), fnAtExitType);
724+
725+ // Replace the dtor call with a call to __cxa_atexit(&dtor, &var,
726+ // &__dso_handle)
727+ builder.setInsertionPointAfter (dtorCall);
728+ mlir::Value args[3 ];
729+ auto dtorPtrTy = cir::PointerType::get (dtorFunc.getFunctionType ());
730+ // dtorPtrTy
731+ args[0 ] = cir::GetGlobalOp::create (builder, dtorCall.getLoc (), dtorPtrTy,
732+ dtorFunc.getSymName ());
733+ args[0 ] = cir::CastOp::create (builder, dtorCall.getLoc (), voidFnPtrTy,
734+ cir::CastKind::bitcast, args[0 ]);
735+ args[1 ] =
736+ cir::CastOp::create (builder, dtorCall.getLoc (), voidPtrTy,
737+ cir::CastKind::bitcast, dtorCall.getArgOperand (0 ));
738+ args[2 ] = cir::GetGlobalOp::create (builder, handle.getLoc (), handlePtrTy,
739+ handle.getSymName ());
740+ builder.createCallOp (dtorCall.getLoc (), fnAtExit, args);
741+ dtorCall->erase ();
742+ entryBB->getOperations ().splice (entryBB->end (), dtorBlock.getOperations (),
743+ dtorBlock.begin (),
744+ std::prev (dtorBlock.end ()));
660745 }
661746
662747 // Replace cir.yield with cir.return
@@ -666,11 +751,12 @@ LoweringPreparePass::buildCXXGlobalVarDeclInitFunc(cir::GlobalOp op) {
666751 mlir::Block &block = op.getCtorRegion ().front ();
667752 yieldOp = &block.getOperations ().back ();
668753 } else {
669- assert (!cir::MissingFeatures::opGlobalDtorLowering ());
670- llvm_unreachable (" dtor region lowering is NYI" );
754+ assert (!dtorRegion.empty ());
755+ mlir::Block &block = dtorRegion.front ();
756+ yieldOp = &block.getOperations ().back ();
671757 }
672758
673- assert (isa<YieldOp>(*yieldOp));
759+ assert (isa<cir:: YieldOp>(*yieldOp));
674760 cir::ReturnOp::create (builder, yieldOp->getLoc ());
675761 return f;
676762}
@@ -715,7 +801,10 @@ void LoweringPreparePass::buildGlobalCtorDtorList() {
715801 mlir::ArrayAttr::get (&getContext (), globalCtors));
716802 }
717803
718- assert (!cir::MissingFeatures::opGlobalDtorLowering ());
804+ // We will eventual need to populate a global_dtor list, but that's not
805+ // needed for globals with destructors. It will only be needed for functions
806+ // that are marked as global destructors with an attribute.
807+ assert (!cir::MissingFeatures::opGlobalDtorList ());
719808}
720809
721810void LoweringPreparePass::buildCXXGlobalInitFunc () {
0 commit comments