================
@@ -693,6 +696,101 @@ void LoweringPreparePass::lowerUnaryOp(cir::UnaryOp op) {
op.erase();
}
+cir::FuncOp LoweringPreparePass::getOrCreateDtorFunc(CIRBaseBuilderTy &builder,
+ cir::GlobalOp op,
+ mlir::Region &dtorRegion,
+ cir::CallOp &dtorCall) {
+ assert(!cir::MissingFeatures::astVarDeclInterface());
+ assert(!cir::MissingFeatures::opGlobalThreadLocal());
+
+ cir::VoidType voidTy = builder.getVoidTy();
+ auto voidPtrTy = cir::PointerType::get(voidTy);
+
+ // Look for operations in dtorBlock
+ mlir::Block &dtorBlock = dtorRegion.front();
+
+ // The first operation should be a get_global to retrieve the address
+ // of the global variable we're destroying.
+ auto opIt = dtorBlock.getOperations().begin();
+ cir::GetGlobalOp ggop = mlir::cast<cir::GetGlobalOp>(*opIt);
+
+ // The simple case is just a call to a destructor, like this:
+ //
+ // %0 = cir.get_global %globalS : !cir.ptr<!rec_S>
+ // cir.call %_ZN1SD1Ev(%0) : (!cir.ptr<!rec_S>) -> ()
+ // (implicit cir.yield)
+ //
+ // That is, if the second operation is a call that takes the get_global
result
+ // as its only operand, and the only other operation is a yield, then we can
+ // just return the called function.
+ if (dtorBlock.getOperations().size() == 3) {
+ auto callOp = mlir::dyn_cast<cir::CallOp>(&*(++opIt));
+ auto yieldOp = mlir::dyn_cast<cir::YieldOp>(&*(++opIt));
+ if (yieldOp && callOp && callOp.getNumOperands() == 1 &&
+ callOp.getArgOperand(0) == ggop) {
+ dtorCall = callOp;
+ return getCalledFunction(callOp);
+ }
+ }
+
+ // Otherwise, we need to create a helper function to replace the dtor region.
+ // This name is kind of arbitrary, but it matches the name that classic
+ // codegen uses, based on the expected case that gets us here.
+ builder.setInsertionPointAfter(op);
+ SmallString<256> fnName("__cxx_global_array_dtor");
+ uint32_t cnt = dynamicInitializerNames[fnName]++;
+ if (cnt)
+ fnName += "." + llvm::Twine(cnt).str();
+
+ // Create the helper function.
+ auto fnType = cir::FuncType::get({voidPtrTy}, voidTy);
+ cir::FuncOp dtorFunc =
+ buildRuntimeFunction(builder, fnName, op.getLoc(), fnType,
+ cir::GlobalLinkageKind::InternalLinkage);
+ mlir::Block *entryBB = dtorFunc.addEntryBlock();
+
+ // Move everything from the dtor region into the helper function.
+ entryBB->getOperations().splice(entryBB->begin(), dtorBlock.getOperations(),
+ dtorBlock.begin(), dtorBlock.end());
+
+ // Before erasing this, clone it back into the dtor region
+ cir::GetGlobalOp dtorGGop =
+ mlir::cast<cir::GetGlobalOp>(entryBB->getOperations().front());
+ builder.setInsertionPointToStart(&dtorBlock);
+ builder.clone(*dtorGGop.getOperation());
----------------
andykaylor wrote:
This was tricky to make work correctly. I needed a copy of the operation in the
helper function so I could replace its uses before erasing it, and I needed to
leave a copy in the original dtor region because it's passed to the
`__cxa_atexit` call.
However, you'll notice in the test that OGCG doesn't pass this as an argument
to `__cxa_atexit` and just references the global directly in the helper
function rather than referencing it via argument. (GCC does it that way too,
FWIW.) If we did that, it would simplify this code a bit, but I didn't notice
that difference in behavior until after I had it working this way, so I decided
to leave it in for the review at least. I think it's better this way, but doing
the same thing as OGCG has benefits too.
https://github.com/llvm/llvm-project/pull/169070
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits