[WebAssembly][GlobalISel] CallLowering lowerFormalArguments#180263
[WebAssembly][GlobalISel] CallLowering lowerFormalArguments#180263QuantumSegfault wants to merge 3 commits intollvm:mainfrom
lowerFormalArguments#180263Conversation
|
@llvm/pr-subscribers-backend-webassembly @llvm/pr-subscribers-llvm-globalisel Author: Demetrius Kanios (QuantumSegfault) ChangesImplements Split from #157161 Patch is 31.59 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/180263.diff 4 Files Affected:
diff --git a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyCallLowering.cpp b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyCallLowering.cpp
index 3d0bbcc9a1c7b..b90c5cc43467c 100644
--- a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyCallLowering.cpp
+++ b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyCallLowering.cpp
@@ -19,6 +19,8 @@
#include "WebAssemblySubtarget.h"
#include "WebAssemblyUtilities.h"
#include "llvm/CodeGen/Analysis.h"
+#include "llvm/CodeGen/FunctionLoweringInfo.h"
+#include "llvm/CodeGen/GlobalISel/MachineIRBuilder.h"
#include "llvm/IR/DataLayout.h"
#include "llvm/IR/DebugLoc.h"
#include "llvm/IR/Value.h"
@@ -27,6 +29,21 @@
using namespace llvm;
+// Test whether the given calling convention is supported.
+static bool callingConvSupported(CallingConv::ID CallConv) {
+ // We currently support the language-independent target-independent
+ // conventions. We don't yet have a way to annotate calls with properties like
+ // "cold", and we don't have any call-clobbered registers, so these are mostly
+ // all handled the same.
+ return CallConv == CallingConv::C || CallConv == CallingConv::Fast ||
+ CallConv == CallingConv::Cold ||
+ CallConv == CallingConv::PreserveMost ||
+ CallConv == CallingConv::PreserveAll ||
+ CallConv == CallingConv::CXX_FAST_TLS ||
+ CallConv == CallingConv::WASM_EmscriptenInvoke ||
+ CallConv == CallingConv::Swift;
+}
+
WebAssemblyCallLowering::WebAssemblyCallLowering(
const WebAssemblyTargetLowering &TLI)
: CallLowering(&TLI) {}
@@ -50,13 +67,199 @@ 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");
+}
+
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();
+ auto &TLI = *getTLI<WebAssemblyTargetLowering>();
+ auto &Subtarget = MF.getSubtarget<WebAssemblySubtarget>();
+ auto &TRI = *Subtarget.getRegisterInfo();
+ auto &TII = *Subtarget.getInstrInfo();
+ auto &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 auto &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 (auto &Arg : SplitArgs) {
+ EVT OrigVT = TLI.getValueType(DL, Arg.Ty);
+ MVT NewVT = TLI.getRegisterTypeForCallingConv(Ctx, CallConv, OrigVT);
+ LLT OrigLLT = getLLTForType(*Arg.Ty, DL);
+ LLT NewLLT = getLLTForMVT(NewVT);
+
+ // If we need to split the type over multiple regs, check it's a scenario
+ // we currently support.
+ unsigned NumParts =
+ TLI.getNumRegistersForCallingConv(Ctx, CallConv, OrigVT);
+
+ 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 || OrigVT != NewVT) {
+ // 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) {
+ auto 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 (NumParts != 1 || 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.
+ auto PtrVT = TLI.getPointerTy(DL);
+ if (CallConv == CallingConv::Swift) {
+ if (!HasSwiftSelfArg) {
+ MFI->addParam(PtrVT);
+ }
+ if (!HasSwiftErrorArg) {
+ MFI->addParam(PtrVT);
+ }
+ }
+
+ // Varargs are copied into a buffer allocated by the caller, and a pointer to
+ // the buffer is passed as an argument.
+ if (F.isVarArg()) {
+ auto PtrVT = TLI.getPointerTy(DL, 0);
+ auto PtrLLT = LLT::pointer(0, DL.getPointerSizeInBits(0));
+ Register VarargVreg = MF.getRegInfo().createGenericVirtualRegister(PtrLLT);
+
+ MFI->setVarargBufferVreg(VarargVreg);
+
+ auto ArgInst = MIRBuilder.buildInstr(getWASMArgumentOpcode(PtrVT))
+ .addDef(VarargVreg)
+ .addImm(FinalArgIdx);
+
+ constrainOperandRegClass(MF, TRI, MRI, TII, RBI, *ArgInst,
+ ArgInst->getDesc(), ArgInst->getOperand(0), 0);
+
+ MFI->addParam(PtrVT);
+ ++FinalArgIdx;
+ }
+
+ // Record the number and types of arguments and results.
+ SmallVector<MVT, 4> Params;
+ SmallVector<MVT, 4> Results;
+ computeSignatureVTs(MF.getFunction().getFunctionType(), &MF.getFunction(),
+ MF.getFunction(), MF.getTarget(), Params, Results);
+ for (MVT VT : Results)
+ MFI->addResult(VT);
+
+ // TODO: Use signatures in WebAssemblyMachineFunctionInfo too and unify
+ // the param logic here with ComputeSignatureVTs
+ assert(MFI->getParams().size() == Params.size() &&
+ std::equal(MFI->getParams().begin(), MFI->getParams().end(),
+ Params.begin()));
+ return true;
}
bool WebAssemblyCallLowering::lowerCall(MachineIRBuilder &MIRBuilder,
diff --git a/llvm/test/CodeGen/WebAssembly/GlobalISel/irtranslator/args-simd.ll b/llvm/test/CodeGen/WebAssembly/GlobalISel/irtranslator/args-simd.ll
new file mode 100644
index 0000000000000..fb167796bec68
--- /dev/null
+++ b/llvm/test/CodeGen/WebAssembly/GlobalISel/irtranslator/args-simd.ll
@@ -0,0 +1,171 @@
+; NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py UTC_ARGS: --version 6
+; RUN: llc -mtriple=wasm32 -mattr=-simd128 -global-isel -stop-after=irtranslator -verify-machineinstrs < %s | FileCheck %s -check-prefixes=NO-SIMD
+; RUN: llc -mtriple=wasm32 -mattr=+simd128,-fp16 -global-isel -stop-after=irtranslator -verify-machineinstrs < %s | FileCheck %s -check-prefixes=SIMD,SIMD-NO-F16
+; RUN: llc -mtriple=wasm32 -mattr=+simd128,+fp16 -global-isel -stop-after=irtranslator -verify-machineinstrs < %s | FileCheck %s -check-prefixes=SIMD,SIMD-F16
+
+define void @test_v16i8_arg(<16 x i8> %arg) {
+ ; NO-SIMD-LABEL: name: test_v16i8_arg
+ ; NO-SIMD: bb.1 (%ir-block.0):
+ ; NO-SIMD-NEXT: liveins: $arguments
+ ; NO-SIMD-NEXT: {{ $}}
+ ; NO-SIMD-NEXT: [[ARGUMENT_i32_:%[0-9]+]]:i32(s32) = ARGUMENT_i32 0, implicit $arguments
+ ; NO-SIMD-NEXT: [[ARGUMENT_i32_1:%[0-9]+]]:i32(s32) = ARGUMENT_i32 1, implicit $arguments
+ ; NO-SIMD-NEXT: [[ARGUMENT_i32_2:%[0-9]+]]:i32(s32) = ARGUMENT_i32 2, implicit $arguments
+ ; NO-SIMD-NEXT: [[ARGUMENT_i32_3:%[0-9]+]]:i32(s32) = ARGUMENT_i32 3, implicit $arguments
+ ; NO-SIMD-NEXT: [[ARGUMENT_i32_4:%[0-9]+]]:i32(s32) = ARGUMENT_i32 4, implicit $arguments
+ ; NO-SIMD-NEXT: [[ARGUMENT_i32_5:%[0-9]+]]:i32(s32) = ARGUMENT_i32 5, implicit $arguments
+ ; NO-SIMD-NEXT: [[ARGUMENT_i32_6:%[0-9]+]]:i32(s32) = ARGUMENT_i32 6, implicit $arguments
+ ; NO-SIMD-NEXT: [[ARGUMENT_i32_7:%[0-9]+]]:i32(s32) = ARGUMENT_i32 7, implicit $arguments
+ ; NO-SIMD-NEXT: [[ARGUMENT_i32_8:%[0-9]+]]:i32(s32) = ARGUMENT_i32 8, implicit $arguments
+ ; NO-SIMD-NEXT: [[ARGUMENT_i32_9:%[0-9]+]]:i32(s32) = ARGUMENT_i32 9, implicit $arguments
+ ; NO-SIMD-NEXT: [[ARGUMENT_i32_10:%[0-9]+]]:i32(s32) = ARGUMENT_i32 10, implicit $arguments
+ ; NO-SIMD-NEXT: [[ARGUMENT_i32_11:%[0-9]+]]:i32(s32) = ARGUMENT_i32 11, implicit $arguments
+ ; NO-SIMD-NEXT: [[ARGUMENT_i32_12:%[0-9]+]]:i32(s32) = ARGUMENT_i32 12, implicit $arguments
+ ; NO-SIMD-NEXT: [[ARGUMENT_i32_13:%[0-9]+]]:i32(s32) = ARGUMENT_i32 13, implicit $arguments
+ ; NO-SIMD-NEXT: [[ARGUMENT_i32_14:%[0-9]+]]:i32(s32) = ARGUMENT_i32 14, implicit $arguments
+ ; NO-SIMD-NEXT: [[ARGUMENT_i32_15:%[0-9]+]]:i32(s32) = ARGUMENT_i32 15, implicit $arguments
+ ; NO-SIMD-NEXT: [[BUILD_VECTOR:%[0-9]+]]:_(<16 x s32>) = G_BUILD_VECTOR [[ARGUMENT_i32_]](s32), [[ARGUMENT_i32_1]](s32), [[ARGUMENT_i32_2]](s32), [[ARGUMENT_i32_3]](s32), [[ARGUMENT_i32_4]](s32), [[ARGUMENT_i32_5]](s32), [[ARGUMENT_i32_6]](s32), [[ARGUMENT_i32_7]](s32), [[ARGUMENT_i32_8]](s32), [[ARGUMENT_i32_9]](s32), [[ARGUMENT_i32_10]](s32), [[ARGUMENT_i32_11]](s32), [[ARGUMENT_i32_12]](s32), [[ARGUMENT_i32_13]](s32), [[ARGUMENT_i32_14]](s32), [[ARGUMENT_i32_15]](s32)
+ ; NO-SIMD-NEXT: [[TRUNC:%[0-9]+]]:_(<16 x s8>) = G_TRUNC [[BUILD_VECTOR]](<16 x s32>)
+ ;
+ ; SIMD-LABEL: name: test_v16i8_arg
+ ; SIMD: bb.1 (%ir-block.0):
+ ; SIMD-NEXT: liveins: $arguments
+ ; SIMD-NEXT: {{ $}}
+ ; SIMD-NEXT: [[ARGUMENT_v16i8_:%[0-9]+]]:v128(<16 x s8>) = ARGUMENT_v16i8 0, implicit $arguments
+ ret void
+}
+
+define void @test_v8i16_arg(<8 x i16> %arg) {
+ ; NO-SIMD-LABEL: name: test_v8i16_arg
+ ; NO-SIMD: bb.1 (%ir-block.0):
+ ; NO-SIMD-NEXT: liveins: $arguments
+ ; NO-SIMD-NEXT: {{ $}}
+ ; NO-SIMD-NEXT: [[ARGUMENT_i32_:%[0-9]+]]:i32(s32) = ARGUMENT_i32 0, implicit $arguments
+ ; NO-SIMD-NEXT: [[ARGUMENT_i32_1:%[0-9]+]]:i32(s32) = ARGUMENT_i32 1, implicit $arguments
+ ; NO-SIMD-NEXT: [[ARGUMENT_i32_2:%[0-9]+]]:i32(s32) = ARGUMENT_i32 2, implicit $arguments
+ ; NO-SIMD-NEXT: [[ARGUMENT_i32_3:%[0-9]+]]:i32(s32) = ARGUMENT_i32 3, implicit $arguments
+ ; NO-SIMD-NEXT: [[ARGUMENT_i32_4:%[0-9]+]]:i32(s32) = ARGUMENT_i32 4, implicit $arguments
+ ; NO-SIMD-NEXT: [[ARGUMENT_i32_5:%[0-9]+]]:i32(s32) = ARGUMENT_i32 5, implicit $arguments
+ ; NO-SIMD-NEXT: [[ARGUMENT_i32_6:%[0-9]+]]:i32(s32) = ARGUMENT_i32 6, implicit $arguments
+ ; NO-SIMD-NEXT: [[ARGUMENT_i32_7:%[0-9]+]]:i32(s32) = ARGUMENT_i32 7, implicit $arguments
+ ; NO-SIMD-NEXT: [[BUILD_VECTOR:%[0-9]+]]:_(<8 x s32>) = G_BUILD_VECTOR [[ARGUMENT_i32_]](s32), [[ARGUMENT_i32_1]](s32), [[ARGUMENT_i32_2]](s32), [[ARGUMENT_i32_3]](s32), [[ARGUMENT_i32_4]](s32), [[ARGUMENT_i32_5]](s32), [[ARGUMENT_i32_6]](s32), [[ARGUMENT_i32_7]](s32)
+ ; NO-SIMD-NEXT: [[TRUNC:%[0-9]+]]:_(<8 x s16>) = G_TRUNC [[BUILD_VECTOR]](<8 x s32>)
+ ;
+ ; SIMD-LABEL: name: test_v8i16_arg
+ ; SIMD: bb.1 (%ir-block.0):
+ ; SIMD-NEXT: liveins: $arguments
+ ; SIMD-NEXT: {{ $}}
+ ; SIMD-NEXT: [[ARGUMENT_v8i16_:%[0-9]+]]:v128(<8 x s16>) = ARGUMENT_v8i16 0, implicit $arguments
+ ret void
+}
+
+define void @test_v4i32_arg(<4 x i32> %arg) {
+ ; NO-SIMD-LABEL: name: test_v4i32_arg
+ ; NO-SIMD: bb.1 (%ir-block.0):
+ ; NO-SIMD-NEXT: liveins: $arguments
+ ; NO-SIMD-NEXT: {{ $}}
+ ; NO-SIMD-NEXT: [[ARGUMENT_i32_:%[0-9]+]]:i32(s32) = ARGUMENT_i32 0, implicit $arguments
+ ; NO-SIMD-NEXT: [[ARGUMENT_i32_1:%[0-9]+]]:i32(s32) = ARGUMENT_i32 1, implicit $arguments
+ ; NO-SIMD-NEXT: [[ARGUMENT_i32_2:%[0-9]+]]:i32(s32) = ARGUMENT_i32 2, implicit $arguments
+ ; NO-SIMD-NEXT: [[ARGUMENT_i32_3:%[0-9]+]]:i32(s32) = ARGUMENT_i32 3, implicit $arguments
+ ; NO-SIMD-NEXT: [[BUILD_VECTOR:%[0-9]+]]:_(<4 x s32>) = G_BUILD_VECTOR [[ARGUMENT_i32_]](s32), [[ARGUMENT_i32_1]](s32), [[ARGUMENT_i32_2]](s32), [[ARGUMENT_i32_3]](s32)
+ ;
+ ; SIMD-LABEL: name: test_v4i32_arg
+ ; SIMD: bb.1 (%ir-block.0):
+ ; SIMD-NEXT: liveins: $arguments
+ ; SIMD-NEXT: {{ $}}
+ ; SIMD-NEXT: [[ARGUMENT_v4i32_:%[0-9]+]]:v128(<4 x s32>) = ARGUMENT_v4i32 0, implicit $arguments
+ ret void
+}
+
+define void @test_v2i64_arg(<2 x i64> %arg) {
+ ; NO-SIMD-LABEL: name: test_v2i64_arg
+ ; NO-SIMD: bb.1 (%ir-block.0):
+ ; NO-SIMD-NEXT: liveins: $arguments
+ ; NO-SIMD-NEXT: {{ $}}
+ ; NO-SIMD-NEXT: [[ARGUMENT_i64_:%[0-9]+]]:i64(s64) = ARGUMENT_i64 0, implicit $arguments
+ ; NO-SIMD-NEXT: [[ARGUMENT_i64_1:%[0-9]+]]:i64(s64) = ARGUMENT_i64 1, implicit $arguments
+ ; NO-SIMD-NEXT: [[BUILD_VECTOR:%[0-9]+]]:_(<2 x s64>) = G_BUILD_VECTOR [[ARGUMENT_i64_]](s64), [[ARGUMENT_i64_1]](s64)
+ ;
+ ; SIMD-LABEL: name: test_v2i64_arg
+ ; SIMD: bb.1 (%ir-block.0):
+ ; SIMD-NEXT: liveins: $arguments
+ ; SIMD-NEXT: {{ $}}
+ ; SIMD-NEXT: [[ARGUMENT_v2i64_:%[0-9]+]]:v128(<2 x s64>) = ARGUMENT_v2i64 0, implicit $arguments
+ ret void
+}
+
+define void @test_v8f16_arg(<8 x half> %arg) {
+ ; NO-SIMD-LABEL: name: test_v8f16_arg
+ ; NO-SIMD: bb.1 (%ir-block.0):
+ ; NO-SIMD-NEXT: liveins: $arguments
+ ; NO-SIMD-NEXT: {{ $}}
+ ; NO-SIMD-NEXT: [[ARGUMENT_i32_:%[0-9]+]]:i32(s32) = ARGUMENT_i32 0, implicit $arguments
+ ; NO-SIMD-NEXT: [[ARGUMENT_i32_1:%[0-9]+]]:i32(s32) = ARGUMENT_i32 1, implicit $arguments
+ ; NO-SIMD-NEXT: [[ARGUMENT_i32_2:%[0-9]+]]:i32(s32) = ARGUMENT_i32 2, implicit $arguments
+ ; NO-SIMD-NEXT: [[ARGUMENT_i32_3:%[0-9]+]]:i32(s32) = ARGUMENT_i32 3, implicit $arguments
+ ; NO-SIMD-NEXT: [[ARGUMENT_i32_4:%[0-9]+]]:i32(s32) = ARGUMENT_i32 4, implicit $arguments
+ ; NO-SIMD-NEXT: [[ARGUMENT_i32_5:%[0-9]+]]:i32(s32) = ARGUMENT_i32 5, implicit $arguments
+ ; NO-SIMD-NEXT: [[ARGUMENT_i32_6:%[0-9]+]]:i32(s32) = ARGUMENT_i32 6, implicit $arguments
+ ; NO-SIMD-NEXT: [[ARGUMENT_i32_7:%[0-9]+]]:i32(s32) = ARGUMENT_i32 7, implicit $arguments
+ ; NO-SIMD-NEXT: [[BUILD_VECTOR:%[0-9]+]]:_(<8 x s32>) = G_BUILD_VECTOR [[ARGUMENT_i32_]](s32), [[ARGUMENT_i32_1]](s32), [[ARGUMENT_i32_2]](s32), [[ARGUMENT_i32_3]](s32), [[ARGUMENT_i32_4]](s32), [[ARGUMENT_i32_5]](s32), [[ARGUMENT_i32_6]](s32), [[ARGUMENT_i32_7]](s32)
+ ; NO-SIMD-NEXT: [[TRUNC:%[0-9]+]]:_(<8 x s16>) = G_TRUNC [[BUILD_VECTOR]](<8 x s32>)
+ ;
+ ; SIMD-NO-F16-LABEL: name: test_v8f16_arg
+ ; SIMD-NO-F16: bb.1 (%ir-block.0):
+ ; SIMD-NO-F16-NEXT: liveins: $arguments
+ ; SIMD-NO-F16-NEXT: {{ $}}
+ ; SIMD-NO-F16-NEXT: [[ARGUMENT_i32_:%[0-9]+]]:i32(s32) = ARGUMENT_i32 0, implicit $arguments
+ ; SIMD-NO-F16-NEXT: [[ARGUMENT_i32_1:%[0-9]+]]:i32(s32) = ARGUMENT_i32 1, implicit $arguments
+ ; SIMD-NO-F16-NEXT: [[ARGUMENT_i32_2:%[0-9]+]]:i32(s32) = ARGUMENT_i32 2, implicit $arguments
+ ; SIMD-NO-F16-NEXT: [[ARGUMENT_i32_3:%[0-9]+]]:i32(s32) = ARGUMENT_i32 3, implicit $arguments
+ ; SIMD-NO-F16-NEXT: [[ARGUMENT_i32_4:%[0-9]+]]:i32(s32) = ARGUMENT_i32 4, implicit $arguments
+ ; SIMD-NO-F16-NEXT: [[ARGUMENT_i32_5:%[0-9]+]]:i32(s32) = ARGUMENT_i32 5, implicit $arguments
+ ; SIMD-NO-F16-NEXT: [[ARGUMENT_i32_6:%[0-9]+]]:i32(s32) = ARGUMENT_i32 6, implicit $arguments
+ ; SIMD-NO-F16-NEXT: [[ARGUMENT_i32_7:%[0-9]+]]:i32(s32) = ARGUMENT_i32 7, implicit $arguments
+ ; SIMD-NO-F16-NEXT: [[BUILD_VECTOR:%[0-9]+]]:_(<8 x s32>) = G_BUILD_VECTOR [[ARGUMENT_i32_]](s32), [[ARGUMENT_i32_1]](s32), [[ARGUMENT_i32_2]](s32), [[ARGUMENT_i32_3]](s32), [[ARGUMENT_i32_4]](s32), [[ARGUMENT_i32_5]](s32), [[ARGUMENT_i32_6]](s32), [[ARGUMENT_i32_7]](s32)
+ ; SIMD-NO-F16-NEXT: [[TRUNC:%[0-9]+]]:_(<8 x s16>) = G_TRUNC [[BUILD_VECTOR]](<8 x s32>)
+ ;
+ ; SIMD-F16-LABEL: name: test_v8f16_arg
+ ; SIMD-F16: bb.1 (%ir-block.0):
+ ; SIMD-F16-NEXT: liveins: $arguments
+ ; SIMD-F16-NEXT: {{ $}}
+ ; SIMD-F16-NEXT: [[ARGUMENT_v8f16_:%[0-9]+]]:v128(<8 x s16>) = ARGUMENT_v8f16 0, implicit $arguments
+ ret void
+}
+
+define void @test_v4f32_arg(<4 x float> %arg) {
+ ; NO-SIMD-LABEL: name: test_v4f32_arg
+ ; NO-SIMD: bb.1 (%ir-block.0):
+ ; NO-SIMD-NEXT: liveins: $arguments
+ ; NO-SIMD-NEXT: {{ $}}
+ ; NO-SIMD-NEXT: [[ARGUMENT_f32_:%[0-9]+]]:f32(s32) = ARGUMENT_f32 0, implicit $arguments
+ ; NO-SIMD-NEXT: [[ARGUMENT_f32_1:%[0-9]+]]:f32(s32) = ARGUMENT_f32 1, implicit $arguments
+ ; NO-SIMD-NEXT: [[ARGUMENT_f32_2:%[0-9]+]]:f32(s32) = ARGUMENT_f32 2, implicit $arguments
+ ; NO-SIMD-NEXT: [[ARGUMENT_f32_3:%[0-9]+]]:f32(s32) = ARGUMENT_f32 3, implicit $arguments
+ ; NO-SIMD-NEXT: [[BUILD_VECTOR:%[0-9]+]]:_(<4 x s32>) = G_BUILD_VECTOR [[ARGUMENT_f32_]](s32), [[ARGUMENT_f32_1]](s32), [[ARGUMENT_f32_2]](s32), [[ARGUMENT_f32_3]](s32)
+ ;
+ ; SIMD-LABEL: name: test_v4f32_arg
+ ; SIMD: bb.1 (%ir-block.0):
+ ; SIMD-NEXT: liveins: $arguments
+ ; SIMD-NEXT: {{ $}}
+ ; SIMD-NEXT: [[ARGUMENT_v4f32_:%[0-9]+]]:v128(<4 x s32>) = ARGUMENT_v4f32 0, implicit $arguments
+ ret void
+}
+
+define void @test_v2f64_arg(<2 x double> %arg) {
+ ; NO-SIMD-LABEL: name: test_v2f64_arg
+ ; NO-SIMD: bb.1 (%ir-block.0):
+ ; NO-SIMD-NEXT: liveins: $arguments
+ ; NO-SIMD-NEXT: {{ $}}
+ ; NO-SIMD-NEXT: [[ARGUMENT_f64_:%[0-9]+]]:f64(s64) = ARGUMENT_f64 0, implicit $arguments
+ ; NO-SIMD-NEXT: [[ARGUMENT_f64_1:%[0-9]+]]:f64(s64) = ARGUMENT_f64 1, implicit $arguments
+ ; NO-SIMD-NEXT: [[BUILD_VECTOR:%[0-9]+]]:_(<2 x s64>) = G_BUILD_VECTOR [[ARGUMENT_f64_]](s64), [[ARGUMENT_f64_1]](s64)
+ ;
+ ; SIMD-LABEL: name: test_v2f64_arg
+ ; SIMD: bb.1 (%ir-block.0):
+ ; SIMD-NEXT: liveins: $arguments
+ ; SIMD-NEXT: {{ $}}
+ ; SIMD-NEXT: [[ARGUMENT_v2f64_:%[0-9]+]]:v128(<2 x s64>) = ARGUMENT_v2f64 0, implicit $arguments
+ ret void
+}
diff --git a/llvm/test/CodeGen/WebAssembly/GlobalISel/irtranslator/args-swiftcc.ll b/llvm/test/CodeGen/WebAssembly/GlobalISel/irtranslator/args-swiftcc.ll
new file mode 100644
index 0000000000000..1971e1dc89c9e
--- /dev/null
+++ b/llvm/test/CodeGen/WebAssembly/GlobalISel/irtranslator/args-swiftcc.ll
@@ -0,0 +1,73 @@
+; RUN: llc -mtriple=wasm32 -global-isel -stop-after=irtranslator -verify-machineinstrs < %s | FileCheck %s -check-prefixes=CHECK,WASM32
+; RUN: llc -mtriple=wasm64 -global-isel -stop-after=irtranslator -verify-machineinstrs < %s | FileCheck %s -check-prefixes=CHECK,WASM64
+
+define swift...
[truncated]
|
| return false; | ||
| } | ||
|
|
||
| static unsigned getWASMArgumentOpcode(MVT ArgType) { |
There was a problem hiding this comment.
I'm guessing this was copied from somewhere else. Either share it, or you shouldn't need it?
There was a problem hiding this comment.
No, not copied. ThoughWebAssemblyFastISel::fastLowerArguments does something similar.
| // Varargs are copied into a buffer allocated by the caller, and a pointer to | ||
| // the buffer is passed as an argument. | ||
| if (F.isVarArg()) { | ||
| auto PtrVT = TLI.getPointerTy(DL, 0); |
There was a problem hiding this comment.
No auto. Also you should hardly ever need to call getPointerTy. Especially here since it returns an MVT
There was a problem hiding this comment.
I need an MVT. Both to determine the correct argument opcode, but also to encode into the WASM function signature (MFI->addParam(PtrVT);). The hidden VarArg buffer argument is going to be a pointer, thus either i32 vs i64 on wasm32 vs wasm64.
Is there a better way to do this?
There was a problem hiding this comment.
Even in the DAG, getPointerTy should be used sparingly. In the vast majority of situations, the correct type to use is implied by the original operation. getPointerTy (and other TargetLowering getType functions) are only necessary when synthesizing operations out of thin air
There was a problem hiding this comment.
Well...then it seems we have a legit use case here then. Cause we are injecting hidden parameters
| if (Arg.hasInAllocaAttr()) { | ||
| return false; | ||
| } | ||
| if (Arg.hasNestAttr()) { | ||
| return false; | ||
| } |
There was a problem hiding this comment.
Untested? What about sret and byval?
There was a problem hiding this comment.
True, untested...I could add some tests to make sure it fails (which in -global-isel-abort=2 fallback to SelectionDAG, and provide an explicit error message about these not being supported in Wasm).
I can test sret as part of the next PR (lowerReturn), cause otherwise there's nothing special about it; it's just any other pointer. As far as byval, on the lowerFormalArugments side it's nothing special...just a pointer argument. The copy is made on the callee side.
There was a problem hiding this comment.
Actually sret testing has to be done alongside lowerCall. Within the callee it's just a pointer to be written into as any other.
|
|
||
| MFI->setVarargBufferVreg(VarargVreg); | ||
|
|
||
| auto ArgInst = MIRBuilder.buildInstr(getWASMArgumentOpcode(PtrVT)) |
There was a problem hiding this comment.
Why isn't this using IncomingValueHandler?
There was a problem hiding this comment.
I can re-investigate. But when I started out, it seemed overkill, as there is never stack spilling, nor are there any actual physical registers to copy into. Things remain in vreg end-to-end, so it seemed to make more sense to take the necessary bits only.
There was a problem hiding this comment.
Yeah. I looked into again and it's just not the right fit. We don't have the typical CCAssignFn generated by tablegen. And even if I wrote a custom one, my options are to assign values into physical registers (MCRegister), memory, or "custom" which bypasses all the automatic handling and puts me right back where I started with manually splits and copies.
As far VarArg specifically, it's just passed as a hidden pointer argument, and only manipulated with vararg related instructions/intrinsics, so there's no unpacking to do for anything
| ; NO-SIMD-NEXT: [[ARGUMENT_i32_12:%[0-9]+]]:i32(s32) = ARGUMENT_i32 12, implicit $arguments | ||
| ; NO-SIMD-NEXT: [[ARGUMENT_i32_13:%[0-9]+]]:i32(s32) = ARGUMENT_i32 13, implicit $arguments | ||
| ; NO-SIMD-NEXT: [[ARGUMENT_i32_14:%[0-9]+]]:i32(s32) = ARGUMENT_i32 14, implicit $arguments | ||
| ; NO-SIMD-NEXT: [[ARGUMENT_i32_15:%[0-9]+]]:i32(s32) = ARGUMENT_i32 15, implicit $arguments |
There was a problem hiding this comment.
Do arguments always use these ARGUMENT_ instructions, or do you ever have cases with overflow stack arguments?
There was a problem hiding this comment.
As far as I know, all LLVM arguments are passed directly through individual Wasm-side arguments (ARGUMENT_xxxx eventually lowers to just a local.get). Aggregates are recursively split into individual arguments.
There is no stack-spilling. The closest there is to that is the handling for varargs, which are written onto the stack and passed as a single pointer, by necessity.
This is partially why I avoided IncomingValueHandler
| // 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. | ||
| auto PtrVT = TLI.getPointerTy(DL); |
There was a problem hiding this comment.
Same question as before? Is there a better way to get MVT::i32 vs MVT::i64 to give to MFI->addParam
|
For more context, see You can see that in my Lines 156-213 are a stripped down version of what would normally be handled by The rest is adapted from the commonalities of the target-specific implementations of Does that clear some things up? |
|
✅ With the latest revision this PR passed the C/C++ code formatter. |
81f6675 to
7d13abe
Compare
lowerFormalArgumentslowerFormalArguments
Implements
WebAssemblyCallLowering::lowerFormalArgumentsSplit from #157161