blob: 3009c2334273d85f5451ca14df899780ac3b1ca1 [file]
//===-- WebAssemblyCallLowering.cpp - Call lowering for GlobalISel -*- C++ -*-//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
///
/// \file
/// This file implements the lowering of LLVM calls to machine code calls for
/// GlobalISel.
///
//===----------------------------------------------------------------------===//
#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/LowLevelTypeUtils.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"
#include "llvm/Support/ErrorHandling.h"
#define DEBUG_TYPE "wasm-call-lowering"
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;
}
static unsigned extendOpFromFlags(ISD::ArgFlagsTy Flags) {
if (Flags.isSExt())
return TargetOpcode::G_SEXT;
if (Flags.isZExt())
return TargetOpcode::G_ZEXT;
return TargetOpcode::G_ANYEXT;
}
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);
}
WebAssemblyCallLowering::WebAssemblyCallLowering(
const WebAssemblyTargetLowering &TLI)
: CallLowering(&TLI) {}
bool WebAssemblyCallLowering::canLowerReturn(MachineFunction &MF,
CallingConv::ID CallConv,
SmallVectorImpl<BaseArgInfo> &Outs,
bool IsVarArg) const {
return WebAssembly::canLowerReturn(Outs.size(),
&MF.getSubtarget<WebAssemblySubtarget>());
}
bool WebAssemblyCallLowering::lowerReturn(MachineIRBuilder &MIRBuilder,
const Value *Val,
ArrayRef<Register> VRegs,
FunctionLoweringInfo &FLI,
Register SwiftErrorVReg) const {
MachineFunction &MF = MIRBuilder.getMF();
const WebAssemblySubtarget &Subtarget =
MF.getSubtarget<WebAssemblySubtarget>();
const RegisterBankInfo &RBI = *Subtarget.getRegBankInfo();
const Function &F = MF.getFunction();
MachineRegisterInfo &MRI = MF.getRegInfo();
const WebAssemblyTargetLowering &TLI = *getTLI<WebAssemblyTargetLowering>();
const DataLayout &DL = F.getDataLayout();
MachineInstrBuilder MIB = MIRBuilder.buildInstrNoInsert(WebAssembly::RETURN);
assert(((Val && !VRegs.empty()) || (!Val && VRegs.empty())) &&
"Return value without a vreg or vice versa");
if (Val) {
LLVMContext &Ctx = Val->getType()->getContext();
if (!FLI.CanLowerReturn) {
insertSRetStores(MIRBuilder, Val->getType(), VRegs, FLI.DemoteRegister);
} else {
SmallVector<EVT, 4> SplitEVTs;
ComputeValueVTs(TLI, DL, Val->getType(), SplitEVTs);
assert(VRegs.size() == SplitEVTs.size() &&
"For each split Type there should be exactly one VReg.");
SmallVector<ArgInfo, 8> SplitRets;
CallingConv::ID CallConv = F.getCallingConv();
unsigned RetIdx = 0;
for (EVT SplitEVT : SplitEVTs) {
Register CurVReg = VRegs[RetIdx];
ArgInfo CurRetInfo = ArgInfo{CurVReg, SplitEVT.getTypeForEVT(Ctx), 0};
setArgFlags(CurRetInfo, AttributeList::ReturnIndex, DL, F);
splitToValueTypes(CurRetInfo, SplitRets, DL, CallConv);
++RetIdx;
}
for (ArgInfo &Ret : SplitRets) {
const EVT OrigVT = TLI.getValueType(DL, Ret.Ty);
const MVT NewVT =
TLI.getRegisterTypeForCallingConv(Ctx, CallConv, OrigVT);
const LLT OrigLLT =
getLLTForType(*OrigVT.getTypeForEVT(F.getContext()), DL);
const LLT NewLLT = getLLTForWasmMVT(NewVT, DL);
const TargetRegisterClass &NewRegClass = *TLI.getRegClassFor(NewVT);
// 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 = Ret.Flags[0];
Ret.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();
}
Ret.Flags.push_back(Flags);
}
Ret.OrigRegs.assign(Ret.Regs.begin(), Ret.Regs.end());
if (NumParts != 1 || OrigLLT != NewLLT) {
// If we can't directly assign the register, we need one or more
// intermediate values.
Ret.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) {
Register NewOutReg = MRI.createGenericVirtualRegister(NewLLT);
if (!RBI.constrainGenericRegister(NewOutReg, NewRegClass, MRI))
reportFatalInternalError(
"Couldn't constrain brand-new register?");
MIB.addUse(NewOutReg);
Ret.Regs[Part] = NewOutReg;
}
buildCopyToRegs(MIRBuilder, Ret.Regs, Ret.OrigRegs[0], OrigLLT,
NewLLT, extendOpFromFlags(Ret.Flags[0]));
} else {
MIB.addUse(Ret.Regs[0]);
}
}
}
}
if (SwiftErrorVReg) {
reportFatalInternalError(
"Wasm does not `supportSwiftError`, yet SwiftErrorVReg is "
"improperly valid.");
}
MIRBuilder.insertInstr(MIB);
return true;
}
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 {
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();
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,
CallLoweringInfo &Info) const {
return false;
}