if (IsVarArg)
fail(DL, DAG, "WebAssembly doesn't support varargs yet");
+ // 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);
+
for (const ISD::InputArg &In : Ins) {
if (In.Flags.isByVal())
fail(DL, DAG, "WebAssembly hasn't implemented byval arguments");
///
//===----------------------------------------------------------------------===//
+let Defs = [ARGUMENTS] in {
+
// The call sequence start/end LLVM-isms isn't useful to WebAssembly since it's
// a virtual ISA.
let isCodeGenOnly = 1 in {
[(WebAssemblycall0 I32:$callee)],
"call_indirect\t$callee">;
} // Uses = [SP32,SP64], isCall = 1
+
+} // Defs = [ARGUMENTS]
///
//===----------------------------------------------------------------------===//
+let Defs = [ARGUMENTS] in {
+
let isBranch = 1, isTerminator = 1, hasCtrlDep = 1 in {
def BR_IF : I<(outs), (ins I32:$a, bb_op:$dst),
[(brcond I32:$a, bb:$dst)],
} // isReturn = 1
def UNREACHABLE : I<(outs), (ins), [(trap)], "unreachable">;
} // isTerminator = 1, hasCtrlDep = 1, isBarrier = 1
+
+} // Defs = [ARGUMENTS]
///
//===----------------------------------------------------------------------===//
+let Defs = [ARGUMENTS] in {
+
def I32_WRAP_I64 : I<(outs I32:$dst), (ins I64:$src),
[(set I32:$dst, (trunc I64:$src))],
"i32.wrap/i64\t$dst, $src">;
def F64_REINTERPRET_I64 : I<(outs F64:$dst), (ins I64:$src),
[(set F64:$dst, (bitconvert I64:$src))],
"f64.reinterpret/i64\t$dst, $src">;
+
+} // Defs = [ARGUMENTS]
///
//===----------------------------------------------------------------------===//
+let Defs = [ARGUMENTS] in {
+
defm ADD : BinaryFP<fadd, "add ">;
defm SUB : BinaryFP<fsub, "sub ">;
defm MUL : BinaryFP<fmul, "mul ">;
defm TRUNC : UnaryFP<ftrunc, "trunc">;
defm NEAREST : UnaryFP<fnearbyint, "nearest">;
+} // Defs = [ARGUMENTS]
+
// WebAssembly doesn't expose inexact exceptions, so map frint to fnearbyint.
def : Pat<(frint f32:$src), (NEAREST_F32 f32:$src)>;
def : Pat<(frint f64:$src), (NEAREST_F64 f64:$src)>;
+let Defs = [ARGUMENTS] in {
+
defm EQ : ComparisonFP<SETOEQ, "eq ">;
defm NE : ComparisonFP<SETUNE, "ne ">;
defm LT : ComparisonFP<SETOLT, "lt ">;
defm GT : ComparisonFP<SETOGT, "gt ">;
defm GE : ComparisonFP<SETOGE, "ge ">;
+} // Defs = [ARGUMENTS]
+
// Don't care floating-point comparisons, supported via other comparisons.
def : Pat<(seteq f32:$lhs, f32:$rhs), (EQ_F32 f32:$lhs, f32:$rhs)>;
def : Pat<(setne f32:$lhs, f32:$rhs), (NE_F32 f32:$lhs, f32:$rhs)>;
def : Pat<(setgt f64:$lhs, f64:$rhs), (GT_F64 f64:$lhs, f64:$rhs)>;
def : Pat<(setge f64:$lhs, f64:$rhs), (GE_F64 f64:$lhs, f64:$rhs)>;
+let Defs = [ARGUMENTS] in {
+
def SELECT_F32 : I<(outs F32:$dst), (ins I32:$cond, F32:$lhs, F32:$rhs),
[(set F32:$dst, (select I32:$cond, F32:$lhs, F32:$rhs))],
"f32.select\t$dst, $cond, $lhs, $rhs">;
def SELECT_F64 : I<(outs F64:$dst), (ins I32:$cond, F64:$lhs, F64:$rhs),
[(set F64:$dst, (select I32:$cond, F64:$lhs, F64:$rhs))],
"f64.select\t$dst, $cond, $lhs, $rhs">;
+
+} // Defs = [ARGUMENTS]
//===----------------------------------------------------------------------===//
multiclass ARGUMENT<WebAssemblyRegClass vt> {
+ let hasSideEffects = 1, Uses = [ARGUMENTS] in
def ARGUMENT_#vt : I<(outs vt:$res), (ins i32imm:$argno),
[(set vt:$res, (WebAssemblyargument timm:$argno))]>;
}
defm : ARGUMENT<F32>;
defm : ARGUMENT<F64>;
+let Defs = [ARGUMENTS] in {
+
// get_local and set_local are not generated by instruction selection; they
// are implied by virtual register uses and defs in most contexts. However,
// they are explicitly emitted for special purposes.
[(set F64:$res, fpimm:$imm)],
"f64.const\t$res, $imm">;
+} // Defs = [ARGUMENTS]
+
def : Pat<(i32 (WebAssemblywrapper tglobaladdr:$dst)),
(CONST_I32 tglobaladdr:$dst)>;
def : Pat<(i32 (WebAssemblywrapper texternalsym:$dst)),
(CONST_I32 texternalsym:$dst)>;
+let Defs = [ARGUMENTS] in {
+
def JUMP_TABLE : I<(outs I32:$dst), (ins tjumptable_op:$addr),
[(set I32:$dst, (WebAssemblywrapper tjumptable:$addr))],
"jump_table\t$dst, $addr">;
def RESULT : I<(outs), (ins variable_ops), [], ".result \t">;
def LOCAL : I<(outs), (ins variable_ops), [], ".local \t">;
+} // Defs = [ARGUMENTS]
+
//===----------------------------------------------------------------------===//
// Additional sets of instructions.
//===----------------------------------------------------------------------===//
///
//===----------------------------------------------------------------------===//
+let Defs = [ARGUMENTS] in {
+
// The spaces after the names are for aesthetic purposes only, to make
// operands line up vertically after tab expansion.
defm ADD : BinaryInt<add, "add ">;
defm CTZ : UnaryInt<cttz, "ctz ">;
defm POPCNT : UnaryInt<ctpop, "popcnt">;
+} // Defs = [ARGUMENTS]
+
// Expand the "don't care" operations to supported operations.
def : Pat<(ctlz_zero_undef I32:$src), (CLZ_I32 I32:$src)>;
def : Pat<(ctlz_zero_undef I64:$src), (CLZ_I64 I64:$src)>;
def : Pat<(cttz_zero_undef I32:$src), (CTZ_I32 I32:$src)>;
def : Pat<(cttz_zero_undef I64:$src), (CTZ_I64 I64:$src)>;
+let Defs = [ARGUMENTS] in {
+
def SELECT_I32 : I<(outs I32:$dst), (ins I32:$cond, I32:$lhs, I32:$rhs),
[(set I32:$dst, (select I32:$cond, I32:$lhs, I32:$rhs))],
"i32.select\t$dst, $cond, $lhs, $rhs">;
def SELECT_I64 : I<(outs I64:$dst), (ins I32:$cond, I64:$lhs, I64:$rhs),
[(set I64:$dst, (select I32:$cond, I64:$lhs, I64:$rhs))],
"i64.select\t$dst, $cond, $lhs, $rhs">;
+
+} // Defs = [ARGUMENTS]
// local types. These memory-only types instead zero- or sign-extend into local
// types when loading, and truncate when storing.
+let Defs = [ARGUMENTS] in {
+
// Basic load.
def LOAD_I32 : I<(outs I32:$dst), (ins I32:$addr),
[(set I32:$dst, (load I32:$addr))],
[(set I64:$dst, (zextloadi32 I32:$addr))],
"i64.load32_u\t$dst, $addr">;
+} // Defs = [ARGUMENTS]
+
// "Don't care" extending load become zero-extending load.
def : Pat<(i32 (extloadi8 I32:$addr)), (LOAD8_U_I32 $addr)>;
def : Pat<(i32 (extloadi16 I32:$addr)), (LOAD16_U_I32 $addr)>;
def : Pat<(i64 (extloadi16 I32:$addr)), (LOAD16_U_I64 $addr)>;
def : Pat<(i64 (extloadi32 I32:$addr)), (LOAD32_U_I64 $addr)>;
+let Defs = [ARGUMENTS] in {
+
// Basic store.
// Note that we split the patterns out of the instruction definitions because
// WebAssembly's stores return their operand value, and tablegen doesn't like
def STORE_F64 : I<(outs F64:$dst), (ins I32:$addr, F64:$val), [],
"f64.store\t$dst, $addr, $val">;
+} // Defs = [ARGUMENTS]
+
def : Pat<(store I32:$val, I32:$addr), (STORE_I32 I32:$addr, I32:$val)>;
def : Pat<(store I64:$val, I32:$addr), (STORE_I64 I32:$addr, I64:$val)>;
def : Pat<(store F32:$val, I32:$addr), (STORE_F32 I32:$addr, F32:$val)>;
def : Pat<(store F64:$val, I32:$addr), (STORE_F64 I32:$addr, F64:$val)>;
+let Defs = [ARGUMENTS] in {
+
// Truncating store.
def STORE8_I32 : I<(outs I32:$dst), (ins I32:$addr, I32:$val), [],
"i32.store8\t$dst, $addr, $val">;
def STORE32_I64 : I<(outs I64:$dst), (ins I32:$addr, I64:$val), [],
"i64.store32\t$dst, $addr, $val">;
+} // Defs = [ARGUMENTS]
+
def : Pat<(truncstorei8 I32:$val, I32:$addr),
(STORE8_I32 I32:$addr, I32:$val)>;
def : Pat<(truncstorei16 I32:$val, I32:$addr),
def : Pat<(truncstorei32 I64:$val, I32:$addr),
(STORE32_I64 I32:$addr, I64:$val)>;
+let Defs = [ARGUMENTS] in {
+
// Memory size.
def MEMORY_SIZE_I32 : I<(outs I32:$dst), (ins),
[(set I32:$dst, (int_wasm_memory_size))],
[(int_wasm_grow_memory I64:$delta)],
"grow_memory\t$delta">,
Requires<[HasAddr64]>;
+
+} // Defs = [ARGUMENTS]
#include "WebAssembly.h"
#include "WebAssemblyMachineFunctionInfo.h"
-#include "MCTargetDesc/WebAssemblyMCTargetDesc.h" // for WebAssembly::ARGUMENT_*
#include "llvm/CodeGen/LiveIntervalAnalysis.h"
#include "llvm/CodeGen/MachineBlockFrequencyInfo.h"
#include "llvm/CodeGen/MachineRegisterInfo.h"
AU.addRequired<MachineBlockFrequencyInfo>();
AU.addPreserved<MachineBlockFrequencyInfo>();
AU.addPreservedID(MachineDominatorsID);
- AU.addRequired<SlotIndexes>(); // for ARGUMENT fixups
MachineFunctionPass::getAnalysisUsage(AU);
}
SmallVector<LiveInterval *, 0> SortedIntervals;
SortedIntervals.reserve(NumVRegs);
- // FIXME: If scheduling has moved an ARGUMENT virtual register, move it back,
- // and recompute liveness. This is a temporary hack.
- bool MovedArg = false;
- MachineBasicBlock &EntryMBB = MF.front();
- MachineBasicBlock::iterator InsertPt = EntryMBB.end();
- // Look for the first NonArg instruction.
- for (auto MII = EntryMBB.begin(), MIE = EntryMBB.end(); MII != MIE; ++MII) {
- MachineInstr *MI = MII;
- if (MI->getOpcode() != WebAssembly::ARGUMENT_I32 &&
- MI->getOpcode() != WebAssembly::ARGUMENT_I64 &&
- MI->getOpcode() != WebAssembly::ARGUMENT_F32 &&
- MI->getOpcode() != WebAssembly::ARGUMENT_F64) {
- InsertPt = MII;
- break;
- }
- }
- // Now move any argument instructions later in the block
- // to before our first NonArg instruction.
- for (auto I = InsertPt, E = EntryMBB.end(); I != E; ++I) {
- MachineInstr *MI = I;
- if (MI->getOpcode() == WebAssembly::ARGUMENT_I32 ||
- MI->getOpcode() == WebAssembly::ARGUMENT_I64 ||
- MI->getOpcode() == WebAssembly::ARGUMENT_F32 ||
- MI->getOpcode() == WebAssembly::ARGUMENT_F64) {
- EntryMBB.insert(InsertPt, MI->removeFromParent());
- MovedArg = true;
- }
- }
- if (MovedArg) {
- SlotIndexes &Slots = getAnalysis<SlotIndexes>();
- Liveness->releaseMemory();
- Slots.releaseMemory();
- Slots.runOnMachineFunction(MF);
- Liveness->runOnMachineFunction(MF);
- }
-
DEBUG(dbgs() << "Interesting register intervals:\n");
for (unsigned i = 0; i < NumVRegs; ++i) {
unsigned VReg = TargetRegisterInfo::index2VirtReg(i);
// order uses and defs that must remain in FIFO order.
def EXPR_STACK : WebAssemblyReg<"STACK">;
+// The incoming arguments "register". This is an opaque entity which serves to
+// order the ARGUMENT instructions that are emulating live-in registers and
+// must not be scheduled below other instructions.
+def ARGUMENTS : WebAssemblyReg<"ARGUMENTS">;
+
//===----------------------------------------------------------------------===//
// Register classes
//===----------------------------------------------------------------------===//
define void @foo(i32* nocapture %a, i32 %w, i32 %h) {
; CHECK-LABEL: foo:
; CHECK-NEXT: .param i32, i32, i32{{$}}
-; CHECK-NEXT: .local i32, i32, i32, i32, i32, i32, i32, i32, i32{{$}}
+; CHECK-NEXT: .local i32, i32, i32, i32, i32, i32, i32{{$}}
entry:
%cmp.19 = icmp sgt i32 %h, 0
br i1 %cmp.19, label %for.cond.1.preheader.lr.ph, label %for.end.7