[llvm-branch-commits] [clang] [CIR] Implement ArgKind::Expand in CallConvLowering (PR #201718)

Andy Kaylor via llvm-branch-commits llvm-branch-commits at lists.llvm.org
Mon Jun 15 09:46:55 PDT 2026


================
@@ -328,19 +353,75 @@ void insertArgCoercion(FunctionOpInterface funcOp,
     return;
   Block &entry = body.front();
 
-  for (auto [idx, ac] : llvm::enumerate(fc.argInfos)) {
-    unsigned blockIdx = idx + sretOffset;
-    if (blockIdx >= entry.getNumArguments())
+  // Running block argument index.  Each non-Expand classification occupies
+  // one block argument slot; each Expand classification occupies N slots
+  // (one per struct field), so the running index must be incremented by N
+  // rather than 1 after processing an Expand arg.
+  unsigned blockArgIdx = sretOffset;
+
+  for (const ArgClassification &ac : fc.argInfos) {
+    assert(blockArgIdx < entry.getNumArguments() &&
+           "classification count must not exceed entry block arguments");
+
+    if (ac.kind == ArgKind::Expand) {
+      // The block arg at blockArgIdx currently has the original struct type.
+      // Replace it with N scalar args (one per field) and store each field
+      // directly into the parameter's own alloca.
+      BlockArgument origArg = entry.getArgument(blockArgIdx);
+      auto recTy = cast<cir::RecordType>(origArg.getType());
+      assert(recTy.isStruct() &&
+             "Expand classification requires a struct type, not a union");
+      unsigned numFields = recTy.getNumElements();
+      assert(numFields > 0 &&
+             "Expand classification requires at least one struct field");
+      Location loc = funcOp.getLoc();
+
+      // CIRGen spills every by-value struct parameter into its local alloca
+      // with a single store before any other use, so the struct block arg's
+      // only use is that store.  Capture it and the destination alloca, then
+      // store the expanded fields straight into that alloca and erase the
+      // original store.  This keeps the alloca's variable name and `init`
+      // flag and avoids a reassemble-then-reload roundtrip.
+      assert(origArg.hasOneUse() &&
+             "Expand arg must have exactly one use (the CIRGen param spill)");
+      auto paramStore = cast<cir::StoreOp>(*origArg.user_begin());
+      assert(paramStore.getValue() == origArg &&
+             "Expand arg's use must be the value operand of its store");
+      auto destAlloca =
+          cast<cir::AllocaOp>(paramStore.getAddr().getDefiningOp());
+
+      // Split the single struct block arg into N scalar field block args:
+      // slot 0 becomes field 0; insert slots 1..N-1 after it.
+      origArg.setType(recTy.getElementType(0));
----------------
andykaylor wrote:

This makes the paramStore instruction invalid. That's probably OK, given that you're going to erase it below, but it would be better to set the insert point to the following operation and erase the store before you change the type here.

https://github.com/llvm/llvm-project/pull/201718


More information about the llvm-branch-commits mailing list