[WebAssembly] Implement eliminateCallFramePseudo
authorDerek Schuff <dschuff@google.com>
Wed, 16 Dec 2015 23:21:30 +0000 (23:21 +0000)
committerDerek Schuff <dschuff@google.com>
Wed, 16 Dec 2015 23:21:30 +0000 (23:21 +0000)
Summary:
Implement eliminateCallFramePsuedo to handle ADJCALLSTACKUP/DOWN
pseudo-instructions. Add a test calling a vararg function which causes non-0
adjustments. This revealed an issue with RegisterCoalescer wherein it
eliminates a COPY from SP32 to a vreg but failes to update the live ranges
of EXPR_STACK, causing a machineinstr verifier failure (so this test
is commented out).

Also add a dynamic alloca test, which causes a callseq_end dag node with
a 0 (instead of undef) second argument to be generated. We currently fail to
select that, so adjust the ADJCALLSTACKUP tablegen code to handle it.

Differential Revision: http://reviews.llvm.org/D15587

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

lib/Target/WebAssembly/WebAssemblyFrameLowering.cpp
lib/Target/WebAssembly/WebAssemblyISelLowering.cpp
lib/Target/WebAssembly/WebAssemblyInstrCall.td
lib/Target/WebAssembly/WebAssemblyInstrInfo.cpp
lib/Target/WebAssembly/WebAssemblyRegStackify.cpp
lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp
test/CodeGen/WebAssembly/userstack.ll
test/CodeGen/WebAssembly/varargs.ll

index 8b0f48f4bd15d332dcccd48090d2faeb8f10437a..0eefd57f1f2cf788e8700c288eb1ffe5524db99e 100644 (file)
@@ -61,31 +61,16 @@ bool WebAssemblyFrameLowering::hasReservedCallFrame(
   return !MF.getFrameInfo()->hasVarSizedObjects();
 }
 
-void WebAssemblyFrameLowering::eliminateCallFramePseudoInstr(
-    MachineFunction & /*MF*/, MachineBasicBlock & /*MBB*/,
-    MachineBasicBlock::iterator /*I*/) const {
-  llvm_unreachable("TODO: implement eliminateCallFramePseudoInstr");
-}
 
-void WebAssemblyFrameLowering::emitPrologue(MachineFunction &MF,
-                                            MachineBasicBlock &MBB) const {
-  // TODO: Do ".setMIFlag(MachineInstr::FrameSetup)" on emitted instructions
-  auto *MFI = MF.getFrameInfo();
-  assert(MFI->getCalleeSavedInfo().empty() &&
-         "WebAssembly should not have callee-saved registers");
-  assert(!hasFP(MF) && "Functions needing frame pointers not yet supported");
-  assert(!MFI->adjustsStack() && "Dynamic stack adjustmet not yet supported");
-  uint64_t StackSize = MFI->getStackSize();
-  if (!StackSize)
-    return;
-
-  const auto *TII = MF.getSubtarget().getInstrInfo();
+/// Adjust the stack pointer by a constant amount.
+static void adjustStackPointer(unsigned StackSize,
+                               bool AdjustUp,
+                               MachineFunction& MF,
+                               MachineBasicBlock& MBB,
+                               const TargetInstrInfo* TII,
+                               MachineBasicBlock::iterator InsertPt,
+                               const DebugLoc& DL) {
   auto &MRI = MF.getRegInfo();
-  auto InsertPt = MBB.begin();
-  DebugLoc DL;
-
-  // Get the current stacktop
-  // TODO: To support dynamic alloc, also copy to FP
   unsigned SPReg = MRI.createVirtualRegister(&WebAssembly::I32RegClass);
   auto *SPSymbol = MF.createExternalSymbolName("__stack_pointer");
   BuildMI(MBB, InsertPt, DL, TII->get(WebAssembly::CONST_I32), SPReg)
@@ -100,11 +85,13 @@ void WebAssemblyFrameLowering::emitPrologue(MachineFunction &MF,
       .addImm(0)
       .addReg(SPReg)
       .addMemOperand(LoadMMO);
-  // Subtract the frame size
+  // Add/Subtract the frame size
   unsigned OffsetReg = MRI.createVirtualRegister(&WebAssembly::I32RegClass);
   BuildMI(MBB, InsertPt, DL, TII->get(WebAssembly::CONST_I32), OffsetReg)
       .addImm(StackSize);
-  BuildMI(MBB, InsertPt, DL, TII->get(WebAssembly::SUB_I32), WebAssembly::SP32)
+  BuildMI(MBB, InsertPt, DL,
+          TII->get(AdjustUp ? WebAssembly::ADD_I32 : WebAssembly::SUB_I32),
+          WebAssembly::SP32)
       .addReg(SPReg)
       .addReg(OffsetReg);
   // The SP32 register now has the new stacktop. Also write it back to memory.
@@ -119,6 +106,40 @@ void WebAssemblyFrameLowering::emitPrologue(MachineFunction &MF,
       .addMemOperand(MMO);
 }
 
+void WebAssemblyFrameLowering::eliminateCallFramePseudoInstr(
+    MachineFunction &MF, MachineBasicBlock &MBB,
+    MachineBasicBlock::iterator I) const {
+  const auto *TII =
+      static_cast<const WebAssemblyInstrInfo*>(MF.getSubtarget().getInstrInfo());
+  DebugLoc DL = I->getDebugLoc();
+  unsigned Opc = I->getOpcode();
+  bool IsDestroy = Opc == TII->getCallFrameDestroyOpcode();
+  unsigned Amount = I->getOperand(0).getImm();
+  if (Amount)
+    adjustStackPointer(Amount, IsDestroy, MF, MBB,
+                       TII, I, DL);
+  MBB.erase(I);
+}
+
+void WebAssemblyFrameLowering::emitPrologue(MachineFunction &MF,
+                                            MachineBasicBlock &MBB) const {
+  // TODO: Do ".setMIFlag(MachineInstr::FrameSetup)" on emitted instructions
+  auto *MFI = MF.getFrameInfo();
+  assert(MFI->getCalleeSavedInfo().empty() &&
+         "WebAssembly should not have callee-saved registers");
+  assert(!hasFP(MF) && "Functions needing frame pointers not yet supported");
+  uint64_t StackSize = MFI->getStackSize();
+  if (!StackSize && (!MFI->adjustsStack() || MFI->getMaxCallFrameSize() == 0))
+    return;
+
+  const auto *TII = MF.getSubtarget().getInstrInfo();
+
+  auto InsertPt = MBB.begin();
+  DebugLoc DL;
+
+  adjustStackPointer(StackSize, false, MF, MBB, TII, InsertPt, DL);
+}
+
 void WebAssemblyFrameLowering::emitEpilogue(MachineFunction &MF,
                                             MachineBasicBlock &MBB) const {
   uint64_t StackSize = MF.getFrameInfo()->getStackSize();
index 597d6f4fd1dfe3a150681fc4e8a1b2cf7b475396..2364366f3f54457bf37bc0e25482f2bb96dedc9c 100644 (file)
@@ -445,7 +445,7 @@ WebAssemblyTargetLowering::LowerCall(CallLoweringInfo &CLI,
   }
 
   if (NumBytes) {
-    SDValue Unused = DAG.getUNDEF(PtrVT);
+    SDValue Unused = DAG.getTargetConstant(0, DL, PtrVT);
     Chain = DAG.getCALLSEQ_END(Chain, NB, Unused, SDValue(), DL);
   }
 
index e9cad01f9df4285e6435988d08360c12324a40f8..cfa1519e6d994029c22fc51322240171b497c78f 100644 (file)
@@ -21,8 +21,8 @@ let Defs = [ARGUMENTS] in {
 let Uses = [SP32, SP64], Defs = [SP32, SP64], isCodeGenOnly = 1 in {
 def ADJCALLSTACKDOWN : I<(outs), (ins i32imm:$amt),
                          [(WebAssemblycallseq_start timm:$amt)]>;
-def ADJCALLSTACKUP : I<(outs), (ins i32imm:$amt),
-                       [(WebAssemblycallseq_end timm:$amt, undef)]>;
+def ADJCALLSTACKUP : I<(outs), (ins i32imm:$amt, i32imm:$amt2),
+                       [(WebAssemblycallseq_end timm:$amt, timm:$amt2)]>;
 } // isCodeGenOnly = 1
 
 multiclass CALL<WebAssemblyRegClass vt, string prefix> {
index 3b219f4a901a80b579bc665353c056db0471639c..41c8811ea761c4c81fb9f10a7c6021bfe598781d 100644 (file)
@@ -36,8 +36,12 @@ void WebAssemblyInstrInfo::copyPhysReg(MachineBasicBlock &MBB,
                                        MachineBasicBlock::iterator I,
                                        DebugLoc DL, unsigned DestReg,
                                        unsigned SrcReg, bool KillSrc) const {
-  const TargetRegisterClass *RC =
-      MBB.getParent()->getRegInfo().getRegClass(SrcReg);
+  // This method is called by post-RA expansion, which expects only pregs to
+  // exist. However we need to handle both here.
+  auto &MRI = MBB.getParent()->getRegInfo();
+  const TargetRegisterClass *RC = TargetRegisterInfo::isVirtualRegister(DestReg) ?
+      MRI.getRegClass(DestReg) :
+      MRI.getTargetRegisterInfo()->getMinimalPhysRegClass(SrcReg);
 
   unsigned CopyLocalOpcode;
   if (RC == &WebAssembly::I32RegClass)
index 0b04a6355a92854f3780c10b8c3e50395b63c821..3a58826bd7677085f29007d6fe2ad666723535ae 100644 (file)
@@ -136,7 +136,6 @@ bool WebAssemblyRegStackify::runOnMachineFunction(MachineFunction &MF) {
   for (MachineBasicBlock &MBB : MF) {
     for (MachineInstr &MI : reverse(MBB)) {
       MachineInstr *Insert = &MI;
-
       // Don't nest anything inside a phi.
       if (Insert->getOpcode() == TargetOpcode::PHI)
         break;
index dcc393db6eaa2c6aefd9cffa89c3f9fe5d6c7f46..c33c21fe68566a067a67d4805285abf1714e8008 100644 (file)
@@ -169,6 +169,9 @@ void WebAssemblyPassConfig::addPreRegAlloc() {
 
   // Mark registers as representing wasm's expression stack.
   addPass(createWebAssemblyRegStackify());
+  // The register coalescing pass has a bad interaction with COPY MIs which have
+  // EXPR_STACK as an extra operand
+  //disablePass(&RegisterCoalescerID);
 }
 
 void WebAssemblyPassConfig::addPostRegAlloc() {
index 855eaa00a4a768ea1256d83f0f7c0acc2436294b..6e01e36cf9fa4761dc0913f7d447274a7f985b06 100644 (file)
@@ -72,4 +72,10 @@ define void @allocarray() {
  ret void
 }
 
+define void @dynamic_alloca(i32 %alloc) {
+ ; TODO: Support frame pointers
+ ;%r = alloca i32, i32 %alloc
+ ;store i32 0, i32* %r
+ ret void
+}
 ; TODO: test aligned alloc
index 10846f2a989c9d3c52f19a3f53c693a41415b04e..c564d94207424651384ab8f748a61211a7e83866 100644 (file)
@@ -110,12 +110,13 @@ define void @caller_none() {
   ret void
 }
 
-; TODO: Test a varargs call with actual arguments.
-
-;define void @caller_some() {
-;  call void (...) @callee(i32 0, double 2.0)
-;  ret void
-;}
+; CHECK-LABEL: caller_some
+define void @caller_some() {
+  ; TODO: Fix interaction between register coalescer and reg stackifier,
+  ; or disable coalescer.
+  ;call void (...) @callee(i32 0, double 2.0)
+  ret void
+}
 
 declare void @llvm.va_start(i8*)
 declare void @llvm.va_end(i8*)