@@ -41,6 +41,16 @@ static SmallString<128> getTransformedFileName(mlir::ModuleOp mlirModule) {
41
41
return fileName;
42
42
}
43
43
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
+
44
54
namespace {
45
55
struct LoweringPreparePass : public LoweringPrepareBase <LoweringPreparePass> {
46
56
LoweringPreparePass () = default ;
@@ -69,6 +79,12 @@ struct LoweringPreparePass : public LoweringPrepareBase<LoweringPreparePass> {
69
79
cir::FuncType type,
70
80
cir::GlobalLinkageKind linkage = cir::GlobalLinkageKind::ExternalLinkage);
71
81
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
+
72
88
// /
73
89
// / AST related
74
90
// / -----------
@@ -90,6 +106,25 @@ struct LoweringPreparePass : public LoweringPrepareBase<LoweringPreparePass> {
90
106
91
107
} // namespace
92
108
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
+
93
128
cir::FuncOp LoweringPreparePass::buildRuntimeFunction (
94
129
mlir::OpBuilder &builder, llvm::StringRef name, mlir::Location loc,
95
130
cir::FuncType type, cir::GlobalLinkageKind linkage) {
@@ -640,7 +675,8 @@ LoweringPreparePass::buildCXXGlobalVarDeclInitFunc(cir::GlobalOp op) {
640
675
// Create a variable initialization function.
641
676
CIRBaseBuilderTy builder (getContext ());
642
677
builder.setInsertionPointAfter (op);
643
- auto fnType = cir::FuncType::get ({}, builder.getVoidTy ());
678
+ cir::VoidType voidTy = builder.getVoidTy ();
679
+ auto fnType = cir::FuncType::get ({}, voidTy);
644
680
FuncOp f = buildRuntimeFunction (builder, fnName, op.getLoc (), fnType,
645
681
cir::GlobalLinkageKind::InternalLinkage);
646
682
@@ -655,8 +691,57 @@ LoweringPreparePass::buildCXXGlobalVarDeclInitFunc(cir::GlobalOp op) {
655
691
// Register the destructor call with __cxa_atexit
656
692
mlir::Region &dtorRegion = op.getDtorRegion ();
657
693
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 ()));
660
745
}
661
746
662
747
// Replace cir.yield with cir.return
@@ -666,11 +751,12 @@ LoweringPreparePass::buildCXXGlobalVarDeclInitFunc(cir::GlobalOp op) {
666
751
mlir::Block &block = op.getCtorRegion ().front ();
667
752
yieldOp = &block.getOperations ().back ();
668
753
} 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 ();
671
757
}
672
758
673
- assert (isa<YieldOp>(*yieldOp));
759
+ assert (isa<cir:: YieldOp>(*yieldOp));
674
760
cir::ReturnOp::create (builder, yieldOp->getLoc ());
675
761
return f;
676
762
}
@@ -715,7 +801,10 @@ void LoweringPreparePass::buildGlobalCtorDtorList() {
715
801
mlir::ArrayAttr::get (&getContext (), globalCtors));
716
802
}
717
803
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 ());
719
808
}
720
809
721
810
void LoweringPreparePass::buildCXXGlobalInitFunc () {
0 commit comments