[WebAssembly] Support for register stackifying with load and store instructions.
authorDan Gohman <dan433584@gmail.com>
Wed, 25 Nov 2015 16:55:01 +0000 (16:55 +0000)
committerDan Gohman <dan433584@gmail.com>
Wed, 25 Nov 2015 16:55:01 +0000 (16:55 +0000)
git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@254076 91177308-0d34-0410-b5e6-96231b3b80d8

18 files changed:
lib/Target/WebAssembly/CMakeLists.txt
lib/Target/WebAssembly/README.txt
lib/Target/WebAssembly/WebAssembly.h
lib/Target/WebAssembly/WebAssemblyISelLowering.h
lib/Target/WebAssembly/WebAssemblyOptimizeReturned.cpp [new file with mode: 0644]
lib/Target/WebAssembly/WebAssemblyPeephole.cpp [new file with mode: 0644]
lib/Target/WebAssembly/WebAssemblyRegStackify.cpp
lib/Target/WebAssembly/WebAssemblyStoreResults.cpp [new file with mode: 0644]
lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp
test/CodeGen/WebAssembly/cfg-stackify.ll
test/CodeGen/WebAssembly/global.ll
test/CodeGen/WebAssembly/load-ext.ll
test/CodeGen/WebAssembly/load-store-i1.ll
test/CodeGen/WebAssembly/load.ll
test/CodeGen/WebAssembly/memory-addr32.ll
test/CodeGen/WebAssembly/memory-addr64.ll
test/CodeGen/WebAssembly/reg-stackify.ll [new file with mode: 0644]
test/CodeGen/WebAssembly/returned.ll [new file with mode: 0644]

index 82f7226..6a58949 100644 (file)
@@ -20,11 +20,14 @@ add_llvm_target(WebAssemblyCodeGen
   WebAssemblyInstrInfo.cpp
   WebAssemblyMachineFunctionInfo.cpp
   WebAssemblyMCInstLower.cpp
+  WebAssemblyOptimizeReturned.cpp
+  WebAssemblyPeephole.cpp
   WebAssemblyRegisterInfo.cpp
   WebAssemblyRegColoring.cpp
   WebAssemblyRegNumbering.cpp
   WebAssemblyRegStackify.cpp
   WebAssemblySelectionDAGInfo.cpp
+  WebAssemblyStoreResults.cpp
   WebAssemblySubtarget.cpp
   WebAssemblyTargetMachine.cpp
   WebAssemblyTargetTransformInfo.cpp
index 8604528..bfb124d 100644 (file)
@@ -22,7 +22,7 @@ Interesting work that remains to be done:
 
 //===---------------------------------------------------------------------===//
 
-set_local and store instructions have a return value. We should (a) model this,
+set_local instructions have a return value. We should (a) model this,
 and (b) write optimizations which take advantage of it. Keep in mind that
 many set_local instructions are implicit!
 
index 59856de..001f9f9 100644 (file)
@@ -23,13 +23,17 @@ namespace llvm {
 class WebAssemblyTargetMachine;
 class FunctionPass;
 
+FunctionPass *createWebAssemblyOptimizeReturned();
+
 FunctionPass *createWebAssemblyISelDag(WebAssemblyTargetMachine &TM,
                                        CodeGenOpt::Level OptLevel);
 
+FunctionPass *createWebAssemblyStoreResults();
 FunctionPass *createWebAssemblyRegStackify();
 FunctionPass *createWebAssemblyRegColoring();
 FunctionPass *createWebAssemblyCFGStackify();
 FunctionPass *createWebAssemblyRegNumbering();
+FunctionPass *createWebAssemblyPeephole();
 
 FunctionPass *createWebAssemblyRelooper();
 
index 3621571..af5eab6 100644 (file)
@@ -74,6 +74,7 @@ private:
   // Custom lowering hooks.
   SDValue LowerOperation(SDValue Op, SelectionDAG &DAG) const override;
   SDValue LowerGlobalAddress(SDValue Op, SelectionDAG &DAG) const;
+  SDValue LowerExternalSymbol(SDValue Op, SelectionDAG &DAG) const;
   SDValue LowerBR_JT(SDValue Op, SelectionDAG &DAG) const;
   SDValue LowerJumpTable(SDValue Op, SelectionDAG &DAG) const;
 };
diff --git a/lib/Target/WebAssembly/WebAssemblyOptimizeReturned.cpp b/lib/Target/WebAssembly/WebAssemblyOptimizeReturned.cpp
new file mode 100644 (file)
index 0000000..dea419c
--- /dev/null
@@ -0,0 +1,73 @@
+//===-- WebAssemblyOptimizeReturned.cpp - Optimize "returned" attributes --===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// \brief Optimize calls with "returned" attributes for WebAssembly.
+///
+//===----------------------------------------------------------------------===//
+
+#include "WebAssembly.h"
+#include "llvm/IR/Dominators.h"
+#include "llvm/IR/InstVisitor.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/raw_ostream.h"
+using namespace llvm;
+
+#define DEBUG_TYPE "wasm-optimize-returned"
+
+namespace {
+class OptimizeReturned final : public FunctionPass,
+                               public InstVisitor<OptimizeReturned> {
+  const char *getPassName() const override {
+    return "WebAssembly Optimize Returned";
+  }
+
+  void getAnalysisUsage(AnalysisUsage &AU) const override {
+    AU.setPreservesCFG();
+    AU.addRequired<DominatorTreeWrapperPass>();
+    AU.addPreserved<DominatorTreeWrapperPass>();
+    FunctionPass::getAnalysisUsage(AU);
+  }
+
+  bool runOnFunction(Function &F) override;
+
+  DominatorTree *DT;
+
+public:
+  static char ID;
+  OptimizeReturned() : FunctionPass(ID), DT(nullptr) {}
+
+  void visitCallSite(CallSite CS);
+};
+} // End anonymous namespace
+
+char OptimizeReturned::ID = 0;
+FunctionPass *llvm::createWebAssemblyOptimizeReturned() {
+  return new OptimizeReturned();
+}
+
+void OptimizeReturned::visitCallSite(CallSite CS) {
+  for (unsigned i = 0, e = CS.getNumArgOperands(); i < e; ++i)
+    if (CS.paramHasAttr(1 + i, Attribute::Returned)) {
+      Instruction *Inst = CS.getInstruction();
+      Value *Arg = CS.getArgOperand(i);
+      // Like replaceDominatedUsesWith but using Instruction/Use dominance.
+      for (auto UI = Arg->use_begin(), UE = Arg->use_end(); UI != UE;) {
+        Use &U = *UI++;
+        if (DT->dominates(Inst, U))
+          U.set(Inst);
+      }
+    }
+}
+
+bool OptimizeReturned::runOnFunction(Function &F) {
+  DT = &getAnalysis<DominatorTreeWrapperPass>().getDomTree();
+  visit(F);
+  return true;
+}
diff --git a/lib/Target/WebAssembly/WebAssemblyPeephole.cpp b/lib/Target/WebAssembly/WebAssemblyPeephole.cpp
new file mode 100644 (file)
index 0000000..1399562
--- /dev/null
@@ -0,0 +1,77 @@
+//===-- WebAssemblyPeephole.cpp - WebAssembly Peephole Optimiztions -------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// \brief Late peephole optimizations for WebAssembly.
+///
+//===----------------------------------------------------------------------===//
+
+#include "WebAssembly.h"
+#include "MCTargetDesc/WebAssemblyMCTargetDesc.h"
+#include "WebAssemblyMachineFunctionInfo.h"
+#include "llvm/CodeGen/MachineFunctionPass.h"
+using namespace llvm;
+
+#define DEBUG_TYPE "wasm-peephole"
+
+namespace {
+class WebAssemblyPeephole final : public MachineFunctionPass {
+  const char *getPassName() const override {
+    return "WebAssembly late peephole optimizer";
+  }
+
+  bool runOnMachineFunction(MachineFunction &MF) override;
+
+public:
+  static char ID;
+  WebAssemblyPeephole() : MachineFunctionPass(ID) {}
+};
+} // end anonymous namespace
+
+char WebAssemblyPeephole::ID = 0;
+FunctionPass *llvm::createWebAssemblyPeephole() {
+  return new WebAssemblyPeephole();
+}
+
+bool WebAssemblyPeephole::runOnMachineFunction(MachineFunction &MF) {
+  bool Changed = false;
+
+  MachineRegisterInfo &MRI = MF.getRegInfo();
+  WebAssemblyFunctionInfo &MFI = *MF.getInfo<WebAssemblyFunctionInfo>();
+
+  for (auto &MBB : MF)
+    for (auto &MI : MBB)
+      switch (MI.getOpcode()) {
+      default:
+        break;
+      case WebAssembly::STORE8_I32:
+      case WebAssembly::STORE16_I32:
+      case WebAssembly::STORE8_I64:
+      case WebAssembly::STORE16_I64:
+      case WebAssembly::STORE32_I64:
+      case WebAssembly::STORE_F32:
+      case WebAssembly::STORE_F64:
+      case WebAssembly::STORE_I32:
+      case WebAssembly::STORE_I64: {
+        // Store instructions return their value operand. If we ended up using
+        // the same register for both, replace it with a dead def so that it
+        // can use $discard instead.
+        MachineOperand &MO = MI.getOperand(0);
+        unsigned OldReg = MO.getReg();
+        if (OldReg == MI.getOperand(2).getReg()) {
+          unsigned NewReg = MRI.createVirtualRegister(MRI.getRegClass(OldReg));
+          MO.setReg(NewReg);
+          MO.setIsDead();
+          MFI.stackifyVReg(NewReg);
+        }
+      }
+      }
+
+  return Changed;
+}
index 5ba7c31..ba2a0e2 100644 (file)
@@ -23,6 +23,7 @@
 #include "WebAssembly.h"
 #include "WebAssemblyMachineFunctionInfo.h"
 #include "MCTargetDesc/WebAssemblyMCTargetDesc.h" // for WebAssembly::ARGUMENT_*
+#include "llvm/Analysis/AliasAnalysis.h"
 #include "llvm/CodeGen/MachineBlockFrequencyInfo.h"
 #include "llvm/CodeGen/MachineRegisterInfo.h"
 #include "llvm/CodeGen/Passes.h"
@@ -40,6 +41,7 @@ class WebAssemblyRegStackify final : public MachineFunctionPass {
 
   void getAnalysisUsage(AnalysisUsage &AU) const override {
     AU.setPreservesCFG();
+    AU.addRequired<AAResultsWrapperPass>();
     AU.addPreserved<MachineBlockFrequencyInfo>();
     AU.addPreservedID(MachineDominatorsID);
     MachineFunctionPass::getAnalysisUsage(AU);
@@ -70,6 +72,24 @@ static void ImposeStackOrdering(MachineInstr *MI) {
                                            /*isImp=*/true));
 }
 
+// Test whether it's safe to move Def to just before Insert. Note that this
+// doesn't account for physical register dependencies, because WebAssembly
+// doesn't have any (other than special ones like EXPR_STACK).
+// TODO: Compute memory dependencies in a way that doesn't require always
+// walking the block.
+// TODO: Compute memory dependencies in a way that uses AliasAnalysis to be
+// more precise.
+static bool IsSafeToMove(const MachineInstr *Def, const MachineInstr *Insert,
+                         AliasAnalysis &AA) {
+  bool SawStore = false, SawSideEffects = false;
+  MachineBasicBlock::const_iterator D(Def), I(Insert);
+  for (--I; I != D; --I)
+    SawSideEffects |= I->isSafeToMove(&AA, SawStore);
+
+  return !(SawStore && Def->mayLoad() && !Def->isInvariantLoad(&AA)) &&
+         !(SawSideEffects && !Def->isSafeToMove(&AA, SawStore));
+}
+
 bool WebAssemblyRegStackify::runOnMachineFunction(MachineFunction &MF) {
   DEBUG(dbgs() << "********** Register Stackifying **********\n"
                   "********** Function: "
@@ -78,6 +98,7 @@ bool WebAssemblyRegStackify::runOnMachineFunction(MachineFunction &MF) {
   bool Changed = false;
   MachineRegisterInfo &MRI = MF.getRegInfo();
   WebAssemblyFunctionInfo &MFI = *MF.getInfo<WebAssemblyFunctionInfo>();
+  AliasAnalysis &AA = getAnalysis<AAResultsWrapperPass>().getAAResults();
 
   // Walk the instructions from the bottom up. Currently we don't look past
   // block boundaries, and the blocks aren't ordered so the block visitation
@@ -90,12 +111,17 @@ bool WebAssemblyRegStackify::runOnMachineFunction(MachineFunction &MF) {
       if (Insert->getOpcode() == TargetOpcode::PHI)
         break;
 
+      // Don't nest anything inside an inline asm, because we don't have
+      // constraints for $push inputs.
+      if (Insert->getOpcode() == TargetOpcode::INLINEASM)
+        break;
+
       // Iterate through the inputs in reverse order, since we'll be pulling
       // operands off the stack in FIFO order.
       bool AnyStackified = false;
       for (MachineOperand &Op : reverse(Insert->uses())) {
         // We're only interested in explicit virtual register operands.
-        if (!Op.isReg() || Op.isImplicit())
+        if (!Op.isReg() || Op.isImplicit() || !Op.isUse())
           continue;
 
         unsigned Reg = Op.getReg();
@@ -112,6 +138,15 @@ bool WebAssemblyRegStackify::runOnMachineFunction(MachineFunction &MF) {
         if (Def->getOpcode() == TargetOpcode::IMPLICIT_DEF)
           continue;
 
+        // Don't nest an INLINE_ASM def into anything, because we don't have
+        // constraints for $pop outputs.
+        if (Def->getOpcode() == TargetOpcode::INLINEASM)
+          continue;
+
+        // Don't nest PHIs inside of anything.
+        if (Def->getOpcode() == TargetOpcode::PHI)
+          continue;
+
         // Argument instructions represent live-in registers and not real
         // instructions.
         if (Def->getOpcode() == WebAssembly::ARGUMENT_I32 ||
@@ -124,8 +159,7 @@ bool WebAssemblyRegStackify::runOnMachineFunction(MachineFunction &MF) {
         // they be trivially clonable.
         // TODO: Eventually we'll relax this, to take advantage of set_local
         // returning its result.
-        bool OneUse = MRI.hasOneUse(Reg);
-        if (!OneUse && !Def->isMoveImmediate())
+        if (!MRI.hasOneUse(Reg))
           continue;
 
         // For now, be conservative and don't look across block boundaries,
@@ -134,35 +168,19 @@ bool WebAssemblyRegStackify::runOnMachineFunction(MachineFunction &MF) {
         if (Def->getParent() != &MBB && !Def->isMoveImmediate())
           continue;
 
-        // For now, be simple and don't reorder loads, stores, or side effects.
-        // TODO: Be more aggressive.
-        if ((Def->mayLoad() || Def->mayStore() ||
-             Def->hasUnmodeledSideEffects()))
+        // Don't move instructions that have side effects or memory dependencies
+        // or other complications.
+        if (!IsSafeToMove(Def, Insert, AA))
           continue;
 
         Changed = true;
         AnyStackified = true;
-        if (OneUse) {
-          // Move the def down and nest it in the current instruction.
-          MBB.insert(MachineBasicBlock::instr_iterator(Insert),
-                     Def->removeFromParent());
-          MFI.stackifyVReg(Reg);
-          ImposeStackOrdering(Def);
-          Insert = Def;
-        } else {
-          // Clone the def down and nest it in the current instruction.
-          MachineInstr *Clone = MF.CloneMachineInstr(Def);
-          unsigned OldReg = Def->getOperand(0).getReg();
-          unsigned NewReg = MRI.createVirtualRegister(MRI.getRegClass(OldReg));
-          assert(Op.getReg() == OldReg);
-          assert(Clone->getOperand(0).getReg() == OldReg);
-          Op.setReg(NewReg);
-          Clone->getOperand(0).setReg(NewReg);
-          MBB.insert(MachineBasicBlock::instr_iterator(Insert), Clone);
-          MFI.stackifyVReg(Reg);
-          ImposeStackOrdering(Clone);
-          Insert = Clone;
-        }
+        // Move the def down and nest it in the current instruction.
+        MBB.insert(MachineBasicBlock::instr_iterator(Insert),
+                   Def->removeFromParent());
+        MFI.stackifyVReg(Reg);
+        ImposeStackOrdering(Def);
+        Insert = Def;
       }
       if (AnyStackified)
         ImposeStackOrdering(&MI);
diff --git a/lib/Target/WebAssembly/WebAssemblyStoreResults.cpp b/lib/Target/WebAssembly/WebAssemblyStoreResults.cpp
new file mode 100644 (file)
index 0000000..d0735b8
--- /dev/null
@@ -0,0 +1,102 @@
+//===-- WebAssemblyStoreResults.cpp - Optimize using store result values --===//
+//
+//                     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 an optimization pass using store result values.
+///
+/// WebAssembly's store instructions return the stored value, specifically to
+/// enable the optimization of reducing get_local/set_local traffic, which is
+/// what we're doing here.
+///
+//===----------------------------------------------------------------------===//
+
+#include "WebAssembly.h"
+#include "MCTargetDesc/WebAssemblyMCTargetDesc.h"
+#include "WebAssemblyMachineFunctionInfo.h"
+#include "WebAssemblySubtarget.h"
+#include "llvm/CodeGen/MachineBlockFrequencyInfo.h"
+#include "llvm/CodeGen/MachineDominators.h"
+#include "llvm/CodeGen/MachineRegisterInfo.h"
+#include "llvm/CodeGen/Passes.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/raw_ostream.h"
+using namespace llvm;
+
+#define DEBUG_TYPE "wasm-store-results"
+
+namespace {
+class WebAssemblyStoreResults final : public MachineFunctionPass {
+public:
+  static char ID; // Pass identification, replacement for typeid
+  WebAssemblyStoreResults() : MachineFunctionPass(ID) {}
+
+  const char *getPassName() const override {
+    return "WebAssembly Store Results";
+  }
+
+  void getAnalysisUsage(AnalysisUsage &AU) const override {
+    AU.setPreservesCFG();
+    AU.addRequired<MachineBlockFrequencyInfo>();
+    AU.addPreserved<MachineBlockFrequencyInfo>();
+    AU.addRequired<MachineDominatorTree>();
+    AU.addPreserved<MachineDominatorTree>();
+    MachineFunctionPass::getAnalysisUsage(AU);
+  }
+
+  bool runOnMachineFunction(MachineFunction &MF) override;
+
+private:
+};
+} // end anonymous namespace
+
+char WebAssemblyStoreResults::ID = 0;
+FunctionPass *llvm::createWebAssemblyStoreResults() {
+  return new WebAssemblyStoreResults();
+}
+
+bool WebAssemblyStoreResults::runOnMachineFunction(MachineFunction &MF) {
+  DEBUG({
+    dbgs() << "********** Store Results **********\n"
+           << "********** Function: " << MF.getName() << '\n';
+  });
+
+  const MachineRegisterInfo &MRI = MF.getRegInfo();
+  MachineDominatorTree &MDT = getAnalysis<MachineDominatorTree>();
+
+  for (auto &MBB : MF)
+    for (auto &MI : MBB)
+      switch (MI.getOpcode()) {
+      default:
+        break;
+      case WebAssembly::STORE8_I32:
+      case WebAssembly::STORE16_I32:
+      case WebAssembly::STORE8_I64:
+      case WebAssembly::STORE16_I64:
+      case WebAssembly::STORE32_I64:
+      case WebAssembly::STORE_F32:
+      case WebAssembly::STORE_F64:
+      case WebAssembly::STORE_I32:
+      case WebAssembly::STORE_I64:
+        unsigned ToReg = MI.getOperand(0).getReg();
+        unsigned FromReg = MI.getOperand(2).getReg();
+        for (auto I = MRI.use_begin(FromReg), E = MRI.use_end(); I != E;) {
+          MachineOperand &O = *I++;
+          MachineInstr *Where = O.getParent();
+          if (Where->getOpcode() == TargetOpcode::PHI)
+            Where = Where->getOperand(&O - &Where->getOperand(0) + 1)
+                        .getMBB()
+                        ->getFirstTerminator();
+          if (&MI == Where || !MDT.dominates(&MI, Where))
+            continue;
+          O.setReg(ToReg);
+        }
+      }
+
+  return true;
+}
index d60c41a..493e4be 100644 (file)
@@ -143,6 +143,9 @@ void WebAssemblyPassConfig::addIRPasses() {
     // control specifically what gets lowered.
     addPass(createAtomicExpandPass(TM));
 
+  // Optimize "returned" function attributes.
+  addPass(createWebAssemblyOptimizeReturned());
+
   TargetPassConfig::addIRPasses();
 }
 
@@ -157,6 +160,9 @@ bool WebAssemblyPassConfig::addInstSelector() {
 bool WebAssemblyPassConfig::addILPOpts() { return true; }
 
 void WebAssemblyPassConfig::addPreRegAlloc() {
+  // Prepare store instructions for register stackifying.
+  addPass(createWebAssemblyStoreResults());
+
   // Mark registers as representing wasm's expression stack.
   addPass(createWebAssemblyRegStackify());
 }
@@ -183,4 +189,5 @@ void WebAssemblyPassConfig::addPreSched2() {}
 void WebAssemblyPassConfig::addPreEmitPass() {
   addPass(createWebAssemblyCFGStackify());
   addPass(createWebAssemblyRegNumbering());
+  addPass(createWebAssemblyPeephole());
 }
index 4f912f3..4a53f6e 100644 (file)
@@ -174,7 +174,7 @@ exit:
 
 ; CHECK-LABEL: single_block:
 ; CHECK-NOT: br
-; CHECK: return ${{[0-9]+}}{{$}}
+; CHECK: return $pop{{[0-9]+}}{{$}}
 define i32 @single_block(i32* %p) {
 entry:
   store volatile i32 0, i32* %p
index 4fd1b08..818c454 100644 (file)
@@ -11,8 +11,8 @@ target triple = "wasm32-unknown-unknown"
 
 ; CHECK: foo:
 ; CHECK: i32.const $push0=, answer{{$}}
-; CHECK-NEXT: i32.load $0=, $pop0{{$}}
-; CHECK-NEXT: return $0{{$}}
+; CHECK-NEXT: i32.load $push1=, $pop0{{$}}
+; CHECK-NEXT: return $pop1{{$}}
 define i32 @foo() {
   %a = load i32, i32* @answer
   ret i32 %a
index 8630baa..bdccfff 100644 (file)
@@ -6,8 +6,8 @@ target datalayout = "e-p:32:32-i64:64-n32:64-S128"
 target triple = "wasm32-unknown-unknown"
 
 ; CHECK-LABEL: sext_i8_i32:
-; CHECK: i32.load8_s $0=, $0{{$}}
-; CHECK-NEXT: return $0{{$}}
+; CHECK: i32.load8_s $push0=, $0{{$}}
+; CHECK-NEXT: return $pop0{{$}}
 define i32 @sext_i8_i32(i8 *%p) {
   %v = load i8, i8* %p
   %e = sext i8 %v to i32
@@ -15,8 +15,8 @@ define i32 @sext_i8_i32(i8 *%p) {
 }
 
 ; CHECK-LABEL: zext_i8_i32:
-; CHECK: i32.load8_u $0=, $0{{$}}
-; CHECK-NEXT: return $0{{$}}
+; CHECK: i32.load8_u $push0=, $0{{$}}
+; CHECK-NEXT: return $pop0{{$}}
 define i32 @zext_i8_i32(i8 *%p) {
   %v = load i8, i8* %p
   %e = zext i8 %v to i32
@@ -24,8 +24,8 @@ define i32 @zext_i8_i32(i8 *%p) {
 }
 
 ; CHECK-LABEL: sext_i16_i32:
-; CHECK: i32.load16_s $0=, $0{{$}}
-; CHECK-NEXT: return $0{{$}}
+; CHECK: i32.load16_s $push0=, $0{{$}}
+; CHECK-NEXT: return $pop0{{$}}
 define i32 @sext_i16_i32(i16 *%p) {
   %v = load i16, i16* %p
   %e = sext i16 %v to i32
@@ -33,8 +33,8 @@ define i32 @sext_i16_i32(i16 *%p) {
 }
 
 ; CHECK-LABEL: zext_i16_i32:
-; CHECK: i32.load16_u $0=, $0{{$}}
-; CHECK-NEXT: return $0{{$}}
+; CHECK: i32.load16_u $push0=, $0{{$}}
+; CHECK-NEXT: return $pop0{{$}}
 define i32 @zext_i16_i32(i16 *%p) {
   %v = load i16, i16* %p
   %e = zext i16 %v to i32
@@ -42,8 +42,8 @@ define i32 @zext_i16_i32(i16 *%p) {
 }
 
 ; CHECK-LABEL: sext_i8_i64:
-; CHECK: i64.load8_s $1=, $0{{$}}
-; CHECK-NEXT: return $1{{$}}
+; CHECK: i64.load8_s $push0=, $0{{$}}
+; CHECK-NEXT: return $pop0{{$}}
 define i64 @sext_i8_i64(i8 *%p) {
   %v = load i8, i8* %p
   %e = sext i8 %v to i64
@@ -51,8 +51,8 @@ define i64 @sext_i8_i64(i8 *%p) {
 }
 
 ; CHECK-LABEL: zext_i8_i64:
-; CHECK: i64.load8_u $1=, $0{{$}}
-; CHECK-NEXT: return $1{{$}}
+; CHECK: i64.load8_u $push0=, $0{{$}}
+; CHECK-NEXT: return $pop0{{$}}
 define i64 @zext_i8_i64(i8 *%p) {
   %v = load i8, i8* %p
   %e = zext i8 %v to i64
@@ -60,8 +60,8 @@ define i64 @zext_i8_i64(i8 *%p) {
 }
 
 ; CHECK-LABEL: sext_i16_i64:
-; CHECK: i64.load16_s $1=, $0{{$}}
-; CHECK-NEXT: return $1{{$}}
+; CHECK: i64.load16_s $push0=, $0{{$}}
+; CHECK-NEXT: return $pop0{{$}}
 define i64 @sext_i16_i64(i16 *%p) {
   %v = load i16, i16* %p
   %e = sext i16 %v to i64
@@ -69,8 +69,8 @@ define i64 @sext_i16_i64(i16 *%p) {
 }
 
 ; CHECK-LABEL: zext_i16_i64:
-; CHECK: i64.load16_u $1=, $0{{$}}
-; CHECK-NEXT: return $1{{$}}
+; CHECK: i64.load16_u $push0=, $0{{$}}
+; CHECK-NEXT: return $pop0{{$}}
 define i64 @zext_i16_i64(i16 *%p) {
   %v = load i16, i16* %p
   %e = zext i16 %v to i64
@@ -78,8 +78,8 @@ define i64 @zext_i16_i64(i16 *%p) {
 }
 
 ; CHECK-LABEL: sext_i32_i64:
-; CHECK: i64.load32_s $1=, $0{{$}}
-; CHECK-NEXT: return $1{{$}}
+; CHECK: i64.load32_s $push0=, $0{{$}}
+; CHECK-NEXT: return $pop0{{$}}
 define i64 @sext_i32_i64(i32 *%p) {
   %v = load i32, i32* %p
   %e = sext i32 %v to i64
@@ -87,8 +87,8 @@ define i64 @sext_i32_i64(i32 *%p) {
 }
 
 ; CHECK-LABEL: zext_i32_i64:
-; CHECK: i64.load32_u $1=, $0{{$}}
-; CHECK: return $1{{$}}
+; CHECK: i64.load32_u $push0=, $0{{$}}
+; CHECK: return $pop0{{$}}
 define i64 @zext_i32_i64(i32 *%p) {
   %v = load i32, i32* %p
   %e = zext i32 %v to i64
index baaff01..33d3aee 100644 (file)
@@ -6,8 +6,8 @@ target datalayout = "e-p:32:32-i64:64-n32:64-S128"
 target triple = "wasm32-unknown-unknown"
 
 ; CHECK-LABEL: load_u_i1_i32:
-; CHECK:      i32.load8_u $[[NUM0:[0-9]+]]=, $0{{$}}
-; CHECK-NEXT: return $[[NUM0]]{{$}}
+; CHECK:      i32.load8_u $push[[NUM0:[0-9]+]]=, $0{{$}}
+; CHECK-NEXT: return $pop[[NUM0]]{{$}}
 define i32 @load_u_i1_i32(i1* %p) {
   %v = load i1, i1* %p
   %e = zext i1 %v to i32
@@ -15,9 +15,9 @@ define i32 @load_u_i1_i32(i1* %p) {
 }
 
 ; CHECK-LABEL: load_s_i1_i32:
-; CHECK:      i32.load8_u $[[NUM0:[0-9]+]]=, $0{{$}}
+; CHECK:      i32.load8_u $push[[NUM0:[0-9]+]]=, $0{{$}}
 ; CHECK-NEXT: i32.const $[[NUM1:[0-9]+]]=, 31{{$}}
-; CHECK-NEXT: shl $push[[NUM2:[0-9]+]]=, $[[NUM0]], $[[NUM1]]{{$}}
+; CHECK-NEXT: shl $push[[NUM2:[0-9]+]]=, $pop[[NUM0]], $[[NUM1]]{{$}}
 ; CHECK-NEXT: shr_s $push[[NUM3:[0-9]+]]=, $pop[[NUM2]], $[[NUM1]]{{$}}
 ; CHECK-NEXT: return $pop[[NUM3]]{{$}}
 define i32 @load_s_i1_i32(i1* %p) {
@@ -27,8 +27,8 @@ define i32 @load_s_i1_i32(i1* %p) {
 }
 
 ; CHECK-LABEL: load_u_i1_i64:
-; CHECK:      i64.load8_u $[[NUM0:[0-9]+]]=, $0{{$}}
-; CHECK-NEXT: return $[[NUM0]]{{$}}
+; CHECK:      i64.load8_u $push[[NUM0:[0-9]+]]=, $0{{$}}
+; CHECK-NEXT: return $pop[[NUM0]]{{$}}
 define i64 @load_u_i1_i64(i1* %p) {
   %v = load i1, i1* %p
   %e = zext i1 %v to i64
@@ -36,9 +36,9 @@ define i64 @load_u_i1_i64(i1* %p) {
 }
 
 ; CHECK-LABEL: load_s_i1_i64:
-; CHECK:      i64.load8_u $[[NUM0:[0-9]+]]=, $0{{$}}
+; CHECK:      i64.load8_u $push[[NUM0:[0-9]+]]=, $0{{$}}
 ; CHECK-NEXT: i64.const $[[NUM1:[0-9]+]]=, 63{{$}}
-; CHECK-NEXT: shl $push[[NUM2:[0-9]+]]=, $[[NUM0]], $[[NUM1]]{{$}}
+; CHECK-NEXT: shl $push[[NUM2:[0-9]+]]=, $pop[[NUM0]], $[[NUM1]]{{$}}
 ; CHECK-NEXT: shr_s $push[[NUM3:[0-9]+]]=, $pop[[NUM2]], $[[NUM1]]{{$}}
 ; CHECK-NEXT: return $pop[[NUM3]]{{$}}
 define i64 @load_s_i1_i64(i1* %p) {
index 707897b..1017167 100644 (file)
@@ -8,8 +8,8 @@ target triple = "wasm32-unknown-unknown"
 ; CHECK-LABEL: ldi32:
 ; CHECK-NEXT: .param i32{{$}}
 ; CHECK-NEXT: .result i32{{$}}
-; CHECK-NEXT: i32.load $[[NUM:[0-9]+]]=, $0{{$}}
-; CHECK-NEXT: return $[[NUM]]{{$}}
+; CHECK-NEXT: i32.load $push[[NUM:[0-9]+]]=, $0{{$}}
+; CHECK-NEXT: return $pop[[NUM]]{{$}}
 define i32 @ldi32(i32 *%p) {
   %v = load i32, i32* %p
   ret i32 %v
@@ -18,9 +18,8 @@ define i32 @ldi32(i32 *%p) {
 ; CHECK-LABEL: ldi64:
 ; CHECK-NEXT: .param i32{{$}}
 ; CHECK-NEXT: .result i64{{$}}
-; CHECK-NEXT: .local i64{{$}}
-; CHECK-NEXT: i64.load $[[NUM:[0-9]+]]=, $0{{$}}
-; CHECK-NEXT: return $[[NUM]]{{$}}
+; CHECK-NEXT: i64.load $push[[NUM:[0-9]+]]=, $0{{$}}
+; CHECK-NEXT: return $pop[[NUM]]{{$}}
 define i64 @ldi64(i64 *%p) {
   %v = load i64, i64* %p
   ret i64 %v
@@ -29,9 +28,8 @@ define i64 @ldi64(i64 *%p) {
 ; CHECK-LABEL: ldf32:
 ; CHECK-NEXT: .param i32{{$}}
 ; CHECK-NEXT: .result f32{{$}}
-; CHECK-NEXT: .local f32{{$}}
-; CHECK-NEXT: f32.load $[[NUM:[0-9]+]]=, $0{{$}}
-; CHECK-NEXT: return $[[NUM]]{{$}}
+; CHECK-NEXT: f32.load $push[[NUM:[0-9]+]]=, $0{{$}}
+; CHECK-NEXT: return $pop[[NUM]]{{$}}
 define float @ldf32(float *%p) {
   %v = load float, float* %p
   ret float %v
@@ -40,9 +38,8 @@ define float @ldf32(float *%p) {
 ; CHECK-LABEL: ldf64:
 ; CHECK-NEXT: .param i32{{$}}
 ; CHECK-NEXT: .result f64{{$}}
-; CHECK-NEXT: .local f64{{$}}
-; CHECK-NEXT: f64.load $[[NUM:[0-9]+]]=, $0{{$}}
-; CHECK-NEXT: return $[[NUM]]{{$}}
+; CHECK-NEXT: f64.load $push[[NUM:[0-9]+]]=, $0{{$}}
+; CHECK-NEXT: return $pop[[NUM]]{{$}}
 define double @ldf64(double *%p) {
   %v = load double, double* %p
   ret double %v
index 674131b..4b3aba9 100644 (file)
@@ -10,9 +10,8 @@ declare void @llvm.wasm.grow.memory.i32(i32) nounwind
 
 ; CHECK-LABEL: memory_size:
 ; CHECK-NEXT: .result i32{{$}}
-; CHECK-NEXT: .local i32{{$}}
-; CHECK-NEXT: memory_size $0={{$}}
-; CHECK-NEXT: return $0{{$}}
+; CHECK-NEXT: memory_size $push0={{$}}
+; CHECK-NEXT: return $pop0{{$}}
 define i32 @memory_size() {
   %a = call i32 @llvm.wasm.memory.size.i32()
   ret i32 %a
index bb3fc27..a44c5f0 100644 (file)
@@ -10,9 +10,8 @@ declare void @llvm.wasm.grow.memory.i64(i64) nounwind
 
 ; CHECK-LABEL: memory_size:
 ; CHECK-NEXT: .result i64{{$}}
-; CHECK-NEXT: .local i64{{$}}
-; CHECK-NEXT: memory_size $0={{$}}
-; CHECK-NEXT: return $0{{$}}
+; CHECK-NEXT: memory_size $push0={{$}}
+; CHECK-NEXT: return $pop0{{$}}
 define i64 @memory_size() {
   %a = call i64 @llvm.wasm.memory.size.i64()
   ret i64 %a
diff --git a/test/CodeGen/WebAssembly/reg-stackify.ll b/test/CodeGen/WebAssembly/reg-stackify.ll
new file mode 100644 (file)
index 0000000..f3000aa
--- /dev/null
@@ -0,0 +1,47 @@
+; RUN: llc < %s -asm-verbose=false | FileCheck %s
+
+; Test the register stackifier pass.
+
+target datalayout = "e-p:32:32-i64:64-n32:64-S128"
+target triple = "wasm32-unknown-unknown"
+
+; No because of pointer aliasing.
+
+; CHECK-LABEL: no0:
+; CHECK: return $1{{$}}
+define i32 @no0(i32* %p, i32* %q) {
+  %t = load i32, i32* %q
+  store i32 0, i32* %p
+  ret i32 %t
+}
+
+; No because of side effects.
+
+; CHECK-LABEL: no1:
+; CHECK: return $1{{$}}
+define i32 @no1(i32* %p, i32* dereferenceable(4) %q) {
+  %t = load volatile i32, i32* %q, !invariant.load !0
+  store volatile i32 0, i32* %p
+  ret i32 %t
+}
+
+; Yes because of invariant load and no side effects.
+
+; CHECK-LABEL: yes0:
+; CHECK: return $pop0{{$}}
+define i32 @yes0(i32* %p, i32* dereferenceable(4) %q) {
+  %t = load i32, i32* %q, !invariant.load !0
+  store i32 0, i32* %p
+  ret i32 %t
+}
+
+; Yes because of no intervening side effects.
+
+; CHECK-LABEL: yes1:
+; CHECK: return $pop0{{$}}
+define i32 @yes1(i32* %q) {
+  %t = load volatile i32, i32* %q
+  ret i32 %t
+}
+
+!0 = !{}
diff --git a/test/CodeGen/WebAssembly/returned.ll b/test/CodeGen/WebAssembly/returned.ll
new file mode 100644 (file)
index 0000000..d65e2a8
--- /dev/null
@@ -0,0 +1,35 @@
+; RUN: llc < %s -asm-verbose=false | FileCheck %s
+
+; Test that the "returned" attribute is optimized effectively.
+
+target datalayout = "e-p:32:32-i64:64-n32:64-S128"
+target triple = "wasm32-unknown-unknown"
+
+; CHECK-LABEL: _Z3foov:
+; CHECK-NEXT: .result   i32{{$}}
+; CHECK-NEXT: i32.const $push0=, 1{{$}}
+; CHECK-NEXT: call      $push1=, _Znwm, $pop0{{$}}
+; CHECK-NEXT: call      $push2=, _ZN5AppleC1Ev, $pop1{{$}}
+; CHECK-NEXT: return    $pop2{{$}}
+%class.Apple = type { i8 }
+declare noalias i8* @_Znwm(i32)
+declare %class.Apple* @_ZN5AppleC1Ev(%class.Apple* returned)
+define %class.Apple* @_Z3foov() {
+entry:
+  %call = tail call noalias i8* @_Znwm(i32 1)
+  %0 = bitcast i8* %call to %class.Apple*
+  %call1 = tail call %class.Apple* @_ZN5AppleC1Ev(%class.Apple* %0)
+  ret %class.Apple* %0
+}
+
+; CHECK-LABEL: _Z3barPvS_l:
+; CHECK-NEXT: .param   i32, i32, i32{{$}}
+; CHECK-NEXT: .result  i32{{$}}
+; CHECK-NEXT: call     $push0=, memcpy, $0, $1, $2{{$}}
+; CHECK-NEXT: return   $pop0{{$}}
+declare i8* @memcpy(i8* returned, i8*, i32)
+define i8* @_Z3barPvS_l(i8* %p, i8* %s, i32 %n) {
+entry:
+  %call = tail call i8* @memcpy(i8* %p, i8* %s, i32 %n)
+  ret i8* %p
+}