[WebAssembly][GlobalISel] CallLowering lowerFormalArguments (#180263)

Implements `WebAssemblyCallLowering::lowerFormalArguments`

Split from #157161
This commit is contained in:
Demetrius Kanios
2026-04-01 16:12:38 -07:00
committed by GitHub
parent 52fb23eef8
commit 29391328ab
4 changed files with 686 additions and 3 deletions

View File

@@ -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<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);
}
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<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,

View File

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

View File

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

View File

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