diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyExplicitLocals.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyExplicitLocals.cpp
--- a/llvm/lib/Target/WebAssembly/WebAssemblyExplicitLocals.cpp
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyExplicitLocals.cpp
@@ -173,11 +173,18 @@
 /// start of the expression tree.
 static MachineInstr *findStartOfTree(MachineOperand &MO,
                                      MachineRegisterInfo &MRI,
-                                     WebAssemblyFunctionInfo &MFI) {
+                                     const WebAssemblyFunctionInfo &MFI) {
   Register Reg = MO.getReg();
   assert(MFI.isVRegStackified(Reg));
   MachineInstr *Def = MRI.getVRegDef(Reg);
 
+  // If this instruction has any non-stackified defs, it is the start
+  for (auto DefReg : Def->defs()) {
+    if (!MFI.isVRegStackified(DefReg.getReg())) {
+      return Def;
+    }
+  }
+
   // Find the first stackified use and proceed from there.
   for (MachineOperand &DefMO : Def->explicit_uses()) {
     if (!DefMO.isReg())
@@ -274,11 +281,10 @@
         continue;
       }
 
-      // Insert local.sets for any defs that aren't stackified yet. Currently
-      // we handle at most one def.
-      assert(MI.getDesc().getNumDefs() <= 1);
-      if (MI.getDesc().getNumDefs() == 1) {
-        Register OldReg = MI.getOperand(0).getReg();
+      // Insert local.sets for any defs that aren't stackified yet.
+      bool DidEraseMI = false;
+      for (auto &Def : MI.defs()) {
+        Register OldReg = Def.getReg();
         if (!MFI.isVRegStackified(OldReg)) {
           const TargetRegisterClass *RC = MRI.getRegClass(OldReg);
           Register NewReg = MRI.createVirtualRegister(RC);
@@ -286,6 +292,7 @@
           if (MI.getOpcode() == WebAssembly::IMPLICIT_DEF) {
             MI.eraseFromParent();
             Changed = true;
+            DidEraseMI = true;
             continue;
           }
           if (UseEmpty[Register::virtReg2Index(OldReg)]) {
@@ -305,16 +312,20 @@
                 .addImm(LocalId)
                 .addReg(NewReg);
           }
-          MI.getOperand(0).setReg(NewReg);
           // This register operand of the original instruction is now being used
           // by the inserted drop or local.set instruction, so make it not dead
           // yet.
-          MI.getOperand(0).setIsDead(false);
+          Def.setReg(NewReg);
+          Def.setIsDead(false);
           MFI.stackifyVReg(NewReg);
           Changed = true;
         }
       }
 
+      if (DidEraseMI) {
+        continue;
+      }
+
       // Insert local.gets for any uses that aren't stackified yet.
       MachineInstr *InsertPt = &MI;
       for (MachineOperand &MO : reverse(MI.explicit_uses())) {
diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyRegStackify.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyRegStackify.cpp
--- a/llvm/lib/Target/WebAssembly/WebAssemblyRegStackify.cpp
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyRegStackify.cpp
@@ -36,6 +36,7 @@
 #include "llvm/CodeGen/Passes.h"
 #include "llvm/Support/Debug.h"
 #include "llvm/Support/raw_ostream.h"
+#include <iterator>
 using namespace llvm;
 
 #define DEBUG_TYPE "wasm-reg-stackify"
@@ -120,6 +121,7 @@
         Type::getDoubleTy(MF.getFunction().getContext())));
     MI->addOperand(MachineOperand::CreateFPImm(Val));
   } else if (RegClass == &WebAssembly::V128RegClass) {
+    // TODO: Replace this with v128.const 0 once that is supported in V8
     Register TempReg = MRI.createVirtualRegister(&WebAssembly::I32RegClass);
     MI->setDesc(TII->get(WebAssembly::SPLAT_v4i32));
     MI->addOperand(MachineOperand::CreateReg(TempReg, false));
@@ -312,25 +314,59 @@
 // 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, const MachineRegisterInfo &MRI) {
-  assert(Def->getParent() == Insert->getParent());
+static bool isSafeToMove(const MachineOperand *Def, const MachineOperand *Use,
+                         const MachineInstr *Insert, AliasAnalysis &AA,
+                         const WebAssemblyFunctionInfo &MFI,
+                         const MachineRegisterInfo &MRI) {
+  const MachineInstr *DefI = Def->getParent();
+  const MachineInstr *UseI = Use->getParent();
+  assert(DefI->getParent() == Insert->getParent());
+  assert(UseI->getParent() == Insert->getParent());
+
+  // The first def of a multivalue instruction can be stackified by moving,
+  // since the later defs can always be placed into locals if necessary. Later
+  // defs can only be stackified if all previous defs are already stackified
+  // since it is impossible to place a def in a local if a subsequent def is
+  // stackified. But only one def can be stackified by moving the instruction,
+  // so it must be the first one.
+  //
+  // TODO: This could be loosened to be the first *live* def, but care would
+  // have to be taken to ensure the drops of the initial dead defs can be
+  // placed. This would require checking that no previous defs are used in the
+  // same instruction as subsequent defs.
+  if (Def != DefI->defs().begin())
+    return false;
+
+  // If any subsequent def is used prior to the current value by the same
+  // instruction in which the current value is used, we cannot
+  // stackify. Stackifying in this case would require that def moving below the
+  // current def in the stack, which cannot be achieved, even with locals.
+  for (const auto &SubsequentDef : drop_begin(DefI->defs(), 1)) {
+    for (const auto &PriorUse : UseI->uses()) {
+      if (&PriorUse == Use)
+        break;
+      if (PriorUse.isReg() && SubsequentDef.getReg() == PriorUse.getReg())
+        return false;
+    }
+  }
+
+  // If moving is a semantic nop, it is always allowed
+  const MachineBasicBlock *MBB = DefI->getParent();
+  auto NextI = std::next(MachineBasicBlock::const_iterator(DefI));
+  for (auto E = MBB->end(); NextI != E && NextI->isDebugInstr(); ++NextI)
+    ;
+  if (NextI == Insert)
+    return true;
 
   // 'catch' and 'extract_exception' should be the first instruction of a BB and
   // cannot move.
-  if (Def->getOpcode() == WebAssembly::CATCH ||
-      Def->getOpcode() == WebAssembly::EXTRACT_EXCEPTION_I32) {
-    const MachineBasicBlock *MBB = Def->getParent();
-    auto NextI = std::next(MachineBasicBlock::const_iterator(Def));
-    for (auto E = MBB->end(); NextI != E && NextI->isDebugInstr(); ++NextI)
-      ;
-    if (NextI != Insert)
-      return false;
-  }
+  if (DefI->getOpcode() == WebAssembly::CATCH ||
+      DefI->getOpcode() == WebAssembly::EXTRACT_EXCEPTION_I32)
+    return false;
 
   // Check for register dependencies.
   SmallVector<unsigned, 4> MutableRegisters;
-  for (const MachineOperand &MO : Def->operands()) {
+  for (const MachineOperand &MO : DefI->operands()) {
     if (!MO.isReg() || MO.isUndef())
       continue;
     Register Reg = MO.getReg();
@@ -360,7 +396,7 @@
   }
 
   bool Read = false, Write = false, Effects = false, StackPointer = false;
-  query(*Def, AA, Read, Write, Effects, StackPointer);
+  query(*DefI, AA, Read, Write, Effects, StackPointer);
 
   // If the instruction does not access memory and has no side effects, it has
   // no additional dependencies.
@@ -368,8 +404,8 @@
   if (!Read && !Write && !Effects && !StackPointer && !HasMutableRegisters)
     return true;
 
-  // Scan through the intervening instructions between Def and Insert.
-  MachineBasicBlock::const_iterator D(Def), I(Insert);
+  // Scan through the intervening instructions between DefI and Insert.
+  MachineBasicBlock::const_iterator D(DefI), I(Insert);
   for (--I; I != D; --I) {
     bool InterveningRead = false;
     bool InterveningWrite = false;
@@ -800,32 +836,32 @@
       CommutingState Commuting;
       TreeWalkerState TreeWalker(Insert);
       while (!TreeWalker.done()) {
-        MachineOperand &Op = TreeWalker.pop();
+        MachineOperand &Use = TreeWalker.pop();
 
         // We're only interested in explicit virtual register operands.
-        if (!Op.isReg())
+        if (!Use.isReg())
           continue;
 
-        Register Reg = Op.getReg();
-        assert(Op.isUse() && "explicit_uses() should only iterate over uses");
-        assert(!Op.isImplicit() &&
+        Register Reg = Use.getReg();
+        assert(Use.isUse() && "explicit_uses() should only iterate over uses");
+        assert(!Use.isImplicit() &&
                "explicit_uses() should only iterate over explicit operands");
         if (Register::isPhysicalRegister(Reg))
           continue;
 
         // Identify the definition for this register at this point.
-        MachineInstr *Def = getVRegDef(Reg, Insert, MRI, LIS);
-        if (!Def)
+        MachineInstr *DefI = getVRegDef(Reg, Insert, MRI, LIS);
+        if (!DefI)
           continue;
 
         // Don't nest an INLINE_ASM def into anything, because we don't have
         // constraints for $pop outputs.
-        if (Def->isInlineAsm())
+        if (DefI->isInlineAsm())
           continue;
 
         // Argument instructions represent live-in registers and not real
         // instructions.
-        if (WebAssembly::isArgument(Def->getOpcode()))
+        if (WebAssembly::isArgument(DefI->getOpcode()))
           continue;
 
         // Currently catch's return value register cannot be stackified, because
@@ -842,28 +878,32 @@
         // register should be assigned to a local to be propagated across
         // 'block' boundary now.
         //
-        // TODO Fix this once we support the multi-value proposal.
-        if (Def->getOpcode() == WebAssembly::CATCH)
+        // TODO: Fix this once we support the multivalue blocks
+        if (DefI->getOpcode() == WebAssembly::CATCH)
           continue;
 
+        MachineOperand *Def = DefI->findRegisterDefOperand(Reg);
+        assert(Def != nullptr);
+
         // Decide which strategy to take. Prefer to move a single-use value
         // over cloning it, and prefer cloning over introducing a tee.
         // For moving, we require the def to be in the same block as the use;
         // this makes things simpler (LiveIntervals' handleMove function only
         // supports intra-block moves) and it's MachineSink's job to catch all
         // the sinking opportunities anyway.
-        bool SameBlock = Def->getParent() == &MBB;
-        bool CanMove = SameBlock && isSafeToMove(Def, Insert, AA, MRI) &&
+        bool SameBlock = DefI->getParent() == &MBB;
+        bool CanMove = SameBlock &&
+                       isSafeToMove(Def, &Use, Insert, AA, MFI, MRI) &&
                        !TreeWalker.isOnStack(Reg);
-        if (CanMove && hasOneUse(Reg, Def, MRI, MDT, LIS)) {
-          Insert = moveForSingleUse(Reg, Op, Def, MBB, Insert, LIS, MFI, MRI);
-        } else if (shouldRematerialize(*Def, AA, TII)) {
+        if (CanMove && hasOneUse(Reg, DefI, MRI, MDT, LIS)) {
+          Insert = moveForSingleUse(Reg, Use, DefI, MBB, Insert, LIS, MFI, MRI);
+        } else if (shouldRematerialize(*DefI, AA, TII)) {
           Insert =
-              rematerializeCheapDef(Reg, Op, *Def, MBB, Insert->getIterator(),
+              rematerializeCheapDef(Reg, Use, *DefI, MBB, Insert->getIterator(),
                                     LIS, MFI, MRI, TII, TRI);
-        } else if (CanMove &&
-                   oneUseDominatesOtherUses(Reg, Op, MBB, MRI, MDT, LIS, MFI)) {
-          Insert = moveAndTeeForMultiUse(Reg, Op, Def, MBB, Insert, LIS, MFI,
+        } else if (CanMove && oneUseDominatesOtherUses(Reg, Use, MBB, MRI, MDT,
+                                                       LIS, MFI)) {
+          Insert = moveAndTeeForMultiUse(Reg, Use, DefI, MBB, Insert, LIS, MFI,
                                          MRI, TII);
         } else {
           // We failed to stackify the operand. If the problem was ordering
@@ -874,6 +914,24 @@
           continue;
         }
 
+        // Stackifying a multivalue def may unlock in-place stackification of
+        // subsequent defs.
+        auto *SubsequentDef = DefI->defs().begin();
+        auto *SubsequentUse = &Use;
+        while (SubsequentDef != DefI->defs().end() &&
+               SubsequentUse != Use.getParent()->uses().end()) {
+          if (!SubsequentDef->isReg() || !SubsequentUse->isReg())
+            break;
+          unsigned DefReg = SubsequentDef->getReg();
+          unsigned UseReg = SubsequentUse->getReg();
+          // TODO: This single-use restriction could be relaxed by using tees
+          if (DefReg != UseReg || !MRI.hasOneUse(DefReg))
+            break;
+          MFI.stackifyVReg(DefReg);
+          ++SubsequentDef;
+          ++SubsequentUse;
+        }
+
         // If the instruction we just stackified is an IMPLICIT_DEF, convert it
         // to a constant 0 so that the def is explicit, and the push/pop
         // correspondence is maintained.
@@ -911,18 +969,20 @@
     for (MachineInstr &MI : MBB) {
       if (MI.isDebugInstr())
         continue;
-      for (MachineOperand &MO : reverse(MI.explicit_operands())) {
+      for (MachineOperand &MO : reverse(MI.explicit_uses())) {
         if (!MO.isReg())
           continue;
         Register Reg = MO.getReg();
-
-        if (MFI.isVRegStackified(Reg)) {
-          if (MO.isDef())
-            Stack.push_back(Reg);
-          else
-            assert(Stack.pop_back_val() == Reg &&
-                   "Register stack pop should be paired with a push");
-        }
+        if (MFI.isVRegStackified(Reg))
+          assert(Stack.pop_back_val() == Reg &&
+                 "Register stack pop should be paired with a push");
+      }
+      for (MachineOperand &MO : MI.defs()) {
+        if (!MO.isReg())
+          continue;
+        Register Reg = MO.getReg();
+        if (MFI.isVRegStackified(Reg))
+          Stack.push_back(MO.getReg());
       }
     }
     // TODO: Generalize this code to support keeping values on the stack across
diff --git a/llvm/test/CodeGen/WebAssembly/multivalue-stackify.ll b/llvm/test/CodeGen/WebAssembly/multivalue-stackify.ll
new file mode 100644
--- /dev/null
+++ b/llvm/test/CodeGen/WebAssembly/multivalue-stackify.ll
@@ -0,0 +1,3255 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
+; NOTE: Test functions have been generated by multivalue-stackify.py.
+
+; RUN: llc < %s -verify-machineinstrs -mattr=+multivalue | FileCheck %s
+
+; Test that the multivalue stackification works
+
+target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
+target triple = "wasm32-unknown-unknown"
+
+declare {i32} @op_0_to_1()
+declare {i32, i32} @op_0_to_2()
+declare {i32, i32, i32} @op_0_to_3()
+declare void @op_1_to_0(i32 %t0)
+declare {i32} @op_1_to_1(i32 %t0)
+declare {i32, i32} @op_1_to_2(i32 %t0)
+declare {i32, i32, i32} @op_1_to_3(i32 %t0)
+declare void @op_2_to_0(i32 %t0, i32 %t1)
+declare {i32} @op_2_to_1(i32 %t0, i32 %t1)
+declare {i32, i32} @op_2_to_2(i32 %t0, i32 %t1)
+declare {i32, i32, i32} @op_2_to_3(i32 %t0, i32 %t1)
+
+define void @f2() {
+; CHECK-LABEL: f2:
+; CHECK:         .functype f2 () -> ()
+; CHECK-NEXT:  # %bb.0:
+; CHECK-NEXT:    call op_0_to_2
+; CHECK-NEXT:    drop
+; CHECK-NEXT:    drop
+; CHECK-NEXT:    # fallthrough-return
+  %t0 = call {i32, i32} @op_0_to_2()
+  ret void
+}
+
+define void @f3() {
+; CHECK-LABEL: f3:
+; CHECK:         .functype f3 () -> ()
+; CHECK-NEXT:  # %bb.0:
+; CHECK-NEXT:    call op_0_to_3
+; CHECK-NEXT:    drop
+; CHECK-NEXT:    drop
+; CHECK-NEXT:    drop
+; CHECK-NEXT:    # fallthrough-return
+  %t0 = call {i32, i32, i32} @op_0_to_3()
+  ret void
+}
+
+define void @f12() {
+; CHECK-LABEL: f12:
+; CHECK:         .functype f12 () -> ()
+; CHECK-NEXT:  # %bb.0:
+; CHECK-NEXT:    call op_0_to_2
+; CHECK-NEXT:    drop
+; CHECK-NEXT:    call op_1_to_0
+; CHECK-NEXT:    # fallthrough-return
+  %t0 = call {i32, i32} @op_0_to_2()
+  %t1 = extractvalue {i32, i32} %t0, 0
+  call void @op_1_to_0(i32 %t1)
+  ret void
+}
+
+define void @f13() {
+; CHECK-LABEL: f13:
+; CHECK:         .functype f13 () -> ()
+; CHECK-NEXT:    .local i32
+; CHECK-NEXT:  # %bb.0:
+; CHECK-NEXT:    call op_0_to_2
+; CHECK-NEXT:    local.set 0
+; CHECK-NEXT:    drop
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    call op_1_to_0
+; CHECK-NEXT:    # fallthrough-return
+  %t0 = call {i32, i32} @op_0_to_2()
+  %t1 = extractvalue {i32, i32} %t0, 1
+  call void @op_1_to_0(i32 %t1)
+  ret void
+}
+
+define void @f14() {
+; CHECK-LABEL: f14:
+; CHECK:         .functype f14 () -> ()
+; CHECK-NEXT:    .local i32
+; CHECK-NEXT:  # %bb.0:
+; CHECK-NEXT:    call op_0_to_2
+; CHECK-NEXT:    drop
+; CHECK-NEXT:    local.tee 0
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    # fallthrough-return
+  %t0 = call {i32, i32} @op_0_to_2()
+  %t1 = extractvalue {i32, i32} %t0, 0
+  %t2 = extractvalue {i32, i32} %t0, 0
+  call void @op_2_to_0(i32 %t1, i32 %t2)
+  ret void
+}
+
+define void @f15() {
+; CHECK-LABEL: f15:
+; CHECK:         .functype f15 () -> ()
+; CHECK-NEXT:  # %bb.0:
+; CHECK-NEXT:    call op_0_to_2
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    # fallthrough-return
+  %t0 = call {i32, i32} @op_0_to_2()
+  %t1 = extractvalue {i32, i32} %t0, 0
+  %t2 = extractvalue {i32, i32} %t0, 1
+  call void @op_2_to_0(i32 %t1, i32 %t2)
+  ret void
+}
+
+define void @f16() {
+; CHECK-LABEL: f16:
+; CHECK:         .functype f16 () -> ()
+; CHECK-NEXT:    .local i32, i32
+; CHECK-NEXT:  # %bb.0:
+; CHECK-NEXT:    call op_0_to_2
+; CHECK-NEXT:    local.set 1
+; CHECK-NEXT:    local.set 0
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    # fallthrough-return
+  %t0 = call {i32, i32} @op_0_to_2()
+  %t1 = extractvalue {i32, i32} %t0, 1
+  %t2 = extractvalue {i32, i32} %t0, 0
+  call void @op_2_to_0(i32 %t1, i32 %t2)
+  ret void
+}
+
+define void @f17() {
+; CHECK-LABEL: f17:
+; CHECK:         .functype f17 () -> ()
+; CHECK-NEXT:    .local i32
+; CHECK-NEXT:  # %bb.0:
+; CHECK-NEXT:    call op_0_to_2
+; CHECK-NEXT:    local.set 0
+; CHECK-NEXT:    drop
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    # fallthrough-return
+  %t0 = call {i32, i32} @op_0_to_2()
+  %t1 = extractvalue {i32, i32} %t0, 1
+  %t2 = extractvalue {i32, i32} %t0, 1
+  call void @op_2_to_0(i32 %t1, i32 %t2)
+  ret void
+}
+
+define void @f25() {
+; CHECK-LABEL: f25:
+; CHECK:         .functype f25 () -> ()
+; CHECK-NEXT:  # %bb.0:
+; CHECK-NEXT:    call op_0_to_3
+; CHECK-NEXT:    drop
+; CHECK-NEXT:    drop
+; CHECK-NEXT:    call op_1_to_0
+; CHECK-NEXT:    # fallthrough-return
+  %t0 = call {i32, i32, i32} @op_0_to_3()
+  %t1 = extractvalue {i32, i32, i32} %t0, 0
+  call void @op_1_to_0(i32 %t1)
+  ret void
+}
+
+define void @f26() {
+; CHECK-LABEL: f26:
+; CHECK:         .functype f26 () -> ()
+; CHECK-NEXT:    .local i32
+; CHECK-NEXT:  # %bb.0:
+; CHECK-NEXT:    call op_0_to_3
+; CHECK-NEXT:    drop
+; CHECK-NEXT:    local.set 0
+; CHECK-NEXT:    drop
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    call op_1_to_0
+; CHECK-NEXT:    # fallthrough-return
+  %t0 = call {i32, i32, i32} @op_0_to_3()
+  %t1 = extractvalue {i32, i32, i32} %t0, 1
+  call void @op_1_to_0(i32 %t1)
+  ret void
+}
+
+define void @f27() {
+; CHECK-LABEL: f27:
+; CHECK:         .functype f27 () -> ()
+; CHECK-NEXT:    .local i32
+; CHECK-NEXT:  # %bb.0:
+; CHECK-NEXT:    call op_0_to_3
+; CHECK-NEXT:    local.set 0
+; CHECK-NEXT:    drop
+; CHECK-NEXT:    drop
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    call op_1_to_0
+; CHECK-NEXT:    # fallthrough-return
+  %t0 = call {i32, i32, i32} @op_0_to_3()
+  %t1 = extractvalue {i32, i32, i32} %t0, 2
+  call void @op_1_to_0(i32 %t1)
+  ret void
+}
+
+define void @f28() {
+; CHECK-LABEL: f28:
+; CHECK:         .functype f28 () -> ()
+; CHECK-NEXT:    .local i32
+; CHECK-NEXT:  # %bb.0:
+; CHECK-NEXT:    call op_0_to_3
+; CHECK-NEXT:    drop
+; CHECK-NEXT:    drop
+; CHECK-NEXT:    local.tee 0
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    # fallthrough-return
+  %t0 = call {i32, i32, i32} @op_0_to_3()
+  %t1 = extractvalue {i32, i32, i32} %t0, 0
+  %t2 = extractvalue {i32, i32, i32} %t0, 0
+  call void @op_2_to_0(i32 %t1, i32 %t2)
+  ret void
+}
+
+define void @f29() {
+; CHECK-LABEL: f29:
+; CHECK:         .functype f29 () -> ()
+; CHECK-NEXT:  # %bb.0:
+; CHECK-NEXT:    call op_0_to_3
+; CHECK-NEXT:    drop
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    # fallthrough-return
+  %t0 = call {i32, i32, i32} @op_0_to_3()
+  %t1 = extractvalue {i32, i32, i32} %t0, 0
+  %t2 = extractvalue {i32, i32, i32} %t0, 1
+  call void @op_2_to_0(i32 %t1, i32 %t2)
+  ret void
+}
+
+define void @f30() {
+; CHECK-LABEL: f30:
+; CHECK:         .functype f30 () -> ()
+; CHECK-NEXT:    .local i32
+; CHECK-NEXT:  # %bb.0:
+; CHECK-NEXT:    call op_0_to_3
+; CHECK-NEXT:    local.set 0
+; CHECK-NEXT:    drop
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    # fallthrough-return
+  %t0 = call {i32, i32, i32} @op_0_to_3()
+  %t1 = extractvalue {i32, i32, i32} %t0, 0
+  %t2 = extractvalue {i32, i32, i32} %t0, 2
+  call void @op_2_to_0(i32 %t1, i32 %t2)
+  ret void
+}
+
+define void @f31() {
+; CHECK-LABEL: f31:
+; CHECK:         .functype f31 () -> ()
+; CHECK-NEXT:    .local i32, i32
+; CHECK-NEXT:  # %bb.0:
+; CHECK-NEXT:    call op_0_to_3
+; CHECK-NEXT:    drop
+; CHECK-NEXT:    local.set 1
+; CHECK-NEXT:    local.set 0
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    # fallthrough-return
+  %t0 = call {i32, i32, i32} @op_0_to_3()
+  %t1 = extractvalue {i32, i32, i32} %t0, 1
+  %t2 = extractvalue {i32, i32, i32} %t0, 0
+  call void @op_2_to_0(i32 %t1, i32 %t2)
+  ret void
+}
+
+define void @f32() {
+; CHECK-LABEL: f32:
+; CHECK:         .functype f32 () -> ()
+; CHECK-NEXT:    .local i32
+; CHECK-NEXT:  # %bb.0:
+; CHECK-NEXT:    call op_0_to_3
+; CHECK-NEXT:    drop
+; CHECK-NEXT:    local.set 0
+; CHECK-NEXT:    drop
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    # fallthrough-return
+  %t0 = call {i32, i32, i32} @op_0_to_3()
+  %t1 = extractvalue {i32, i32, i32} %t0, 1
+  %t2 = extractvalue {i32, i32, i32} %t0, 1
+  call void @op_2_to_0(i32 %t1, i32 %t2)
+  ret void
+}
+
+define void @f33() {
+; CHECK-LABEL: f33:
+; CHECK:         .functype f33 () -> ()
+; CHECK-NEXT:    .local i32, i32
+; CHECK-NEXT:  # %bb.0:
+; CHECK-NEXT:    call op_0_to_3
+; CHECK-NEXT:    local.set 1
+; CHECK-NEXT:    local.set 0
+; CHECK-NEXT:    drop
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    # fallthrough-return
+  %t0 = call {i32, i32, i32} @op_0_to_3()
+  %t1 = extractvalue {i32, i32, i32} %t0, 1
+  %t2 = extractvalue {i32, i32, i32} %t0, 2
+  call void @op_2_to_0(i32 %t1, i32 %t2)
+  ret void
+}
+
+define void @f34() {
+; CHECK-LABEL: f34:
+; CHECK:         .functype f34 () -> ()
+; CHECK-NEXT:    .local i32, i32
+; CHECK-NEXT:  # %bb.0:
+; CHECK-NEXT:    call op_0_to_3
+; CHECK-NEXT:    local.set 1
+; CHECK-NEXT:    drop
+; CHECK-NEXT:    local.set 0
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    # fallthrough-return
+  %t0 = call {i32, i32, i32} @op_0_to_3()
+  %t1 = extractvalue {i32, i32, i32} %t0, 2
+  %t2 = extractvalue {i32, i32, i32} %t0, 0
+  call void @op_2_to_0(i32 %t1, i32 %t2)
+  ret void
+}
+
+define void @f35() {
+; CHECK-LABEL: f35:
+; CHECK:         .functype f35 () -> ()
+; CHECK-NEXT:    .local i32, i32
+; CHECK-NEXT:  # %bb.0:
+; CHECK-NEXT:    call op_0_to_3
+; CHECK-NEXT:    local.set 1
+; CHECK-NEXT:    local.set 0
+; CHECK-NEXT:    drop
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    # fallthrough-return
+  %t0 = call {i32, i32, i32} @op_0_to_3()
+  %t1 = extractvalue {i32, i32, i32} %t0, 2
+  %t2 = extractvalue {i32, i32, i32} %t0, 1
+  call void @op_2_to_0(i32 %t1, i32 %t2)
+  ret void
+}
+
+define void @f36() {
+; CHECK-LABEL: f36:
+; CHECK:         .functype f36 () -> ()
+; CHECK-NEXT:    .local i32
+; CHECK-NEXT:  # %bb.0:
+; CHECK-NEXT:    call op_0_to_3
+; CHECK-NEXT:    local.set 0
+; CHECK-NEXT:    drop
+; CHECK-NEXT:    drop
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    # fallthrough-return
+  %t0 = call {i32, i32, i32} @op_0_to_3()
+  %t1 = extractvalue {i32, i32, i32} %t0, 2
+  %t2 = extractvalue {i32, i32, i32} %t0, 2
+  call void @op_2_to_0(i32 %t1, i32 %t2)
+  ret void
+}
+
+define void @f129() {
+; CHECK-LABEL: f129:
+; CHECK:         .functype f129 () -> ()
+; CHECK-NEXT:    .local i32
+; CHECK-NEXT:  # %bb.0:
+; CHECK-NEXT:    call op_0_to_2
+; CHECK-NEXT:    local.set 0
+; CHECK-NEXT:    call op_1_to_0
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    call op_1_to_0
+; CHECK-NEXT:    # fallthrough-return
+  %t0 = call {i32, i32} @op_0_to_2()
+  %t1 = extractvalue {i32, i32} %t0, 0
+  call void @op_1_to_0(i32 %t1)
+  %t2 = extractvalue {i32, i32} %t0, 1
+  call void @op_1_to_0(i32 %t2)
+  ret void
+}
+
+define void @f131() {
+; CHECK-LABEL: f131:
+; CHECK:         .functype f131 () -> ()
+; CHECK-NEXT:    .local i32, i32
+; CHECK-NEXT:  # %bb.0:
+; CHECK-NEXT:    call op_0_to_2
+; CHECK-NEXT:    local.set 0
+; CHECK-NEXT:    local.tee 1
+; CHECK-NEXT:    call op_1_to_0
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    # fallthrough-return
+  %t0 = call {i32, i32} @op_0_to_2()
+  %t1 = extractvalue {i32, i32} %t0, 0
+  call void @op_1_to_0(i32 %t1)
+  %t2 = extractvalue {i32, i32} %t0, 0
+  %t3 = extractvalue {i32, i32} %t0, 1
+  call void @op_2_to_0(i32 %t2, i32 %t3)
+  ret void
+}
+
+define void @f132() {
+; CHECK-LABEL: f132:
+; CHECK:         .functype f132 () -> ()
+; CHECK-NEXT:    .local i32, i32
+; CHECK-NEXT:  # %bb.0:
+; CHECK-NEXT:    call op_0_to_2
+; CHECK-NEXT:    local.set 0
+; CHECK-NEXT:    local.tee 1
+; CHECK-NEXT:    call op_1_to_0
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    # fallthrough-return
+  %t0 = call {i32, i32} @op_0_to_2()
+  %t1 = extractvalue {i32, i32} %t0, 0
+  call void @op_1_to_0(i32 %t1)
+  %t2 = extractvalue {i32, i32} %t0, 1
+  %t3 = extractvalue {i32, i32} %t0, 0
+  call void @op_2_to_0(i32 %t2, i32 %t3)
+  ret void
+}
+
+define void @f133() {
+; CHECK-LABEL: f133:
+; CHECK:         .functype f133 () -> ()
+; CHECK-NEXT:    .local i32
+; CHECK-NEXT:  # %bb.0:
+; CHECK-NEXT:    call op_0_to_2
+; CHECK-NEXT:    local.set 0
+; CHECK-NEXT:    call op_1_to_0
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    # fallthrough-return
+  %t0 = call {i32, i32} @op_0_to_2()
+  %t1 = extractvalue {i32, i32} %t0, 0
+  call void @op_1_to_0(i32 %t1)
+  %t2 = extractvalue {i32, i32} %t0, 1
+  %t3 = extractvalue {i32, i32} %t0, 1
+  call void @op_2_to_0(i32 %t2, i32 %t3)
+  ret void
+}
+
+define void @f141() {
+; CHECK-LABEL: f141:
+; CHECK:         .functype f141 () -> ()
+; CHECK-NEXT:    .local i32, i32
+; CHECK-NEXT:  # %bb.0:
+; CHECK-NEXT:    call op_0_to_2
+; CHECK-NEXT:    local.set 1
+; CHECK-NEXT:    local.set 0
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    call op_1_to_0
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    call op_1_to_0
+; CHECK-NEXT:    # fallthrough-return
+  %t0 = call {i32, i32} @op_0_to_2()
+  %t1 = extractvalue {i32, i32} %t0, 1
+  call void @op_1_to_0(i32 %t1)
+  %t2 = extractvalue {i32, i32} %t0, 0
+  call void @op_1_to_0(i32 %t2)
+  ret void
+}
+
+define void @f143() {
+; CHECK-LABEL: f143:
+; CHECK:         .functype f143 () -> ()
+; CHECK-NEXT:    .local i32, i32
+; CHECK-NEXT:  # %bb.0:
+; CHECK-NEXT:    call op_0_to_2
+; CHECK-NEXT:    local.set 1
+; CHECK-NEXT:    local.set 0
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    call op_1_to_0
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    # fallthrough-return
+  %t0 = call {i32, i32} @op_0_to_2()
+  %t1 = extractvalue {i32, i32} %t0, 1
+  call void @op_1_to_0(i32 %t1)
+  %t2 = extractvalue {i32, i32} %t0, 0
+  %t3 = extractvalue {i32, i32} %t0, 0
+  call void @op_2_to_0(i32 %t2, i32 %t3)
+  ret void
+}
+
+define void @f144() {
+; CHECK-LABEL: f144:
+; CHECK:         .functype f144 () -> ()
+; CHECK-NEXT:    .local i32, i32
+; CHECK-NEXT:  # %bb.0:
+; CHECK-NEXT:    call op_0_to_2
+; CHECK-NEXT:    local.set 1
+; CHECK-NEXT:    local.set 0
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    call op_1_to_0
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    # fallthrough-return
+  %t0 = call {i32, i32} @op_0_to_2()
+  %t1 = extractvalue {i32, i32} %t0, 1
+  call void @op_1_to_0(i32 %t1)
+  %t2 = extractvalue {i32, i32} %t0, 0
+  %t3 = extractvalue {i32, i32} %t0, 1
+  call void @op_2_to_0(i32 %t2, i32 %t3)
+  ret void
+}
+
+define void @f145() {
+; CHECK-LABEL: f145:
+; CHECK:         .functype f145 () -> ()
+; CHECK-NEXT:    .local i32, i32
+; CHECK-NEXT:  # %bb.0:
+; CHECK-NEXT:    call op_0_to_2
+; CHECK-NEXT:    local.set 1
+; CHECK-NEXT:    local.set 0
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    call op_1_to_0
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    # fallthrough-return
+  %t0 = call {i32, i32} @op_0_to_2()
+  %t1 = extractvalue {i32, i32} %t0, 1
+  call void @op_1_to_0(i32 %t1)
+  %t2 = extractvalue {i32, i32} %t0, 1
+  %t3 = extractvalue {i32, i32} %t0, 0
+  call void @op_2_to_0(i32 %t2, i32 %t3)
+  ret void
+}
+
+define void @f155() {
+; CHECK-LABEL: f155:
+; CHECK:         .functype f155 () -> ()
+; CHECK-NEXT:    .local i32, i32
+; CHECK-NEXT:  # %bb.0:
+; CHECK-NEXT:    call op_0_to_2
+; CHECK-NEXT:    local.set 0
+; CHECK-NEXT:    local.tee 1
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    call op_1_to_0
+; CHECK-NEXT:    # fallthrough-return
+  %t0 = call {i32, i32} @op_0_to_2()
+  %t1 = extractvalue {i32, i32} %t0, 0
+  %t2 = extractvalue {i32, i32} %t0, 0
+  call void @op_2_to_0(i32 %t1, i32 %t2)
+  %t3 = extractvalue {i32, i32} %t0, 1
+  call void @op_1_to_0(i32 %t3)
+  ret void
+}
+
+define void @f159() {
+; CHECK-LABEL: f159:
+; CHECK:         .functype f159 () -> ()
+; CHECK-NEXT:    .local i32, i32
+; CHECK-NEXT:  # %bb.0:
+; CHECK-NEXT:    call op_0_to_2
+; CHECK-NEXT:    local.set 0
+; CHECK-NEXT:    local.tee 1
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    # fallthrough-return
+  %t0 = call {i32, i32} @op_0_to_2()
+  %t1 = extractvalue {i32, i32} %t0, 0
+  %t2 = extractvalue {i32, i32} %t0, 0
+  call void @op_2_to_0(i32 %t1, i32 %t2)
+  %t3 = extractvalue {i32, i32} %t0, 1
+  %t4 = extractvalue {i32, i32} %t0, 1
+  call void @op_2_to_0(i32 %t3, i32 %t4)
+  ret void
+}
+
+define void @f167() {
+; CHECK-LABEL: f167:
+; CHECK:         .functype f167 () -> ()
+; CHECK-NEXT:    .local i32, i32
+; CHECK-NEXT:  # %bb.0:
+; CHECK-NEXT:    call op_0_to_2
+; CHECK-NEXT:    local.set 0
+; CHECK-NEXT:    local.tee 1
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    call op_1_to_0
+; CHECK-NEXT:    # fallthrough-return
+  %t0 = call {i32, i32} @op_0_to_2()
+  %t1 = extractvalue {i32, i32} %t0, 0
+  %t2 = extractvalue {i32, i32} %t0, 1
+  call void @op_2_to_0(i32 %t1, i32 %t2)
+  %t3 = extractvalue {i32, i32} %t0, 0
+  call void @op_1_to_0(i32 %t3)
+  ret void
+}
+
+define void @f168() {
+; CHECK-LABEL: f168:
+; CHECK:         .functype f168 () -> ()
+; CHECK-NEXT:    .local i32
+; CHECK-NEXT:  # %bb.0:
+; CHECK-NEXT:    call op_0_to_2
+; CHECK-NEXT:    local.set 0
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    call op_1_to_0
+; CHECK-NEXT:    # fallthrough-return
+  %t0 = call {i32, i32} @op_0_to_2()
+  %t1 = extractvalue {i32, i32} %t0, 0
+  %t2 = extractvalue {i32, i32} %t0, 1
+  call void @op_2_to_0(i32 %t1, i32 %t2)
+  %t3 = extractvalue {i32, i32} %t0, 1
+  call void @op_1_to_0(i32 %t3)
+  ret void
+}
+
+define void @f171() {
+; CHECK-LABEL: f171:
+; CHECK:         .functype f171 () -> ()
+; CHECK-NEXT:    .local i32, i32
+; CHECK-NEXT:  # %bb.0:
+; CHECK-NEXT:    call op_0_to_2
+; CHECK-NEXT:    local.set 0
+; CHECK-NEXT:    local.tee 1
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    # fallthrough-return
+  %t0 = call {i32, i32} @op_0_to_2()
+  %t1 = extractvalue {i32, i32} %t0, 0
+  %t2 = extractvalue {i32, i32} %t0, 1
+  call void @op_2_to_0(i32 %t1, i32 %t2)
+  %t3 = extractvalue {i32, i32} %t0, 1
+  %t4 = extractvalue {i32, i32} %t0, 0
+  call void @op_2_to_0(i32 %t3, i32 %t4)
+  ret void
+}
+
+define void @f180() {
+; CHECK-LABEL: f180:
+; CHECK:         .functype f180 () -> ()
+; CHECK-NEXT:    .local i32, i32
+; CHECK-NEXT:  # %bb.0:
+; CHECK-NEXT:    call op_0_to_2
+; CHECK-NEXT:    local.set 1
+; CHECK-NEXT:    local.set 0
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    call op_1_to_0
+; CHECK-NEXT:    # fallthrough-return
+  %t0 = call {i32, i32} @op_0_to_2()
+  %t1 = extractvalue {i32, i32} %t0, 1
+  %t2 = extractvalue {i32, i32} %t0, 0
+  call void @op_2_to_0(i32 %t1, i32 %t2)
+  %t3 = extractvalue {i32, i32} %t0, 0
+  call void @op_1_to_0(i32 %t3)
+  ret void
+}
+
+define void @f181() {
+; CHECK-LABEL: f181:
+; CHECK:         .functype f181 () -> ()
+; CHECK-NEXT:    .local i32, i32
+; CHECK-NEXT:  # %bb.0:
+; CHECK-NEXT:    call op_0_to_2
+; CHECK-NEXT:    local.set 1
+; CHECK-NEXT:    local.set 0
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    call op_1_to_0
+; CHECK-NEXT:    # fallthrough-return
+  %t0 = call {i32, i32} @op_0_to_2()
+  %t1 = extractvalue {i32, i32} %t0, 1
+  %t2 = extractvalue {i32, i32} %t0, 0
+  call void @op_2_to_0(i32 %t1, i32 %t2)
+  %t3 = extractvalue {i32, i32} %t0, 1
+  call void @op_1_to_0(i32 %t3)
+  ret void
+}
+
+define void @f183() {
+; CHECK-LABEL: f183:
+; CHECK:         .functype f183 () -> ()
+; CHECK-NEXT:    .local i32, i32
+; CHECK-NEXT:  # %bb.0:
+; CHECK-NEXT:    call op_0_to_2
+; CHECK-NEXT:    local.set 1
+; CHECK-NEXT:    local.set 0
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    # fallthrough-return
+  %t0 = call {i32, i32} @op_0_to_2()
+  %t1 = extractvalue {i32, i32} %t0, 1
+  %t2 = extractvalue {i32, i32} %t0, 0
+  call void @op_2_to_0(i32 %t1, i32 %t2)
+  %t3 = extractvalue {i32, i32} %t0, 0
+  %t4 = extractvalue {i32, i32} %t0, 1
+  call void @op_2_to_0(i32 %t3, i32 %t4)
+  ret void
+}
+
+define void @f193() {
+; CHECK-LABEL: f193:
+; CHECK:         .functype f193 () -> ()
+; CHECK-NEXT:    .local i32, i32
+; CHECK-NEXT:  # %bb.0:
+; CHECK-NEXT:    call op_0_to_2
+; CHECK-NEXT:    local.set 1
+; CHECK-NEXT:    local.set 0
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    call op_1_to_0
+; CHECK-NEXT:    # fallthrough-return
+  %t0 = call {i32, i32} @op_0_to_2()
+  %t1 = extractvalue {i32, i32} %t0, 1
+  %t2 = extractvalue {i32, i32} %t0, 1
+  call void @op_2_to_0(i32 %t1, i32 %t2)
+  %t3 = extractvalue {i32, i32} %t0, 0
+  call void @op_1_to_0(i32 %t3)
+  ret void
+}
+
+define void @f195() {
+; CHECK-LABEL: f195:
+; CHECK:         .functype f195 () -> ()
+; CHECK-NEXT:    .local i32, i32
+; CHECK-NEXT:  # %bb.0:
+; CHECK-NEXT:    call op_0_to_2
+; CHECK-NEXT:    local.set 1
+; CHECK-NEXT:    local.set 0
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    # fallthrough-return
+  %t0 = call {i32, i32} @op_0_to_2()
+  %t1 = extractvalue {i32, i32} %t0, 1
+  %t2 = extractvalue {i32, i32} %t0, 1
+  call void @op_2_to_0(i32 %t1, i32 %t2)
+  %t3 = extractvalue {i32, i32} %t0, 0
+  %t4 = extractvalue {i32, i32} %t0, 0
+  call void @op_2_to_0(i32 %t3, i32 %t4)
+  ret void
+}
+
+define void @f291() {
+; CHECK-LABEL: f291:
+; CHECK:         .functype f291 () -> ()
+; CHECK-NEXT:    .local i32
+; CHECK-NEXT:  # %bb.0:
+; CHECK-NEXT:    call op_0_to_3
+; CHECK-NEXT:    drop
+; CHECK-NEXT:    local.set 0
+; CHECK-NEXT:    call op_1_to_0
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    call op_1_to_0
+; CHECK-NEXT:    # fallthrough-return
+  %t0 = call {i32, i32, i32} @op_0_to_3()
+  %t1 = extractvalue {i32, i32, i32} %t0, 0
+  call void @op_1_to_0(i32 %t1)
+  %t2 = extractvalue {i32, i32, i32} %t0, 1
+  call void @op_1_to_0(i32 %t2)
+  ret void
+}
+
+define void @f292() {
+; CHECK-LABEL: f292:
+; CHECK:         .functype f292 () -> ()
+; CHECK-NEXT:    .local i32
+; CHECK-NEXT:  # %bb.0:
+; CHECK-NEXT:    call op_0_to_3
+; CHECK-NEXT:    local.set 0
+; CHECK-NEXT:    drop
+; CHECK-NEXT:    call op_1_to_0
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    call op_1_to_0
+; CHECK-NEXT:    # fallthrough-return
+  %t0 = call {i32, i32, i32} @op_0_to_3()
+  %t1 = extractvalue {i32, i32, i32} %t0, 0
+  call void @op_1_to_0(i32 %t1)
+  %t2 = extractvalue {i32, i32, i32} %t0, 2
+  call void @op_1_to_0(i32 %t2)
+  ret void
+}
+
+define void @f294() {
+; CHECK-LABEL: f294:
+; CHECK:         .functype f294 () -> ()
+; CHECK-NEXT:    .local i32, i32
+; CHECK-NEXT:  # %bb.0:
+; CHECK-NEXT:    call op_0_to_3
+; CHECK-NEXT:    drop
+; CHECK-NEXT:    local.set 0
+; CHECK-NEXT:    local.tee 1
+; CHECK-NEXT:    call op_1_to_0
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    # fallthrough-return
+  %t0 = call {i32, i32, i32} @op_0_to_3()
+  %t1 = extractvalue {i32, i32, i32} %t0, 0
+  call void @op_1_to_0(i32 %t1)
+  %t2 = extractvalue {i32, i32, i32} %t0, 0
+  %t3 = extractvalue {i32, i32, i32} %t0, 1
+  call void @op_2_to_0(i32 %t2, i32 %t3)
+  ret void
+}
+
+define void @f295() {
+; CHECK-LABEL: f295:
+; CHECK:         .functype f295 () -> ()
+; CHECK-NEXT:    .local i32, i32
+; CHECK-NEXT:  # %bb.0:
+; CHECK-NEXT:    call op_0_to_3
+; CHECK-NEXT:    local.set 0
+; CHECK-NEXT:    drop
+; CHECK-NEXT:    local.tee 1
+; CHECK-NEXT:    call op_1_to_0
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    # fallthrough-return
+  %t0 = call {i32, i32, i32} @op_0_to_3()
+  %t1 = extractvalue {i32, i32, i32} %t0, 0
+  call void @op_1_to_0(i32 %t1)
+  %t2 = extractvalue {i32, i32, i32} %t0, 0
+  %t3 = extractvalue {i32, i32, i32} %t0, 2
+  call void @op_2_to_0(i32 %t2, i32 %t3)
+  ret void
+}
+
+define void @f296() {
+; CHECK-LABEL: f296:
+; CHECK:         .functype f296 () -> ()
+; CHECK-NEXT:    .local i32, i32
+; CHECK-NEXT:  # %bb.0:
+; CHECK-NEXT:    call op_0_to_3
+; CHECK-NEXT:    drop
+; CHECK-NEXT:    local.set 0
+; CHECK-NEXT:    local.tee 1
+; CHECK-NEXT:    call op_1_to_0
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    # fallthrough-return
+  %t0 = call {i32, i32, i32} @op_0_to_3()
+  %t1 = extractvalue {i32, i32, i32} %t0, 0
+  call void @op_1_to_0(i32 %t1)
+  %t2 = extractvalue {i32, i32, i32} %t0, 1
+  %t3 = extractvalue {i32, i32, i32} %t0, 0
+  call void @op_2_to_0(i32 %t2, i32 %t3)
+  ret void
+}
+
+define void @f297() {
+; CHECK-LABEL: f297:
+; CHECK:         .functype f297 () -> ()
+; CHECK-NEXT:    .local i32
+; CHECK-NEXT:  # %bb.0:
+; CHECK-NEXT:    call op_0_to_3
+; CHECK-NEXT:    drop
+; CHECK-NEXT:    local.set 0
+; CHECK-NEXT:    call op_1_to_0
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    # fallthrough-return
+  %t0 = call {i32, i32, i32} @op_0_to_3()
+  %t1 = extractvalue {i32, i32, i32} %t0, 0
+  call void @op_1_to_0(i32 %t1)
+  %t2 = extractvalue {i32, i32, i32} %t0, 1
+  %t3 = extractvalue {i32, i32, i32} %t0, 1
+  call void @op_2_to_0(i32 %t2, i32 %t3)
+  ret void
+}
+
+define void @f298() {
+; CHECK-LABEL: f298:
+; CHECK:         .functype f298 () -> ()
+; CHECK-NEXT:    .local i32, i32
+; CHECK-NEXT:  # %bb.0:
+; CHECK-NEXT:    call op_0_to_3
+; CHECK-NEXT:    local.set 1
+; CHECK-NEXT:    local.set 0
+; CHECK-NEXT:    call op_1_to_0
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    # fallthrough-return
+  %t0 = call {i32, i32, i32} @op_0_to_3()
+  %t1 = extractvalue {i32, i32, i32} %t0, 0
+  call void @op_1_to_0(i32 %t1)
+  %t2 = extractvalue {i32, i32, i32} %t0, 1
+  %t3 = extractvalue {i32, i32, i32} %t0, 2
+  call void @op_2_to_0(i32 %t2, i32 %t3)
+  ret void
+}
+
+define void @f299() {
+; CHECK-LABEL: f299:
+; CHECK:         .functype f299 () -> ()
+; CHECK-NEXT:    .local i32, i32
+; CHECK-NEXT:  # %bb.0:
+; CHECK-NEXT:    call op_0_to_3
+; CHECK-NEXT:    local.set 0
+; CHECK-NEXT:    drop
+; CHECK-NEXT:    local.tee 1
+; CHECK-NEXT:    call op_1_to_0
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    # fallthrough-return
+  %t0 = call {i32, i32, i32} @op_0_to_3()
+  %t1 = extractvalue {i32, i32, i32} %t0, 0
+  call void @op_1_to_0(i32 %t1)
+  %t2 = extractvalue {i32, i32, i32} %t0, 2
+  %t3 = extractvalue {i32, i32, i32} %t0, 0
+  call void @op_2_to_0(i32 %t2, i32 %t3)
+  ret void
+}
+
+define void @f300() {
+; CHECK-LABEL: f300:
+; CHECK:         .functype f300 () -> ()
+; CHECK-NEXT:    .local i32, i32
+; CHECK-NEXT:  # %bb.0:
+; CHECK-NEXT:    call op_0_to_3
+; CHECK-NEXT:    local.set 1
+; CHECK-NEXT:    local.set 0
+; CHECK-NEXT:    call op_1_to_0
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    # fallthrough-return
+  %t0 = call {i32, i32, i32} @op_0_to_3()
+  %t1 = extractvalue {i32, i32, i32} %t0, 0
+  call void @op_1_to_0(i32 %t1)
+  %t2 = extractvalue {i32, i32, i32} %t0, 2
+  %t3 = extractvalue {i32, i32, i32} %t0, 1
+  call void @op_2_to_0(i32 %t2, i32 %t3)
+  ret void
+}
+
+define void @f301() {
+; CHECK-LABEL: f301:
+; CHECK:         .functype f301 () -> ()
+; CHECK-NEXT:    .local i32
+; CHECK-NEXT:  # %bb.0:
+; CHECK-NEXT:    call op_0_to_3
+; CHECK-NEXT:    local.set 0
+; CHECK-NEXT:    drop
+; CHECK-NEXT:    call op_1_to_0
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    # fallthrough-return
+  %t0 = call {i32, i32, i32} @op_0_to_3()
+  %t1 = extractvalue {i32, i32, i32} %t0, 0
+  call void @op_1_to_0(i32 %t1)
+  %t2 = extractvalue {i32, i32, i32} %t0, 2
+  %t3 = extractvalue {i32, i32, i32} %t0, 2
+  call void @op_2_to_0(i32 %t2, i32 %t3)
+  ret void
+}
+
+define void @f302() {
+; CHECK-LABEL: f302:
+; CHECK:         .functype f302 () -> ()
+; CHECK-NEXT:    .local i32, i32
+; CHECK-NEXT:  # %bb.0:
+; CHECK-NEXT:    call op_0_to_3
+; CHECK-NEXT:    drop
+; CHECK-NEXT:    local.set 1
+; CHECK-NEXT:    local.set 0
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    call op_1_to_0
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    call op_1_to_0
+; CHECK-NEXT:    # fallthrough-return
+  %t0 = call {i32, i32, i32} @op_0_to_3()
+  %t1 = extractvalue {i32, i32, i32} %t0, 1
+  call void @op_1_to_0(i32 %t1)
+  %t2 = extractvalue {i32, i32, i32} %t0, 0
+  call void @op_1_to_0(i32 %t2)
+  ret void
+}
+
+define void @f304() {
+; CHECK-LABEL: f304:
+; CHECK:         .functype f304 () -> ()
+; CHECK-NEXT:    .local i32, i32
+; CHECK-NEXT:  # %bb.0:
+; CHECK-NEXT:    call op_0_to_3
+; CHECK-NEXT:    local.set 1
+; CHECK-NEXT:    local.set 0
+; CHECK-NEXT:    drop
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    call op_1_to_0
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    call op_1_to_0
+; CHECK-NEXT:    # fallthrough-return
+  %t0 = call {i32, i32, i32} @op_0_to_3()
+  %t1 = extractvalue {i32, i32, i32} %t0, 1
+  call void @op_1_to_0(i32 %t1)
+  %t2 = extractvalue {i32, i32, i32} %t0, 2
+  call void @op_1_to_0(i32 %t2)
+  ret void
+}
+
+define void @f305() {
+; CHECK-LABEL: f305:
+; CHECK:         .functype f305 () -> ()
+; CHECK-NEXT:    .local i32, i32
+; CHECK-NEXT:  # %bb.0:
+; CHECK-NEXT:    call op_0_to_3
+; CHECK-NEXT:    drop
+; CHECK-NEXT:    local.set 1
+; CHECK-NEXT:    local.set 0
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    call op_1_to_0
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    # fallthrough-return
+  %t0 = call {i32, i32, i32} @op_0_to_3()
+  %t1 = extractvalue {i32, i32, i32} %t0, 1
+  call void @op_1_to_0(i32 %t1)
+  %t2 = extractvalue {i32, i32, i32} %t0, 0
+  %t3 = extractvalue {i32, i32, i32} %t0, 0
+  call void @op_2_to_0(i32 %t2, i32 %t3)
+  ret void
+}
+
+define void @f306() {
+; CHECK-LABEL: f306:
+; CHECK:         .functype f306 () -> ()
+; CHECK-NEXT:    .local i32, i32
+; CHECK-NEXT:  # %bb.0:
+; CHECK-NEXT:    call op_0_to_3
+; CHECK-NEXT:    drop
+; CHECK-NEXT:    local.set 1
+; CHECK-NEXT:    local.set 0
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    call op_1_to_0
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    # fallthrough-return
+  %t0 = call {i32, i32, i32} @op_0_to_3()
+  %t1 = extractvalue {i32, i32, i32} %t0, 1
+  call void @op_1_to_0(i32 %t1)
+  %t2 = extractvalue {i32, i32, i32} %t0, 0
+  %t3 = extractvalue {i32, i32, i32} %t0, 1
+  call void @op_2_to_0(i32 %t2, i32 %t3)
+  ret void
+}
+
+define void @f307() {
+; CHECK-LABEL: f307:
+; CHECK:         .functype f307 () -> ()
+; CHECK-NEXT:    .local i32, i32, i32
+; CHECK-NEXT:  # %bb.0:
+; CHECK-NEXT:    call op_0_to_3
+; CHECK-NEXT:    local.set 2
+; CHECK-NEXT:    local.set 1
+; CHECK-NEXT:    local.set 0
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    call op_1_to_0
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    local.get 2
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    # fallthrough-return
+  %t0 = call {i32, i32, i32} @op_0_to_3()
+  %t1 = extractvalue {i32, i32, i32} %t0, 1
+  call void @op_1_to_0(i32 %t1)
+  %t2 = extractvalue {i32, i32, i32} %t0, 0
+  %t3 = extractvalue {i32, i32, i32} %t0, 2
+  call void @op_2_to_0(i32 %t2, i32 %t3)
+  ret void
+}
+
+define void @f308() {
+; CHECK-LABEL: f308:
+; CHECK:         .functype f308 () -> ()
+; CHECK-NEXT:    .local i32, i32
+; CHECK-NEXT:  # %bb.0:
+; CHECK-NEXT:    call op_0_to_3
+; CHECK-NEXT:    drop
+; CHECK-NEXT:    local.set 1
+; CHECK-NEXT:    local.set 0
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    call op_1_to_0
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    # fallthrough-return
+  %t0 = call {i32, i32, i32} @op_0_to_3()
+  %t1 = extractvalue {i32, i32, i32} %t0, 1
+  call void @op_1_to_0(i32 %t1)
+  %t2 = extractvalue {i32, i32, i32} %t0, 1
+  %t3 = extractvalue {i32, i32, i32} %t0, 0
+  call void @op_2_to_0(i32 %t2, i32 %t3)
+  ret void
+}
+
+define void @f310() {
+; CHECK-LABEL: f310:
+; CHECK:         .functype f310 () -> ()
+; CHECK-NEXT:    .local i32, i32
+; CHECK-NEXT:  # %bb.0:
+; CHECK-NEXT:    call op_0_to_3
+; CHECK-NEXT:    local.set 1
+; CHECK-NEXT:    local.set 0
+; CHECK-NEXT:    drop
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    call op_1_to_0
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    # fallthrough-return
+  %t0 = call {i32, i32, i32} @op_0_to_3()
+  %t1 = extractvalue {i32, i32, i32} %t0, 1
+  call void @op_1_to_0(i32 %t1)
+  %t2 = extractvalue {i32, i32, i32} %t0, 1
+  %t3 = extractvalue {i32, i32, i32} %t0, 2
+  call void @op_2_to_0(i32 %t2, i32 %t3)
+  ret void
+}
+
+define void @f311() {
+; CHECK-LABEL: f311:
+; CHECK:         .functype f311 () -> ()
+; CHECK-NEXT:    .local i32, i32, i32
+; CHECK-NEXT:  # %bb.0:
+; CHECK-NEXT:    call op_0_to_3
+; CHECK-NEXT:    local.set 2
+; CHECK-NEXT:    local.set 1
+; CHECK-NEXT:    local.set 0
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    call op_1_to_0
+; CHECK-NEXT:    local.get 2
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    # fallthrough-return
+  %t0 = call {i32, i32, i32} @op_0_to_3()
+  %t1 = extractvalue {i32, i32, i32} %t0, 1
+  call void @op_1_to_0(i32 %t1)
+  %t2 = extractvalue {i32, i32, i32} %t0, 2
+  %t3 = extractvalue {i32, i32, i32} %t0, 0
+  call void @op_2_to_0(i32 %t2, i32 %t3)
+  ret void
+}
+
+define void @f312() {
+; CHECK-LABEL: f312:
+; CHECK:         .functype f312 () -> ()
+; CHECK-NEXT:    .local i32, i32
+; CHECK-NEXT:  # %bb.0:
+; CHECK-NEXT:    call op_0_to_3
+; CHECK-NEXT:    local.set 1
+; CHECK-NEXT:    local.set 0
+; CHECK-NEXT:    drop
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    call op_1_to_0
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    # fallthrough-return
+  %t0 = call {i32, i32, i32} @op_0_to_3()
+  %t1 = extractvalue {i32, i32, i32} %t0, 1
+  call void @op_1_to_0(i32 %t1)
+  %t2 = extractvalue {i32, i32, i32} %t0, 2
+  %t3 = extractvalue {i32, i32, i32} %t0, 1
+  call void @op_2_to_0(i32 %t2, i32 %t3)
+  ret void
+}
+
+define void @f313() {
+; CHECK-LABEL: f313:
+; CHECK:         .functype f313 () -> ()
+; CHECK-NEXT:    .local i32, i32
+; CHECK-NEXT:  # %bb.0:
+; CHECK-NEXT:    call op_0_to_3
+; CHECK-NEXT:    local.set 1
+; CHECK-NEXT:    local.set 0
+; CHECK-NEXT:    drop
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    call op_1_to_0
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    # fallthrough-return
+  %t0 = call {i32, i32, i32} @op_0_to_3()
+  %t1 = extractvalue {i32, i32, i32} %t0, 1
+  call void @op_1_to_0(i32 %t1)
+  %t2 = extractvalue {i32, i32, i32} %t0, 2
+  %t3 = extractvalue {i32, i32, i32} %t0, 2
+  call void @op_2_to_0(i32 %t2, i32 %t3)
+  ret void
+}
+
+define void @f314() {
+; CHECK-LABEL: f314:
+; CHECK:         .functype f314 () -> ()
+; CHECK-NEXT:    .local i32, i32
+; CHECK-NEXT:  # %bb.0:
+; CHECK-NEXT:    call op_0_to_3
+; CHECK-NEXT:    local.set 1
+; CHECK-NEXT:    drop
+; CHECK-NEXT:    local.set 0
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    call op_1_to_0
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    call op_1_to_0
+; CHECK-NEXT:    # fallthrough-return
+  %t0 = call {i32, i32, i32} @op_0_to_3()
+  %t1 = extractvalue {i32, i32, i32} %t0, 2
+  call void @op_1_to_0(i32 %t1)
+  %t2 = extractvalue {i32, i32, i32} %t0, 0
+  call void @op_1_to_0(i32 %t2)
+  ret void
+}
+
+define void @f315() {
+; CHECK-LABEL: f315:
+; CHECK:         .functype f315 () -> ()
+; CHECK-NEXT:    .local i32, i32
+; CHECK-NEXT:  # %bb.0:
+; CHECK-NEXT:    call op_0_to_3
+; CHECK-NEXT:    local.set 1
+; CHECK-NEXT:    local.set 0
+; CHECK-NEXT:    drop
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    call op_1_to_0
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    call op_1_to_0
+; CHECK-NEXT:    # fallthrough-return
+  %t0 = call {i32, i32, i32} @op_0_to_3()
+  %t1 = extractvalue {i32, i32, i32} %t0, 2
+  call void @op_1_to_0(i32 %t1)
+  %t2 = extractvalue {i32, i32, i32} %t0, 1
+  call void @op_1_to_0(i32 %t2)
+  ret void
+}
+
+define void @f317() {
+; CHECK-LABEL: f317:
+; CHECK:         .functype f317 () -> ()
+; CHECK-NEXT:    .local i32, i32
+; CHECK-NEXT:  # %bb.0:
+; CHECK-NEXT:    call op_0_to_3
+; CHECK-NEXT:    local.set 1
+; CHECK-NEXT:    drop
+; CHECK-NEXT:    local.set 0
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    call op_1_to_0
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    # fallthrough-return
+  %t0 = call {i32, i32, i32} @op_0_to_3()
+  %t1 = extractvalue {i32, i32, i32} %t0, 2
+  call void @op_1_to_0(i32 %t1)
+  %t2 = extractvalue {i32, i32, i32} %t0, 0
+  %t3 = extractvalue {i32, i32, i32} %t0, 0
+  call void @op_2_to_0(i32 %t2, i32 %t3)
+  ret void
+}
+
+define void @f318() {
+; CHECK-LABEL: f318:
+; CHECK:         .functype f318 () -> ()
+; CHECK-NEXT:    .local i32, i32, i32
+; CHECK-NEXT:  # %bb.0:
+; CHECK-NEXT:    call op_0_to_3
+; CHECK-NEXT:    local.set 2
+; CHECK-NEXT:    local.set 1
+; CHECK-NEXT:    local.set 0
+; CHECK-NEXT:    local.get 2
+; CHECK-NEXT:    call op_1_to_0
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    # fallthrough-return
+  %t0 = call {i32, i32, i32} @op_0_to_3()
+  %t1 = extractvalue {i32, i32, i32} %t0, 2
+  call void @op_1_to_0(i32 %t1)
+  %t2 = extractvalue {i32, i32, i32} %t0, 0
+  %t3 = extractvalue {i32, i32, i32} %t0, 1
+  call void @op_2_to_0(i32 %t2, i32 %t3)
+  ret void
+}
+
+define void @f319() {
+; CHECK-LABEL: f319:
+; CHECK:         .functype f319 () -> ()
+; CHECK-NEXT:    .local i32, i32
+; CHECK-NEXT:  # %bb.0:
+; CHECK-NEXT:    call op_0_to_3
+; CHECK-NEXT:    local.set 1
+; CHECK-NEXT:    drop
+; CHECK-NEXT:    local.set 0
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    call op_1_to_0
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    # fallthrough-return
+  %t0 = call {i32, i32, i32} @op_0_to_3()
+  %t1 = extractvalue {i32, i32, i32} %t0, 2
+  call void @op_1_to_0(i32 %t1)
+  %t2 = extractvalue {i32, i32, i32} %t0, 0
+  %t3 = extractvalue {i32, i32, i32} %t0, 2
+  call void @op_2_to_0(i32 %t2, i32 %t3)
+  ret void
+}
+
+define void @f320() {
+; CHECK-LABEL: f320:
+; CHECK:         .functype f320 () -> ()
+; CHECK-NEXT:    .local i32, i32, i32
+; CHECK-NEXT:  # %bb.0:
+; CHECK-NEXT:    call op_0_to_3
+; CHECK-NEXT:    local.set 2
+; CHECK-NEXT:    local.set 1
+; CHECK-NEXT:    local.set 0
+; CHECK-NEXT:    local.get 2
+; CHECK-NEXT:    call op_1_to_0
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    # fallthrough-return
+  %t0 = call {i32, i32, i32} @op_0_to_3()
+  %t1 = extractvalue {i32, i32, i32} %t0, 2
+  call void @op_1_to_0(i32 %t1)
+  %t2 = extractvalue {i32, i32, i32} %t0, 1
+  %t3 = extractvalue {i32, i32, i32} %t0, 0
+  call void @op_2_to_0(i32 %t2, i32 %t3)
+  ret void
+}
+
+define void @f321() {
+; CHECK-LABEL: f321:
+; CHECK:         .functype f321 () -> ()
+; CHECK-NEXT:    .local i32, i32
+; CHECK-NEXT:  # %bb.0:
+; CHECK-NEXT:    call op_0_to_3
+; CHECK-NEXT:    local.set 1
+; CHECK-NEXT:    local.set 0
+; CHECK-NEXT:    drop
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    call op_1_to_0
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    # fallthrough-return
+  %t0 = call {i32, i32, i32} @op_0_to_3()
+  %t1 = extractvalue {i32, i32, i32} %t0, 2
+  call void @op_1_to_0(i32 %t1)
+  %t2 = extractvalue {i32, i32, i32} %t0, 1
+  %t3 = extractvalue {i32, i32, i32} %t0, 1
+  call void @op_2_to_0(i32 %t2, i32 %t3)
+  ret void
+}
+
+define void @f322() {
+; CHECK-LABEL: f322:
+; CHECK:         .functype f322 () -> ()
+; CHECK-NEXT:    .local i32, i32
+; CHECK-NEXT:  # %bb.0:
+; CHECK-NEXT:    call op_0_to_3
+; CHECK-NEXT:    local.set 1
+; CHECK-NEXT:    local.set 0
+; CHECK-NEXT:    drop
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    call op_1_to_0
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    # fallthrough-return
+  %t0 = call {i32, i32, i32} @op_0_to_3()
+  %t1 = extractvalue {i32, i32, i32} %t0, 2
+  call void @op_1_to_0(i32 %t1)
+  %t2 = extractvalue {i32, i32, i32} %t0, 1
+  %t3 = extractvalue {i32, i32, i32} %t0, 2
+  call void @op_2_to_0(i32 %t2, i32 %t3)
+  ret void
+}
+
+define void @f323() {
+; CHECK-LABEL: f323:
+; CHECK:         .functype f323 () -> ()
+; CHECK-NEXT:    .local i32, i32
+; CHECK-NEXT:  # %bb.0:
+; CHECK-NEXT:    call op_0_to_3
+; CHECK-NEXT:    local.set 1
+; CHECK-NEXT:    drop
+; CHECK-NEXT:    local.set 0
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    call op_1_to_0
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    # fallthrough-return
+  %t0 = call {i32, i32, i32} @op_0_to_3()
+  %t1 = extractvalue {i32, i32, i32} %t0, 2
+  call void @op_1_to_0(i32 %t1)
+  %t2 = extractvalue {i32, i32, i32} %t0, 2
+  %t3 = extractvalue {i32, i32, i32} %t0, 0
+  call void @op_2_to_0(i32 %t2, i32 %t3)
+  ret void
+}
+
+define void @f324() {
+; CHECK-LABEL: f324:
+; CHECK:         .functype f324 () -> ()
+; CHECK-NEXT:    .local i32, i32
+; CHECK-NEXT:  # %bb.0:
+; CHECK-NEXT:    call op_0_to_3
+; CHECK-NEXT:    local.set 1
+; CHECK-NEXT:    local.set 0
+; CHECK-NEXT:    drop
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    call op_1_to_0
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    # fallthrough-return
+  %t0 = call {i32, i32, i32} @op_0_to_3()
+  %t1 = extractvalue {i32, i32, i32} %t0, 2
+  call void @op_1_to_0(i32 %t1)
+  %t2 = extractvalue {i32, i32, i32} %t0, 2
+  %t3 = extractvalue {i32, i32, i32} %t0, 1
+  call void @op_2_to_0(i32 %t2, i32 %t3)
+  ret void
+}
+
+define void @f327() {
+; CHECK-LABEL: f327:
+; CHECK:         .functype f327 () -> ()
+; CHECK-NEXT:    .local i32, i32
+; CHECK-NEXT:  # %bb.0:
+; CHECK-NEXT:    call op_0_to_3
+; CHECK-NEXT:    drop
+; CHECK-NEXT:    local.set 0
+; CHECK-NEXT:    local.tee 1
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    call op_1_to_0
+; CHECK-NEXT:    # fallthrough-return
+  %t0 = call {i32, i32, i32} @op_0_to_3()
+  %t1 = extractvalue {i32, i32, i32} %t0, 0
+  %t2 = extractvalue {i32, i32, i32} %t0, 0
+  call void @op_2_to_0(i32 %t1, i32 %t2)
+  %t3 = extractvalue {i32, i32, i32} %t0, 1
+  call void @op_1_to_0(i32 %t3)
+  ret void
+}
+
+define void @f328() {
+; CHECK-LABEL: f328:
+; CHECK:         .functype f328 () -> ()
+; CHECK-NEXT:    .local i32, i32
+; CHECK-NEXT:  # %bb.0:
+; CHECK-NEXT:    call op_0_to_3
+; CHECK-NEXT:    local.set 0
+; CHECK-NEXT:    drop
+; CHECK-NEXT:    local.tee 1
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    call op_1_to_0
+; CHECK-NEXT:    # fallthrough-return
+  %t0 = call {i32, i32, i32} @op_0_to_3()
+  %t1 = extractvalue {i32, i32, i32} %t0, 0
+  %t2 = extractvalue {i32, i32, i32} %t0, 0
+  call void @op_2_to_0(i32 %t1, i32 %t2)
+  %t3 = extractvalue {i32, i32, i32} %t0, 2
+  call void @op_1_to_0(i32 %t3)
+  ret void
+}
+
+define void @f333() {
+; CHECK-LABEL: f333:
+; CHECK:         .functype f333 () -> ()
+; CHECK-NEXT:    .local i32, i32
+; CHECK-NEXT:  # %bb.0:
+; CHECK-NEXT:    call op_0_to_3
+; CHECK-NEXT:    drop
+; CHECK-NEXT:    local.set 0
+; CHECK-NEXT:    local.tee 1
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    # fallthrough-return
+  %t0 = call {i32, i32, i32} @op_0_to_3()
+  %t1 = extractvalue {i32, i32, i32} %t0, 0
+  %t2 = extractvalue {i32, i32, i32} %t0, 0
+  call void @op_2_to_0(i32 %t1, i32 %t2)
+  %t3 = extractvalue {i32, i32, i32} %t0, 1
+  %t4 = extractvalue {i32, i32, i32} %t0, 1
+  call void @op_2_to_0(i32 %t3, i32 %t4)
+  ret void
+}
+
+define void @f334() {
+; CHECK-LABEL: f334:
+; CHECK:         .functype f334 () -> ()
+; CHECK-NEXT:    .local i32, i32, i32
+; CHECK-NEXT:  # %bb.0:
+; CHECK-NEXT:    call op_0_to_3
+; CHECK-NEXT:    local.set 1
+; CHECK-NEXT:    local.set 0
+; CHECK-NEXT:    local.tee 2
+; CHECK-NEXT:    local.get 2
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    # fallthrough-return
+  %t0 = call {i32, i32, i32} @op_0_to_3()
+  %t1 = extractvalue {i32, i32, i32} %t0, 0
+  %t2 = extractvalue {i32, i32, i32} %t0, 0
+  call void @op_2_to_0(i32 %t1, i32 %t2)
+  %t3 = extractvalue {i32, i32, i32} %t0, 1
+  %t4 = extractvalue {i32, i32, i32} %t0, 2
+  call void @op_2_to_0(i32 %t3, i32 %t4)
+  ret void
+}
+
+define void @f336() {
+; CHECK-LABEL: f336:
+; CHECK:         .functype f336 () -> ()
+; CHECK-NEXT:    .local i32, i32, i32
+; CHECK-NEXT:  # %bb.0:
+; CHECK-NEXT:    call op_0_to_3
+; CHECK-NEXT:    local.set 1
+; CHECK-NEXT:    local.set 0
+; CHECK-NEXT:    local.tee 2
+; CHECK-NEXT:    local.get 2
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    # fallthrough-return
+  %t0 = call {i32, i32, i32} @op_0_to_3()
+  %t1 = extractvalue {i32, i32, i32} %t0, 0
+  %t2 = extractvalue {i32, i32, i32} %t0, 0
+  call void @op_2_to_0(i32 %t1, i32 %t2)
+  %t3 = extractvalue {i32, i32, i32} %t0, 2
+  %t4 = extractvalue {i32, i32, i32} %t0, 1
+  call void @op_2_to_0(i32 %t3, i32 %t4)
+  ret void
+}
+
+define void @f337() {
+; CHECK-LABEL: f337:
+; CHECK:         .functype f337 () -> ()
+; CHECK-NEXT:    .local i32, i32
+; CHECK-NEXT:  # %bb.0:
+; CHECK-NEXT:    call op_0_to_3
+; CHECK-NEXT:    local.set 0
+; CHECK-NEXT:    drop
+; CHECK-NEXT:    local.tee 1
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    # fallthrough-return
+  %t0 = call {i32, i32, i32} @op_0_to_3()
+  %t1 = extractvalue {i32, i32, i32} %t0, 0
+  %t2 = extractvalue {i32, i32, i32} %t0, 0
+  call void @op_2_to_0(i32 %t1, i32 %t2)
+  %t3 = extractvalue {i32, i32, i32} %t0, 2
+  %t4 = extractvalue {i32, i32, i32} %t0, 2
+  call void @op_2_to_0(i32 %t3, i32 %t4)
+  ret void
+}
+
+define void @f338() {
+; CHECK-LABEL: f338:
+; CHECK:         .functype f338 () -> ()
+; CHECK-NEXT:    .local i32, i32
+; CHECK-NEXT:  # %bb.0:
+; CHECK-NEXT:    call op_0_to_3
+; CHECK-NEXT:    drop
+; CHECK-NEXT:    local.set 0
+; CHECK-NEXT:    local.tee 1
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    call op_1_to_0
+; CHECK-NEXT:    # fallthrough-return
+  %t0 = call {i32, i32, i32} @op_0_to_3()
+  %t1 = extractvalue {i32, i32, i32} %t0, 0
+  %t2 = extractvalue {i32, i32, i32} %t0, 1
+  call void @op_2_to_0(i32 %t1, i32 %t2)
+  %t3 = extractvalue {i32, i32, i32} %t0, 0
+  call void @op_1_to_0(i32 %t3)
+  ret void
+}
+
+define void @f339() {
+; CHECK-LABEL: f339:
+; CHECK:         .functype f339 () -> ()
+; CHECK-NEXT:    .local i32
+; CHECK-NEXT:  # %bb.0:
+; CHECK-NEXT:    call op_0_to_3
+; CHECK-NEXT:    drop
+; CHECK-NEXT:    local.set 0
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    call op_1_to_0
+; CHECK-NEXT:    # fallthrough-return
+  %t0 = call {i32, i32, i32} @op_0_to_3()
+  %t1 = extractvalue {i32, i32, i32} %t0, 0
+  %t2 = extractvalue {i32, i32, i32} %t0, 1
+  call void @op_2_to_0(i32 %t1, i32 %t2)
+  %t3 = extractvalue {i32, i32, i32} %t0, 1
+  call void @op_1_to_0(i32 %t3)
+  ret void
+}
+
+define void @f340() {
+; CHECK-LABEL: f340:
+; CHECK:         .functype f340 () -> ()
+; CHECK-NEXT:    .local i32
+; CHECK-NEXT:  # %bb.0:
+; CHECK-NEXT:    call op_0_to_3
+; CHECK-NEXT:    local.set 0
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    call op_1_to_0
+; CHECK-NEXT:    # fallthrough-return
+  %t0 = call {i32, i32, i32} @op_0_to_3()
+  %t1 = extractvalue {i32, i32, i32} %t0, 0
+  %t2 = extractvalue {i32, i32, i32} %t0, 1
+  call void @op_2_to_0(i32 %t1, i32 %t2)
+  %t3 = extractvalue {i32, i32, i32} %t0, 2
+  call void @op_1_to_0(i32 %t3)
+  ret void
+}
+
+define void @f343() {
+; CHECK-LABEL: f343:
+; CHECK:         .functype f343 () -> ()
+; CHECK-NEXT:    .local i32, i32, i32
+; CHECK-NEXT:  # %bb.0:
+; CHECK-NEXT:    call op_0_to_3
+; CHECK-NEXT:    local.set 1
+; CHECK-NEXT:    local.set 0
+; CHECK-NEXT:    local.tee 2
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    local.get 2
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    # fallthrough-return
+  %t0 = call {i32, i32, i32} @op_0_to_3()
+  %t1 = extractvalue {i32, i32, i32} %t0, 0
+  %t2 = extractvalue {i32, i32, i32} %t0, 1
+  call void @op_2_to_0(i32 %t1, i32 %t2)
+  %t3 = extractvalue {i32, i32, i32} %t0, 0
+  %t4 = extractvalue {i32, i32, i32} %t0, 2
+  call void @op_2_to_0(i32 %t3, i32 %t4)
+  ret void
+}
+
+define void @f344() {
+; CHECK-LABEL: f344:
+; CHECK:         .functype f344 () -> ()
+; CHECK-NEXT:    .local i32, i32
+; CHECK-NEXT:  # %bb.0:
+; CHECK-NEXT:    call op_0_to_3
+; CHECK-NEXT:    drop
+; CHECK-NEXT:    local.set 0
+; CHECK-NEXT:    local.tee 1
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    # fallthrough-return
+  %t0 = call {i32, i32, i32} @op_0_to_3()
+  %t1 = extractvalue {i32, i32, i32} %t0, 0
+  %t2 = extractvalue {i32, i32, i32} %t0, 1
+  call void @op_2_to_0(i32 %t1, i32 %t2)
+  %t3 = extractvalue {i32, i32, i32} %t0, 1
+  %t4 = extractvalue {i32, i32, i32} %t0, 0
+  call void @op_2_to_0(i32 %t3, i32 %t4)
+  ret void
+}
+
+define void @f346() {
+; CHECK-LABEL: f346:
+; CHECK:         .functype f346 () -> ()
+; CHECK-NEXT:    .local i32, i32
+; CHECK-NEXT:  # %bb.0:
+; CHECK-NEXT:    call op_0_to_3
+; CHECK-NEXT:    local.set 1
+; CHECK-NEXT:    local.set 0
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    # fallthrough-return
+  %t0 = call {i32, i32, i32} @op_0_to_3()
+  %t1 = extractvalue {i32, i32, i32} %t0, 0
+  %t2 = extractvalue {i32, i32, i32} %t0, 1
+  call void @op_2_to_0(i32 %t1, i32 %t2)
+  %t3 = extractvalue {i32, i32, i32} %t0, 1
+  %t4 = extractvalue {i32, i32, i32} %t0, 2
+  call void @op_2_to_0(i32 %t3, i32 %t4)
+  ret void
+}
+
+define void @f347() {
+; CHECK-LABEL: f347:
+; CHECK:         .functype f347 () -> ()
+; CHECK-NEXT:    .local i32, i32, i32
+; CHECK-NEXT:  # %bb.0:
+; CHECK-NEXT:    call op_0_to_3
+; CHECK-NEXT:    local.set 1
+; CHECK-NEXT:    local.set 0
+; CHECK-NEXT:    local.tee 2
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    local.get 2
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    # fallthrough-return
+  %t0 = call {i32, i32, i32} @op_0_to_3()
+  %t1 = extractvalue {i32, i32, i32} %t0, 0
+  %t2 = extractvalue {i32, i32, i32} %t0, 1
+  call void @op_2_to_0(i32 %t1, i32 %t2)
+  %t3 = extractvalue {i32, i32, i32} %t0, 2
+  %t4 = extractvalue {i32, i32, i32} %t0, 0
+  call void @op_2_to_0(i32 %t3, i32 %t4)
+  ret void
+}
+
+define void @f348() {
+; CHECK-LABEL: f348:
+; CHECK:         .functype f348 () -> ()
+; CHECK-NEXT:    .local i32, i32
+; CHECK-NEXT:  # %bb.0:
+; CHECK-NEXT:    call op_0_to_3
+; CHECK-NEXT:    local.set 1
+; CHECK-NEXT:    local.set 0
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    # fallthrough-return
+  %t0 = call {i32, i32, i32} @op_0_to_3()
+  %t1 = extractvalue {i32, i32, i32} %t0, 0
+  %t2 = extractvalue {i32, i32, i32} %t0, 1
+  call void @op_2_to_0(i32 %t1, i32 %t2)
+  %t3 = extractvalue {i32, i32, i32} %t0, 2
+  %t4 = extractvalue {i32, i32, i32} %t0, 1
+  call void @op_2_to_0(i32 %t3, i32 %t4)
+  ret void
+}
+
+define void @f349() {
+; CHECK-LABEL: f349:
+; CHECK:         .functype f349 () -> ()
+; CHECK-NEXT:    .local i32
+; CHECK-NEXT:  # %bb.0:
+; CHECK-NEXT:    call op_0_to_3
+; CHECK-NEXT:    local.set 0
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    # fallthrough-return
+  %t0 = call {i32, i32, i32} @op_0_to_3()
+  %t1 = extractvalue {i32, i32, i32} %t0, 0
+  %t2 = extractvalue {i32, i32, i32} %t0, 1
+  call void @op_2_to_0(i32 %t1, i32 %t2)
+  %t3 = extractvalue {i32, i32, i32} %t0, 2
+  %t4 = extractvalue {i32, i32, i32} %t0, 2
+  call void @op_2_to_0(i32 %t3, i32 %t4)
+  ret void
+}
+
+define void @f350() {
+; CHECK-LABEL: f350:
+; CHECK:         .functype f350 () -> ()
+; CHECK-NEXT:    .local i32, i32
+; CHECK-NEXT:  # %bb.0:
+; CHECK-NEXT:    call op_0_to_3
+; CHECK-NEXT:    local.set 0
+; CHECK-NEXT:    drop
+; CHECK-NEXT:    local.tee 1
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    call op_1_to_0
+; CHECK-NEXT:    # fallthrough-return
+  %t0 = call {i32, i32, i32} @op_0_to_3()
+  %t1 = extractvalue {i32, i32, i32} %t0, 0
+  %t2 = extractvalue {i32, i32, i32} %t0, 2
+  call void @op_2_to_0(i32 %t1, i32 %t2)
+  %t3 = extractvalue {i32, i32, i32} %t0, 0
+  call void @op_1_to_0(i32 %t3)
+  ret void
+}
+
+define void @f351() {
+; CHECK-LABEL: f351:
+; CHECK:         .functype f351 () -> ()
+; CHECK-NEXT:    .local i32, i32
+; CHECK-NEXT:  # %bb.0:
+; CHECK-NEXT:    call op_0_to_3
+; CHECK-NEXT:    local.set 1
+; CHECK-NEXT:    local.set 0
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    call op_1_to_0
+; CHECK-NEXT:    # fallthrough-return
+  %t0 = call {i32, i32, i32} @op_0_to_3()
+  %t1 = extractvalue {i32, i32, i32} %t0, 0
+  %t2 = extractvalue {i32, i32, i32} %t0, 2
+  call void @op_2_to_0(i32 %t1, i32 %t2)
+  %t3 = extractvalue {i32, i32, i32} %t0, 1
+  call void @op_1_to_0(i32 %t3)
+  ret void
+}
+
+define void @f352() {
+; CHECK-LABEL: f352:
+; CHECK:         .functype f352 () -> ()
+; CHECK-NEXT:    .local i32
+; CHECK-NEXT:  # %bb.0:
+; CHECK-NEXT:    call op_0_to_3
+; CHECK-NEXT:    local.set 0
+; CHECK-NEXT:    drop
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    call op_1_to_0
+; CHECK-NEXT:    # fallthrough-return
+  %t0 = call {i32, i32, i32} @op_0_to_3()
+  %t1 = extractvalue {i32, i32, i32} %t0, 0
+  %t2 = extractvalue {i32, i32, i32} %t0, 2
+  call void @op_2_to_0(i32 %t1, i32 %t2)
+  %t3 = extractvalue {i32, i32, i32} %t0, 2
+  call void @op_1_to_0(i32 %t3)
+  ret void
+}
+
+define void @f354() {
+; CHECK-LABEL: f354:
+; CHECK:         .functype f354 () -> ()
+; CHECK-NEXT:    .local i32, i32, i32
+; CHECK-NEXT:  # %bb.0:
+; CHECK-NEXT:    call op_0_to_3
+; CHECK-NEXT:    local.set 1
+; CHECK-NEXT:    local.set 0
+; CHECK-NEXT:    local.tee 2
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    local.get 2
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    # fallthrough-return
+  %t0 = call {i32, i32, i32} @op_0_to_3()
+  %t1 = extractvalue {i32, i32, i32} %t0, 0
+  %t2 = extractvalue {i32, i32, i32} %t0, 2
+  call void @op_2_to_0(i32 %t1, i32 %t2)
+  %t3 = extractvalue {i32, i32, i32} %t0, 0
+  %t4 = extractvalue {i32, i32, i32} %t0, 1
+  call void @op_2_to_0(i32 %t3, i32 %t4)
+  ret void
+}
+
+define void @f356() {
+; CHECK-LABEL: f356:
+; CHECK:         .functype f356 () -> ()
+; CHECK-NEXT:    .local i32, i32, i32
+; CHECK-NEXT:  # %bb.0:
+; CHECK-NEXT:    call op_0_to_3
+; CHECK-NEXT:    local.set 1
+; CHECK-NEXT:    local.set 0
+; CHECK-NEXT:    local.tee 2
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    local.get 2
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    # fallthrough-return
+  %t0 = call {i32, i32, i32} @op_0_to_3()
+  %t1 = extractvalue {i32, i32, i32} %t0, 0
+  %t2 = extractvalue {i32, i32, i32} %t0, 2
+  call void @op_2_to_0(i32 %t1, i32 %t2)
+  %t3 = extractvalue {i32, i32, i32} %t0, 1
+  %t4 = extractvalue {i32, i32, i32} %t0, 0
+  call void @op_2_to_0(i32 %t3, i32 %t4)
+  ret void
+}
+
+define void @f357() {
+; CHECK-LABEL: f357:
+; CHECK:         .functype f357 () -> ()
+; CHECK-NEXT:    .local i32, i32
+; CHECK-NEXT:  # %bb.0:
+; CHECK-NEXT:    call op_0_to_3
+; CHECK-NEXT:    local.set 1
+; CHECK-NEXT:    local.set 0
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    # fallthrough-return
+  %t0 = call {i32, i32, i32} @op_0_to_3()
+  %t1 = extractvalue {i32, i32, i32} %t0, 0
+  %t2 = extractvalue {i32, i32, i32} %t0, 2
+  call void @op_2_to_0(i32 %t1, i32 %t2)
+  %t3 = extractvalue {i32, i32, i32} %t0, 1
+  %t4 = extractvalue {i32, i32, i32} %t0, 1
+  call void @op_2_to_0(i32 %t3, i32 %t4)
+  ret void
+}
+
+define void @f358() {
+; CHECK-LABEL: f358:
+; CHECK:         .functype f358 () -> ()
+; CHECK-NEXT:    .local i32, i32
+; CHECK-NEXT:  # %bb.0:
+; CHECK-NEXT:    call op_0_to_3
+; CHECK-NEXT:    local.set 1
+; CHECK-NEXT:    local.set 0
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    # fallthrough-return
+  %t0 = call {i32, i32, i32} @op_0_to_3()
+  %t1 = extractvalue {i32, i32, i32} %t0, 0
+  %t2 = extractvalue {i32, i32, i32} %t0, 2
+  call void @op_2_to_0(i32 %t1, i32 %t2)
+  %t3 = extractvalue {i32, i32, i32} %t0, 1
+  %t4 = extractvalue {i32, i32, i32} %t0, 2
+  call void @op_2_to_0(i32 %t3, i32 %t4)
+  ret void
+}
+
+define void @f359() {
+; CHECK-LABEL: f359:
+; CHECK:         .functype f359 () -> ()
+; CHECK-NEXT:    .local i32, i32
+; CHECK-NEXT:  # %bb.0:
+; CHECK-NEXT:    call op_0_to_3
+; CHECK-NEXT:    local.set 0
+; CHECK-NEXT:    drop
+; CHECK-NEXT:    local.tee 1
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    # fallthrough-return
+  %t0 = call {i32, i32, i32} @op_0_to_3()
+  %t1 = extractvalue {i32, i32, i32} %t0, 0
+  %t2 = extractvalue {i32, i32, i32} %t0, 2
+  call void @op_2_to_0(i32 %t1, i32 %t2)
+  %t3 = extractvalue {i32, i32, i32} %t0, 2
+  %t4 = extractvalue {i32, i32, i32} %t0, 0
+  call void @op_2_to_0(i32 %t3, i32 %t4)
+  ret void
+}
+
+define void @f360() {
+; CHECK-LABEL: f360:
+; CHECK:         .functype f360 () -> ()
+; CHECK-NEXT:    .local i32, i32
+; CHECK-NEXT:  # %bb.0:
+; CHECK-NEXT:    call op_0_to_3
+; CHECK-NEXT:    local.set 1
+; CHECK-NEXT:    local.set 0
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    # fallthrough-return
+  %t0 = call {i32, i32, i32} @op_0_to_3()
+  %t1 = extractvalue {i32, i32, i32} %t0, 0
+  %t2 = extractvalue {i32, i32, i32} %t0, 2
+  call void @op_2_to_0(i32 %t1, i32 %t2)
+  %t3 = extractvalue {i32, i32, i32} %t0, 2
+  %t4 = extractvalue {i32, i32, i32} %t0, 1
+  call void @op_2_to_0(i32 %t3, i32 %t4)
+  ret void
+}
+
+define void @f362() {
+; CHECK-LABEL: f362:
+; CHECK:         .functype f362 () -> ()
+; CHECK-NEXT:    .local i32, i32
+; CHECK-NEXT:  # %bb.0:
+; CHECK-NEXT:    call op_0_to_3
+; CHECK-NEXT:    drop
+; CHECK-NEXT:    local.set 1
+; CHECK-NEXT:    local.set 0
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    call op_1_to_0
+; CHECK-NEXT:    # fallthrough-return
+  %t0 = call {i32, i32, i32} @op_0_to_3()
+  %t1 = extractvalue {i32, i32, i32} %t0, 1
+  %t2 = extractvalue {i32, i32, i32} %t0, 0
+  call void @op_2_to_0(i32 %t1, i32 %t2)
+  %t3 = extractvalue {i32, i32, i32} %t0, 0
+  call void @op_1_to_0(i32 %t3)
+  ret void
+}
+
+define void @f363() {
+; CHECK-LABEL: f363:
+; CHECK:         .functype f363 () -> ()
+; CHECK-NEXT:    .local i32, i32
+; CHECK-NEXT:  # %bb.0:
+; CHECK-NEXT:    call op_0_to_3
+; CHECK-NEXT:    drop
+; CHECK-NEXT:    local.set 1
+; CHECK-NEXT:    local.set 0
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    call op_1_to_0
+; CHECK-NEXT:    # fallthrough-return
+  %t0 = call {i32, i32, i32} @op_0_to_3()
+  %t1 = extractvalue {i32, i32, i32} %t0, 1
+  %t2 = extractvalue {i32, i32, i32} %t0, 0
+  call void @op_2_to_0(i32 %t1, i32 %t2)
+  %t3 = extractvalue {i32, i32, i32} %t0, 1
+  call void @op_1_to_0(i32 %t3)
+  ret void
+}
+
+define void @f364() {
+; CHECK-LABEL: f364:
+; CHECK:         .functype f364 () -> ()
+; CHECK-NEXT:    .local i32, i32, i32
+; CHECK-NEXT:  # %bb.0:
+; CHECK-NEXT:    call op_0_to_3
+; CHECK-NEXT:    local.set 2
+; CHECK-NEXT:    local.set 1
+; CHECK-NEXT:    local.set 0
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    local.get 2
+; CHECK-NEXT:    call op_1_to_0
+; CHECK-NEXT:    # fallthrough-return
+  %t0 = call {i32, i32, i32} @op_0_to_3()
+  %t1 = extractvalue {i32, i32, i32} %t0, 1
+  %t2 = extractvalue {i32, i32, i32} %t0, 0
+  call void @op_2_to_0(i32 %t1, i32 %t2)
+  %t3 = extractvalue {i32, i32, i32} %t0, 2
+  call void @op_1_to_0(i32 %t3)
+  ret void
+}
+
+define void @f366() {
+; CHECK-LABEL: f366:
+; CHECK:         .functype f366 () -> ()
+; CHECK-NEXT:    .local i32, i32
+; CHECK-NEXT:  # %bb.0:
+; CHECK-NEXT:    call op_0_to_3
+; CHECK-NEXT:    drop
+; CHECK-NEXT:    local.set 1
+; CHECK-NEXT:    local.set 0
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    # fallthrough-return
+  %t0 = call {i32, i32, i32} @op_0_to_3()
+  %t1 = extractvalue {i32, i32, i32} %t0, 1
+  %t2 = extractvalue {i32, i32, i32} %t0, 0
+  call void @op_2_to_0(i32 %t1, i32 %t2)
+  %t3 = extractvalue {i32, i32, i32} %t0, 0
+  %t4 = extractvalue {i32, i32, i32} %t0, 1
+  call void @op_2_to_0(i32 %t3, i32 %t4)
+  ret void
+}
+
+define void @f367() {
+; CHECK-LABEL: f367:
+; CHECK:         .functype f367 () -> ()
+; CHECK-NEXT:    .local i32, i32, i32
+; CHECK-NEXT:  # %bb.0:
+; CHECK-NEXT:    call op_0_to_3
+; CHECK-NEXT:    local.set 2
+; CHECK-NEXT:    local.set 1
+; CHECK-NEXT:    local.set 0
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    local.get 2
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    # fallthrough-return
+  %t0 = call {i32, i32, i32} @op_0_to_3()
+  %t1 = extractvalue {i32, i32, i32} %t0, 1
+  %t2 = extractvalue {i32, i32, i32} %t0, 0
+  call void @op_2_to_0(i32 %t1, i32 %t2)
+  %t3 = extractvalue {i32, i32, i32} %t0, 0
+  %t4 = extractvalue {i32, i32, i32} %t0, 2
+  call void @op_2_to_0(i32 %t3, i32 %t4)
+  ret void
+}
+
+define void @f370() {
+; CHECK-LABEL: f370:
+; CHECK:         .functype f370 () -> ()
+; CHECK-NEXT:    .local i32, i32, i32
+; CHECK-NEXT:  # %bb.0:
+; CHECK-NEXT:    call op_0_to_3
+; CHECK-NEXT:    local.set 2
+; CHECK-NEXT:    local.set 1
+; CHECK-NEXT:    local.set 0
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    local.get 2
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    # fallthrough-return
+  %t0 = call {i32, i32, i32} @op_0_to_3()
+  %t1 = extractvalue {i32, i32, i32} %t0, 1
+  %t2 = extractvalue {i32, i32, i32} %t0, 0
+  call void @op_2_to_0(i32 %t1, i32 %t2)
+  %t3 = extractvalue {i32, i32, i32} %t0, 1
+  %t4 = extractvalue {i32, i32, i32} %t0, 2
+  call void @op_2_to_0(i32 %t3, i32 %t4)
+  ret void
+}
+
+define void @f371() {
+; CHECK-LABEL: f371:
+; CHECK:         .functype f371 () -> ()
+; CHECK-NEXT:    .local i32, i32, i32
+; CHECK-NEXT:  # %bb.0:
+; CHECK-NEXT:    call op_0_to_3
+; CHECK-NEXT:    local.set 2
+; CHECK-NEXT:    local.set 1
+; CHECK-NEXT:    local.set 0
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    local.get 2
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    # fallthrough-return
+  %t0 = call {i32, i32, i32} @op_0_to_3()
+  %t1 = extractvalue {i32, i32, i32} %t0, 1
+  %t2 = extractvalue {i32, i32, i32} %t0, 0
+  call void @op_2_to_0(i32 %t1, i32 %t2)
+  %t3 = extractvalue {i32, i32, i32} %t0, 2
+  %t4 = extractvalue {i32, i32, i32} %t0, 0
+  call void @op_2_to_0(i32 %t3, i32 %t4)
+  ret void
+}
+
+define void @f372() {
+; CHECK-LABEL: f372:
+; CHECK:         .functype f372 () -> ()
+; CHECK-NEXT:    .local i32, i32, i32
+; CHECK-NEXT:  # %bb.0:
+; CHECK-NEXT:    call op_0_to_3
+; CHECK-NEXT:    local.set 2
+; CHECK-NEXT:    local.set 1
+; CHECK-NEXT:    local.set 0
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    local.get 2
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    # fallthrough-return
+  %t0 = call {i32, i32, i32} @op_0_to_3()
+  %t1 = extractvalue {i32, i32, i32} %t0, 1
+  %t2 = extractvalue {i32, i32, i32} %t0, 0
+  call void @op_2_to_0(i32 %t1, i32 %t2)
+  %t3 = extractvalue {i32, i32, i32} %t0, 2
+  %t4 = extractvalue {i32, i32, i32} %t0, 1
+  call void @op_2_to_0(i32 %t3, i32 %t4)
+  ret void
+}
+
+define void @f373() {
+; CHECK-LABEL: f373:
+; CHECK:         .functype f373 () -> ()
+; CHECK-NEXT:    .local i32, i32, i32
+; CHECK-NEXT:  # %bb.0:
+; CHECK-NEXT:    call op_0_to_3
+; CHECK-NEXT:    local.set 2
+; CHECK-NEXT:    local.set 1
+; CHECK-NEXT:    local.set 0
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    local.get 2
+; CHECK-NEXT:    local.get 2
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    # fallthrough-return
+  %t0 = call {i32, i32, i32} @op_0_to_3()
+  %t1 = extractvalue {i32, i32, i32} %t0, 1
+  %t2 = extractvalue {i32, i32, i32} %t0, 0
+  call void @op_2_to_0(i32 %t1, i32 %t2)
+  %t3 = extractvalue {i32, i32, i32} %t0, 2
+  %t4 = extractvalue {i32, i32, i32} %t0, 2
+  call void @op_2_to_0(i32 %t3, i32 %t4)
+  ret void
+}
+
+define void @f374() {
+; CHECK-LABEL: f374:
+; CHECK:         .functype f374 () -> ()
+; CHECK-NEXT:    .local i32, i32
+; CHECK-NEXT:  # %bb.0:
+; CHECK-NEXT:    call op_0_to_3
+; CHECK-NEXT:    drop
+; CHECK-NEXT:    local.set 1
+; CHECK-NEXT:    local.set 0
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    call op_1_to_0
+; CHECK-NEXT:    # fallthrough-return
+  %t0 = call {i32, i32, i32} @op_0_to_3()
+  %t1 = extractvalue {i32, i32, i32} %t0, 1
+  %t2 = extractvalue {i32, i32, i32} %t0, 1
+  call void @op_2_to_0(i32 %t1, i32 %t2)
+  %t3 = extractvalue {i32, i32, i32} %t0, 0
+  call void @op_1_to_0(i32 %t3)
+  ret void
+}
+
+define void @f376() {
+; CHECK-LABEL: f376:
+; CHECK:         .functype f376 () -> ()
+; CHECK-NEXT:    .local i32, i32
+; CHECK-NEXT:  # %bb.0:
+; CHECK-NEXT:    call op_0_to_3
+; CHECK-NEXT:    local.set 1
+; CHECK-NEXT:    local.set 0
+; CHECK-NEXT:    drop
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    call op_1_to_0
+; CHECK-NEXT:    # fallthrough-return
+  %t0 = call {i32, i32, i32} @op_0_to_3()
+  %t1 = extractvalue {i32, i32, i32} %t0, 1
+  %t2 = extractvalue {i32, i32, i32} %t0, 1
+  call void @op_2_to_0(i32 %t1, i32 %t2)
+  %t3 = extractvalue {i32, i32, i32} %t0, 2
+  call void @op_1_to_0(i32 %t3)
+  ret void
+}
+
+define void @f377() {
+; CHECK-LABEL: f377:
+; CHECK:         .functype f377 () -> ()
+; CHECK-NEXT:    .local i32, i32
+; CHECK-NEXT:  # %bb.0:
+; CHECK-NEXT:    call op_0_to_3
+; CHECK-NEXT:    drop
+; CHECK-NEXT:    local.set 1
+; CHECK-NEXT:    local.set 0
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    # fallthrough-return
+  %t0 = call {i32, i32, i32} @op_0_to_3()
+  %t1 = extractvalue {i32, i32, i32} %t0, 1
+  %t2 = extractvalue {i32, i32, i32} %t0, 1
+  call void @op_2_to_0(i32 %t1, i32 %t2)
+  %t3 = extractvalue {i32, i32, i32} %t0, 0
+  %t4 = extractvalue {i32, i32, i32} %t0, 0
+  call void @op_2_to_0(i32 %t3, i32 %t4)
+  ret void
+}
+
+define void @f379() {
+; CHECK-LABEL: f379:
+; CHECK:         .functype f379 () -> ()
+; CHECK-NEXT:    .local i32, i32, i32
+; CHECK-NEXT:  # %bb.0:
+; CHECK-NEXT:    call op_0_to_3
+; CHECK-NEXT:    local.set 2
+; CHECK-NEXT:    local.set 1
+; CHECK-NEXT:    local.set 0
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    local.get 2
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    # fallthrough-return
+  %t0 = call {i32, i32, i32} @op_0_to_3()
+  %t1 = extractvalue {i32, i32, i32} %t0, 1
+  %t2 = extractvalue {i32, i32, i32} %t0, 1
+  call void @op_2_to_0(i32 %t1, i32 %t2)
+  %t3 = extractvalue {i32, i32, i32} %t0, 0
+  %t4 = extractvalue {i32, i32, i32} %t0, 2
+  call void @op_2_to_0(i32 %t3, i32 %t4)
+  ret void
+}
+
+define void @f383() {
+; CHECK-LABEL: f383:
+; CHECK:         .functype f383 () -> ()
+; CHECK-NEXT:    .local i32, i32, i32
+; CHECK-NEXT:  # %bb.0:
+; CHECK-NEXT:    call op_0_to_3
+; CHECK-NEXT:    local.set 2
+; CHECK-NEXT:    local.set 1
+; CHECK-NEXT:    local.set 0
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    local.get 2
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    # fallthrough-return
+  %t0 = call {i32, i32, i32} @op_0_to_3()
+  %t1 = extractvalue {i32, i32, i32} %t0, 1
+  %t2 = extractvalue {i32, i32, i32} %t0, 1
+  call void @op_2_to_0(i32 %t1, i32 %t2)
+  %t3 = extractvalue {i32, i32, i32} %t0, 2
+  %t4 = extractvalue {i32, i32, i32} %t0, 0
+  call void @op_2_to_0(i32 %t3, i32 %t4)
+  ret void
+}
+
+define void @f385() {
+; CHECK-LABEL: f385:
+; CHECK:         .functype f385 () -> ()
+; CHECK-NEXT:    .local i32, i32
+; CHECK-NEXT:  # %bb.0:
+; CHECK-NEXT:    call op_0_to_3
+; CHECK-NEXT:    local.set 1
+; CHECK-NEXT:    local.set 0
+; CHECK-NEXT:    drop
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    # fallthrough-return
+  %t0 = call {i32, i32, i32} @op_0_to_3()
+  %t1 = extractvalue {i32, i32, i32} %t0, 1
+  %t2 = extractvalue {i32, i32, i32} %t0, 1
+  call void @op_2_to_0(i32 %t1, i32 %t2)
+  %t3 = extractvalue {i32, i32, i32} %t0, 2
+  %t4 = extractvalue {i32, i32, i32} %t0, 2
+  call void @op_2_to_0(i32 %t3, i32 %t4)
+  ret void
+}
+
+define void @f386() {
+; CHECK-LABEL: f386:
+; CHECK:         .functype f386 () -> ()
+; CHECK-NEXT:    .local i32, i32, i32
+; CHECK-NEXT:  # %bb.0:
+; CHECK-NEXT:    call op_0_to_3
+; CHECK-NEXT:    local.set 2
+; CHECK-NEXT:    local.set 1
+; CHECK-NEXT:    local.set 0
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    local.get 2
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    call op_1_to_0
+; CHECK-NEXT:    # fallthrough-return
+  %t0 = call {i32, i32, i32} @op_0_to_3()
+  %t1 = extractvalue {i32, i32, i32} %t0, 1
+  %t2 = extractvalue {i32, i32, i32} %t0, 2
+  call void @op_2_to_0(i32 %t1, i32 %t2)
+  %t3 = extractvalue {i32, i32, i32} %t0, 0
+  call void @op_1_to_0(i32 %t3)
+  ret void
+}
+
+define void @f387() {
+; CHECK-LABEL: f387:
+; CHECK:         .functype f387 () -> ()
+; CHECK-NEXT:    .local i32, i32
+; CHECK-NEXT:  # %bb.0:
+; CHECK-NEXT:    call op_0_to_3
+; CHECK-NEXT:    local.set 1
+; CHECK-NEXT:    local.set 0
+; CHECK-NEXT:    drop
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    call op_1_to_0
+; CHECK-NEXT:    # fallthrough-return
+  %t0 = call {i32, i32, i32} @op_0_to_3()
+  %t1 = extractvalue {i32, i32, i32} %t0, 1
+  %t2 = extractvalue {i32, i32, i32} %t0, 2
+  call void @op_2_to_0(i32 %t1, i32 %t2)
+  %t3 = extractvalue {i32, i32, i32} %t0, 1
+  call void @op_1_to_0(i32 %t3)
+  ret void
+}
+
+define void @f388() {
+; CHECK-LABEL: f388:
+; CHECK:         .functype f388 () -> ()
+; CHECK-NEXT:    .local i32, i32
+; CHECK-NEXT:  # %bb.0:
+; CHECK-NEXT:    call op_0_to_3
+; CHECK-NEXT:    local.set 1
+; CHECK-NEXT:    local.set 0
+; CHECK-NEXT:    drop
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    call op_1_to_0
+; CHECK-NEXT:    # fallthrough-return
+  %t0 = call {i32, i32, i32} @op_0_to_3()
+  %t1 = extractvalue {i32, i32, i32} %t0, 1
+  %t2 = extractvalue {i32, i32, i32} %t0, 2
+  call void @op_2_to_0(i32 %t1, i32 %t2)
+  %t3 = extractvalue {i32, i32, i32} %t0, 2
+  call void @op_1_to_0(i32 %t3)
+  ret void
+}
+
+define void @f389() {
+; CHECK-LABEL: f389:
+; CHECK:         .functype f389 () -> ()
+; CHECK-NEXT:    .local i32, i32, i32
+; CHECK-NEXT:  # %bb.0:
+; CHECK-NEXT:    call op_0_to_3
+; CHECK-NEXT:    local.set 2
+; CHECK-NEXT:    local.set 1
+; CHECK-NEXT:    local.set 0
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    local.get 2
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    # fallthrough-return
+  %t0 = call {i32, i32, i32} @op_0_to_3()
+  %t1 = extractvalue {i32, i32, i32} %t0, 1
+  %t2 = extractvalue {i32, i32, i32} %t0, 2
+  call void @op_2_to_0(i32 %t1, i32 %t2)
+  %t3 = extractvalue {i32, i32, i32} %t0, 0
+  %t4 = extractvalue {i32, i32, i32} %t0, 0
+  call void @op_2_to_0(i32 %t3, i32 %t4)
+  ret void
+}
+
+define void @f390() {
+; CHECK-LABEL: f390:
+; CHECK:         .functype f390 () -> ()
+; CHECK-NEXT:    .local i32, i32, i32
+; CHECK-NEXT:  # %bb.0:
+; CHECK-NEXT:    call op_0_to_3
+; CHECK-NEXT:    local.set 2
+; CHECK-NEXT:    local.set 1
+; CHECK-NEXT:    local.set 0
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    local.get 2
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    # fallthrough-return
+  %t0 = call {i32, i32, i32} @op_0_to_3()
+  %t1 = extractvalue {i32, i32, i32} %t0, 1
+  %t2 = extractvalue {i32, i32, i32} %t0, 2
+  call void @op_2_to_0(i32 %t1, i32 %t2)
+  %t3 = extractvalue {i32, i32, i32} %t0, 0
+  %t4 = extractvalue {i32, i32, i32} %t0, 1
+  call void @op_2_to_0(i32 %t3, i32 %t4)
+  ret void
+}
+
+define void @f391() {
+; CHECK-LABEL: f391:
+; CHECK:         .functype f391 () -> ()
+; CHECK-NEXT:    .local i32, i32, i32
+; CHECK-NEXT:  # %bb.0:
+; CHECK-NEXT:    call op_0_to_3
+; CHECK-NEXT:    local.set 2
+; CHECK-NEXT:    local.set 1
+; CHECK-NEXT:    local.set 0
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    local.get 2
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    local.get 2
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    # fallthrough-return
+  %t0 = call {i32, i32, i32} @op_0_to_3()
+  %t1 = extractvalue {i32, i32, i32} %t0, 1
+  %t2 = extractvalue {i32, i32, i32} %t0, 2
+  call void @op_2_to_0(i32 %t1, i32 %t2)
+  %t3 = extractvalue {i32, i32, i32} %t0, 0
+  %t4 = extractvalue {i32, i32, i32} %t0, 2
+  call void @op_2_to_0(i32 %t3, i32 %t4)
+  ret void
+}
+
+define void @f392() {
+; CHECK-LABEL: f392:
+; CHECK:         .functype f392 () -> ()
+; CHECK-NEXT:    .local i32, i32, i32
+; CHECK-NEXT:  # %bb.0:
+; CHECK-NEXT:    call op_0_to_3
+; CHECK-NEXT:    local.set 2
+; CHECK-NEXT:    local.set 1
+; CHECK-NEXT:    local.set 0
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    local.get 2
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    # fallthrough-return
+  %t0 = call {i32, i32, i32} @op_0_to_3()
+  %t1 = extractvalue {i32, i32, i32} %t0, 1
+  %t2 = extractvalue {i32, i32, i32} %t0, 2
+  call void @op_2_to_0(i32 %t1, i32 %t2)
+  %t3 = extractvalue {i32, i32, i32} %t0, 1
+  %t4 = extractvalue {i32, i32, i32} %t0, 0
+  call void @op_2_to_0(i32 %t3, i32 %t4)
+  ret void
+}
+
+define void @f395() {
+; CHECK-LABEL: f395:
+; CHECK:         .functype f395 () -> ()
+; CHECK-NEXT:    .local i32, i32, i32
+; CHECK-NEXT:  # %bb.0:
+; CHECK-NEXT:    call op_0_to_3
+; CHECK-NEXT:    local.set 2
+; CHECK-NEXT:    local.set 1
+; CHECK-NEXT:    local.set 0
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    local.get 2
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    local.get 2
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    # fallthrough-return
+  %t0 = call {i32, i32, i32} @op_0_to_3()
+  %t1 = extractvalue {i32, i32, i32} %t0, 1
+  %t2 = extractvalue {i32, i32, i32} %t0, 2
+  call void @op_2_to_0(i32 %t1, i32 %t2)
+  %t3 = extractvalue {i32, i32, i32} %t0, 2
+  %t4 = extractvalue {i32, i32, i32} %t0, 0
+  call void @op_2_to_0(i32 %t3, i32 %t4)
+  ret void
+}
+
+define void @f396() {
+; CHECK-LABEL: f396:
+; CHECK:         .functype f396 () -> ()
+; CHECK-NEXT:    .local i32, i32
+; CHECK-NEXT:  # %bb.0:
+; CHECK-NEXT:    call op_0_to_3
+; CHECK-NEXT:    local.set 1
+; CHECK-NEXT:    local.set 0
+; CHECK-NEXT:    drop
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    # fallthrough-return
+  %t0 = call {i32, i32, i32} @op_0_to_3()
+  %t1 = extractvalue {i32, i32, i32} %t0, 1
+  %t2 = extractvalue {i32, i32, i32} %t0, 2
+  call void @op_2_to_0(i32 %t1, i32 %t2)
+  %t3 = extractvalue {i32, i32, i32} %t0, 2
+  %t4 = extractvalue {i32, i32, i32} %t0, 1
+  call void @op_2_to_0(i32 %t3, i32 %t4)
+  ret void
+}
+
+define void @f398() {
+; CHECK-LABEL: f398:
+; CHECK:         .functype f398 () -> ()
+; CHECK-NEXT:    .local i32, i32
+; CHECK-NEXT:  # %bb.0:
+; CHECK-NEXT:    call op_0_to_3
+; CHECK-NEXT:    local.set 1
+; CHECK-NEXT:    drop
+; CHECK-NEXT:    local.set 0
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    call op_1_to_0
+; CHECK-NEXT:    # fallthrough-return
+  %t0 = call {i32, i32, i32} @op_0_to_3()
+  %t1 = extractvalue {i32, i32, i32} %t0, 2
+  %t2 = extractvalue {i32, i32, i32} %t0, 0
+  call void @op_2_to_0(i32 %t1, i32 %t2)
+  %t3 = extractvalue {i32, i32, i32} %t0, 0
+  call void @op_1_to_0(i32 %t3)
+  ret void
+}
+
+define void @f399() {
+; CHECK-LABEL: f399:
+; CHECK:         .functype f399 () -> ()
+; CHECK-NEXT:    .local i32, i32, i32
+; CHECK-NEXT:  # %bb.0:
+; CHECK-NEXT:    call op_0_to_3
+; CHECK-NEXT:    local.set 2
+; CHECK-NEXT:    local.set 1
+; CHECK-NEXT:    local.set 0
+; CHECK-NEXT:    local.get 2
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    call op_1_to_0
+; CHECK-NEXT:    # fallthrough-return
+  %t0 = call {i32, i32, i32} @op_0_to_3()
+  %t1 = extractvalue {i32, i32, i32} %t0, 2
+  %t2 = extractvalue {i32, i32, i32} %t0, 0
+  call void @op_2_to_0(i32 %t1, i32 %t2)
+  %t3 = extractvalue {i32, i32, i32} %t0, 1
+  call void @op_1_to_0(i32 %t3)
+  ret void
+}
+
+define void @f400() {
+; CHECK-LABEL: f400:
+; CHECK:         .functype f400 () -> ()
+; CHECK-NEXT:    .local i32, i32
+; CHECK-NEXT:  # %bb.0:
+; CHECK-NEXT:    call op_0_to_3
+; CHECK-NEXT:    local.set 1
+; CHECK-NEXT:    drop
+; CHECK-NEXT:    local.set 0
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    call op_1_to_0
+; CHECK-NEXT:    # fallthrough-return
+  %t0 = call {i32, i32, i32} @op_0_to_3()
+  %t1 = extractvalue {i32, i32, i32} %t0, 2
+  %t2 = extractvalue {i32, i32, i32} %t0, 0
+  call void @op_2_to_0(i32 %t1, i32 %t2)
+  %t3 = extractvalue {i32, i32, i32} %t0, 2
+  call void @op_1_to_0(i32 %t3)
+  ret void
+}
+
+define void @f402() {
+; CHECK-LABEL: f402:
+; CHECK:         .functype f402 () -> ()
+; CHECK-NEXT:    .local i32, i32, i32
+; CHECK-NEXT:  # %bb.0:
+; CHECK-NEXT:    call op_0_to_3
+; CHECK-NEXT:    local.set 2
+; CHECK-NEXT:    local.set 1
+; CHECK-NEXT:    local.set 0
+; CHECK-NEXT:    local.get 2
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    # fallthrough-return
+  %t0 = call {i32, i32, i32} @op_0_to_3()
+  %t1 = extractvalue {i32, i32, i32} %t0, 2
+  %t2 = extractvalue {i32, i32, i32} %t0, 0
+  call void @op_2_to_0(i32 %t1, i32 %t2)
+  %t3 = extractvalue {i32, i32, i32} %t0, 0
+  %t4 = extractvalue {i32, i32, i32} %t0, 1
+  call void @op_2_to_0(i32 %t3, i32 %t4)
+  ret void
+}
+
+define void @f403() {
+; CHECK-LABEL: f403:
+; CHECK:         .functype f403 () -> ()
+; CHECK-NEXT:    .local i32, i32
+; CHECK-NEXT:  # %bb.0:
+; CHECK-NEXT:    call op_0_to_3
+; CHECK-NEXT:    local.set 1
+; CHECK-NEXT:    drop
+; CHECK-NEXT:    local.set 0
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    # fallthrough-return
+  %t0 = call {i32, i32, i32} @op_0_to_3()
+  %t1 = extractvalue {i32, i32, i32} %t0, 2
+  %t2 = extractvalue {i32, i32, i32} %t0, 0
+  call void @op_2_to_0(i32 %t1, i32 %t2)
+  %t3 = extractvalue {i32, i32, i32} %t0, 0
+  %t4 = extractvalue {i32, i32, i32} %t0, 2
+  call void @op_2_to_0(i32 %t3, i32 %t4)
+  ret void
+}
+
+define void @f404() {
+; CHECK-LABEL: f404:
+; CHECK:         .functype f404 () -> ()
+; CHECK-NEXT:    .local i32, i32, i32
+; CHECK-NEXT:  # %bb.0:
+; CHECK-NEXT:    call op_0_to_3
+; CHECK-NEXT:    local.set 2
+; CHECK-NEXT:    local.set 1
+; CHECK-NEXT:    local.set 0
+; CHECK-NEXT:    local.get 2
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    # fallthrough-return
+  %t0 = call {i32, i32, i32} @op_0_to_3()
+  %t1 = extractvalue {i32, i32, i32} %t0, 2
+  %t2 = extractvalue {i32, i32, i32} %t0, 0
+  call void @op_2_to_0(i32 %t1, i32 %t2)
+  %t3 = extractvalue {i32, i32, i32} %t0, 1
+  %t4 = extractvalue {i32, i32, i32} %t0, 0
+  call void @op_2_to_0(i32 %t3, i32 %t4)
+  ret void
+}
+
+define void @f405() {
+; CHECK-LABEL: f405:
+; CHECK:         .functype f405 () -> ()
+; CHECK-NEXT:    .local i32, i32, i32
+; CHECK-NEXT:  # %bb.0:
+; CHECK-NEXT:    call op_0_to_3
+; CHECK-NEXT:    local.set 2
+; CHECK-NEXT:    local.set 1
+; CHECK-NEXT:    local.set 0
+; CHECK-NEXT:    local.get 2
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    # fallthrough-return
+  %t0 = call {i32, i32, i32} @op_0_to_3()
+  %t1 = extractvalue {i32, i32, i32} %t0, 2
+  %t2 = extractvalue {i32, i32, i32} %t0, 0
+  call void @op_2_to_0(i32 %t1, i32 %t2)
+  %t3 = extractvalue {i32, i32, i32} %t0, 1
+  %t4 = extractvalue {i32, i32, i32} %t0, 1
+  call void @op_2_to_0(i32 %t3, i32 %t4)
+  ret void
+}
+
+define void @f406() {
+; CHECK-LABEL: f406:
+; CHECK:         .functype f406 () -> ()
+; CHECK-NEXT:    .local i32, i32, i32
+; CHECK-NEXT:  # %bb.0:
+; CHECK-NEXT:    call op_0_to_3
+; CHECK-NEXT:    local.set 2
+; CHECK-NEXT:    local.set 1
+; CHECK-NEXT:    local.set 0
+; CHECK-NEXT:    local.get 2
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    local.get 2
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    # fallthrough-return
+  %t0 = call {i32, i32, i32} @op_0_to_3()
+  %t1 = extractvalue {i32, i32, i32} %t0, 2
+  %t2 = extractvalue {i32, i32, i32} %t0, 0
+  call void @op_2_to_0(i32 %t1, i32 %t2)
+  %t3 = extractvalue {i32, i32, i32} %t0, 1
+  %t4 = extractvalue {i32, i32, i32} %t0, 2
+  call void @op_2_to_0(i32 %t3, i32 %t4)
+  ret void
+}
+
+define void @f408() {
+; CHECK-LABEL: f408:
+; CHECK:         .functype f408 () -> ()
+; CHECK-NEXT:    .local i32, i32, i32
+; CHECK-NEXT:  # %bb.0:
+; CHECK-NEXT:    call op_0_to_3
+; CHECK-NEXT:    local.set 2
+; CHECK-NEXT:    local.set 1
+; CHECK-NEXT:    local.set 0
+; CHECK-NEXT:    local.get 2
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    local.get 2
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    # fallthrough-return
+  %t0 = call {i32, i32, i32} @op_0_to_3()
+  %t1 = extractvalue {i32, i32, i32} %t0, 2
+  %t2 = extractvalue {i32, i32, i32} %t0, 0
+  call void @op_2_to_0(i32 %t1, i32 %t2)
+  %t3 = extractvalue {i32, i32, i32} %t0, 2
+  %t4 = extractvalue {i32, i32, i32} %t0, 1
+  call void @op_2_to_0(i32 %t3, i32 %t4)
+  ret void
+}
+
+define void @f410() {
+; CHECK-LABEL: f410:
+; CHECK:         .functype f410 () -> ()
+; CHECK-NEXT:    .local i32, i32, i32
+; CHECK-NEXT:  # %bb.0:
+; CHECK-NEXT:    call op_0_to_3
+; CHECK-NEXT:    local.set 2
+; CHECK-NEXT:    local.set 1
+; CHECK-NEXT:    local.set 0
+; CHECK-NEXT:    local.get 2
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    call op_1_to_0
+; CHECK-NEXT:    # fallthrough-return
+  %t0 = call {i32, i32, i32} @op_0_to_3()
+  %t1 = extractvalue {i32, i32, i32} %t0, 2
+  %t2 = extractvalue {i32, i32, i32} %t0, 1
+  call void @op_2_to_0(i32 %t1, i32 %t2)
+  %t3 = extractvalue {i32, i32, i32} %t0, 0
+  call void @op_1_to_0(i32 %t3)
+  ret void
+}
+
+define void @f411() {
+; CHECK-LABEL: f411:
+; CHECK:         .functype f411 () -> ()
+; CHECK-NEXT:    .local i32, i32
+; CHECK-NEXT:  # %bb.0:
+; CHECK-NEXT:    call op_0_to_3
+; CHECK-NEXT:    local.set 1
+; CHECK-NEXT:    local.set 0
+; CHECK-NEXT:    drop
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    call op_1_to_0
+; CHECK-NEXT:    # fallthrough-return
+  %t0 = call {i32, i32, i32} @op_0_to_3()
+  %t1 = extractvalue {i32, i32, i32} %t0, 2
+  %t2 = extractvalue {i32, i32, i32} %t0, 1
+  call void @op_2_to_0(i32 %t1, i32 %t2)
+  %t3 = extractvalue {i32, i32, i32} %t0, 1
+  call void @op_1_to_0(i32 %t3)
+  ret void
+}
+
+define void @f412() {
+; CHECK-LABEL: f412:
+; CHECK:         .functype f412 () -> ()
+; CHECK-NEXT:    .local i32, i32
+; CHECK-NEXT:  # %bb.0:
+; CHECK-NEXT:    call op_0_to_3
+; CHECK-NEXT:    local.set 1
+; CHECK-NEXT:    local.set 0
+; CHECK-NEXT:    drop
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    call op_1_to_0
+; CHECK-NEXT:    # fallthrough-return
+  %t0 = call {i32, i32, i32} @op_0_to_3()
+  %t1 = extractvalue {i32, i32, i32} %t0, 2
+  %t2 = extractvalue {i32, i32, i32} %t0, 1
+  call void @op_2_to_0(i32 %t1, i32 %t2)
+  %t3 = extractvalue {i32, i32, i32} %t0, 2
+  call void @op_1_to_0(i32 %t3)
+  ret void
+}
+
+define void @f413() {
+; CHECK-LABEL: f413:
+; CHECK:         .functype f413 () -> ()
+; CHECK-NEXT:    .local i32, i32, i32
+; CHECK-NEXT:  # %bb.0:
+; CHECK-NEXT:    call op_0_to_3
+; CHECK-NEXT:    local.set 2
+; CHECK-NEXT:    local.set 1
+; CHECK-NEXT:    local.set 0
+; CHECK-NEXT:    local.get 2
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    # fallthrough-return
+  %t0 = call {i32, i32, i32} @op_0_to_3()
+  %t1 = extractvalue {i32, i32, i32} %t0, 2
+  %t2 = extractvalue {i32, i32, i32} %t0, 1
+  call void @op_2_to_0(i32 %t1, i32 %t2)
+  %t3 = extractvalue {i32, i32, i32} %t0, 0
+  %t4 = extractvalue {i32, i32, i32} %t0, 0
+  call void @op_2_to_0(i32 %t3, i32 %t4)
+  ret void
+}
+
+define void @f414() {
+; CHECK-LABEL: f414:
+; CHECK:         .functype f414 () -> ()
+; CHECK-NEXT:    .local i32, i32, i32
+; CHECK-NEXT:  # %bb.0:
+; CHECK-NEXT:    call op_0_to_3
+; CHECK-NEXT:    local.set 2
+; CHECK-NEXT:    local.set 1
+; CHECK-NEXT:    local.set 0
+; CHECK-NEXT:    local.get 2
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    # fallthrough-return
+  %t0 = call {i32, i32, i32} @op_0_to_3()
+  %t1 = extractvalue {i32, i32, i32} %t0, 2
+  %t2 = extractvalue {i32, i32, i32} %t0, 1
+  call void @op_2_to_0(i32 %t1, i32 %t2)
+  %t3 = extractvalue {i32, i32, i32} %t0, 0
+  %t4 = extractvalue {i32, i32, i32} %t0, 1
+  call void @op_2_to_0(i32 %t3, i32 %t4)
+  ret void
+}
+
+define void @f415() {
+; CHECK-LABEL: f415:
+; CHECK:         .functype f415 () -> ()
+; CHECK-NEXT:    .local i32, i32, i32
+; CHECK-NEXT:  # %bb.0:
+; CHECK-NEXT:    call op_0_to_3
+; CHECK-NEXT:    local.set 2
+; CHECK-NEXT:    local.set 1
+; CHECK-NEXT:    local.set 0
+; CHECK-NEXT:    local.get 2
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    local.get 2
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    # fallthrough-return
+  %t0 = call {i32, i32, i32} @op_0_to_3()
+  %t1 = extractvalue {i32, i32, i32} %t0, 2
+  %t2 = extractvalue {i32, i32, i32} %t0, 1
+  call void @op_2_to_0(i32 %t1, i32 %t2)
+  %t3 = extractvalue {i32, i32, i32} %t0, 0
+  %t4 = extractvalue {i32, i32, i32} %t0, 2
+  call void @op_2_to_0(i32 %t3, i32 %t4)
+  ret void
+}
+
+define void @f416() {
+; CHECK-LABEL: f416:
+; CHECK:         .functype f416 () -> ()
+; CHECK-NEXT:    .local i32, i32, i32
+; CHECK-NEXT:  # %bb.0:
+; CHECK-NEXT:    call op_0_to_3
+; CHECK-NEXT:    local.set 2
+; CHECK-NEXT:    local.set 1
+; CHECK-NEXT:    local.set 0
+; CHECK-NEXT:    local.get 2
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    # fallthrough-return
+  %t0 = call {i32, i32, i32} @op_0_to_3()
+  %t1 = extractvalue {i32, i32, i32} %t0, 2
+  %t2 = extractvalue {i32, i32, i32} %t0, 1
+  call void @op_2_to_0(i32 %t1, i32 %t2)
+  %t3 = extractvalue {i32, i32, i32} %t0, 1
+  %t4 = extractvalue {i32, i32, i32} %t0, 0
+  call void @op_2_to_0(i32 %t3, i32 %t4)
+  ret void
+}
+
+define void @f418() {
+; CHECK-LABEL: f418:
+; CHECK:         .functype f418 () -> ()
+; CHECK-NEXT:    .local i32, i32
+; CHECK-NEXT:  # %bb.0:
+; CHECK-NEXT:    call op_0_to_3
+; CHECK-NEXT:    local.set 1
+; CHECK-NEXT:    local.set 0
+; CHECK-NEXT:    drop
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    # fallthrough-return
+  %t0 = call {i32, i32, i32} @op_0_to_3()
+  %t1 = extractvalue {i32, i32, i32} %t0, 2
+  %t2 = extractvalue {i32, i32, i32} %t0, 1
+  call void @op_2_to_0(i32 %t1, i32 %t2)
+  %t3 = extractvalue {i32, i32, i32} %t0, 1
+  %t4 = extractvalue {i32, i32, i32} %t0, 2
+  call void @op_2_to_0(i32 %t3, i32 %t4)
+  ret void
+}
+
+define void @f419() {
+; CHECK-LABEL: f419:
+; CHECK:         .functype f419 () -> ()
+; CHECK-NEXT:    .local i32, i32, i32
+; CHECK-NEXT:  # %bb.0:
+; CHECK-NEXT:    call op_0_to_3
+; CHECK-NEXT:    local.set 2
+; CHECK-NEXT:    local.set 1
+; CHECK-NEXT:    local.set 0
+; CHECK-NEXT:    local.get 2
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    local.get 2
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    # fallthrough-return
+  %t0 = call {i32, i32, i32} @op_0_to_3()
+  %t1 = extractvalue {i32, i32, i32} %t0, 2
+  %t2 = extractvalue {i32, i32, i32} %t0, 1
+  call void @op_2_to_0(i32 %t1, i32 %t2)
+  %t3 = extractvalue {i32, i32, i32} %t0, 2
+  %t4 = extractvalue {i32, i32, i32} %t0, 0
+  call void @op_2_to_0(i32 %t3, i32 %t4)
+  ret void
+}
+
+define void @f422() {
+; CHECK-LABEL: f422:
+; CHECK:         .functype f422 () -> ()
+; CHECK-NEXT:    .local i32, i32
+; CHECK-NEXT:  # %bb.0:
+; CHECK-NEXT:    call op_0_to_3
+; CHECK-NEXT:    local.set 1
+; CHECK-NEXT:    drop
+; CHECK-NEXT:    local.set 0
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    call op_1_to_0
+; CHECK-NEXT:    # fallthrough-return
+  %t0 = call {i32, i32, i32} @op_0_to_3()
+  %t1 = extractvalue {i32, i32, i32} %t0, 2
+  %t2 = extractvalue {i32, i32, i32} %t0, 2
+  call void @op_2_to_0(i32 %t1, i32 %t2)
+  %t3 = extractvalue {i32, i32, i32} %t0, 0
+  call void @op_1_to_0(i32 %t3)
+  ret void
+}
+
+define void @f423() {
+; CHECK-LABEL: f423:
+; CHECK:         .functype f423 () -> ()
+; CHECK-NEXT:    .local i32, i32
+; CHECK-NEXT:  # %bb.0:
+; CHECK-NEXT:    call op_0_to_3
+; CHECK-NEXT:    local.set 1
+; CHECK-NEXT:    local.set 0
+; CHECK-NEXT:    drop
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    call op_1_to_0
+; CHECK-NEXT:    # fallthrough-return
+  %t0 = call {i32, i32, i32} @op_0_to_3()
+  %t1 = extractvalue {i32, i32, i32} %t0, 2
+  %t2 = extractvalue {i32, i32, i32} %t0, 2
+  call void @op_2_to_0(i32 %t1, i32 %t2)
+  %t3 = extractvalue {i32, i32, i32} %t0, 1
+  call void @op_1_to_0(i32 %t3)
+  ret void
+}
+
+define void @f425() {
+; CHECK-LABEL: f425:
+; CHECK:         .functype f425 () -> ()
+; CHECK-NEXT:    .local i32, i32
+; CHECK-NEXT:  # %bb.0:
+; CHECK-NEXT:    call op_0_to_3
+; CHECK-NEXT:    local.set 1
+; CHECK-NEXT:    drop
+; CHECK-NEXT:    local.set 0
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    # fallthrough-return
+  %t0 = call {i32, i32, i32} @op_0_to_3()
+  %t1 = extractvalue {i32, i32, i32} %t0, 2
+  %t2 = extractvalue {i32, i32, i32} %t0, 2
+  call void @op_2_to_0(i32 %t1, i32 %t2)
+  %t3 = extractvalue {i32, i32, i32} %t0, 0
+  %t4 = extractvalue {i32, i32, i32} %t0, 0
+  call void @op_2_to_0(i32 %t3, i32 %t4)
+  ret void
+}
+
+define void @f426() {
+; CHECK-LABEL: f426:
+; CHECK:         .functype f426 () -> ()
+; CHECK-NEXT:    .local i32, i32, i32
+; CHECK-NEXT:  # %bb.0:
+; CHECK-NEXT:    call op_0_to_3
+; CHECK-NEXT:    local.set 2
+; CHECK-NEXT:    local.set 1
+; CHECK-NEXT:    local.set 0
+; CHECK-NEXT:    local.get 2
+; CHECK-NEXT:    local.get 2
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    # fallthrough-return
+  %t0 = call {i32, i32, i32} @op_0_to_3()
+  %t1 = extractvalue {i32, i32, i32} %t0, 2
+  %t2 = extractvalue {i32, i32, i32} %t0, 2
+  call void @op_2_to_0(i32 %t1, i32 %t2)
+  %t3 = extractvalue {i32, i32, i32} %t0, 0
+  %t4 = extractvalue {i32, i32, i32} %t0, 1
+  call void @op_2_to_0(i32 %t3, i32 %t4)
+  ret void
+}
+
+define void @f428() {
+; CHECK-LABEL: f428:
+; CHECK:         .functype f428 () -> ()
+; CHECK-NEXT:    .local i32, i32, i32
+; CHECK-NEXT:  # %bb.0:
+; CHECK-NEXT:    call op_0_to_3
+; CHECK-NEXT:    local.set 2
+; CHECK-NEXT:    local.set 1
+; CHECK-NEXT:    local.set 0
+; CHECK-NEXT:    local.get 2
+; CHECK-NEXT:    local.get 2
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    # fallthrough-return
+  %t0 = call {i32, i32, i32} @op_0_to_3()
+  %t1 = extractvalue {i32, i32, i32} %t0, 2
+  %t2 = extractvalue {i32, i32, i32} %t0, 2
+  call void @op_2_to_0(i32 %t1, i32 %t2)
+  %t3 = extractvalue {i32, i32, i32} %t0, 1
+  %t4 = extractvalue {i32, i32, i32} %t0, 0
+  call void @op_2_to_0(i32 %t3, i32 %t4)
+  ret void
+}
+
+define void @f429() {
+; CHECK-LABEL: f429:
+; CHECK:         .functype f429 () -> ()
+; CHECK-NEXT:    .local i32, i32
+; CHECK-NEXT:  # %bb.0:
+; CHECK-NEXT:    call op_0_to_3
+; CHECK-NEXT:    local.set 1
+; CHECK-NEXT:    local.set 0
+; CHECK-NEXT:    drop
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    # fallthrough-return
+  %t0 = call {i32, i32, i32} @op_0_to_3()
+  %t1 = extractvalue {i32, i32, i32} %t0, 2
+  %t2 = extractvalue {i32, i32, i32} %t0, 2
+  call void @op_2_to_0(i32 %t1, i32 %t2)
+  %t3 = extractvalue {i32, i32, i32} %t0, 1
+  %t4 = extractvalue {i32, i32, i32} %t0, 1
+  call void @op_2_to_0(i32 %t3, i32 %t4)
+  ret void
+}
+
diff --git a/llvm/test/CodeGen/WebAssembly/multivalue-stackify.py b/llvm/test/CodeGen/WebAssembly/multivalue-stackify.py
new file mode 100755
--- /dev/null
+++ b/llvm/test/CodeGen/WebAssembly/multivalue-stackify.py
@@ -0,0 +1,214 @@
+#!/usr/bin/env python3
+
+"""A test case generator for register stackification.
+
+This script exhaustively generates small linear SSA programs, then filters them
+based on heuristics designed to keep interesting multivalue test cases and
+prints them as LLVM IR functions in a FileCheck test file.
+
+The output of this script is meant to be used in conjunction with
+update_llc_test_checks.py.
+
+Programs are represented internally as lists of operations, where each operation
+is a pair of tuples, the first of which specifies the operation's uses and the
+second of which specifies its defs.
+
+TODO: Before embarking on a rewrite of the register stackifier, an abstract
+interpreter should be written to automatically check that the test assertions
+generated by update_llc_test_checks.py have the same semantics as the functions
+generated by this script. Once that is done, exhaustive testing can be done by
+making `is_interesting` return True.
+"""
+
+
+from itertools import product
+from collections import deque
+
+
+MAX_OPS = 4
+MAX_DEFS = 3
+MAX_USES = 2
+
+
+def get_num_defs(program):
+  num_defs = 0
+  for _, defs in program:
+    num_defs += len(defs)
+  return num_defs
+
+
+def possible_ops(program):
+  program_defs = get_num_defs(program)
+  for num_defs in range(MAX_DEFS - program_defs + 1):
+    for num_uses in range(MAX_USES + 1):
+      if num_defs == 0 and num_uses == 0:
+        continue
+      for uses in product(range(program_defs), repeat=num_uses):
+        yield uses, tuple(program_defs + i for i in range(num_defs))
+
+
+def generate_programs():
+  queue = deque()
+  queue.append([])
+  program_id = 0
+  while True:
+    program = queue.popleft()
+    if len(program) == MAX_OPS:
+      break
+    for op in possible_ops(program):
+      program_id += 1
+      new_program = program + [op]
+      queue.append(new_program)
+      yield program_id, new_program
+
+
+def get_num_terminal_ops(program):
+  num_terminal_ops = 0
+  for _, defs in program:
+    if len(defs) == 0:
+      num_terminal_ops += 1
+  return num_terminal_ops
+
+
+def get_max_uses(program):
+  num_uses = [0] * MAX_DEFS
+  for uses, _ in program:
+    for u in uses:
+      num_uses[u] += 1
+  return max(num_uses)
+
+
+def has_unused_op(program):
+  used = [False] * MAX_DEFS
+  for uses, defs in program[::-1]:
+    if defs and all(not used[d] for d in defs):
+      return True
+    for u in uses:
+      used[u] = True
+  return False
+
+
+def has_multivalue_use(program):
+  is_multi = [False] * MAX_DEFS
+  for uses, defs in program:
+    if any(is_multi[u] for u in uses):
+      return True
+    if len(defs) >= 2:
+      for d in defs:
+        is_multi[d] = True
+  return False
+
+
+def has_mvp_use(program):
+  is_mvp = [False] * MAX_DEFS
+  for uses, defs in program:
+    if uses and all(is_mvp[u] for u in uses):
+      return True
+    if len(defs) <= 1:
+      if any(is_mvp[u] for u in uses):
+        return True
+      for d in defs:
+        is_mvp[d] = True
+  return False
+
+
+def is_interesting(program):
+  # Allow only multivalue single-op programs
+  if len(program) == 1:
+    return len(program[0][1]) > 1
+
+  # Reject programs where the last two instructions are identical
+  if len(program) >= 2 and program[-1][0] == program[-2][0]:
+    return False
+
+  # Reject programs with too many ops that don't produce values
+  if get_num_terminal_ops(program) > 2:
+    return False
+
+  # The third use of a value is no more interesting than the second
+  if get_max_uses(program) >= 3:
+    return False
+
+  # Reject nontrivial programs that have unused instructions
+  if has_unused_op(program):
+    return False
+
+  # Reject programs that have boring MVP uses of MVP defs
+  if has_mvp_use(program):
+    return False
+
+  # Otherwise if it has multivalue usage it is interesting
+  return has_multivalue_use(program)
+
+
+def make_llvm_type(num_defs):
+  if num_defs == 0:
+    return 'void'
+  else:
+    return '{' + ', '.join(['i32'] * num_defs) + '}'
+
+
+def make_llvm_op_name(num_uses, num_defs):
+  return f'op_{num_uses}_to_{num_defs}'
+
+
+def make_llvm_args(first_use, num_uses):
+  return ', '.join([f'i32 %t{first_use + i}' for i in range(num_uses)])
+
+
+def print_llvm_program(program, name):
+  tmp = 0
+  def_data = []
+  print(f'define void @{name}() {{')
+  for uses, defs in program:
+    first_arg = tmp
+    # Extract operands
+    for use in uses:
+      ret_type, var, idx = def_data[use]
+      print(f'  %t{tmp} = extractvalue {ret_type} %t{var}, {idx}')
+      tmp += 1
+    # Print instruction
+    assignment = ''
+    if len(defs) > 0:
+      assignment = f'%t{tmp} = '
+      result_var = tmp
+      tmp += 1
+    ret_type = make_llvm_type(len(defs))
+    op_name = make_llvm_op_name(len(uses), len(defs))
+    args = make_llvm_args(first_arg, len(uses))
+    print(f'  {assignment}call {ret_type} @{op_name}({args})')
+    # Update def_data
+    for i in range(len(defs)):
+      def_data.append((ret_type, result_var, i))
+  print('  ret void')
+  print('}')
+
+
+def print_header():
+  print('; NOTE: Test functions have been generated by multivalue-stackify.py.')
+  print()
+  print('; RUN: llc < %s -verify-machineinstrs -mattr=+multivalue',
+        '| FileCheck %s')
+  print()
+  print('; Test that the multivalue stackification works')
+  print()
+  print('target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"')
+  print('target triple = "wasm32-unknown-unknown"')
+  print()
+  for num_uses in range(MAX_USES + 1):
+    for num_defs in range(MAX_DEFS + 1):
+      if num_uses == 0 and num_defs == 0:
+        continue
+      ret_type = make_llvm_type(num_defs)
+      op_name = make_llvm_op_name(num_uses, num_defs)
+      args = make_llvm_args(0, num_uses)
+      print(f'declare {ret_type} @{op_name}({args})')
+  print()
+
+
+if __name__ == '__main__':
+  print_header()
+  for i, program in generate_programs():
+    if is_interesting(program):
+      print_llvm_program(program, 'f' + str(i))
+      print()
diff --git a/llvm/test/CodeGen/WebAssembly/multivalue.ll b/llvm/test/CodeGen/WebAssembly/multivalue.ll
--- a/llvm/test/CodeGen/WebAssembly/multivalue.ll
+++ b/llvm/test/CodeGen/WebAssembly/multivalue.ll
@@ -1,4 +1,4 @@
-; RUN: llc < %s -asm-verbose=false -verify-machineinstrs -disable-wasm-fallthrough-return-opt -wasm-disable-explicit-locals -wasm-keep-registers -mattr=+multivalue,+tail-call | FileCheck %s
+; RUN: llc < %s -asm-verbose=false -verify-machineinstrs -mattr=+multivalue,+tail-call | FileCheck %s
 ; RUN: llc < %s --filetype=obj -mattr=+multivalue,+tail-call | obj2yaml | FileCheck %s --check-prefix OBJ
 
 ; Test that the multivalue calls, returns, function types, and block
@@ -8,107 +8,207 @@
 target triple = "wasm32-unknown-unknown"
 
 %pair = type { i32, i64 }
-%packed_pair = type <{ i32, i64 }>
+%rpair = type { i64, i32 }
+
+declare void @use_i32(i32)
+declare void @use_i64(i64)
 
 
 ; CHECK-LABEL: pair_const:
 ; CHECK-NEXT: .functype pair_const () -> (i32, i64)
-; CHECK-NEXT: i32.const $push[[L0:[0-9]+]]=, 42{{$}}
-; CHECK-NEXT: i64.const $push[[L1:[0-9]+]]=, 42{{$}}
-; CHECK-NEXT: return $pop[[L0]], $pop[[L1]]{{$}}
+; CHECK-NEXT: i32.const 42{{$}}
+; CHECK-NEXT: i64.const 42{{$}}
+; CHECK-NEXT: end_function{{$}}
 define %pair @pair_const() {
   ret %pair { i32 42, i64 42 }
 }
 
-; CHECK-LABEL: packed_pair_const:
-; CHECK-NEXT: .functype packed_pair_const () -> (i32, i64)
-; CHECK-NEXT: i32.const $push[[L0:[0-9]+]]=, 42{{$}}
-; CHECK-NEXT: i64.const $push[[L1:[0-9]+]]=, 42{{$}}
-; CHECK-NEXT: return $pop[[L0]], $pop[[L1]]{{$}}
-define %packed_pair @packed_pair_const() {
-  ret %packed_pair <{ i32 42, i64 42 }>
-}
-
 ; CHECK-LABEL: pair_ident:
 ; CHECK-NEXT: .functype pair_ident (i32, i64) -> (i32, i64)
-; CHECK-NEXT: return $0, $1{{$}}
+; CHECK-NEXT: local.get 0{{$}}
+; CHECK-NEXT: local.get 1{{$}}
+; CHECK-NEXT: end_function{{$}}
 define %pair @pair_ident(%pair %p) {
   ret %pair %p
 }
 
-; CHECK-LABEL: packed_pair_ident:
-; CHECK-NEXT: .functype packed_pair_ident (i32, i64) -> (i32, i64)
-; CHECK-NEXT: return $0, $1{{$}}
-define %packed_pair @packed_pair_ident(%packed_pair %p) {
-  ret %packed_pair %p
-}
-
-;; TODO: Multivalue calls are a WIP and do not necessarily produce
-;; correct output. For now, just check that they don't cause any
-;; crashes.
-
+; CHECK-LABEL: pair_call:
+; CHECK-NEXT: .functype pair_call () -> ()
+; CHECK-NEXT: call pair_const{{$}}
+; CHECK-NEXT: drop{{$}}
+; CHECK-NEXT: drop{{$}}
+; CHECK-NEXT: end_function{{$}}
 define void @pair_call() {
   %p = call %pair @pair_const()
   ret void
 }
 
-define void @packed_pair_call() {
-  %p = call %packed_pair @packed_pair_const()
-  ret void
-}
-
+; CHECK-LABEL: pair_call_return:
+; CHECK-NEXT: .functype pair_call_return () -> (i32, i64)
+; CHECK-NEXT: call pair_const{{$}}
+; CHECK-NEXT: end_function{{$}}
 define %pair @pair_call_return() {
   %p = call %pair @pair_const()
   ret %pair %p
 }
 
-define %packed_pair @packed_pair_call_return() {
-  %p = call %packed_pair @packed_pair_const()
-  ret %packed_pair %p
-}
-
+; CHECK-LABEL: pair_tail_call:
+; CHECK-NEXT: .functype pair_tail_call () -> (i32, i64)
+; CHECK-NEXT: return_call pair_const{{$}}
+; CHECK-NEXT: end_function{{$}}
 define %pair @pair_tail_call() {
   %p = musttail call %pair @pair_const()
   ret %pair %p
 }
 
-define %packed_pair @packed_pair_tail_call() {
-  %p = musttail call %packed_pair @packed_pair_const()
-  ret %packed_pair %p
-}
 
+; CHECK-LABEL: pair_call_return_first:
+; CHECK-NEXT: .functype pair_call_return_first () -> (i32)
+; CHECK-NEXT: call pair_const{{$}}
+; CHECK-NEXT: drop{{$}}
+; CHECK-NEXT: end_function{{$}}
 define i32 @pair_call_return_first() {
   %p = call %pair @pair_const()
   %v = extractvalue %pair %p, 0
   ret i32 %v
 }
 
-define i32 @packed_pair_call_return_first() {
-  %p = call %packed_pair @packed_pair_const()
-  %v = extractvalue %packed_pair %p, 0
-  ret i32 %v
-}
-
+; CHECK-LABEL: pair_call_return_second:
+; CHECK-NEXT: .functype pair_call_return_second () -> (i64)
+; CHECK-NEXT: .local i64{{$}}
+; CHECK-NEXT: call pair_const{{$}}
+; CHECK-NEXT: local.set 0{{$}}
+; CHECK-NEXT: drop{{$}}
+; CHECK-NEXT: local.get 0{{$}}
+; CHECK-NEXT: end_function{{$}}
 define i64 @pair_call_return_second() {
   %p = call %pair @pair_const()
   %v = extractvalue %pair %p, 1
   ret i64 %v
 }
 
-define i64 @packed_pair_call_return_second() {
-  %p = call %packed_pair @packed_pair_const()
-  %v = extractvalue %packed_pair %p, 1
-  ret i64 %v
+; CHECK-LABEL: pair_call_use_first:
+; CHECK-NEXT: .functype pair_call_use_first () -> ()
+; CHECK-NEXT: call pair_const{{$}}
+; CHECK-NEXT: drop{{$}}
+; CHECK-NEXT: call use_i32{{$}}
+; CHECK-NEXT: end_function{{$}}
+define void @pair_call_use_first() {
+  %p = call %pair @pair_const()
+  %v = extractvalue %pair %p, 0
+  call void @use_i32(i32 %v)
+  ret void
 }
 
+; CHECK-LABEL: pair_call_use_second:
+; CHECK-NEXT: .functype pair_call_use_second () -> ()
+; CHECK-NEXT: .local i64
+; CHECK-NEXT: call pair_const{{$}}
+; CHECK-NEXT: local.set 0{{$}}
+; CHECK-NEXT: drop{{$}}
+; CHECK-NEXT: local.get 0{{$}}
+; CHECK-NEXT: call use_i64{{$}}
+; CHECK-NEXT: end_function{{$}}
+define void @pair_call_use_second() {
+  %p = call %pair @pair_const()
+  %v = extractvalue %pair %p, 1
+  call void @use_i64(i64 %v)
+  ret void
+}
+
+; CHECK-LABEL: pair_call_use_first_return_second:
+; CHECK-NEXT: .functype pair_call_use_first_return_second () -> (i64)
+; CHECK-NEXT: .local i64{{$}}
+; CHECK-NEXT: call pair_const{{$}}
+; CHECK-NEXT: local.set 0{{$}}
+; CHECK-NEXT: call use_i32{{$}}
+; CHECK-NEXT: local.get 0{{$}}
+; CHECK-NEXT: end_function{{$}}
+define i64 @pair_call_use_first_return_second() {
+  %p = call %pair @pair_const()
+  %v = extractvalue %pair %p, 0
+  call void @use_i32(i32 %v)
+  %r = extractvalue %pair %p, 1
+  ret i64 %r
+}
+
+; CHECK-LABEL: pair_call_use_second_return_first:
+; CHECK-NEXT: .functype pair_call_use_second_return_first () -> (i32)
+; CHECK-NEXT: .local i32, i64{{$}}
+; CHECK-NEXT: call pair_const{{$}}
+; CHECK-NEXT: local.set 1{{$}}
+; CHECK-NEXT: local.set 0{{$}}
+; CHECK-NEXT: local.get 1{{$}}
+; CHECK-NEXT: call use_i64{{$}}
+; CHECK-NEXT: local.get 0{{$}}
+; CHECK-NEXT: end_function{{$}}
+define i32 @pair_call_use_second_return_first() {
+  %p = call %pair @pair_const()
+  %v = extractvalue %pair %p, 1
+  call void @use_i64(i64 %v)
+  %r = extractvalue %pair %p, 0
+  ret i32 %r
+}
+
+; CHECK-LABEL: pair_pass_through:
+; CHECK-NEXT: .functype pair_pass_through (i32, i64) -> (i32, i64)
+; CHECK-NEXT: local.get 0
+; CHECK-NEXT: local.get 1
+; CHECK-NEXT: call pair_ident{{$}}
+; CHECK-NEXT: end_function{{$}}
 define %pair @pair_pass_through(%pair %p) {
   %r = call %pair @pair_ident(%pair %p)
   ret %pair %r
 }
 
-define %packed_pair @packed_pair_pass_through(%packed_pair %p) {
-  %r = call %packed_pair @packed_pair_ident(%packed_pair %p)
-  ret %packed_pair %r
+; CHECK-LABEL: pair_swap:
+; CHECK-NEXT: .functype pair_swap (i32, i64) -> (i64, i32)
+; CHECK-NEXT: local.get 1{{$}}
+; CHECK-NEXT: local.get 0{{$}}
+; CHECK-NEXT: end_function{{$}}
+define %rpair @pair_swap(%pair %p) {
+  %first = extractvalue %pair %p, 0
+  %second = extractvalue %pair %p, 1
+  %r1 = insertvalue %rpair undef, i32 %first, 1
+  %r2 = insertvalue %rpair %r1, i64 %second, 0
+  ret %rpair %r2
+}
+
+; CHECK-LABEL: pair_call_swap:
+; CHECK-NEXT: .functype pair_call_swap () -> (i64, i32)
+; CHECK-NEXT: .local i32, i64{{$}}
+; CHECK-NEXT: call pair_const{{$}}
+; CHECK-NEXT: local.set 1{{$}}
+; CHECK-NEXT: local.set 0{{$}}
+; CHECK-NEXT: local.get 1{{$}}
+; CHECK-NEXT: local.get 0{{$}}
+; CHECK-NEXT: end_function{{$}}
+define %rpair @pair_call_swap() {
+  %p = call %pair @pair_const()
+  %first = extractvalue %pair %p, 0
+  %second = extractvalue %pair %p, 1
+  %r1 = insertvalue %rpair undef, i32 %first, 1
+  %r2 = insertvalue %rpair %r1, i64 %second, 0
+  ret %rpair %r2
+}
+
+; CHECK-LABEL: pair_pass_through_swap:
+; CHECK-NEXT: .functype pair_pass_through_swap (i32, i64) -> (i64, i32)
+; CHECK-NEXT: local.get 0{{$}}
+; CHECK-NEXT: local.get 1{{$}}
+; CHECK-NEXT: call pair_ident{{$}}
+; CHECK-NEXT: local.set 1{{$}}
+; CHECK-NEXT: local.set 0{{$}}
+; CHECK-NEXT: local.get 1{{$}}
+; CHECK-NEXT: local.get 0{{$}}
+; CHECK-NEXT: end_function{{$}}
+define %rpair @pair_pass_through_swap(%pair %p) {
+  %p1 = call %pair @pair_ident(%pair %p)
+  %first = extractvalue %pair %p1, 0
+  %second = extractvalue %pair %p1, 1
+  %r1 = insertvalue %rpair undef, i32 %first, 1
+  %r2 = insertvalue %rpair %r1, i64 %second, 0
+  ret %rpair %r2
 }
 
 ; CHECK-LABEL: minimal_loop:
@@ -118,6 +218,7 @@
 ; CHECK-NEXT: br 0{{$}}
 ; CHECK-NEXT: .LBB{{[0-9]+}}_2:
 ; CHECK-NEXT: end_loop{{$}}
+; CHECK-NEXT: end_function{{$}}
 define %pair @minimal_loop(i32* %p) {
 entry:
   br label %loop
@@ -162,6 +263,26 @@
 ; OBJ-NEXT:       - Index:           5
 ; OBJ-NEXT:         ParamTypes:
 ; OBJ-NEXT:           - I32
+; OBJ-NEXT:         ReturnTypes:     []
+; OBJ-NEXT:       - Index:           6
+; OBJ-NEXT:         ParamTypes:
+; OBJ-NEXT:           - I64
+; OBJ-NEXT:         ReturnTypes:     []
+; OBJ-NEXT:       - Index:           7
+; OBJ-NEXT:         ParamTypes:
+; OBJ-NEXT:           - I32
+; OBJ-NEXT:           - I64
+; OBJ-NEXT:         ReturnTypes:
+; OBJ-NEXT:           - I64
+; OBJ-NEXT:           - I32
+; OBJ-NEXT:       - Index:           8
+; OBJ-NEXT:         ParamTypes:      []
+; OBJ-NEXT:         ReturnTypes:
+; OBJ-NEXT:           - I64
+; OBJ-NEXT:           - I32
+; OBJ-NEXT:       - Index:           9
+; OBJ-NEXT:         ParamTypes:
+; OBJ-NEXT:           - I32
 ; OBJ-NEXT:         ReturnTypes:
 ; OBJ-NEXT:           - I32
 ; OBJ-NEXT:           - I64