[WebAssembly] Fix handling of COPY instructions in WebAssemblyRegStackify.
authorDan Gohman <dan433584@gmail.com>
Fri, 25 Dec 2015 00:31:02 +0000 (00:31 +0000)
committerDan Gohman <dan433584@gmail.com>
Fri, 25 Dec 2015 00:31:02 +0000 (00:31 +0000)
Move RegStackify after coalescing and teach it to use LiveIntervals instead
of depending on SSA form. This avoids a problem where a register in a COPY
instruction is stackified and then subsequently coalesced with a register
that is not stackified.

This also puts it after the scheduler, which allows us to simplify the
EXPR_STACK constraint, as we no longer have instructions being reordered
after stackification and before coloring.

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

lib/Target/WebAssembly/WebAssemblyCFGStackify.cpp
lib/Target/WebAssembly/WebAssemblyInstrControl.td
lib/Target/WebAssembly/WebAssemblyRegStackify.cpp
lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp
test/CodeGen/WebAssembly/cfg-stackify.ll
test/CodeGen/WebAssembly/reg-stackify.ll

index 6ac53662359db1b52186c96514a9c900cd41c46b..e9671ee07e69c668e717844c4df8d01848b6dbcc 100644 (file)
@@ -320,11 +320,12 @@ static void PlaceBlockMarker(MachineBasicBlock &MBB, MachineFunction &MF,
     // the BLOCK needs to be above the LOOP.
     InsertPos = Header->begin();
   } else {
     // the BLOCK needs to be above the LOOP.
     InsertPos = Header->begin();
   } else {
-    // Otherwise, insert the BLOCK as late in Header as we can, but before any
-    // existing BLOCKs.
+    // Otherwise, insert the BLOCK as late in Header as we can, but before the
+    // beginning of the local expression tree and any nested BLOCKs.
     InsertPos = Header->getFirstTerminator();
     while (InsertPos != Header->begin() &&
     InsertPos = Header->getFirstTerminator();
     while (InsertPos != Header->begin() &&
-           prev(InsertPos)->getOpcode() == WebAssembly::BLOCK)
+           prev(InsertPos)->definesRegister(WebAssembly::EXPR_STACK) &&
+           prev(InsertPos)->getOpcode() != WebAssembly::LOOP)
       --InsertPos;
   }
 
       --InsertPos;
   }
 
index 9a9468bb3909c242c85c04ee0f0445d9a5c544cf..05efe8903413e552a954636c49beff30ed13ff67 100644 (file)
@@ -50,9 +50,13 @@ def TABLESWITCH_I64 : I<(outs), (ins I64:$index, bb_op:$default, variable_ops),
                         "tableswitch\t$index, $default">;
 } // isTerminator = 1, hasCtrlDep = 1, isBarrier = 1
 
                         "tableswitch\t$index, $default">;
 } // isTerminator = 1, hasCtrlDep = 1, isBarrier = 1
 
-// Placemarkers to indicate the start of a block or loop scope.
+// Placemarkers to indicate the start of a block or loop scope. These
+// use/clobber EXPR_STACK to prevent them from being moved into the middle of
+// an expression tree.
+let Uses = [EXPR_STACK], Defs = [EXPR_STACK] in {
 def BLOCK     : I<(outs), (ins bb_op:$dst), [], "block   \t$dst">;
 def LOOP      : I<(outs), (ins bb_op:$dst), [], "loop    \t$dst">;
 def BLOCK     : I<(outs), (ins bb_op:$dst), [], "block   \t$dst">;
 def LOOP      : I<(outs), (ins bb_op:$dst), [], "loop    \t$dst">;
+} // Uses = [EXPR_STACK], Defs = [EXPR_STACK]
 
 // No-op to indicate to the AsmPrinter that a loop ends here, so a
 // basic block label is needed even if it wouldn't otherwise appear so.
 
 // No-op to indicate to the AsmPrinter that a loop ends here, so a
 // basic block label is needed even if it wouldn't otherwise appear so.
index 3a58826bd7677085f29007d6fe2ad666723535ae..89ef5cdb2bef5a98d60c7e819abdae6f38f8b979 100644 (file)
@@ -24,6 +24,7 @@
 #include "MCTargetDesc/WebAssemblyMCTargetDesc.h" // for WebAssembly::ARGUMENT_*
 #include "WebAssemblyMachineFunctionInfo.h"
 #include "llvm/Analysis/AliasAnalysis.h"
 #include "MCTargetDesc/WebAssemblyMCTargetDesc.h" // for WebAssembly::ARGUMENT_*
 #include "WebAssemblyMachineFunctionInfo.h"
 #include "llvm/Analysis/AliasAnalysis.h"
+#include "llvm/CodeGen/LiveIntervalAnalysis.h"
 #include "llvm/CodeGen/MachineBlockFrequencyInfo.h"
 #include "llvm/CodeGen/MachineRegisterInfo.h"
 #include "llvm/CodeGen/Passes.h"
 #include "llvm/CodeGen/MachineBlockFrequencyInfo.h"
 #include "llvm/CodeGen/MachineRegisterInfo.h"
 #include "llvm/CodeGen/Passes.h"
@@ -42,8 +43,12 @@ class WebAssemblyRegStackify final : public MachineFunctionPass {
   void getAnalysisUsage(AnalysisUsage &AU) const override {
     AU.setPreservesCFG();
     AU.addRequired<AAResultsWrapperPass>();
   void getAnalysisUsage(AnalysisUsage &AU) const override {
     AU.setPreservesCFG();
     AU.addRequired<AAResultsWrapperPass>();
+    AU.addRequired<LiveIntervals>();
     AU.addPreserved<MachineBlockFrequencyInfo>();
     AU.addPreserved<MachineBlockFrequencyInfo>();
+    AU.addPreserved<SlotIndexes>();
+    AU.addPreserved<LiveIntervals>();
     AU.addPreservedID(MachineDominatorsID);
     AU.addPreservedID(MachineDominatorsID);
+    AU.addPreservedID(LiveVariablesID);
     MachineFunctionPass::getAnalysisUsage(AU);
   }
 
     MachineFunctionPass::getAnalysisUsage(AU);
   }
 
@@ -61,59 +66,68 @@ FunctionPass *llvm::createWebAssemblyRegStackify() {
 }
 
 // Decorate the given instruction with implicit operands that enforce the
 }
 
 // Decorate the given instruction with implicit operands that enforce the
-// expression stack ordering constraints needed for an instruction which is
-// consumed by an instruction using the expression stack.
-static void ImposeStackInputOrdering(MachineInstr *MI) {
+// expression stack ordering constraints for an instruction which is on
+// the expression stack.
+static void ImposeStackOrdering(MachineInstr *MI) {
   // Write the opaque EXPR_STACK register.
   if (!MI->definesRegister(WebAssembly::EXPR_STACK))
     MI->addOperand(MachineOperand::CreateReg(WebAssembly::EXPR_STACK,
                                              /*isDef=*/true,
                                              /*isImp=*/true));
   // Write the opaque EXPR_STACK register.
   if (!MI->definesRegister(WebAssembly::EXPR_STACK))
     MI->addOperand(MachineOperand::CreateReg(WebAssembly::EXPR_STACK,
                                              /*isDef=*/true,
                                              /*isImp=*/true));
-}
-
-// Decorate the given instruction with implicit operands that enforce the
-// expression stack ordering constraints for an instruction which is on
-// the expression stack.
-static void ImposeStackOrdering(MachineInstr *MI, MachineRegisterInfo &MRI) {
-  ImposeStackInputOrdering(MI);
 
   // Also read the opaque EXPR_STACK register.
   if (!MI->readsRegister(WebAssembly::EXPR_STACK))
     MI->addOperand(MachineOperand::CreateReg(WebAssembly::EXPR_STACK,
                                              /*isDef=*/false,
                                              /*isImp=*/true));
 
   // Also read the opaque EXPR_STACK register.
   if (!MI->readsRegister(WebAssembly::EXPR_STACK))
     MI->addOperand(MachineOperand::CreateReg(WebAssembly::EXPR_STACK,
                                              /*isDef=*/false,
                                              /*isImp=*/true));
-
-  // Also, mark any inputs to this instruction as being consumed by an
-  // instruction on the expression stack.
-  // TODO: Find a lighter way to describe the appropriate constraints.
-  for (MachineOperand &MO : MI->uses()) {
-    if (!MO.isReg())
-      continue;
-    unsigned Reg = MO.getReg();
-    if (!TargetRegisterInfo::isVirtualRegister(Reg))
-      continue;
-    MachineInstr *Def = MRI.getVRegDef(Reg);
-    if (Def->getOpcode() == TargetOpcode::PHI)
-      continue;
-    ImposeStackInputOrdering(Def);
-  }
 }
 
 }
 
-// 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).
+// Test whether it's safe to move Def to just before Insert.
 // 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,
 // 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) {
+                         AliasAnalysis &AA, LiveIntervals &LIS,
+                         MachineRegisterInfo &MRI) {
   assert(Def->getParent() == Insert->getParent());
   bool SawStore = false, SawSideEffects = false;
   MachineBasicBlock::const_iterator D(Def), I(Insert);
   assert(Def->getParent() == Insert->getParent());
   bool SawStore = false, SawSideEffects = false;
   MachineBasicBlock::const_iterator D(Def), I(Insert);
+
+  // Check for register dependencies.
+  for (const MachineOperand &MO : Def->operands()) {
+    if (!MO.isReg() || MO.isUndef())
+      continue;
+    unsigned Reg = MO.getReg();
+
+    // If the register is dead here and at Insert, ignore it.
+    if (MO.isDead() && Insert->definesRegister(Reg) &&
+        !Insert->readsRegister(Reg))
+      continue;
+
+    if (TargetRegisterInfo::isPhysicalRegister(Reg)) {
+      // If the physical register is never modified, ignore it.
+      if (!MRI.isPhysRegModified(Reg))
+        continue;
+      // Otherwise, it's a physical register with unknown liveness.
+      return false;
+    }
+
+    // Ask LiveIntervals whether moving this virtual register use or def to
+    // Insert will change value numbers are seen.
+    const LiveInterval &LI = LIS.getInterval(Reg);
+    VNInfo *DefVNI = MO.isDef() ?
+        LI.getVNInfoAt(LIS.getInstructionIndex(Def).getRegSlot()) :
+        LI.getVNInfoBefore(LIS.getInstructionIndex(Def));
+    assert(DefVNI && "Instruction input missing value number");
+    VNInfo *InsVNI = LI.getVNInfoBefore(LIS.getInstructionIndex(Insert));
+    if (InsVNI && DefVNI != InsVNI)
+      return false;
+  }
+
+  // Check for memory dependencies and side effects.
   for (--I; I != D; --I)
     SawSideEffects |= I->isSafeToMove(&AA, SawStore);
   for (--I; I != D; --I)
     SawSideEffects |= I->isSafeToMove(&AA, SawStore);
-
   return !(SawStore && Def->mayLoad() && !Def->isInvariantLoad(&AA)) &&
          !(SawSideEffects && !Def->isSafeToMove(&AA, SawStore));
 }
   return !(SawStore && Def->mayLoad() && !Def->isInvariantLoad(&AA)) &&
          !(SawSideEffects && !Def->isSafeToMove(&AA, SawStore));
 }
@@ -127,8 +141,7 @@ bool WebAssemblyRegStackify::runOnMachineFunction(MachineFunction &MF) {
   MachineRegisterInfo &MRI = MF.getRegInfo();
   WebAssemblyFunctionInfo &MFI = *MF.getInfo<WebAssemblyFunctionInfo>();
   AliasAnalysis &AA = getAnalysis<AAResultsWrapperPass>().getAAResults();
   MachineRegisterInfo &MRI = MF.getRegInfo();
   WebAssemblyFunctionInfo &MFI = *MF.getInfo<WebAssemblyFunctionInfo>();
   AliasAnalysis &AA = getAnalysis<AAResultsWrapperPass>().getAAResults();
-
-  assert(MRI.isSSA() && "RegStackify depends on SSA form");
+  LiveIntervals &LIS = getAnalysis<LiveIntervals>();
 
   // 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
 
   // 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
@@ -154,19 +167,10 @@ bool WebAssemblyRegStackify::runOnMachineFunction(MachineFunction &MF) {
           continue;
 
         unsigned Reg = Op.getReg();
           continue;
 
         unsigned Reg = Op.getReg();
-        if (TargetRegisterInfo::isPhysicalRegister(Reg)) {
-          // An instruction with a physical register. Conservatively mark it as
-          // an expression stack input so that it isn't reordered with anything
-          // in an expression stack which might use it (physical registers
-          // aren't in SSA form so it's not trivial to determine this).
-          // TODO: Be less conservative.
-          ImposeStackInputOrdering(Insert);
-          continue;
-        }
 
         // Only consider registers with a single definition.
         // TODO: Eventually we may relax this, to stackify phi transfers.
 
         // Only consider registers with a single definition.
         // TODO: Eventually we may relax this, to stackify phi transfers.
-        MachineInstr *Def = MRI.getVRegDef(Reg);
+        MachineInstr *Def = MRI.getUniqueVRegDef(Reg);
         if (!Def)
           continue;
 
         if (!Def)
           continue;
 
@@ -198,26 +202,26 @@ bool WebAssemblyRegStackify::runOnMachineFunction(MachineFunction &MF) {
           continue;
 
         // For now, be conservative and don't look across block boundaries.
           continue;
 
         // For now, be conservative and don't look across block boundaries.
-        // TODO: Be more aggressive.
+        // TODO: Be more aggressive?
         if (Def->getParent() != &MBB)
           continue;
 
         // Don't move instructions that have side effects or memory dependencies
         // or other complications.
         if (Def->getParent() != &MBB)
           continue;
 
         // Don't move instructions that have side effects or memory dependencies
         // or other complications.
-        if (!IsSafeToMove(Def, Insert, AA))
+        if (!IsSafeToMove(Def, Insert, AA, LIS, MRI))
           continue;
 
         Changed = true;
         AnyStackified = true;
         // Move the def down and nest it in the current instruction.
           continue;
 
         Changed = true;
         AnyStackified = true;
         // Move the def down and nest it in the current instruction.
-        MBB.insert(MachineBasicBlock::instr_iterator(Insert),
-                   Def->removeFromParent());
+        MBB.splice(Insert, &MBB, Def);
+        LIS.handleMove(Def);
         MFI.stackifyVReg(Reg);
         MFI.stackifyVReg(Reg);
-        ImposeStackOrdering(Def, MRI);
+        ImposeStackOrdering(Def);
         Insert = Def;
       }
       if (AnyStackified)
         Insert = Def;
       }
       if (AnyStackified)
-        ImposeStackOrdering(&MI, MRI);
+        ImposeStackOrdering(&MI);
     }
   }
 
     }
   }
 
index c774c120bb5162644536e115c5a464c2b003ed9c..e31ea46de9f5dc1cecc3588fbce822f37419eb9d 100644 (file)
@@ -165,12 +165,6 @@ void WebAssemblyPassConfig::addPreRegAlloc() {
 
   // Prepare store instructions for register stackifying.
   addPass(createWebAssemblyStoreResults());
 
   // Prepare store instructions for register stackifying.
   addPass(createWebAssemblyStoreResults());
-
-  // 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() {
 }
 
 void WebAssemblyPassConfig::addPostRegAlloc() {
@@ -184,6 +178,9 @@ void WebAssemblyPassConfig::addPostRegAlloc() {
   // Fails with: should be run after register allocation.
   disablePass(&MachineCopyPropagationID);
 
   // Fails with: should be run after register allocation.
   disablePass(&MachineCopyPropagationID);
 
+  // Mark registers as representing wasm's expression stack.
+  addPass(createWebAssemblyRegStackify());
+
   // Run the register coloring pass to reduce the total number of registers.
   addPass(createWebAssemblyRegColoring());
 
   // Run the register coloring pass to reduce the total number of registers.
   addPass(createWebAssemblyRegColoring());
 
index a10fb614909fad3015cbcb73b9090ffc08b00803..71f3551347bf5b1c5213bbb005146369421f0056 100644 (file)
@@ -464,7 +464,7 @@ if.end:
 ; CHECK:      block       BB13_8{{$}}
 ; CHECK-NEXT: block       BB13_7{{$}}
 ; CHECK-NEXT: block       BB13_4{{$}}
 ; CHECK:      block       BB13_8{{$}}
 ; CHECK-NEXT: block       BB13_7{{$}}
 ; CHECK-NEXT: block       BB13_4{{$}}
-; CHECK-NEXT: br_if       $pop{{[0-9]*}}, BB13_4{{$}}
+; CHECK:      br_if       $pop{{[0-9]*}}, BB13_4{{$}}
 ; CHECK-NEXT: block       BB13_3{{$}}
 ; CHECK:      br_if       $pop{{[0-9]*}}, BB13_3{{$}}
 ; CHECK:      br_if       $pop{{[0-9]*}}, BB13_7{{$}}
 ; CHECK-NEXT: block       BB13_3{{$}}
 ; CHECK:      br_if       $pop{{[0-9]*}}, BB13_3{{$}}
 ; CHECK:      br_if       $pop{{[0-9]*}}, BB13_7{{$}}
@@ -483,7 +483,7 @@ if.end:
 ; OPT:      block       BB13_8{{$}}
 ; OPT-NEXT: block       BB13_7{{$}}
 ; OPT-NEXT: block       BB13_4{{$}}
 ; OPT:      block       BB13_8{{$}}
 ; OPT-NEXT: block       BB13_7{{$}}
 ; OPT-NEXT: block       BB13_4{{$}}
-; OPT-NEXT: br_if       $pop{{[0-9]*}}, BB13_4{{$}}
+; OPT:      br_if       $pop{{[0-9]*}}, BB13_4{{$}}
 ; OPT-NEXT: block       BB13_3{{$}}
 ; OPT:      br_if       $pop{{[0-9]*}}, BB13_3{{$}}
 ; OPT:      br_if       $pop{{[0-9]*}}, BB13_7{{$}}
 ; OPT-NEXT: block       BB13_3{{$}}
 ; OPT:      br_if       $pop{{[0-9]*}}, BB13_3{{$}}
 ; OPT:      br_if       $pop{{[0-9]*}}, BB13_7{{$}}
@@ -642,7 +642,7 @@ second:
 ; CHECK-NEXT:  loop BB16_5{{$}}
 ; CHECK-NOT:   block
 ; CHECK:       block BB16_4{{$}}
 ; CHECK-NEXT:  loop BB16_5{{$}}
 ; CHECK-NOT:   block
 ; CHECK:       block BB16_4{{$}}
-; CHECK-NEXT:  br_if {{[^,]*}}, BB16_4{{$}}
+; CHECK:       br_if {{[^,]*}}, BB16_4{{$}}
 ; CHECK-NOT:   block
 ; CHECK:       br_if {{[^,]*}}, BB16_1{{$}}
 ; CHECK-NOT:   block
 ; CHECK-NOT:   block
 ; CHECK:       br_if {{[^,]*}}, BB16_1{{$}}
 ; CHECK-NOT:   block
@@ -907,7 +907,7 @@ bb6:
 ; CHECK-NEXT:  br_if        {{[^,]*}}, BB20_4{{$}}
 ; CHECK-NOT:   block
 ; CHECK:       block        BB20_3{{$}}
 ; CHECK-NEXT:  br_if        {{[^,]*}}, BB20_4{{$}}
 ; CHECK-NOT:   block
 ; CHECK:       block        BB20_3{{$}}
-; CHECK-NEXT:  br_if        {{[^,]*}}, BB20_3{{$}}
+; CHECK:       br_if        {{[^,]*}}, BB20_3{{$}}
 ; CHECK-NOT:   block
 ; CHECK:       br_if        {{[^,]*}}, BB20_6{{$}}
 ; CHECK-NEXT:  BB20_3:
 ; CHECK-NOT:   block
 ; CHECK:       br_if        {{[^,]*}}, BB20_6{{$}}
 ; CHECK-NEXT:  BB20_3:
@@ -933,7 +933,7 @@ bb6:
 ; OPT-NEXT:  br_if        $0, BB20_4{{$}}
 ; OPT-NOT:   block
 ; OPT:       block        BB20_3{{$}}
 ; OPT-NEXT:  br_if        $0, BB20_4{{$}}
 ; OPT-NOT:   block
 ; OPT:       block        BB20_3{{$}}
-; OPT-NEXT:  br_if        $0, BB20_3{{$}}
+; OPT:       br_if        $0, BB20_3{{$}}
 ; OPT-NOT:   block
 ; OPT:       br_if        $0, BB20_8{{$}}
 ; OPT-NEXT:  BB20_3:
 ; OPT-NOT:   block
 ; OPT:       br_if        $0, BB20_8{{$}}
 ; OPT-NEXT:  BB20_3:
@@ -991,7 +991,7 @@ bb8:
 ; CHECK:       block       BB21_7{{$}}
 ; CHECK-NEXT:  block       BB21_6{{$}}
 ; CHECK-NEXT:  block       BB21_4{{$}}
 ; CHECK:       block       BB21_7{{$}}
 ; CHECK-NEXT:  block       BB21_6{{$}}
 ; CHECK-NEXT:  block       BB21_4{{$}}
-; CHECK-NEXT:  br_if       {{[^,]*}}, BB21_4{{$}}
+; CHECK:       br_if       {{[^,]*}}, BB21_4{{$}}
 ; CHECK-NOT:   block
 ; CHECK:       br_if       {{[^,]*}}, BB21_7{{$}}
 ; CHECK-NOT:   block
 ; CHECK-NOT:   block
 ; CHECK:       br_if       {{[^,]*}}, BB21_7{{$}}
 ; CHECK-NOT:   block
@@ -1015,7 +1015,7 @@ bb8:
 ; OPT:       block       BB21_7{{$}}
 ; OPT-NEXT:  block       BB21_6{{$}}
 ; OPT-NEXT:  block       BB21_4{{$}}
 ; OPT:       block       BB21_7{{$}}
 ; OPT-NEXT:  block       BB21_6{{$}}
 ; OPT-NEXT:  block       BB21_4{{$}}
-; OPT-NEXT:  br_if       {{[^,]*}}, BB21_4{{$}}
+; OPT:       br_if       {{[^,]*}}, BB21_4{{$}}
 ; OPT-NOT:   block
 ; OPT:       br_if       {{[^,]*}}, BB21_7{{$}}
 ; OPT-NOT:   block
 ; OPT-NOT:   block
 ; OPT:       br_if       {{[^,]*}}, BB21_7{{$}}
 ; OPT-NOT:   block
index 3c34343483613758da8bbb93fbf93f3f522af51d..1c1b1e193f7af801a22b30a5f5ee6e980e93f100 100644 (file)
@@ -53,8 +53,9 @@ define i32 @yes1(i32* %q) {
 ; CHECK-NEXT: .param i32, i32, i32, i32{{$}}
 ; CHECK-NEXT: .result i32{{$}}
 ; CHECK-NEXT: .local i32, i32{{$}}
 ; CHECK-NEXT: .param i32, i32, i32, i32{{$}}
 ; CHECK-NEXT: .result i32{{$}}
 ; CHECK-NEXT: .local i32, i32{{$}}
-; CHECK-NEXT: i32.const   $4=, 1{{$}}
 ; CHECK-NEXT: i32.const   $5=, 2{{$}}
 ; CHECK-NEXT: i32.const   $5=, 2{{$}}
+; CHECK-NEXT: i32.const   $4=, 1{{$}}
+; CHECK-NEXT: block       BB4_2{{$}}
 ; CHECK-NEXT: i32.lt_s    $push0=, $0, $4{{$}}
 ; CHECK-NEXT: i32.lt_s    $push1=, $1, $5{{$}}
 ; CHECK-NEXT: i32.xor     $push4=, $pop0, $pop1{{$}}
 ; CHECK-NEXT: i32.lt_s    $push0=, $0, $4{{$}}
 ; CHECK-NEXT: i32.lt_s    $push1=, $1, $5{{$}}
 ; CHECK-NEXT: i32.xor     $push4=, $pop0, $pop1{{$}}
@@ -63,7 +64,6 @@ define i32 @yes1(i32* %q) {
 ; CHECK-NEXT: i32.xor     $push5=, $pop2, $pop3{{$}}
 ; CHECK-NEXT: i32.xor     $push6=, $pop4, $pop5{{$}}
 ; CHECK-NEXT: i32.ne      $push7=, $pop6, $4{{$}}
 ; CHECK-NEXT: i32.xor     $push5=, $pop2, $pop3{{$}}
 ; CHECK-NEXT: i32.xor     $push6=, $pop4, $pop5{{$}}
 ; CHECK-NEXT: i32.ne      $push7=, $pop6, $4{{$}}
-; CHECK-NEXT: block       BB4_2{{$}}
 ; CHECK-NEXT: br_if       $pop7, BB4_2{{$}}
 ; CHECK-NEXT: i32.const   $push8=, 0{{$}}
 ; CHECK-NEXT: return      $pop8{{$}}
 ; CHECK-NEXT: br_if       $pop7, BB4_2{{$}}
 ; CHECK-NEXT: i32.const   $push8=, 0{{$}}
 ; CHECK-NEXT: return      $pop8{{$}}
@@ -85,4 +85,42 @@ false:
   ret i32 1
 }
 
   ret i32 1
 }
 
+; Test an interesting case where the load has multiple uses and cannot
+; be trivially stackified.
+
+; CHECK-LABEL: multiple_uses:
+; CHECK-NEXT: .param      i32, i32, i32{{$}}
+; CHECK-NEXT: .local      i32{{$}}
+; CHECK-NEXT: i32.load    $3=, 0($2){{$}}
+; CHECK-NEXT: block       BB5_3{{$}}
+; CHECK-NEXT: i32.ge_u    $push0=, $3, $1{{$}}
+; CHECK-NEXT: br_if       $pop0, BB5_3{{$}}
+; CHECK-NEXT: i32.lt_u    $push1=, $3, $0{{$}}
+; CHECK-NEXT: br_if       $pop1, BB5_3{{$}}
+; CHECK-NEXT: i32.store   $discard=, 0($2), $3{{$}}
+; CHECK-NEXT: BB5_3:
+; CHECK-NEXT: return{{$}}
+define void @multiple_uses(i32* %arg0, i32* %arg1, i32* %arg2) nounwind {
+bb:
+  br label %loop
+
+loop:
+  %tmp7 = load i32, i32* %arg2
+  %tmp8 = inttoptr i32 %tmp7 to i32*
+  %tmp9 = icmp uge i32* %tmp8, %arg1
+  %tmp10 = icmp ult i32* %tmp8, %arg0
+  %tmp11 = or i1 %tmp9, %tmp10
+  br i1 %tmp11, label %back, label %then
+
+then:
+  store i32 %tmp7, i32* %arg2
+  br label %back
+
+back:
+  br i1 undef, label %return, label %loop
+
+return:
+  ret void
+}
+
 !0 = !{}
 !0 = !{}