diff --git a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyCallLowering.cpp b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyCallLowering.cpp index 3d0bbcc9a1c7b..9450749a4f6ab 100644 --- a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyCallLowering.cpp +++ b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyCallLowering.cpp @@ -14,11 +14,17 @@ #include "WebAssemblyCallLowering.h" #include "MCTargetDesc/WebAssemblyMCTargetDesc.h" +#include "Utils/WasmAddressSpaces.h" #include "WebAssemblyISelLowering.h" #include "WebAssemblyMachineFunctionInfo.h" +#include "WebAssemblyRegisterInfo.h" #include "WebAssemblySubtarget.h" #include "WebAssemblyUtilities.h" #include "llvm/CodeGen/Analysis.h" +#include "llvm/CodeGen/FunctionLoweringInfo.h" +#include "llvm/CodeGen/GlobalISel/MachineIRBuilder.h" +#include "llvm/CodeGen/MachineInstrBuilder.h" +#include "llvm/CodeGen/RegisterBankInfo.h" #include "llvm/IR/DataLayout.h" #include "llvm/IR/DebugLoc.h" #include "llvm/IR/Value.h" @@ -27,6 +33,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 +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> 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(); + const DataLayout &DL = F.getDataLayout(); + const WebAssemblyTargetLowering &TLI = *getTLI(); + const WebAssemblySubtarget &Subtarget = + MF.getSubtarget(); + 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 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); + } + 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()) { + const MVT PtrVT = TLI.getPointerTy(DL, 0); + const LLT PtrLLT = LLT::pointer(0, DL.getPointerSizeInBits(0)); + Register VarargVreg = MF.getRegInfo().createGenericVirtualRegister(PtrLLT); + + MFI->setVarargBufferVreg(VarargVreg); + + MachineInstrBuilder 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 Params; + SmallVector 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 swiftcc void @test_implicit_self_and_error(float %arg) { + ; CHECK-LABEL: name: test_implicit_self_and_error + + ; CHECK: machineFunctionInfo: + ; WASM32-NEXT: params: [ f32, i32, i32 ] + ; WASM64-NEXT: params: [ f32, i64, i64 ] + ; CHECK-NEXT: results: [ ] + + ; CHECK: bb.1 (%ir-block.0): + ; CHECK-NEXT: liveins: $arguments + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: [[ARGUMENT_f32_:%[0-9]+]]:f32(s32) = ARGUMENT_f32 0, implicit $arguments + ret void +} + +define swiftcc void @test_explicit_self_and_implicit_error(ptr swiftself %self, float %arg) { + ; CHECK-LABEL: name: test_explicit_self_and_implicit_error + + + ; CHECK: machineFunctionInfo: + ; WASM32-NEXT: params: [ i32, f32, i32 ] + ; WASM64-NEXT: params: [ i64, f32, i64 ] + ; CHECK-NEXT: results: [ ] + + ; CHECK: bb.1 (%ir-block.0): + ; CHECK-NEXT: liveins: $arguments + ; CHECK-NEXT: {{ $}} + ; WASM32-NEXT: [[ARGUMENT_i32_:%[0-9]+]]:i32(p0) = ARGUMENT_i32 0, implicit $arguments + ; WASM64-NEXT: [[ARGUMENT_i64_:%[0-9]+]]:i64(p0) = ARGUMENT_i64 0, implicit $arguments + ; CHECK-NEXT: [[ARGUMENT_f32_:%[0-9]+]]:f32(s32) = ARGUMENT_f32 1, implicit $arguments + ret void +} + +define swiftcc void @test_implicit_self_and_explicit_error(ptr swifterror %error, float %arg) { + ; CHECK-LABEL: name: test_implicit_self_and_explicit_error + + + ; CHECK: machineFunctionInfo: + ; WASM32-NEXT: params: [ i32, f32, i32 ] + ; WASM64-NEXT: params: [ i64, f32, i64 ] + ; CHECK-NEXT: results: [ ] + + ; CHECK: bb.1 (%ir-block.0): + ; CHECK-NEXT: liveins: $arguments + ; CHECK-NEXT: {{ $}} + ; WASM32-NEXT: [[ARGUMENT_i32_:%[0-9]+]]:i32(p0) = ARGUMENT_i32 0, implicit $arguments + ; WASM64-NEXT: [[ARGUMENT_i64_:%[0-9]+]]:i64(p0) = ARGUMENT_i64 0, implicit $arguments + ; CHECK-NEXT: [[ARGUMENT_f32_:%[0-9]+]]:f32(s32) = ARGUMENT_f32 1, implicit $arguments + ret void +} + +define swiftcc void @test_explicit_self_and_error(ptr swiftself %self, ptr swifterror %error, float %arg) { + ; CHECK-LABEL: name: test_explicit_self_and_error + + + ; CHECK: machineFunctionInfo: + ; WASM32-NEXT: params: [ i32, i32, f32 ] + ; WASM64-NEXT: params: [ i64, i64, f32 ] + ; CHECK-NEXT: results: [ ] + + ; CHECK: bb.1 (%ir-block.0): + ; CHECK-NEXT: liveins: $arguments + ; CHECK-NEXT: {{ $}} + ; WASM32-NEXT: [[ARGUMENT_i32_:%[0-9]+]]:i32(p0) = ARGUMENT_i32 0, implicit $arguments + ; WASM32-NEXT: [[ARGUMENT_i32_1:%[0-9]+]]:i32(p0) = ARGUMENT_i32 1, implicit $arguments + ; WASM64-NEXT: [[ARGUMENT_i64_:%[0-9]+]]:i64(p0) = ARGUMENT_i64 0, implicit $arguments + ; WASM64-NEXT: [[ARGUMENT_i64_1:%[0-9]+]]:i64(p0) = ARGUMENT_i64 1, implicit $arguments + ; CHECK-NEXT: [[ARGUMENT_f32_:%[0-9]+]]:f32(s32) = ARGUMENT_f32 2, implicit $arguments + ret void +} diff --git a/llvm/test/CodeGen/WebAssembly/GlobalISel/irtranslator/args.ll b/llvm/test/CodeGen/WebAssembly/GlobalISel/irtranslator/args.ll new file mode 100644 index 0000000000000..f9859ac45f863 --- /dev/null +++ b/llvm/test/CodeGen/WebAssembly/GlobalISel/irtranslator/args.ll @@ -0,0 +1,209 @@ +; NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py UTC_ARGS: --version 6 +; 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 void @test_i8_arg(i8 %arg) { + ; CHECK-LABEL: name: test_i8_arg + ; CHECK: bb.1 (%ir-block.0): + ; CHECK-NEXT: liveins: $arguments + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: [[ARGUMENT_i32_:%[0-9]+]]:i32(s32) = ARGUMENT_i32 0, implicit $arguments + ; CHECK-NEXT: [[TRUNC:%[0-9]+]]:_(s8) = G_TRUNC [[ARGUMENT_i32_]](s32) + ret void +} + +define void @test_i8_zeroext_arg(i8 zeroext %arg) { + ; CHECK-LABEL: name: test_i8_zeroext_arg + ; CHECK: bb.1 (%ir-block.0): + ; CHECK-NEXT: liveins: $arguments + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: [[ARGUMENT_i32_:%[0-9]+]]:i32(s32) = ARGUMENT_i32 0, implicit $arguments + ; CHECK-NEXT: [[ASSERT_ZEXT:%[0-9]+]]:_(s32) = G_ASSERT_ZEXT [[ARGUMENT_i32_]], 8 + ; CHECK-NEXT: [[TRUNC:%[0-9]+]]:_(s8) = G_TRUNC [[ASSERT_ZEXT]](s32) + ret void +} + +define void @test_i8_signext_arg(i8 signext %arg) { + ; CHECK-LABEL: name: test_i8_signext_arg + ; CHECK: bb.1 (%ir-block.0): + ; CHECK-NEXT: liveins: $arguments + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: [[ARGUMENT_i32_:%[0-9]+]]:i32(s32) = ARGUMENT_i32 0, implicit $arguments + ; CHECK-NEXT: [[ASSERT_SEXT:%[0-9]+]]:_(s32) = G_ASSERT_SEXT [[ARGUMENT_i32_]], 8 + ; CHECK-NEXT: [[TRUNC:%[0-9]+]]:_(s8) = G_TRUNC [[ASSERT_SEXT]](s32) + ret void +} + +define void @test_i16_arg(i16 %arg) { + ; CHECK-LABEL: name: test_i16_arg + ; CHECK: bb.1 (%ir-block.0): + ; CHECK-NEXT: liveins: $arguments + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: [[ARGUMENT_i32_:%[0-9]+]]:i32(s32) = ARGUMENT_i32 0, implicit $arguments + ; CHECK-NEXT: [[TRUNC:%[0-9]+]]:_(s16) = G_TRUNC [[ARGUMENT_i32_]](s32) + ret void +} + + +define void @test_i32_arg(i32 %arg) { + ; CHECK-LABEL: name: test_i32_arg + ; CHECK: bb.1 (%ir-block.0): + ; CHECK-NEXT: liveins: $arguments + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: [[ARGUMENT_i32_:%[0-9]+]]:i32(s32) = ARGUMENT_i32 0, implicit $arguments + ret void +} + + +define void @test_i64_arg(i64 %arg) { + ; CHECK-LABEL: name: test_i64_arg + ; CHECK: bb.1 (%ir-block.0): + ; CHECK-NEXT: liveins: $arguments + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: [[ARGUMENT_i64_:%[0-9]+]]:i64(s64) = ARGUMENT_i64 0, implicit $arguments + ret void +} + +define void @test_ptr_arg(ptr %arg) { + ; WASM32-LABEL: name: test_ptr_arg + ; WASM32: bb.1 (%ir-block.0): + ; WASM32-NEXT: liveins: $arguments + ; WASM32-NEXT: {{ $}} + ; WASM32-NEXT: [[ARGUMENT_i32_:%[0-9]+]]:i32(p0) = ARGUMENT_i32 0, implicit $arguments + ; + ; WASM64-LABEL: name: test_ptr_arg + ; WASM64: bb.1 (%ir-block.0): + ; WASM64-NEXT: liveins: $arguments + ; WASM64-NEXT: {{ $}} + ; WASM64-NEXT: [[ARGUMENT_i64_:%[0-9]+]]:i64(p0) = ARGUMENT_i64 0, implicit $arguments + ret void +} + +define void @test_i128_arg(i128 %arg) { + ; CHECK-LABEL: name: test_i128_arg + ; CHECK: bb.1 (%ir-block.0): + ; CHECK-NEXT: liveins: $arguments + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: [[ARGUMENT_i64_:%[0-9]+]]:i64(s64) = ARGUMENT_i64 0, implicit $arguments + ; CHECK-NEXT: [[ARGUMENT_i64_1:%[0-9]+]]:i64(s64) = ARGUMENT_i64 1, implicit $arguments + ; CHECK-NEXT: [[MV:%[0-9]+]]:_(s128) = G_MERGE_VALUES [[ARGUMENT_i64_]](s64), [[ARGUMENT_i64_1]](s64) + ret void +} + +define void @test_f16_arg(half %arg) { + ; CHECK-LABEL: name: test_f16_arg + ; CHECK: bb.1 (%ir-block.0): + ; CHECK-NEXT: liveins: $arguments + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: [[ARGUMENT_i32_:%[0-9]+]]:i32(s32) = ARGUMENT_i32 0, implicit $arguments + ; CHECK-NEXT: [[TRUNC:%[0-9]+]]:_(s16) = G_TRUNC [[ARGUMENT_i32_]](s32) + ret void +} + + +define void @test_f32_arg(float %arg) { + ; CHECK-LABEL: name: test_f32_arg + ; CHECK: bb.1 (%ir-block.0): + ; CHECK-NEXT: liveins: $arguments + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: [[ARGUMENT_f32_:%[0-9]+]]:f32(s32) = ARGUMENT_f32 0, implicit $arguments + ret void +} + + +define void @test_f64_arg(double %arg) { + ; CHECK-LABEL: name: test_f64_arg + ; CHECK: bb.1 (%ir-block.0): + ; CHECK-NEXT: liveins: $arguments + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: [[ARGUMENT_f64_:%[0-9]+]]:f64(s64) = ARGUMENT_f64 0, implicit $arguments + ret void +} + +define void @test_f128_arg(fp128 %arg) { + ; CHECK-LABEL: name: test_f128_arg + ; CHECK: bb.1 (%ir-block.0): + ; CHECK-NEXT: liveins: $arguments + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: [[ARGUMENT_i64_:%[0-9]+]]:i64(s64) = ARGUMENT_i64 0, implicit $arguments + ; CHECK-NEXT: [[ARGUMENT_i64_1:%[0-9]+]]:i64(s64) = ARGUMENT_i64 1, implicit $arguments + ; CHECK-NEXT: [[MV:%[0-9]+]]:_(s128) = G_MERGE_VALUES [[ARGUMENT_i64_]](s64), [[ARGUMENT_i64_1]](s64) + ret void +} + +%externref = type ptr addrspace(10) +define void @test_externref_arg(%externref %arg) { + ; CHECK-LABEL: name: test_externref_arg + ; CHECK: bb.1 (%ir-block.0): + ; CHECK-NEXT: liveins: $arguments + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: [[ARGUMENT_externref:%[0-9]+]]:externref(p10) = ARGUMENT_externref 0, implicit $arguments + ret void +} + +%funcref = type ptr addrspace(20) +define void @test_funcref_arg(%funcref %arg) { + ; CHECK-LABEL: name: test_funcref_arg + ; CHECK: bb.1 (%ir-block.0): + ; CHECK-NEXT: liveins: $arguments + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: [[ARGUMENT_funcref:%[0-9]+]]:funcref(p20) = ARGUMENT_funcref 0, implicit $arguments + ret void +} + +define void @test_multiple_args(ptr %arg1, float %arg2, i1 %arg3) { + ; WASM32-LABEL: name: test_multiple_args + ; WASM32: bb.1 (%ir-block.0): + ; WASM32-NEXT: liveins: $arguments + ; WASM32-NEXT: {{ $}} + ; WASM32-NEXT: [[ARGUMENT_i32_:%[0-9]+]]:i32(p0) = ARGUMENT_i32 0, implicit $arguments + ; WASM32-NEXT: [[ARGUMENT_f32_:%[0-9]+]]:f32(s32) = ARGUMENT_f32 1, implicit $arguments + ; WASM32-NEXT: [[ARGUMENT_i32_1:%[0-9]+]]:i32(s32) = ARGUMENT_i32 2, implicit $arguments + ; WASM32-NEXT: [[TRUNC:%[0-9]+]]:_(s1) = G_TRUNC [[ARGUMENT_i32_1]](s32) + ; + ; WASM64-LABEL: name: test_multiple_args + ; WASM64: bb.1 (%ir-block.0): + ; WASM64-NEXT: liveins: $arguments + ; WASM64-NEXT: {{ $}} + ; WASM64-NEXT: [[ARGUMENT_i64_:%[0-9]+]]:i64(p0) = ARGUMENT_i64 0, implicit $arguments + ; WASM64-NEXT: [[ARGUMENT_f32_:%[0-9]+]]:f32(s32) = ARGUMENT_f32 1, implicit $arguments + ; WASM64-NEXT: [[ARGUMENT_i32_:%[0-9]+]]:i32(s32) = ARGUMENT_i32 2, implicit $arguments + ; WASM64-NEXT: [[TRUNC:%[0-9]+]]:_(s1) = G_TRUNC [[ARGUMENT_i32_]](s32) + ret void +} + +define void @test_array_arg([5 x i16] %arg) { + ; CHECK-LABEL: name: test_array_arg + ; CHECK: bb.1 (%ir-block.0): + ; CHECK-NEXT: liveins: $arguments + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: [[ARGUMENT_i32_:%[0-9]+]]:i32(s32) = ARGUMENT_i32 0, implicit $arguments + ; CHECK-NEXT: [[TRUNC:%[0-9]+]]:_(s16) = G_TRUNC [[ARGUMENT_i32_]](s32) + ; CHECK-NEXT: [[ARGUMENT_i32_1:%[0-9]+]]:i32(s32) = ARGUMENT_i32 1, implicit $arguments + ; CHECK-NEXT: [[TRUNC1:%[0-9]+]]:_(s16) = G_TRUNC [[ARGUMENT_i32_1]](s32) + ; CHECK-NEXT: [[ARGUMENT_i32_2:%[0-9]+]]:i32(s32) = ARGUMENT_i32 2, implicit $arguments + ; CHECK-NEXT: [[TRUNC2:%[0-9]+]]:_(s16) = G_TRUNC [[ARGUMENT_i32_2]](s32) + ; CHECK-NEXT: [[ARGUMENT_i32_3:%[0-9]+]]:i32(s32) = ARGUMENT_i32 3, implicit $arguments + ; CHECK-NEXT: [[TRUNC3:%[0-9]+]]:_(s16) = G_TRUNC [[ARGUMENT_i32_3]](s32) + ; CHECK-NEXT: [[ARGUMENT_i32_4:%[0-9]+]]:i32(s32) = ARGUMENT_i32 4, implicit $arguments + ; CHECK-NEXT: [[TRUNC4:%[0-9]+]]:_(s16) = G_TRUNC [[ARGUMENT_i32_4]](s32) + ret void +} + +%StructTy = type { i8, i64, i16, i1, float } + +define void @test_struct_arg(%StructTy %arg) { + ; CHECK-LABEL: name: test_struct_arg + ; CHECK: bb.1 (%ir-block.0): + ; CHECK-NEXT: liveins: $arguments + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: [[ARGUMENT_i32_:%[0-9]+]]:i32(s32) = ARGUMENT_i32 0, implicit $arguments + ; CHECK-NEXT: [[TRUNC:%[0-9]+]]:_(s8) = G_TRUNC [[ARGUMENT_i32_]](s32) + ; CHECK-NEXT: [[ARGUMENT_i64_:%[0-9]+]]:i64(s64) = ARGUMENT_i64 1, implicit $arguments + ; CHECK-NEXT: [[ARGUMENT_i32_1:%[0-9]+]]:i32(s32) = ARGUMENT_i32 2, implicit $arguments + ; CHECK-NEXT: [[TRUNC1:%[0-9]+]]:_(s16) = G_TRUNC [[ARGUMENT_i32_1]](s32) + ; CHECK-NEXT: [[ARGUMENT_i32_2:%[0-9]+]]:i32(s32) = ARGUMENT_i32 3, implicit $arguments + ; CHECK-NEXT: [[TRUNC2:%[0-9]+]]:_(s1) = G_TRUNC [[ARGUMENT_i32_2]](s32) + ; CHECK-NEXT: [[ARGUMENT_f32_:%[0-9]+]]:f32(s32) = ARGUMENT_f32 4, implicit $arguments + ret void +}