[WebAssembly] Use a physical register to describe ARGUMENT liveness.
authorDan Gohman <dan433584@gmail.com>
Wed, 25 Nov 2015 19:36:19 +0000 (19:36 +0000)
committerDan Gohman <dan433584@gmail.com>
Wed, 25 Nov 2015 19:36:19 +0000 (19:36 +0000)
Instead of trying to move ARGUMENT instructions back up to the top after
they've been scheduled or sunk down, use a fake physical register to
create a liveness constraint that prevents ARGUMENT instructions from
moving down in the first place. This is still not entirely ideal, however
it is more robust than letting them move and moving them back.

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@254084 91177308-0d34-0410-b5e6-96231b3b80d8

lib/Target/WebAssembly/WebAssemblyISelLowering.cpp
lib/Target/WebAssembly/WebAssemblyInstrCall.td
lib/Target/WebAssembly/WebAssemblyInstrControl.td
lib/Target/WebAssembly/WebAssemblyInstrConv.td
lib/Target/WebAssembly/WebAssemblyInstrFloat.td
lib/Target/WebAssembly/WebAssemblyInstrInfo.td
lib/Target/WebAssembly/WebAssemblyInstrInteger.td
lib/Target/WebAssembly/WebAssemblyInstrMemory.td
lib/Target/WebAssembly/WebAssemblyRegColoring.cpp
lib/Target/WebAssembly/WebAssemblyRegisterInfo.td
test/CodeGen/WebAssembly/dead-vreg.ll

index 405a2a9..5a3e021 100644 (file)
@@ -377,6 +377,10 @@ SDValue WebAssemblyTargetLowering::LowerFormalArguments(
   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");
index 530411b..4028460 100644 (file)
@@ -12,6 +12,8 @@
 ///
 //===----------------------------------------------------------------------===//
 
+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 {
@@ -42,3 +44,5 @@ let Uses = [SP32, SP64], isCall = 1 in {
                              [(WebAssemblycall0 I32:$callee)],
                              "call_indirect\t$callee">;
 } // Uses = [SP32,SP64], isCall = 1
+
+} // Defs = [ARGUMENTS]
index 7fa4c56..840f7d6 100644 (file)
@@ -12,6 +12,8 @@
 ///
 //===----------------------------------------------------------------------===//
 
+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)],
@@ -59,3 +61,5 @@ let isReturn = 1 in {
 } // isReturn = 1
   def UNREACHABLE : I<(outs), (ins), [(trap)], "unreachable">;
 } // isTerminator = 1, hasCtrlDep = 1, isBarrier = 1
+
+} // Defs = [ARGUMENTS]
index a4ae5fe..a34916e 100644 (file)
@@ -13,6 +13,8 @@
 ///
 //===----------------------------------------------------------------------===//
 
+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">;
@@ -93,3 +95,5 @@ def I64_REINTERPRET_F64 : I<(outs I64:$dst), (ins F64:$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]
index 33efbb3..a24c8bf 100644 (file)
@@ -12,6 +12,8 @@
 ///
 //===----------------------------------------------------------------------===//
 
+let Defs = [ARGUMENTS] in {
+
 defm ADD : BinaryFP<fadd, "add ">;
 defm SUB : BinaryFP<fsub, "sub ">;
 defm MUL : BinaryFP<fmul, "mul ">;
@@ -30,10 +32,14 @@ defm FLOOR : UnaryFP<ffloor, "floor">;
 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  ">;
@@ -41,6 +47,8 @@ defm LE : ComparisonFP<SETOLE, "le  ">;
 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)>;
@@ -55,9 +63,13 @@ def : Pat<(setle f64:$lhs, f64:$rhs), (LE_F64 f64:$lhs, f64:$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]
index e9a16ce..52e0bd6 100644 (file)
@@ -81,6 +81,7 @@ include "WebAssemblyInstrFormats.td"
 //===----------------------------------------------------------------------===//
 
 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))]>;
 }
@@ -89,6 +90,8 @@ defm : ARGUMENT<I64>;
 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.
@@ -125,11 +128,15 @@ def CONST_F64 : I<(outs F64:$res), (ins f64imm:$imm),
                   [(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">;
@@ -139,6 +146,8 @@ def PARAM  : I<(outs), (ins variable_ops), [], ".param  \t">;
 def RESULT : I<(outs), (ins variable_ops), [], ".result \t">;
 def LOCAL  : I<(outs), (ins variable_ops), [], ".local  \t">;
 
+} // Defs = [ARGUMENTS]
+
 //===----------------------------------------------------------------------===//
 // Additional sets of instructions.
 //===----------------------------------------------------------------------===//
index 1d634df..bc00cd6 100644 (file)
@@ -12,6 +12,8 @@
 ///
 //===----------------------------------------------------------------------===//
 
+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 ">;
@@ -43,15 +45,21 @@ defm CLZ : UnaryInt<ctlz, "clz ">;
 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]
index f0cc02a..700a196 100644 (file)
@@ -22,6 +22,8 @@
 // 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))],
@@ -68,6 +70,8 @@ def LOAD32_U_I64 : I<(outs I64:$dst), (ins 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)>;
@@ -75,6 +79,8 @@ def : Pat<(i64 (extloadi8 I32:$addr)),  (LOAD8_U_I64 $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
@@ -90,11 +96,15 @@ def STORE_F32  : I<(outs F32:$dst), (ins I32:$addr, F32:$val), [],
 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">;
@@ -107,6 +117,8 @@ def STORE16_I64 : I<(outs I64:$dst), (ins I32:$addr, I64:$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),
@@ -118,6 +130,8 @@ def : Pat<(truncstorei16 I64:$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))],
@@ -137,3 +151,5 @@ def GROW_MEMORY_I64 : I<(outs), (ins I64:$delta),
                         [(int_wasm_grow_memory I64:$delta)],
                         "grow_memory\t$delta">,
                       Requires<[HasAddr64]>;
+
+} // Defs = [ARGUMENTS]
index b497612..9ec6659 100644 (file)
@@ -19,7 +19,6 @@
 
 #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"
@@ -46,7 +45,6 @@ public:
     AU.addRequired<MachineBlockFrequencyInfo>();
     AU.addPreserved<MachineBlockFrequencyInfo>();
     AU.addPreservedID(MachineDominatorsID);
-    AU.addRequired<SlotIndexes>(); // for ARGUMENT fixups
     MachineFunctionPass::getAnalysisUsage(AU);
   }
 
@@ -96,42 +94,6 @@ bool WebAssemblyRegColoring::runOnMachineFunction(MachineFunction &MF) {
   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);
index 2cf1e38..4057ff7 100644 (file)
@@ -43,6 +43,11 @@ def F64_0 : WebAssemblyReg<"%f64.0">;
 // 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
 //===----------------------------------------------------------------------===//
index e1ae1ae..cf1415c 100644 (file)
@@ -8,7 +8,7 @@ target triple = "wasm32-unknown-unknown"
 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