2015-06-30 07:51:55 +08:00
|
|
|
//=- WebAssemblyISelLowering.cpp - WebAssembly DAG Lowering Implementation -==//
|
|
|
|
//
|
|
|
|
// The LLVM Compiler Infrastructure
|
|
|
|
//
|
|
|
|
// This file is distributed under the University of Illinois Open Source
|
|
|
|
// License. See LICENSE.TXT for details.
|
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
///
|
|
|
|
/// \file
|
|
|
|
/// \brief This file implements the WebAssemblyTargetLowering class.
|
|
|
|
///
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
#include "WebAssemblyISelLowering.h"
|
|
|
|
#include "MCTargetDesc/WebAssemblyMCTargetDesc.h"
|
|
|
|
#include "WebAssemblyMachineFunctionInfo.h"
|
|
|
|
#include "WebAssemblySubtarget.h"
|
|
|
|
#include "WebAssemblyTargetMachine.h"
|
|
|
|
#include "llvm/CodeGen/Analysis.h"
|
2015-08-25 06:16:48 +08:00
|
|
|
#include "llvm/CodeGen/CallingConvLower.h"
|
2015-09-17 00:51:30 +08:00
|
|
|
#include "llvm/CodeGen/MachineJumpTableInfo.h"
|
2015-06-30 07:51:55 +08:00
|
|
|
#include "llvm/CodeGen/MachineRegisterInfo.h"
|
|
|
|
#include "llvm/CodeGen/SelectionDAG.h"
|
2015-07-23 05:28:15 +08:00
|
|
|
#include "llvm/IR/DiagnosticInfo.h"
|
|
|
|
#include "llvm/IR/DiagnosticPrinter.h"
|
2015-06-30 07:51:55 +08:00
|
|
|
#include "llvm/IR/Function.h"
|
|
|
|
#include "llvm/IR/Intrinsics.h"
|
|
|
|
#include "llvm/Support/CommandLine.h"
|
|
|
|
#include "llvm/Support/Debug.h"
|
|
|
|
#include "llvm/Support/ErrorHandling.h"
|
|
|
|
#include "llvm/Support/raw_ostream.h"
|
|
|
|
#include "llvm/Target/TargetOptions.h"
|
|
|
|
using namespace llvm;
|
|
|
|
|
|
|
|
#define DEBUG_TYPE "wasm-lower"
|
|
|
|
|
|
|
|
WebAssemblyTargetLowering::WebAssemblyTargetLowering(
|
|
|
|
const TargetMachine &TM, const WebAssemblySubtarget &STI)
|
2015-07-03 05:36:25 +08:00
|
|
|
: TargetLowering(TM), Subtarget(&STI) {
|
2015-08-25 06:16:48 +08:00
|
|
|
auto MVTPtr = Subtarget->hasAddr64() ? MVT::i64 : MVT::i32;
|
|
|
|
|
2015-08-13 01:53:29 +08:00
|
|
|
// Booleans always contain 0 or 1.
|
|
|
|
setBooleanContents(ZeroOrOneBooleanContent);
|
2015-07-03 05:36:25 +08:00
|
|
|
// WebAssembly does not produce floating-point exceptions on normal floating
|
|
|
|
// point operations.
|
|
|
|
setHasFloatingPointExceptions(false);
|
2015-07-08 06:38:06 +08:00
|
|
|
// We don't know the microarchitecture here, so just reduce register pressure.
|
|
|
|
setSchedulingPreference(Sched::RegPressure);
|
2015-07-23 05:28:15 +08:00
|
|
|
// Tell ISel that we have a stack pointer.
|
|
|
|
setStackPointerRegisterToSaveRestore(
|
|
|
|
Subtarget->hasAddr64() ? WebAssembly::SP64 : WebAssembly::SP32);
|
|
|
|
// Set up the register classes.
|
2015-09-26 09:09:44 +08:00
|
|
|
addRegisterClass(MVT::i32, &WebAssembly::I32RegClass);
|
|
|
|
addRegisterClass(MVT::i64, &WebAssembly::I64RegClass);
|
|
|
|
addRegisterClass(MVT::f32, &WebAssembly::F32RegClass);
|
|
|
|
addRegisterClass(MVT::f64, &WebAssembly::F64RegClass);
|
2015-07-23 05:28:15 +08:00
|
|
|
// Compute derived properties from the register classes.
|
|
|
|
computeRegisterProperties(Subtarget->getRegisterInfo());
|
|
|
|
|
2015-08-25 06:16:48 +08:00
|
|
|
setOperationAction(ISD::GlobalAddress, MVTPtr, Custom);
|
2015-11-26 00:44:29 +08:00
|
|
|
setOperationAction(ISD::ExternalSymbol, MVTPtr, Custom);
|
2015-09-17 00:51:30 +08:00
|
|
|
setOperationAction(ISD::JumpTable, MVTPtr, Custom);
|
2015-08-25 06:16:48 +08:00
|
|
|
|
2015-12-05 07:22:35 +08:00
|
|
|
// Take the default expansion for va_arg, va_copy, and va_end. There is no
|
|
|
|
// default action for va_start, so we do that custom.
|
|
|
|
setOperationAction(ISD::VASTART, MVT::Other, Custom);
|
|
|
|
setOperationAction(ISD::VAARG, MVT::Other, Expand);
|
|
|
|
setOperationAction(ISD::VACOPY, MVT::Other, Expand);
|
|
|
|
setOperationAction(ISD::VAEND, MVT::Other, Expand);
|
|
|
|
|
2015-08-12 05:02:46 +08:00
|
|
|
for (auto T : {MVT::f32, MVT::f64}) {
|
|
|
|
// Don't expand the floating-point types to constant pools.
|
|
|
|
setOperationAction(ISD::ConstantFP, T, Legal);
|
|
|
|
// Expand floating-point comparisons.
|
|
|
|
for (auto CC : {ISD::SETO, ISD::SETUO, ISD::SETUEQ, ISD::SETONE,
|
|
|
|
ISD::SETULT, ISD::SETULE, ISD::SETUGT, ISD::SETUGE})
|
|
|
|
setCondCodeAction(CC, T, Expand);
|
2015-08-21 06:57:13 +08:00
|
|
|
// Expand floating-point library function operators.
|
2015-12-06 03:15:57 +08:00
|
|
|
for (auto Op : {ISD::FSIN, ISD::FCOS, ISD::FSINCOS, ISD::FPOWI, ISD::FPOW,
|
2015-12-10 12:52:33 +08:00
|
|
|
ISD::FREM, ISD::FMA})
|
2015-08-21 06:57:13 +08:00
|
|
|
setOperationAction(Op, T, Expand);
|
2015-08-25 02:23:13 +08:00
|
|
|
// Note supported floating-point library function operators that otherwise
|
|
|
|
// default to expand.
|
2015-11-30 06:32:02 +08:00
|
|
|
for (auto Op :
|
|
|
|
{ISD::FCEIL, ISD::FFLOOR, ISD::FTRUNC, ISD::FNEARBYINT, ISD::FRINT})
|
2015-08-25 02:23:13 +08:00
|
|
|
setOperationAction(Op, T, Legal);
|
2015-11-11 05:40:21 +08:00
|
|
|
// Support minnan and maxnan, which otherwise default to expand.
|
|
|
|
setOperationAction(ISD::FMINNAN, T, Legal);
|
|
|
|
setOperationAction(ISD::FMAXNAN, T, Legal);
|
2015-08-12 05:02:46 +08:00
|
|
|
}
|
2015-08-21 06:57:13 +08:00
|
|
|
|
|
|
|
for (auto T : {MVT::i32, MVT::i64}) {
|
|
|
|
// Expand unavailable integer operations.
|
2015-11-30 06:32:02 +08:00
|
|
|
for (auto Op :
|
|
|
|
{ISD::BSWAP, ISD::ROTL, ISD::ROTR, ISD::SMUL_LOHI, ISD::UMUL_LOHI,
|
|
|
|
ISD::MULHS, ISD::MULHU, ISD::SDIVREM, ISD::UDIVREM, ISD::SHL_PARTS,
|
|
|
|
ISD::SRA_PARTS, ISD::SRL_PARTS, ISD::ADDC, ISD::ADDE, ISD::SUBC,
|
|
|
|
ISD::SUBE}) {
|
2015-08-21 06:57:13 +08:00
|
|
|
setOperationAction(Op, T, Expand);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// As a special case, these operators use the type to mean the type to
|
|
|
|
// sign-extend from.
|
2015-12-10 09:00:19 +08:00
|
|
|
for (auto T : {MVT::i1, MVT::i8, MVT::i16, MVT::i32})
|
2015-08-21 06:57:13 +08:00
|
|
|
setOperationAction(ISD::SIGN_EXTEND_INREG, T, Expand);
|
|
|
|
|
|
|
|
// Dynamic stack allocation: use the default expansion.
|
|
|
|
setOperationAction(ISD::STACKSAVE, MVT::Other, Expand);
|
|
|
|
setOperationAction(ISD::STACKRESTORE, MVT::Other, Expand);
|
2015-08-25 06:31:52 +08:00
|
|
|
setOperationAction(ISD::DYNAMIC_STACKALLOC, MVTPtr, Expand);
|
2015-09-01 06:24:11 +08:00
|
|
|
|
2015-12-12 07:49:46 +08:00
|
|
|
setOperationAction(ISD::FrameIndex, MVT::i32, Custom);
|
|
|
|
|
2015-09-17 00:51:30 +08:00
|
|
|
// Expand these forms; we pattern-match the forms that we can handle in isel.
|
|
|
|
for (auto T : {MVT::i32, MVT::i64, MVT::f32, MVT::f64})
|
|
|
|
for (auto Op : {ISD::BR_CC, ISD::SELECT_CC})
|
|
|
|
setOperationAction(Op, T, Expand);
|
|
|
|
|
|
|
|
// We have custom switch handling.
|
|
|
|
setOperationAction(ISD::BR_JT, MVT::Other, Custom);
|
|
|
|
|
2015-09-01 06:24:11 +08:00
|
|
|
// WebAssembly doesn't have:
|
|
|
|
// - Floating-point extending loads.
|
|
|
|
// - Floating-point truncating stores.
|
|
|
|
// - i1 extending loads.
|
2015-12-10 10:07:53 +08:00
|
|
|
setLoadExtAction(ISD::EXTLOAD, MVT::f64, MVT::f32, Expand);
|
2015-09-01 06:24:11 +08:00
|
|
|
setTruncStoreAction(MVT::f64, MVT::f32, Expand);
|
|
|
|
for (auto T : MVT::integer_valuetypes())
|
|
|
|
for (auto Ext : {ISD::EXTLOAD, ISD::ZEXTLOAD, ISD::SEXTLOAD})
|
|
|
|
setLoadExtAction(Ext, T, MVT::i1, Promote);
|
2015-11-10 08:30:57 +08:00
|
|
|
|
|
|
|
// Trap lowers to wasm unreachable
|
|
|
|
setOperationAction(ISD::TRAP, MVT::Other, Legal);
|
2015-07-03 05:36:25 +08:00
|
|
|
}
|
2015-06-30 07:51:55 +08:00
|
|
|
|
2015-08-25 02:44:37 +08:00
|
|
|
FastISel *WebAssemblyTargetLowering::createFastISel(
|
|
|
|
FunctionLoweringInfo &FuncInfo, const TargetLibraryInfo *LibInfo) const {
|
|
|
|
return WebAssembly::createFastISel(FuncInfo, LibInfo);
|
|
|
|
}
|
|
|
|
|
2015-08-25 06:16:48 +08:00
|
|
|
bool WebAssemblyTargetLowering::isOffsetFoldingLegal(
|
2015-11-30 06:32:02 +08:00
|
|
|
const GlobalAddressSDNode * /*GA*/) const {
|
2015-12-07 03:33:32 +08:00
|
|
|
// All offsets can be folded.
|
|
|
|
return true;
|
2015-08-25 06:16:48 +08:00
|
|
|
}
|
|
|
|
|
2015-11-30 06:32:02 +08:00
|
|
|
MVT WebAssemblyTargetLowering::getScalarShiftAmountTy(const DataLayout & /*DL*/,
|
2015-08-03 08:00:11 +08:00
|
|
|
EVT VT) const {
|
2015-12-10 08:26:26 +08:00
|
|
|
unsigned BitWidth = NextPowerOf2(VT.getSizeInBits() - 1);
|
|
|
|
if (BitWidth > 1 && BitWidth < 8)
|
|
|
|
BitWidth = 8;
|
2015-12-17 07:25:51 +08:00
|
|
|
|
|
|
|
if (BitWidth > 64) {
|
|
|
|
BitWidth = 64;
|
|
|
|
assert(BitWidth >= Log2_32_Ceil(VT.getSizeInBits()) &&
|
|
|
|
"64-bit shift counts ought to be enough for anyone");
|
|
|
|
}
|
|
|
|
|
2015-12-10 08:26:26 +08:00
|
|
|
MVT Result = MVT::getIntegerVT(BitWidth);
|
|
|
|
assert(Result != MVT::INVALID_SIMPLE_VALUE_TYPE &&
|
|
|
|
"Unable to represent scalar shift amount type");
|
|
|
|
return Result;
|
2015-08-03 08:00:11 +08:00
|
|
|
}
|
|
|
|
|
2015-08-12 04:13:18 +08:00
|
|
|
const char *
|
|
|
|
WebAssemblyTargetLowering::getTargetNodeName(unsigned Opcode) const {
|
|
|
|
switch (static_cast<WebAssemblyISD::NodeType>(Opcode)) {
|
2015-08-25 06:16:48 +08:00
|
|
|
case WebAssemblyISD::FIRST_NUMBER:
|
|
|
|
break;
|
|
|
|
#define HANDLE_NODETYPE(NODE) \
|
|
|
|
case WebAssemblyISD::NODE: \
|
|
|
|
return "WebAssemblyISD::" #NODE;
|
|
|
|
#include "WebAssemblyISD.def"
|
|
|
|
#undef HANDLE_NODETYPE
|
2015-08-12 04:13:18 +08:00
|
|
|
}
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2015-11-13 09:42:29 +08:00
|
|
|
std::pair<unsigned, const TargetRegisterClass *>
|
|
|
|
WebAssemblyTargetLowering::getRegForInlineAsmConstraint(
|
|
|
|
const TargetRegisterInfo *TRI, StringRef Constraint, MVT VT) const {
|
|
|
|
// First, see if this is a constraint that directly corresponds to a
|
|
|
|
// WebAssembly register class.
|
|
|
|
if (Constraint.size() == 1) {
|
|
|
|
switch (Constraint[0]) {
|
|
|
|
case 'r':
|
2015-12-06 04:03:44 +08:00
|
|
|
assert(VT != MVT::iPTR && "Pointer MVT not expected here");
|
|
|
|
if (VT.isInteger() && !VT.isVector()) {
|
|
|
|
if (VT.getSizeInBits() <= 32)
|
|
|
|
return std::make_pair(0U, &WebAssembly::I32RegClass);
|
|
|
|
if (VT.getSizeInBits() <= 64)
|
|
|
|
return std::make_pair(0U, &WebAssembly::I64RegClass);
|
|
|
|
}
|
2015-11-26 06:28:50 +08:00
|
|
|
break;
|
2015-11-13 09:42:29 +08:00
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return TargetLowering::getRegForInlineAsmConstraint(TRI, Constraint, VT);
|
|
|
|
}
|
|
|
|
|
2015-11-20 07:04:59 +08:00
|
|
|
bool WebAssemblyTargetLowering::isCheapToSpeculateCttz() const {
|
|
|
|
// Assume ctz is a relatively cheap operation.
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool WebAssemblyTargetLowering::isCheapToSpeculateCtlz() const {
|
|
|
|
// Assume clz is a relatively cheap operation.
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2015-12-16 06:01:29 +08:00
|
|
|
bool WebAssemblyTargetLowering::isLegalAddressingMode(const DataLayout &DL,
|
|
|
|
const AddrMode &AM,
|
|
|
|
Type *Ty,
|
|
|
|
unsigned AS) const {
|
|
|
|
// WebAssembly offsets are added as unsigned without wrapping. The
|
|
|
|
// isLegalAddressingMode gives us no way to determine if wrapping could be
|
|
|
|
// happening, so we approximate this by accepting only non-negative offsets.
|
|
|
|
if (AM.BaseOffs < 0)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
// WebAssembly has no scale register operands.
|
|
|
|
if (AM.Scale != 0)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
// Everything else is legal.
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2016-01-26 11:39:31 +08:00
|
|
|
bool WebAssemblyTargetLowering::allowsMisalignedMemoryAccesses(
|
|
|
|
EVT /*VT*/, unsigned /*AddrSpace*/, unsigned /*Align*/,
|
|
|
|
bool *Fast) const {
|
|
|
|
// WebAssembly supports unaligned accesses, though it should be declared
|
|
|
|
// with the p2align attribute on loads and stores which do so, and there
|
|
|
|
// may be a performance impact. We tell LLVM they're "fast" because
|
2016-01-26 22:55:17 +08:00
|
|
|
// for the kinds of things that LLVM uses this for (merging adjacent stores
|
2016-01-26 11:39:31 +08:00
|
|
|
// of constants, etc.), WebAssembly implementations will either want the
|
|
|
|
// unaligned access or they'll split anyway.
|
|
|
|
if (Fast)
|
|
|
|
*Fast = true;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2015-06-30 07:51:55 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// WebAssembly Lowering private implementation.
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// Lowering Code
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2015-07-23 05:28:15 +08:00
|
|
|
static void fail(SDLoc DL, SelectionDAG &DAG, const char *msg) {
|
|
|
|
MachineFunction &MF = DAG.getMachineFunction();
|
|
|
|
DAG.getContext()->diagnose(
|
2016-01-28 02:03:40 +08:00
|
|
|
DiagnosticInfoUnsupported(*MF.getFunction(), msg, DL));
|
2015-07-23 05:28:15 +08:00
|
|
|
}
|
|
|
|
|
2015-12-05 01:16:07 +08:00
|
|
|
// Test whether the given calling convention is supported.
|
2015-12-05 01:18:32 +08:00
|
|
|
static bool CallingConvSupported(CallingConv::ID CallConv) {
|
2015-12-05 01:16:07 +08:00
|
|
|
// We currently support the language-independent target-independent
|
2015-12-05 02:27:03 +08:00
|
|
|
// 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.
|
2015-12-05 01:18:32 +08:00
|
|
|
return CallConv == CallingConv::C || CallConv == CallingConv::Fast ||
|
2015-12-05 02:27:03 +08:00
|
|
|
CallConv == CallingConv::Cold ||
|
|
|
|
CallConv == CallingConv::PreserveMost ||
|
|
|
|
CallConv == CallingConv::PreserveAll ||
|
|
|
|
CallConv == CallingConv::CXX_FAST_TLS;
|
2015-12-05 01:16:07 +08:00
|
|
|
}
|
|
|
|
|
2015-08-25 05:59:51 +08:00
|
|
|
SDValue
|
|
|
|
WebAssemblyTargetLowering::LowerCall(CallLoweringInfo &CLI,
|
|
|
|
SmallVectorImpl<SDValue> &InVals) const {
|
|
|
|
SelectionDAG &DAG = CLI.DAG;
|
|
|
|
SDLoc DL = CLI.DL;
|
|
|
|
SDValue Chain = CLI.Chain;
|
|
|
|
SDValue Callee = CLI.Callee;
|
|
|
|
MachineFunction &MF = DAG.getMachineFunction();
|
|
|
|
|
|
|
|
CallingConv::ID CallConv = CLI.CallConv;
|
2015-12-05 01:16:07 +08:00
|
|
|
if (!CallingConvSupported(CallConv))
|
2015-10-03 04:54:23 +08:00
|
|
|
fail(DL, DAG,
|
|
|
|
"WebAssembly doesn't support language-specific or target-specific "
|
|
|
|
"calling conventions yet");
|
2015-08-25 05:59:51 +08:00
|
|
|
if (CLI.IsPatchPoint)
|
|
|
|
fail(DL, DAG, "WebAssembly doesn't support patch point yet");
|
|
|
|
|
2015-10-03 04:54:23 +08:00
|
|
|
// WebAssembly doesn't currently support explicit tail calls. If they are
|
|
|
|
// required, fail. Otherwise, just disable them.
|
|
|
|
if ((CallConv == CallingConv::Fast && CLI.IsTailCall &&
|
|
|
|
MF.getTarget().Options.GuaranteedTailCallOpt) ||
|
|
|
|
(CLI.CS && CLI.CS->isMustTailCall()))
|
|
|
|
fail(DL, DAG, "WebAssembly doesn't support tail call yet");
|
|
|
|
CLI.IsTailCall = false;
|
|
|
|
|
2015-08-25 05:59:51 +08:00
|
|
|
SmallVectorImpl<SDValue> &OutVals = CLI.OutVals;
|
2015-09-09 09:52:45 +08:00
|
|
|
|
2015-08-25 05:59:51 +08:00
|
|
|
SmallVectorImpl<ISD::InputArg> &Ins = CLI.Ins;
|
2015-09-09 09:52:45 +08:00
|
|
|
if (Ins.size() > 1)
|
|
|
|
fail(DL, DAG, "WebAssembly doesn't support more than 1 returned value yet");
|
|
|
|
|
2015-12-05 01:12:52 +08:00
|
|
|
SmallVectorImpl<ISD::OutputArg> &Outs = CLI.Outs;
|
|
|
|
for (const ISD::OutputArg &Out : Outs) {
|
2015-12-10 08:22:40 +08:00
|
|
|
if (Out.Flags.isByVal())
|
|
|
|
fail(DL, DAG, "WebAssembly hasn't implemented byval arguments");
|
|
|
|
if (Out.Flags.isNest())
|
|
|
|
fail(DL, DAG, "WebAssembly hasn't implemented nest arguments");
|
2015-12-05 01:12:52 +08:00
|
|
|
if (Out.Flags.isInAlloca())
|
2015-12-10 08:22:40 +08:00
|
|
|
fail(DL, DAG, "WebAssembly hasn't implemented inalloca arguments");
|
2015-12-05 01:12:52 +08:00
|
|
|
if (Out.Flags.isInConsecutiveRegs())
|
2015-12-10 08:22:40 +08:00
|
|
|
fail(DL, DAG, "WebAssembly hasn't implemented cons regs arguments");
|
2015-12-05 01:12:52 +08:00
|
|
|
if (Out.Flags.isInConsecutiveRegsLast())
|
2015-12-10 08:22:40 +08:00
|
|
|
fail(DL, DAG, "WebAssembly hasn't implemented cons regs last arguments");
|
2015-12-05 01:12:52 +08:00
|
|
|
}
|
|
|
|
|
2015-08-25 05:59:51 +08:00
|
|
|
bool IsVarArg = CLI.IsVarArg;
|
2015-12-05 07:22:35 +08:00
|
|
|
unsigned NumFixedArgs = CLI.NumFixedArgs;
|
|
|
|
auto PtrVT = getPointerTy(MF.getDataLayout());
|
2015-09-09 09:52:45 +08:00
|
|
|
|
2015-08-25 05:59:51 +08:00
|
|
|
// Analyze operands of the call, assigning locations to each operand.
|
|
|
|
SmallVector<CCValAssign, 16> ArgLocs;
|
|
|
|
CCState CCInfo(CallConv, IsVarArg, MF, ArgLocs, *DAG.getContext());
|
|
|
|
|
2015-12-05 07:22:35 +08:00
|
|
|
if (IsVarArg) {
|
|
|
|
// Outgoing non-fixed arguments are placed at the top of the stack. First
|
|
|
|
// compute their offsets and the total amount of argument stack space
|
|
|
|
// needed.
|
|
|
|
for (SDValue Arg :
|
|
|
|
make_range(OutVals.begin() + NumFixedArgs, OutVals.end())) {
|
|
|
|
EVT VT = Arg.getValueType();
|
|
|
|
assert(VT != MVT::iPTR && "Legalized args should be concrete");
|
|
|
|
Type *Ty = VT.getTypeForEVT(*DAG.getContext());
|
|
|
|
unsigned Offset =
|
|
|
|
CCInfo.AllocateStack(MF.getDataLayout().getTypeAllocSize(Ty),
|
|
|
|
MF.getDataLayout().getABITypeAlignment(Ty));
|
|
|
|
CCInfo.addLoc(CCValAssign::getMem(ArgLocs.size(), VT.getSimpleVT(),
|
|
|
|
Offset, VT.getSimpleVT(),
|
|
|
|
CCValAssign::Full));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
unsigned NumBytes = CCInfo.getAlignedCallFrameSize();
|
|
|
|
|
[WebAssembly] Fix ADJCALLSTACKDOWN/UP use/defs
Summary:
ADJCALLSTACK{DOWN,UP} (aka CALLSEQ_{START,END}) MIs are supposed to use
and def the stack pointer. Since they do not, all the nodes are being
eliminated by DeadMachineInstructionElim, so they aren't in the IR when
PrologEpilogInserter/eliminateCallFramePseudo needs them.
This change fixes that, but since RegStackify will not stackify across
them (and it runs early, before PEI), change LowerCall to only emit them
when the call frame size is > 0. That makes the current code work the
same way and makes code handled by D15344 also work the same way. We can
expand the condition beyond NumBytes > 0 in the future if needed.
Reviewers: sunfish, jfb
Subscribers: jfb, dschuff, llvm-commits
Differential Revision: http://reviews.llvm.org/D15459
llvm-svn: 255356
2015-12-12 02:55:34 +08:00
|
|
|
SDValue NB;
|
|
|
|
if (NumBytes) {
|
|
|
|
NB = DAG.getConstant(NumBytes, DL, PtrVT, true);
|
|
|
|
Chain = DAG.getCALLSEQ_START(Chain, NB, DL);
|
|
|
|
}
|
2015-08-25 05:59:51 +08:00
|
|
|
|
2015-12-05 07:22:35 +08:00
|
|
|
if (IsVarArg) {
|
|
|
|
// For non-fixed arguments, next emit stores to store the argument values
|
|
|
|
// to the stack at the offsets computed above.
|
|
|
|
SDValue SP = DAG.getCopyFromReg(
|
|
|
|
Chain, DL, getStackPointerRegisterToSaveRestore(), PtrVT);
|
|
|
|
unsigned ValNo = 0;
|
|
|
|
SmallVector<SDValue, 8> Chains;
|
|
|
|
for (SDValue Arg :
|
|
|
|
make_range(OutVals.begin() + NumFixedArgs, OutVals.end())) {
|
|
|
|
assert(ArgLocs[ValNo].getValNo() == ValNo &&
|
|
|
|
"ArgLocs should remain in order and only hold varargs args");
|
|
|
|
unsigned Offset = ArgLocs[ValNo++].getLocMemOffset();
|
|
|
|
SDValue Add = DAG.getNode(ISD::ADD, DL, PtrVT, SP,
|
|
|
|
DAG.getConstant(Offset, DL, PtrVT));
|
|
|
|
Chains.push_back(DAG.getStore(Chain, DL, Arg, Add,
|
|
|
|
MachinePointerInfo::getStack(MF, Offset),
|
|
|
|
false, false, 0));
|
|
|
|
}
|
|
|
|
if (!Chains.empty())
|
|
|
|
Chain = DAG.getNode(ISD::TokenFactor, DL, MVT::Other, Chains);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Compute the operands for the CALLn node.
|
2015-08-25 05:59:51 +08:00
|
|
|
SmallVector<SDValue, 16> Ops;
|
|
|
|
Ops.push_back(Chain);
|
2015-08-25 06:16:48 +08:00
|
|
|
Ops.push_back(Callee);
|
2015-12-05 07:22:35 +08:00
|
|
|
|
|
|
|
// Add all fixed arguments. Note that for non-varargs calls, NumFixedArgs
|
|
|
|
// isn't reliable.
|
|
|
|
Ops.append(OutVals.begin(),
|
|
|
|
IsVarArg ? OutVals.begin() + NumFixedArgs : OutVals.end());
|
2015-08-25 05:59:51 +08:00
|
|
|
|
|
|
|
SmallVector<EVT, 8> Tys;
|
2015-12-05 01:12:52 +08:00
|
|
|
for (const auto &In : Ins) {
|
2015-12-10 08:22:40 +08:00
|
|
|
assert(!In.Flags.isByVal() && "byval is not valid for return values");
|
|
|
|
assert(!In.Flags.isNest() && "nest is not valid for return values");
|
2015-12-05 01:12:52 +08:00
|
|
|
if (In.Flags.isInAlloca())
|
2015-12-10 08:22:40 +08:00
|
|
|
fail(DL, DAG, "WebAssembly hasn't implemented inalloca return values");
|
2015-12-05 01:12:52 +08:00
|
|
|
if (In.Flags.isInConsecutiveRegs())
|
2015-12-10 08:22:40 +08:00
|
|
|
fail(DL, DAG, "WebAssembly hasn't implemented cons regs return values");
|
2015-12-05 01:12:52 +08:00
|
|
|
if (In.Flags.isInConsecutiveRegsLast())
|
2015-12-16 06:01:29 +08:00
|
|
|
fail(DL, DAG,
|
|
|
|
"WebAssembly hasn't implemented cons regs last return values");
|
2015-12-05 01:12:52 +08:00
|
|
|
// Ignore In.getOrigAlign() because all our arguments are passed in
|
|
|
|
// registers.
|
2015-08-25 05:59:51 +08:00
|
|
|
Tys.push_back(In.VT);
|
2015-12-05 01:12:52 +08:00
|
|
|
}
|
2015-08-25 05:59:51 +08:00
|
|
|
Tys.push_back(MVT::Other);
|
2015-08-25 06:16:48 +08:00
|
|
|
SDVTList TyList = DAG.getVTList(Tys);
|
2015-09-10 00:13:47 +08:00
|
|
|
SDValue Res =
|
|
|
|
DAG.getNode(Ins.empty() ? WebAssemblyISD::CALL0 : WebAssemblyISD::CALL1,
|
|
|
|
DL, TyList, Ops);
|
2015-08-25 06:16:48 +08:00
|
|
|
if (Ins.empty()) {
|
|
|
|
Chain = Res;
|
|
|
|
} else {
|
|
|
|
InVals.push_back(Res);
|
|
|
|
Chain = Res.getValue(1);
|
|
|
|
}
|
2015-08-25 05:59:51 +08:00
|
|
|
|
[WebAssembly] Fix ADJCALLSTACKDOWN/UP use/defs
Summary:
ADJCALLSTACK{DOWN,UP} (aka CALLSEQ_{START,END}) MIs are supposed to use
and def the stack pointer. Since they do not, all the nodes are being
eliminated by DeadMachineInstructionElim, so they aren't in the IR when
PrologEpilogInserter/eliminateCallFramePseudo needs them.
This change fixes that, but since RegStackify will not stackify across
them (and it runs early, before PEI), change LowerCall to only emit them
when the call frame size is > 0. That makes the current code work the
same way and makes code handled by D15344 also work the same way. We can
expand the condition beyond NumBytes > 0 in the future if needed.
Reviewers: sunfish, jfb
Subscribers: jfb, dschuff, llvm-commits
Differential Revision: http://reviews.llvm.org/D15459
llvm-svn: 255356
2015-12-12 02:55:34 +08:00
|
|
|
if (NumBytes) {
|
2015-12-17 07:21:30 +08:00
|
|
|
SDValue Unused = DAG.getTargetConstant(0, DL, PtrVT);
|
[WebAssembly] Fix ADJCALLSTACKDOWN/UP use/defs
Summary:
ADJCALLSTACK{DOWN,UP} (aka CALLSEQ_{START,END}) MIs are supposed to use
and def the stack pointer. Since they do not, all the nodes are being
eliminated by DeadMachineInstructionElim, so they aren't in the IR when
PrologEpilogInserter/eliminateCallFramePseudo needs them.
This change fixes that, but since RegStackify will not stackify across
them (and it runs early, before PEI), change LowerCall to only emit them
when the call frame size is > 0. That makes the current code work the
same way and makes code handled by D15344 also work the same way. We can
expand the condition beyond NumBytes > 0 in the future if needed.
Reviewers: sunfish, jfb
Subscribers: jfb, dschuff, llvm-commits
Differential Revision: http://reviews.llvm.org/D15459
llvm-svn: 255356
2015-12-12 02:55:34 +08:00
|
|
|
Chain = DAG.getCALLSEQ_END(Chain, NB, Unused, SDValue(), DL);
|
|
|
|
}
|
2015-08-25 05:59:51 +08:00
|
|
|
|
|
|
|
return Chain;
|
|
|
|
}
|
|
|
|
|
2015-07-23 05:28:15 +08:00
|
|
|
bool WebAssemblyTargetLowering::CanLowerReturn(
|
2015-11-30 06:32:02 +08:00
|
|
|
CallingConv::ID /*CallConv*/, MachineFunction & /*MF*/, bool /*IsVarArg*/,
|
|
|
|
const SmallVectorImpl<ISD::OutputArg> &Outs,
|
|
|
|
LLVMContext & /*Context*/) const {
|
2015-07-23 05:28:15 +08:00
|
|
|
// WebAssembly can't currently handle returning tuples.
|
|
|
|
return Outs.size() <= 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
SDValue WebAssemblyTargetLowering::LowerReturn(
|
2015-12-05 07:22:35 +08:00
|
|
|
SDValue Chain, CallingConv::ID CallConv, bool /*IsVarArg*/,
|
2015-07-23 05:28:15 +08:00
|
|
|
const SmallVectorImpl<ISD::OutputArg> &Outs,
|
|
|
|
const SmallVectorImpl<SDValue> &OutVals, SDLoc DL,
|
|
|
|
SelectionDAG &DAG) const {
|
|
|
|
assert(Outs.size() <= 1 && "WebAssembly can only return up to one value");
|
2015-12-05 01:16:07 +08:00
|
|
|
if (!CallingConvSupported(CallConv))
|
2015-07-23 05:28:15 +08:00
|
|
|
fail(DL, DAG, "WebAssembly doesn't support non-C calling conventions");
|
|
|
|
|
2015-08-01 01:53:38 +08:00
|
|
|
SmallVector<SDValue, 4> RetOps(1, Chain);
|
|
|
|
RetOps.append(OutVals.begin(), OutVals.end());
|
2015-08-01 05:04:18 +08:00
|
|
|
Chain = DAG.getNode(WebAssemblyISD::RETURN, DL, MVT::Other, RetOps);
|
2015-07-23 05:28:15 +08:00
|
|
|
|
2015-11-11 09:33:02 +08:00
|
|
|
// Record the number and types of the return values.
|
|
|
|
for (const ISD::OutputArg &Out : Outs) {
|
2015-12-03 07:40:03 +08:00
|
|
|
assert(!Out.Flags.isByVal() && "byval is not valid for return values");
|
|
|
|
assert(!Out.Flags.isNest() && "nest is not valid for return values");
|
2015-12-05 07:22:35 +08:00
|
|
|
assert(Out.IsFixed && "non-fixed return value is not valid");
|
2015-11-11 09:33:02 +08:00
|
|
|
if (Out.Flags.isInAlloca())
|
|
|
|
fail(DL, DAG, "WebAssembly hasn't implemented inalloca results");
|
|
|
|
if (Out.Flags.isInConsecutiveRegs())
|
|
|
|
fail(DL, DAG, "WebAssembly hasn't implemented cons regs results");
|
|
|
|
if (Out.Flags.isInConsecutiveRegsLast())
|
|
|
|
fail(DL, DAG, "WebAssembly hasn't implemented cons regs last results");
|
|
|
|
}
|
|
|
|
|
2015-07-23 05:28:15 +08:00
|
|
|
return Chain;
|
|
|
|
}
|
|
|
|
|
|
|
|
SDValue WebAssemblyTargetLowering::LowerFormalArguments(
|
2015-12-05 07:22:35 +08:00
|
|
|
SDValue Chain, CallingConv::ID CallConv, bool /*IsVarArg*/,
|
2015-07-23 05:28:15 +08:00
|
|
|
const SmallVectorImpl<ISD::InputArg> &Ins, SDLoc DL, SelectionDAG &DAG,
|
|
|
|
SmallVectorImpl<SDValue> &InVals) const {
|
|
|
|
MachineFunction &MF = DAG.getMachineFunction();
|
|
|
|
|
2015-12-05 01:16:07 +08:00
|
|
|
if (!CallingConvSupported(CallConv))
|
2015-07-23 05:28:15 +08:00
|
|
|
fail(DL, DAG, "WebAssembly doesn't support non-C calling conventions");
|
|
|
|
|
2015-11-26 03:36:19 +08:00
|
|
|
// Set up the incoming ARGUMENTS value, which serves to represent the liveness
|
|
|
|
// of the incoming values before they're represented by virtual registers.
|
|
|
|
MF.getRegInfo().addLiveIn(WebAssembly::ARGUMENTS);
|
|
|
|
|
2015-08-01 01:53:38 +08:00
|
|
|
for (const ISD::InputArg &In : Ins) {
|
|
|
|
if (In.Flags.isByVal())
|
|
|
|
fail(DL, DAG, "WebAssembly hasn't implemented byval arguments");
|
|
|
|
if (In.Flags.isInAlloca())
|
|
|
|
fail(DL, DAG, "WebAssembly hasn't implemented inalloca arguments");
|
|
|
|
if (In.Flags.isNest())
|
|
|
|
fail(DL, DAG, "WebAssembly hasn't implemented nest arguments");
|
|
|
|
if (In.Flags.isInConsecutiveRegs())
|
|
|
|
fail(DL, DAG, "WebAssembly hasn't implemented cons regs arguments");
|
|
|
|
if (In.Flags.isInConsecutiveRegsLast())
|
|
|
|
fail(DL, DAG, "WebAssembly hasn't implemented cons regs last arguments");
|
2015-11-26 02:13:18 +08:00
|
|
|
// Ignore In.getOrigAlign() because all our arguments are passed in
|
|
|
|
// registers.
|
2015-08-01 02:13:27 +08:00
|
|
|
InVals.push_back(
|
|
|
|
In.Used
|
|
|
|
? DAG.getNode(WebAssemblyISD::ARGUMENT, DL, In.VT,
|
2015-11-15 07:28:15 +08:00
|
|
|
DAG.getTargetConstant(InVals.size(), DL, MVT::i32))
|
2015-12-05 01:09:42 +08:00
|
|
|
: DAG.getUNDEF(In.VT));
|
2015-11-11 09:33:02 +08:00
|
|
|
|
|
|
|
// Record the number and types of arguments.
|
|
|
|
MF.getInfo<WebAssemblyFunctionInfo>()->addParam(In.VT);
|
2015-08-01 01:53:38 +08:00
|
|
|
}
|
2015-07-23 05:28:15 +08:00
|
|
|
|
2015-12-05 07:22:35 +08:00
|
|
|
// Incoming varargs arguments are on the stack and will be accessed through
|
|
|
|
// va_arg, so we don't need to do anything for them here.
|
|
|
|
|
2015-07-23 05:28:15 +08:00
|
|
|
return Chain;
|
|
|
|
}
|
|
|
|
|
2015-06-30 07:51:55 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
2015-08-25 06:16:48 +08:00
|
|
|
// Custom lowering hooks.
|
2015-06-30 07:51:55 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2015-08-25 06:16:48 +08:00
|
|
|
SDValue WebAssemblyTargetLowering::LowerOperation(SDValue Op,
|
|
|
|
SelectionDAG &DAG) const {
|
|
|
|
switch (Op.getOpcode()) {
|
|
|
|
default:
|
|
|
|
llvm_unreachable("unimplemented operation lowering");
|
|
|
|
return SDValue();
|
2015-12-12 07:49:46 +08:00
|
|
|
case ISD::FrameIndex:
|
|
|
|
return LowerFrameIndex(Op, DAG);
|
2015-08-25 06:16:48 +08:00
|
|
|
case ISD::GlobalAddress:
|
|
|
|
return LowerGlobalAddress(Op, DAG);
|
2015-11-26 00:44:29 +08:00
|
|
|
case ISD::ExternalSymbol:
|
|
|
|
return LowerExternalSymbol(Op, DAG);
|
2015-09-17 00:51:30 +08:00
|
|
|
case ISD::JumpTable:
|
|
|
|
return LowerJumpTable(Op, DAG);
|
|
|
|
case ISD::BR_JT:
|
|
|
|
return LowerBR_JT(Op, DAG);
|
2015-12-05 07:22:35 +08:00
|
|
|
case ISD::VASTART:
|
|
|
|
return LowerVASTART(Op, DAG);
|
2015-08-25 06:16:48 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-12-12 07:49:46 +08:00
|
|
|
SDValue WebAssemblyTargetLowering::LowerFrameIndex(SDValue Op,
|
|
|
|
SelectionDAG &DAG) const {
|
|
|
|
int FI = cast<FrameIndexSDNode>(Op)->getIndex();
|
|
|
|
return DAG.getTargetFrameIndex(FI, Op.getValueType());
|
|
|
|
}
|
|
|
|
|
2015-08-25 06:16:48 +08:00
|
|
|
SDValue WebAssemblyTargetLowering::LowerGlobalAddress(SDValue Op,
|
|
|
|
SelectionDAG &DAG) const {
|
|
|
|
SDLoc DL(Op);
|
|
|
|
const auto *GA = cast<GlobalAddressSDNode>(Op);
|
|
|
|
EVT VT = Op.getValueType();
|
2016-01-12 07:38:05 +08:00
|
|
|
assert(GA->getTargetFlags() == 0 &&
|
|
|
|
"Unexpected target flags on generic GlobalAddressSDNode");
|
2015-08-25 06:16:48 +08:00
|
|
|
if (GA->getAddressSpace() != 0)
|
|
|
|
fail(DL, DAG, "WebAssembly only expects the 0 address space");
|
2015-12-16 06:01:29 +08:00
|
|
|
return DAG.getNode(
|
|
|
|
WebAssemblyISD::Wrapper, DL, VT,
|
|
|
|
DAG.getTargetGlobalAddress(GA->getGlobal(), DL, VT, GA->getOffset()));
|
2015-08-25 06:16:48 +08:00
|
|
|
}
|
|
|
|
|
2015-11-30 06:32:02 +08:00
|
|
|
SDValue
|
|
|
|
WebAssemblyTargetLowering::LowerExternalSymbol(SDValue Op,
|
|
|
|
SelectionDAG &DAG) const {
|
2015-11-26 00:44:29 +08:00
|
|
|
SDLoc DL(Op);
|
|
|
|
const auto *ES = cast<ExternalSymbolSDNode>(Op);
|
|
|
|
EVT VT = Op.getValueType();
|
2016-01-12 07:38:05 +08:00
|
|
|
assert(ES->getTargetFlags() == 0 &&
|
|
|
|
"Unexpected target flags on generic ExternalSymbolSDNode");
|
|
|
|
// Set the TargetFlags to 0x1 which indicates that this is a "function"
|
|
|
|
// symbol rather than a data symbol. We do this unconditionally even though
|
|
|
|
// we don't know anything about the symbol other than its name, because all
|
|
|
|
// external symbols used in target-independent SelectionDAG code are for
|
|
|
|
// functions.
|
2015-11-26 00:44:29 +08:00
|
|
|
return DAG.getNode(WebAssemblyISD::Wrapper, DL, VT,
|
2016-01-12 07:38:05 +08:00
|
|
|
DAG.getTargetExternalSymbol(ES->getSymbol(), VT,
|
|
|
|
/*TargetFlags=*/0x1));
|
2015-11-26 00:44:29 +08:00
|
|
|
}
|
|
|
|
|
2015-09-17 00:51:30 +08:00
|
|
|
SDValue WebAssemblyTargetLowering::LowerJumpTable(SDValue Op,
|
|
|
|
SelectionDAG &DAG) const {
|
|
|
|
// There's no need for a Wrapper node because we always incorporate a jump
|
2015-11-20 11:02:49 +08:00
|
|
|
// table operand into a TABLESWITCH instruction, rather than ever
|
|
|
|
// materializing it in a register.
|
2015-09-17 00:51:30 +08:00
|
|
|
const JumpTableSDNode *JT = cast<JumpTableSDNode>(Op);
|
|
|
|
return DAG.getTargetJumpTable(JT->getIndex(), Op.getValueType(),
|
|
|
|
JT->getTargetFlags());
|
|
|
|
}
|
|
|
|
|
|
|
|
SDValue WebAssemblyTargetLowering::LowerBR_JT(SDValue Op,
|
|
|
|
SelectionDAG &DAG) const {
|
|
|
|
SDLoc DL(Op);
|
|
|
|
SDValue Chain = Op.getOperand(0);
|
|
|
|
const auto *JT = cast<JumpTableSDNode>(Op.getOperand(1));
|
|
|
|
SDValue Index = Op.getOperand(2);
|
|
|
|
assert(JT->getTargetFlags() == 0 && "WebAssembly doesn't set target flags");
|
|
|
|
|
|
|
|
SmallVector<SDValue, 8> Ops;
|
|
|
|
Ops.push_back(Chain);
|
|
|
|
Ops.push_back(Index);
|
|
|
|
|
|
|
|
MachineJumpTableInfo *MJTI = DAG.getMachineFunction().getJumpTableInfo();
|
|
|
|
const auto &MBBs = MJTI->getJumpTables()[JT->getIndex()].MBBs;
|
|
|
|
|
|
|
|
// TODO: For now, we just pick something arbitrary for a default case for now.
|
|
|
|
// We really want to sniff out the guard and put in the real default case (and
|
|
|
|
// delete the guard).
|
|
|
|
Ops.push_back(DAG.getBasicBlock(MBBs[0]));
|
|
|
|
|
|
|
|
// Add an operand for each case.
|
|
|
|
for (auto MBB : MBBs)
|
|
|
|
Ops.push_back(DAG.getBasicBlock(MBB));
|
|
|
|
|
2015-11-20 11:02:49 +08:00
|
|
|
return DAG.getNode(WebAssemblyISD::TABLESWITCH, DL, MVT::Other, Ops);
|
2015-09-17 00:51:30 +08:00
|
|
|
}
|
|
|
|
|
2015-12-05 07:22:35 +08:00
|
|
|
SDValue WebAssemblyTargetLowering::LowerVASTART(SDValue Op,
|
|
|
|
SelectionDAG &DAG) const {
|
|
|
|
SDLoc DL(Op);
|
|
|
|
EVT PtrVT = getPointerTy(DAG.getMachineFunction().getDataLayout());
|
|
|
|
|
|
|
|
// The incoming non-fixed arguments are placed on the top of the stack, with
|
|
|
|
// natural alignment, at the point of the call, so the base pointer is just
|
|
|
|
// the current frame pointer.
|
|
|
|
DAG.getMachineFunction().getFrameInfo()->setFrameAddressIsTaken(true);
|
|
|
|
unsigned FP =
|
2015-12-08 11:42:50 +08:00
|
|
|
Subtarget->getRegisterInfo()->getFrameRegister(DAG.getMachineFunction());
|
2015-12-05 07:22:35 +08:00
|
|
|
SDValue FrameAddr = DAG.getCopyFromReg(DAG.getEntryNode(), DL, FP, PtrVT);
|
|
|
|
const Value *SV = cast<SrcValueSDNode>(Op.getOperand(2))->getValue();
|
|
|
|
return DAG.getStore(Op.getOperand(0), DL, FrameAddr, Op.getOperand(1),
|
|
|
|
MachinePointerInfo(SV), false, false, 0);
|
|
|
|
}
|
|
|
|
|
2015-06-30 07:51:55 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// WebAssembly Optimization Hooks
|
|
|
|
//===----------------------------------------------------------------------===//
|