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 @@ -178,11 +178,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()) @@ -243,6 +250,12 @@ if (MI.isDebugInstr() || MI.isLabel()) continue; + if (MI.getOpcode() == WebAssembly::IMPLICIT_DEF) { + MI.eraseFromParent(); + Changed = true; + continue; + } + // Replace tee instructions with local.tee. The difference is that tee // instructions have two defs, while local.tee instructions have one def // and an index of a local to write to. @@ -279,20 +292,13 @@ 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. + for (auto &Def : MI.defs()) { + Register OldReg = Def.getReg(); if (!MFI.isVRegStackified(OldReg)) { const TargetRegisterClass *RC = MRI.getRegClass(OldReg); Register NewReg = MRI.createVirtualRegister(RC); auto InsertPt = std::next(MI.getIterator()); - if (MI.getOpcode() == WebAssembly::IMPLICIT_DEF) { - MI.eraseFromParent(); - Changed = true; - continue; - } if (UseEmpty[Register::virtReg2Index(OldReg)]) { unsigned Opc = getDropOpcode(RC); MachineInstr *Drop = @@ -310,11 +316,11 @@ .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; } 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 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 ExplicitLocals will not know how 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 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,34 +878,38 @@ // 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); + if (CanMove && hasOneUse(Reg, DefI, MRI, MDT, LIS)) { + Insert = moveForSingleUse(Reg, Use, DefI, MBB, Insert, LIS, MFI, MRI); // If we are removing the frame base reg completely, remove the debug // info as well. // TODO: Encode this properly as a stackified value. if (MFI.isFrameBaseVirtual() && MFI.getFrameBaseVreg() == Reg) MFI.clearFrameBaseVreg(); - } else if (shouldRematerialize(*Def, AA, TII)) { + } 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 @@ -880,6 +920,25 @@ continue; } + // Stackifying a multivalue def may unlock in-place stackification of + // subsequent defs. TODO: Handle the case where the consecutive uses are + // not all in the same instruction. + 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. @@ -917,18 +976,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,219 @@ +#!/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. + + ``` + ./multivalue-stackify.py > multivalue-stackify.ll + ../../../utils/update_llc_test_checks.py multivalue-stackify.ll + ``` + +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_PROGRAM_OPS = 4 +MAX_PROGRAM_DEFS = 3 +MAX_OP_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_PROGRAM_DEFS - program_defs + 1): + for num_uses in range(MAX_OP_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_PROGRAM_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_PROGRAM_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_PROGRAM_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_PROGRAM_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_PROGRAM_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_OP_USES + 1): + for num_defs in range(MAX_PROGRAM_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 @@ -8,6 +8,10 @@ target triple = "wasm32-unknown-unknown" %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) @@ -27,12 +31,12 @@ ret %pair %p } -;; TODO: Multivalue calls are a WIP and the following test cases do -;; not necessarily produce correct output. For now just check that -;; they do not crash. - ; 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 @@ -40,6 +44,8 @@ ; 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 @@ -47,7 +53,9 @@ ; CHECK-LABEL: pair_call_indirect: ; CHECK-NEXT: .functype pair_call_indirect (i32) -> (i32, i64) -; CHECK: call_indirect () -> (i32, i64){{$}} +; CHECK-NEXT: local.get 0{{$}} +; CHECK-NEXT: call_indirect () -> (i32, i64){{$}} +; CHECK-NEXT: end_function{{$}} define %pair @pair_call_indirect(%pair()* %f) { %p = call %pair %f() ret %pair %p @@ -55,6 +63,8 @@ ; 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 @@ -62,6 +72,9 @@ ; 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 @@ -70,20 +83,142 @@ ; 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 } +; 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 } +; 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: ; CHECK-NEXT: .functype minimal_loop (i32) -> (i32, i64) ; CHECK-NEXT: .LBB{{[0-9]+}}_1: @@ -91,6 +226,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 @@ -138,3 +274,23 @@ ; OBJ-NEXT: ParamTypes: [] ; OBJ-NEXT: ReturnTypes: ; OBJ-NEXT: - I64 +; OBJ-NEXT: - Index: 6 +; OBJ-NEXT: ParamTypes: +; OBJ-NEXT: - I32 +; OBJ-NEXT: ReturnTypes: [] +; OBJ-NEXT: - Index: 7 +; OBJ-NEXT: ParamTypes: +; OBJ-NEXT: - I64 +; OBJ-NEXT: ReturnTypes: [] +; OBJ-NEXT: - Index: 8 +; OBJ-NEXT: ParamTypes: +; OBJ-NEXT: - I32 +; OBJ-NEXT: - I64 +; OBJ-NEXT: ReturnTypes: +; OBJ-NEXT: - I64 +; OBJ-NEXT: - I32 +; OBJ-NEXT: - Index: 9 +; OBJ-NEXT: ParamTypes: [] +; OBJ-NEXT: ReturnTypes: +; OBJ-NEXT: - I64 +; OBJ-NEXT: - I32