[llvm] [WebAssembly][GlobalISel] CallLowering `lowerFormalArguments` (PR #180263)

Matt Arsenault via llvm-commits llvm-commits at lists.llvm.org
Thu Apr 2 10:52:09 PDT 2026


================
@@ -50,13 +71,222 @@ bool WebAssemblyCallLowering::lowerReturn(MachineIRBuilder &MIRBuilder,
   return false;
 }
 
+static unsigned getWASMArgumentOpcode(MVT ArgType) {
+  switch (ArgType.SimpleTy) {
+  case MVT::i32:
+    return WebAssembly::ARGUMENT_i32;
+  case MVT::i64:
+    return WebAssembly::ARGUMENT_i64;
+  case MVT::f32:
+    return WebAssembly::ARGUMENT_f32;
+  case MVT::f64:
+    return WebAssembly::ARGUMENT_f64;
+
+  case MVT::funcref:
+    return WebAssembly::ARGUMENT_funcref;
+  case MVT::externref:
+    return WebAssembly::ARGUMENT_externref;
+  case MVT::exnref:
+    return WebAssembly::ARGUMENT_exnref;
+
+  case MVT::v16i8:
+    return WebAssembly::ARGUMENT_v16i8;
+  case MVT::v8i16:
+    return WebAssembly::ARGUMENT_v8i16;
+  case MVT::v4i32:
+    return WebAssembly::ARGUMENT_v4i32;
+  case MVT::v2i64:
+    return WebAssembly::ARGUMENT_v2i64;
+  case MVT::v8f16:
+    return WebAssembly::ARGUMENT_v8f16;
+  case MVT::v4f32:
+    return WebAssembly::ARGUMENT_v4f32;
+  case MVT::v2f64:
+    return WebAssembly::ARGUMENT_v2f64;
+  default:
+    break;
+  }
+  llvm_unreachable("Found unexpected type for WASM argument");
+}
+
+static LLT getLLTForWasmMVT(MVT Ty, const DataLayout &DL) {
+  if (Ty == MVT::externref) {
+    return LLT::pointer(
+        WebAssembly::WasmAddressSpace::WASM_ADDRESS_SPACE_EXTERNREF,
+        DL.getPointerSizeInBits(
+            WebAssembly::WasmAddressSpace::WASM_ADDRESS_SPACE_EXTERNREF));
+  }
+
+  if (Ty == MVT::funcref) {
+    return LLT::pointer(
+        WebAssembly::WasmAddressSpace::WASM_ADDRESS_SPACE_FUNCREF,
+        DL.getPointerSizeInBits(
+            WebAssembly::WasmAddressSpace::WASM_ADDRESS_SPACE_FUNCREF));
+  }
+
+  return llvm::getLLTForMVT(Ty);
+}
+
 bool WebAssemblyCallLowering::lowerFormalArguments(
     MachineIRBuilder &MIRBuilder, const Function &F,
     ArrayRef<ArrayRef<Register>> VRegs, FunctionLoweringInfo &FLI) const {
-  if (VRegs.empty())
-    return true; // allow only empty signatures for now
+  MachineFunction &MF = MIRBuilder.getMF();
+  MachineRegisterInfo &MRI = MF.getRegInfo();
+  WebAssemblyFunctionInfo *MFI = MF.getInfo<WebAssemblyFunctionInfo>();
+  const DataLayout &DL = F.getDataLayout();
+  const WebAssemblyTargetLowering &TLI = *getTLI<WebAssemblyTargetLowering>();
+  const WebAssemblySubtarget &Subtarget =
+      MF.getSubtarget<WebAssemblySubtarget>();
+  const WebAssemblyRegisterInfo &TRI = *Subtarget.getRegisterInfo();
+  const WebAssemblyInstrInfo &TII = *Subtarget.getInstrInfo();
+  const RegisterBankInfo &RBI = *Subtarget.getRegBankInfo();
 
-  return false;
+  LLVMContext &Ctx = MIRBuilder.getContext();
+  const CallingConv::ID CallConv = F.getCallingConv();
+
+  if (!callingConvSupported(CallConv)) {
+    return false;
+  }
+
+  MF.getRegInfo().addLiveIn(WebAssembly::ARGUMENTS);
+  MF.front().addLiveIn(WebAssembly::ARGUMENTS);
+
+  SmallVector<ArgInfo, 8> SplitArgs;
+
+  if (!FLI.CanLowerReturn) {
+    insertSRetIncomingArgument(F, SplitArgs, FLI.DemoteRegister, MRI, DL);
+  }
+
+  unsigned ArgIdx = 0;
+  bool HasSwiftErrorArg = false;
+  bool HasSwiftSelfArg = false;
+  for (const Argument &Arg : F.args()) {
+    ArgInfo OrigArg{VRegs[ArgIdx], Arg.getType(), ArgIdx};
+    setArgFlags(OrigArg, ArgIdx + AttributeList::FirstArgIndex, DL, F);
+
+    HasSwiftSelfArg |= Arg.hasSwiftSelfAttr();
+    HasSwiftErrorArg |= Arg.hasSwiftErrorAttr();
+    if (Arg.hasInAllocaAttr()) {
+      return false;
+    }
+    if (Arg.hasNestAttr()) {
+      return false;
+    }
+    splitToValueTypes(OrigArg, SplitArgs, DL, F.getCallingConv());
+    ++ArgIdx;
+  }
+
+  unsigned FinalArgIdx = 0;
+  for (ArgInfo &Arg : SplitArgs) {
+    const EVT OrigVT = TLI.getValueType(DL, Arg.Ty);
+    const MVT NewVT = TLI.getRegisterTypeForCallingConv(Ctx, CallConv, OrigVT);
+    const LLT OrigLLT =
+        getLLTForType(*OrigVT.getTypeForEVT(F.getContext()), DL);
+    const LLT NewLLT = getLLTForWasmMVT(NewVT, DL);
+
+    // If we need to split the type over multiple regs, check it's a scenario
+    // we currently support.
+    const unsigned NumParts =
+        TLI.getNumRegistersForCallingConv(Ctx, CallConv, OrigVT);
+
+    const ISD::ArgFlagsTy OrigFlags = Arg.Flags[0];
+    Arg.Flags.clear();
+
+    for (unsigned Part = 0; Part < NumParts; ++Part) {
+      ISD::ArgFlagsTy Flags = OrigFlags;
+      if (Part == 0) {
+        Flags.setSplit();
+      } else {
+        Flags.setOrigAlign(Align(1));
+        if (Part == NumParts - 1)
+          Flags.setSplitEnd();
+      }
+
+      Arg.Flags.push_back(Flags);
+    }
+
+    Arg.OrigRegs.assign(Arg.Regs.begin(), Arg.Regs.end());
+    if (NumParts != 1 || OrigLLT != NewLLT) {
+      // If we can't directly assign the register, we need one or more
+      // intermediate values.
+      Arg.Regs.resize(NumParts);
+
+      // For each split register, create and assign a vreg that will store
+      // the incoming component of the larger value. These will later be
+      // merged to form the final vreg.
+      for (unsigned Part = 0; Part < NumParts; ++Part) {
+        Arg.Regs[Part] = MRI.createGenericVirtualRegister(NewLLT);
+      }
+    }
+
+    for (unsigned Part = 0; Part < NumParts; ++Part) {
+      MachineInstrBuilder ArgInst =
+          MIRBuilder.buildInstr(getWASMArgumentOpcode(NewVT))
+              .addDef(Arg.Regs[Part])
+              .addImm(FinalArgIdx);
+
+      constrainOperandRegClass(MF, TRI, MRI, TII, RBI, *ArgInst,
+                               ArgInst->getDesc(), ArgInst->getOperand(0), 0);
+      MFI->addParam(NewVT);
+      ++FinalArgIdx;
+    }
+
+    if (OrigVT != NewVT) {
+      buildCopyFromRegs(MIRBuilder, Arg.OrigRegs, Arg.Regs, OrigLLT, NewLLT,
+                        Arg.Flags[0]);
+    }
+  }
+
+  // For swiftcc, emit additional swiftself and swifterror arguments
+  // if there aren't. These additional arguments are also added for callee
+  // signature They are necessary to match callee and caller signature for
+  // indirect call.
+  if (CallConv == CallingConv::Swift) {
+    const MVT PtrVT = TLI.getPointerTy(DL);
+
+    if (!HasSwiftSelfArg) {
+      MFI->addParam(PtrVT);
----------------
arsenm wrote:

That probably won't happen. I more meant there should be GIsel native infrastructure instead of reusing the DAG pieces 

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


More information about the llvm-commits mailing list