================
@@ -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

Reply via email to