[llvm] 7b64a59 - Reland "[WebAssembly][InstrEmitter] Foundation for multivalue call lowering"

Thomas Lively via llvm-commits llvm-commits at lists.llvm.org
Tue Feb 18 13:49:52 PST 2020


Author: Thomas Lively
Date: 2020-02-18T13:49:46-08:00
New Revision: 7b64a590600645234412bce8cb6becf45f276de4

URL: https://github.com/llvm/llvm-project/commit/7b64a590600645234412bce8cb6becf45f276de4
DIFF: https://github.com/llvm/llvm-project/commit/7b64a590600645234412bce8cb6becf45f276de4.diff

LOG: Reland "[WebAssembly][InstrEmitter] Foundation for multivalue call lowering"

This reverts commit 649aba93a27170cb03a4b17c98a19b9237a880b8, now that
the approach started there has been shown to be workable in the patch
series culminating in https://reviews.llvm.org/D74192.

Added: 
    

Modified: 
    llvm/include/llvm/Target/TargetMachine.h
    llvm/lib/CodeGen/PrologEpilogInserter.cpp
    llvm/lib/CodeGen/SelectionDAG/InstrEmitter.cpp
    llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.h
    llvm/lib/Target/WebAssembly/WebAssemblyISD.def
    llvm/lib/Target/WebAssembly/WebAssemblyISelDAGToDAG.cpp
    llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp
    llvm/lib/Target/WebAssembly/WebAssemblyInstrCall.td
    llvm/lib/Target/WebAssembly/WebAssemblyRegStackify.cpp
    llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.h
    llvm/lib/Target/WebAssembly/WebAssemblyUtilities.cpp
    llvm/lib/Target/WebAssembly/WebAssemblyUtilities.h
    llvm/test/CodeGen/WebAssembly/multivalue.ll

Removed: 
    


################################################################################
diff  --git a/llvm/include/llvm/Target/TargetMachine.h b/llvm/include/llvm/Target/TargetMachine.h
index 176ae39b17a7..67afbee4681f 100644
--- a/llvm/include/llvm/Target/TargetMachine.h
+++ b/llvm/include/llvm/Target/TargetMachine.h
@@ -361,11 +361,13 @@ class LLVMTargetMachine : public TargetMachine {
                      raw_pwrite_stream *DwoOut, CodeGenFileType FileType,
                      MCContext &Context);
 
-  /// True if the target uses physical regs at Prolog/Epilog insertion
-  /// time. If true (most machines), all vregs must be allocated before
-  /// PEI. If false (virtual-register machines), then callee-save register
-  /// spilling and scavenging are not needed or used.
-  virtual bool usesPhysRegsForPEI() const { return true; }
+  /// True if the target uses physical regs (as nearly all targets do). False
+  /// for stack machines such as WebAssembly and other virtual-register
+  /// machines. If true, all vregs must be allocated before PEI. If false, then
+  /// callee-save register spilling and scavenging are not needed or used. If
+  /// false, implicitly defined registers will still be assumed to be physical
+  /// registers, except that variadic defs will be allocated vregs.
+  virtual bool usesPhysRegsForValues() const { return true; }
 
   /// True if the target wants to use interprocedural register allocation by
   /// default. The -enable-ipra flag can be used to override this.

diff  --git a/llvm/lib/CodeGen/PrologEpilogInserter.cpp b/llvm/lib/CodeGen/PrologEpilogInserter.cpp
index d583643ac68f..889d0261c7ce 100644
--- a/llvm/lib/CodeGen/PrologEpilogInserter.cpp
+++ b/llvm/lib/CodeGen/PrologEpilogInserter.cpp
@@ -237,7 +237,7 @@ bool PEI::runOnMachineFunction(MachineFunction &MF) {
     stashEntryDbgValues(*SaveBlock, EntryDbgValues);
 
   // Handle CSR spilling and restoring, for targets that need it.
-  if (MF.getTarget().usesPhysRegsForPEI())
+  if (MF.getTarget().usesPhysRegsForValues())
     spillCalleeSavedRegs(MF);
 
   // Allow the target machine to make final modifications to the function

diff  --git a/llvm/lib/CodeGen/SelectionDAG/InstrEmitter.cpp b/llvm/lib/CodeGen/SelectionDAG/InstrEmitter.cpp
index 176d71643e1a..95b97872398c 100644
--- a/llvm/lib/CodeGen/SelectionDAG/InstrEmitter.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/InstrEmitter.cpp
@@ -195,7 +195,10 @@ void InstrEmitter::CreateVirtualRegisters(SDNode *Node,
          "IMPLICIT_DEF should have been handled as a special case elsewhere!");
 
   unsigned NumResults = CountResults(Node);
-  for (unsigned i = 0; i < II.getNumDefs(); ++i) {
+  bool HasVRegVariadicDefs = !MF->getTarget().usesPhysRegsForValues() &&
+                             II.isVariadic() && II.variadicOpsAreDefs();
+  unsigned NumVRegs = HasVRegVariadicDefs ? NumResults : II.getNumDefs();
+  for (unsigned i = 0; i < NumVRegs; ++i) {
     // If the specific node value is only used by a CopyToReg and the dest reg
     // is a vreg in the same register class, use the CopyToReg'd destination
     // register instead of creating a new vreg.
@@ -216,7 +219,7 @@ void InstrEmitter::CreateVirtualRegisters(SDNode *Node,
         RC = VTRC;
     }
 
-    if (II.OpInfo[i].isOptionalDef()) {
+    if (II.OpInfo != nullptr && II.OpInfo[i].isOptionalDef()) {
       // Optional def must be a physical register.
       VRBase = cast<RegisterSDNode>(Node->getOperand(i-NumResults))->getReg();
       assert(Register::isPhysicalRegister(VRBase));
@@ -829,7 +832,10 @@ EmitMachineNode(SDNode *Node, bool IsClone, bool IsCloned,
   unsigned NumImpUses = 0;
   unsigned NodeOperands =
     countOperands(Node, II.getNumOperands() - NumDefs, NumImpUses);
-  bool HasPhysRegOuts = NumResults > NumDefs && II.getImplicitDefs()!=nullptr;
+  bool HasVRegVariadicDefs = !MF->getTarget().usesPhysRegsForValues() &&
+                             II.isVariadic() && II.variadicOpsAreDefs();
+  bool HasPhysRegOuts = NumResults > NumDefs &&
+                        II.getImplicitDefs() != nullptr && !HasVRegVariadicDefs;
 #ifndef NDEBUG
   unsigned NumMIOperands = NodeOperands + NumResults;
   if (II.isVariadic())

diff  --git a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.h b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.h
index b339860a381d..f7d1aeb9ec68 100644
--- a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.h
+++ b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.h
@@ -509,8 +509,8 @@ inline bool isCallIndirect(unsigned Opc) {
 
 /// Returns the operand number of a callee, assuming the argument is a call
 /// instruction.
-inline unsigned getCalleeOpNo(unsigned Opc) {
-  switch (Opc) {
+inline const MachineOperand &getCalleeOp(const MachineInstr &MI) {
+  switch (MI.getOpcode()) {
   case WebAssembly::CALL_VOID:
   case WebAssembly::CALL_VOID_S:
   case WebAssembly::CALL_INDIRECT_VOID:
@@ -519,7 +519,7 @@ inline unsigned getCalleeOpNo(unsigned Opc) {
   case WebAssembly::RET_CALL_S:
   case WebAssembly::RET_CALL_INDIRECT:
   case WebAssembly::RET_CALL_INDIRECT_S:
-    return 0;
+    return MI.getOperand(0);
   case WebAssembly::CALL_i32:
   case WebAssembly::CALL_i32_S:
   case WebAssembly::CALL_i64:
@@ -564,7 +564,10 @@ inline unsigned getCalleeOpNo(unsigned Opc) {
   case WebAssembly::CALL_INDIRECT_v2f64_S:
   case WebAssembly::CALL_INDIRECT_exnref:
   case WebAssembly::CALL_INDIRECT_exnref_S:
-    return 1;
+    return MI.getOperand(1);
+  case WebAssembly::CALL:
+  case WebAssembly::CALL_S:
+    return MI.getOperand(MI.getNumDefs());
   default:
     llvm_unreachable("Not a call instruction");
   }

diff  --git a/llvm/lib/Target/WebAssembly/WebAssemblyISD.def b/llvm/lib/Target/WebAssembly/WebAssemblyISD.def
index ba04fd4eb9dd..f9cb87ff7da4 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyISD.def
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyISD.def
@@ -15,6 +15,7 @@
 
 HANDLE_NODETYPE(CALL1)
 HANDLE_NODETYPE(CALL0)
+HANDLE_NODETYPE(CALL)
 HANDLE_NODETYPE(RET_CALL)
 HANDLE_NODETYPE(RETURN)
 HANDLE_NODETYPE(ARGUMENT)

diff  --git a/llvm/lib/Target/WebAssembly/WebAssemblyISelDAGToDAG.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyISelDAGToDAG.cpp
index 531a07b829c8..a45104022d60 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyISelDAGToDAG.cpp
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyISelDAGToDAG.cpp
@@ -206,6 +206,25 @@ void WebAssemblyDAGToDAGISel::Select(SDNode *Node) {
     }
     break;
   }
+  case WebAssemblyISD::CALL: {
+    // CALL has both variable operands and variable results, but ISel only
+    // supports one or the other. Split calls into two nodes glued together, one
+    // for the operands and one for the results. These two nodes will be
+    // recombined in a custom inserter hook.
+    // TODO: Split CALL
+    SmallVector<SDValue, 16> Ops;
+    for (size_t i = 1; i < Node->getNumOperands(); ++i) {
+      SDValue Op = Node->getOperand(i);
+      if (Op->getOpcode() == WebAssemblyISD::Wrapper)
+        Op = Op->getOperand(0);
+      Ops.push_back(Op);
+    }
+    Ops.push_back(Node->getOperand(0));
+    MachineSDNode *Call =
+        CurDAG->getMachineNode(WebAssembly::CALL, DL, Node->getVTList(), Ops);
+    ReplaceNode(Node, Call);
+    return;
+  }
 
   default:
     break;

diff  --git a/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp
index 4d45fb95af20..da975c2d56ec 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp
@@ -699,9 +699,6 @@ WebAssemblyTargetLowering::LowerCall(CallLoweringInfo &CLI,
   }
 
   SmallVectorImpl<ISD::InputArg> &Ins = CLI.Ins;
-  if (Ins.size() > 1)
-    fail(DL, DAG, "WebAssembly doesn't support more than 1 returned value yet");
-
   SmallVectorImpl<ISD::OutputArg> &Outs = CLI.Outs;
   SmallVectorImpl<SDValue> &OutVals = CLI.OutVals;
 
@@ -847,18 +844,27 @@ WebAssemblyTargetLowering::LowerCall(CallLoweringInfo &CLI,
   }
 
   InTys.push_back(MVT::Other);
-  SDVTList InTyList = DAG.getVTList(InTys);
-  SDValue Res =
-      DAG.getNode(Ins.empty() ? WebAssemblyISD::CALL0 : WebAssemblyISD::CALL1,
-                  DL, InTyList, Ops);
-  if (Ins.empty()) {
-    Chain = Res;
-  } else {
-    InVals.push_back(Res);
-    Chain = Res.getValue(1);
+  unsigned Opc;
+  // TODO: Remove CALL0 and CALL1 in favor of CALL
+  switch (Ins.size()) {
+  case 0:
+    Opc = WebAssemblyISD::CALL0;
+    break;
+  case 1:
+    Opc = WebAssemblyISD::CALL1;
+    break;
+  default:
+    Opc = WebAssemblyISD::CALL;
+    break;
   }
+  SDVTList InTyList = DAG.getVTList(InTys);
+  SDValue Res = DAG.getNode(Opc, DL, InTyList, Ops);
 
-  return Chain;
+  for (size_t I = 0; I < Ins.size(); ++I)
+    InVals.push_back(Res.getValue(I));
+
+  // Return the chain
+  return Res.getValue(Ins.size());
 }
 
 bool WebAssemblyTargetLowering::CanLowerReturn(

diff  --git a/llvm/lib/Target/WebAssembly/WebAssemblyInstrCall.td b/llvm/lib/Target/WebAssembly/WebAssemblyInstrCall.td
index 20b74c6d72d2..1dfd75f346e0 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyInstrCall.td
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyInstrCall.td
@@ -55,6 +55,15 @@ multiclass CALL<ValueType vt, WebAssemblyRegClass rt, string prefix,
 }
 
 let Uses = [SP32, SP64], isCall = 1 in {
+
+// TODO: Split CALL into separate nodes for operands and results.
+// TODO: Add an indirect version of the variadic call, delete CALL_*
+let variadicOpsAreDefs = 1 in
+defm CALL :
+  I<(outs), (ins function32_op:$callee, variable_ops),
+    (outs), (ins function32_op:$callee), [],
+    "call    \t$callee", "call\t$callee", 0x10>;
+
 defm "" : CALL<i32, I32, "i32.">;
 defm "" : CALL<i64, I64, "i64.">;
 defm "" : CALL<f32, F32, "f32.">;
@@ -68,6 +77,7 @@ defm "" : CALL<v4f32, V128, "v128.", [HasSIMD128]>;
 defm "" : CALL<v2f64, V128, "v128.", [HasSIMD128]>;
 
 let IsCanonical = 1 in {
+
 defm CALL_VOID :
   I<(outs), (ins function32_op:$callee, variable_ops),
     (outs), (ins function32_op:$callee),

diff  --git a/llvm/lib/Target/WebAssembly/WebAssemblyRegStackify.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyRegStackify.cpp
index 7d54bf0dd05e..e49034467f73 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyRegStackify.cpp
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyRegStackify.cpp
@@ -135,12 +135,12 @@ static void convertImplicitDefToConstZero(MachineInstr *MI,
 // Determine whether a call to the callee referenced by
 // MI->getOperand(CalleeOpNo) reads memory, writes memory, and/or has side
 // effects.
-static void queryCallee(const MachineInstr &MI, unsigned CalleeOpNo, bool &Read,
-                        bool &Write, bool &Effects, bool &StackPointer) {
+static void queryCallee(const MachineInstr &MI, bool &Read, bool &Write,
+                        bool &Effects, bool &StackPointer) {
   // All calls can use the stack pointer.
   StackPointer = true;
 
-  const MachineOperand &MO = MI.getOperand(CalleeOpNo);
+  const MachineOperand &MO = WebAssembly::getCalleeOp(MI);
   if (MO.isGlobal()) {
     const Constant *GV = MO.getGlobal();
     if (const auto *GA = dyn_cast<GlobalAlias>(GV))
@@ -252,8 +252,7 @@ static void query(const MachineInstr &MI, AliasAnalysis &AA, bool &Read,
 
   // Analyze calls.
   if (MI.isCall()) {
-    unsigned CalleeOpNo = WebAssembly::getCalleeOpNo(MI.getOpcode());
-    queryCallee(MI, CalleeOpNo, Read, Write, Effects, StackPointer);
+    queryCallee(MI, Read, Write, Effects, StackPointer);
   }
 }
 

diff  --git a/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.h b/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.h
index 850e6b9a9e9e..dd5b39773313 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.h
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.h
@@ -47,7 +47,7 @@ class WebAssemblyTargetMachine final : public LLVMTargetMachine {
 
   TargetTransformInfo getTargetTransformInfo(const Function &F) override;
 
-  bool usesPhysRegsForPEI() const override { return false; }
+  bool usesPhysRegsForValues() const override { return false; }
 
   yaml::MachineFunctionInfo *createDefaultFuncInfoYAML() const override;
   yaml::MachineFunctionInfo *

diff  --git a/llvm/lib/Target/WebAssembly/WebAssemblyUtilities.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyUtilities.cpp
index a237da8154ab..5b777984ffa6 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyUtilities.cpp
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyUtilities.cpp
@@ -49,7 +49,7 @@ bool WebAssembly::mayThrow(const MachineInstr &MI) {
   if (!MI.isCall())
     return false;
 
-  const MachineOperand &MO = MI.getOperand(getCalleeOpNo(MI.getOpcode()));
+  const MachineOperand &MO = getCalleeOp(MI);
   assert(MO.isGlobal() || MO.isSymbol());
 
   if (MO.isSymbol()) {
@@ -79,3 +79,67 @@ bool WebAssembly::mayThrow(const MachineInstr &MI) {
   // original LLVm IR? (Even when the callee may throw)
   return true;
 }
+
+inline const MachineOperand &getCalleeOp(const MachineInstr &MI) {
+  switch (MI.getOpcode()) {
+  case WebAssembly::CALL_VOID:
+  case WebAssembly::CALL_VOID_S:
+  case WebAssembly::CALL_INDIRECT_VOID:
+  case WebAssembly::CALL_INDIRECT_VOID_S:
+  case WebAssembly::RET_CALL:
+  case WebAssembly::RET_CALL_S:
+  case WebAssembly::RET_CALL_INDIRECT:
+  case WebAssembly::RET_CALL_INDIRECT_S:
+    return MI.getOperand(0);
+  case WebAssembly::CALL_i32:
+  case WebAssembly::CALL_i32_S:
+  case WebAssembly::CALL_i64:
+  case WebAssembly::CALL_i64_S:
+  case WebAssembly::CALL_f32:
+  case WebAssembly::CALL_f32_S:
+  case WebAssembly::CALL_f64:
+  case WebAssembly::CALL_f64_S:
+  case WebAssembly::CALL_v16i8:
+  case WebAssembly::CALL_v16i8_S:
+  case WebAssembly::CALL_v8i16:
+  case WebAssembly::CALL_v8i16_S:
+  case WebAssembly::CALL_v4i32:
+  case WebAssembly::CALL_v4i32_S:
+  case WebAssembly::CALL_v2i64:
+  case WebAssembly::CALL_v2i64_S:
+  case WebAssembly::CALL_v4f32:
+  case WebAssembly::CALL_v4f32_S:
+  case WebAssembly::CALL_v2f64:
+  case WebAssembly::CALL_v2f64_S:
+  case WebAssembly::CALL_exnref:
+  case WebAssembly::CALL_exnref_S:
+  case WebAssembly::CALL_INDIRECT_i32:
+  case WebAssembly::CALL_INDIRECT_i32_S:
+  case WebAssembly::CALL_INDIRECT_i64:
+  case WebAssembly::CALL_INDIRECT_i64_S:
+  case WebAssembly::CALL_INDIRECT_f32:
+  case WebAssembly::CALL_INDIRECT_f32_S:
+  case WebAssembly::CALL_INDIRECT_f64:
+  case WebAssembly::CALL_INDIRECT_f64_S:
+  case WebAssembly::CALL_INDIRECT_v16i8:
+  case WebAssembly::CALL_INDIRECT_v16i8_S:
+  case WebAssembly::CALL_INDIRECT_v8i16:
+  case WebAssembly::CALL_INDIRECT_v8i16_S:
+  case WebAssembly::CALL_INDIRECT_v4i32:
+  case WebAssembly::CALL_INDIRECT_v4i32_S:
+  case WebAssembly::CALL_INDIRECT_v2i64:
+  case WebAssembly::CALL_INDIRECT_v2i64_S:
+  case WebAssembly::CALL_INDIRECT_v4f32:
+  case WebAssembly::CALL_INDIRECT_v4f32_S:
+  case WebAssembly::CALL_INDIRECT_v2f64:
+  case WebAssembly::CALL_INDIRECT_v2f64_S:
+  case WebAssembly::CALL_INDIRECT_exnref:
+  case WebAssembly::CALL_INDIRECT_exnref_S:
+    return MI.getOperand(1);
+  case WebAssembly::CALL:
+  case WebAssembly::CALL_S:
+    return MI.getOperand(MI.getNumDefs());
+  default:
+    llvm_unreachable("Not a call instruction");
+  }
+}

diff  --git a/llvm/lib/Target/WebAssembly/WebAssemblyUtilities.h b/llvm/lib/Target/WebAssembly/WebAssemblyUtilities.h
index 26cf84de89b9..4f0ed43a2481 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyUtilities.h
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyUtilities.h
@@ -44,6 +44,10 @@ template <typename T> MachineBasicBlock *getBottom(const T *Unit) {
   return Bottom;
 }
 
+/// Returns the operand number of a callee, assuming the argument is a call
+/// instruction.
+const MachineOperand &getCalleeOp(const MachineInstr &MI);
+
 } // end namespace WebAssembly
 
 } // end namespace llvm

diff  --git a/llvm/test/CodeGen/WebAssembly/multivalue.ll b/llvm/test/CodeGen/WebAssembly/multivalue.ll
index ee32852ef579..af601f7cbe92 100644
--- a/llvm/test/CodeGen/WebAssembly/multivalue.ll
+++ b/llvm/test/CodeGen/WebAssembly/multivalue.ll
@@ -1,29 +1,116 @@
-; RUN: llc < %s -asm-verbose=false -verify-machineinstrs -disable-wasm-fallthrough-return-opt -wasm-disable-explicit-locals -wasm-keep-registers -mattr=+multivalue | FileCheck %s
-; RUN: llc < %s --filetype=obj -mattr=+multivalue | obj2yaml | FileCheck %s --check-prefix OBJ
+; RUN: llc < %s -asm-verbose=false -verify-machineinstrs -disable-wasm-fallthrough-return-opt -wasm-disable-explicit-locals -wasm-keep-registers -mattr=+multivalue,+tail-call | FileCheck %s
+; RUN: llc < %s --filetype=obj -mattr=+multivalue,+tail-call | obj2yaml | FileCheck %s --check-prefix OBJ
 
-; Test that the multivalue returns, function types, and block types
-; work as expected.
+; Test that the multivalue calls, returns, function types, and block
+; types work as expected.
 
 target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
 target triple = "wasm32-unknown-unknown"
 
-%pair = type { i32, i32 }
-%packed_pair = type <{ i32, i32 }>
+%pair = type { i32, i64 }
+%packed_pair = type <{ i32, i64 }>
+
+
+; CHECK-LABEL: pair_const:
+; CHECK-NEXT: .functype pair_const () -> (i32, i64)
+; CHECK-NEXT: i32.const $push[[L0:[0-9]+]]=, 42{{$}}
+; CHECK-NEXT: i64.const $push[[L1:[0-9]+]]=, 42{{$}}
+; CHECK-NEXT: return $pop[[L0]], $pop[[L1]]{{$}}
+define %pair @pair_const() {
+  ret %pair { i32 42, i64 42 }
+}
+
+; CHECK-LABEL: packed_pair_const:
+; CHECK-NEXT: .functype packed_pair_const () -> (i32, i64)
+; CHECK-NEXT: i32.const $push[[L0:[0-9]+]]=, 42{{$}}
+; CHECK-NEXT: i64.const $push[[L1:[0-9]+]]=, 42{{$}}
+; CHECK-NEXT: return $pop[[L0]], $pop[[L1]]{{$}}
+define %packed_pair @packed_pair_const() {
+  ret %packed_pair <{ i32 42, i64 42 }>
+}
 
 ; CHECK-LABEL: pair_ident:
-; CHECK-NEXT: .functype pair_ident (i32, i32) -> (i32, i32)
+; CHECK-NEXT: .functype pair_ident (i32, i64) -> (i32, i64)
 ; CHECK-NEXT: return $0, $1{{$}}
 define %pair @pair_ident(%pair %p) {
   ret %pair %p
 }
 
 ; CHECK-LABEL: packed_pair_ident:
-; CHECK-NEXT: .functype packed_pair_ident (i32, i32) -> (i32, i32)
+; CHECK-NEXT: .functype packed_pair_ident (i32, i64) -> (i32, i64)
 ; CHECK-NEXT: return $0, $1{{$}}
 define %packed_pair @packed_pair_ident(%packed_pair %p) {
   ret %packed_pair %p
 }
 
+;; TODO: Multivalue calls are a WIP and do not necessarily produce
+;; correct output. For now, just check that they don't cause any
+;; crashes.
+
+define void @pair_call() {
+  %p = call %pair @pair_const()
+  ret void
+}
+
+define void @packed_pair_call() {
+  %p = call %packed_pair @packed_pair_const()
+  ret void
+}
+
+define %pair @pair_call_return() {
+  %p = call %pair @pair_const()
+  ret %pair %p
+}
+
+define %packed_pair @packed_pair_call_return() {
+  %p = call %packed_pair @packed_pair_const()
+  ret %packed_pair %p
+}
+
+define %pair @pair_tail_call() {
+  %p = musttail call %pair @pair_const()
+  ret %pair %p
+}
+
+define %packed_pair @packed_pair_tail_call() {
+  %p = musttail call %packed_pair @packed_pair_const()
+  ret %packed_pair %p
+}
+
+define i32 @pair_call_return_first() {
+  %p = call %pair @pair_const()
+  %v = extractvalue %pair %p, 0
+  ret i32 %v
+}
+
+define i32 @packed_pair_call_return_first() {
+  %p = call %packed_pair @packed_pair_const()
+  %v = extractvalue %packed_pair %p, 0
+  ret i32 %v
+}
+
+define i64 @pair_call_return_second() {
+  %p = call %pair @pair_const()
+  %v = extractvalue %pair %p, 1
+  ret i64 %v
+}
+
+define i64 @packed_pair_call_return_second() {
+  %p = call %packed_pair @packed_pair_const()
+  %v = extractvalue %packed_pair %p, 1
+  ret i64 %v
+}
+
+define %pair @pair_pass_through(%pair %p) {
+  %r = call %pair @pair_ident(%pair %p)
+  ret %pair %r
+}
+
+define %packed_pair @packed_pair_pass_through(%packed_pair %p) {
+  %r = call %packed_pair @packed_pair_ident(%packed_pair %p)
+  ret %packed_pair %r
+}
+
 ; CHECK-LABEL: minimal_loop:
 ; CHECK-NEXT: .functype minimal_loop (i32) -> (i32, i64)
 ; CHECK-NEXT: .LBB{{[0-9]+}}_1:
@@ -31,7 +118,7 @@ define %packed_pair @packed_pair_ident(%packed_pair %p) {
 ; CHECK-NEXT: br 0{{$}}
 ; CHECK-NEXT: .LBB{{[0-9]+}}_2:
 ; CHECK-NEXT: end_loop{{$}}
-define {i32, i64} @minimal_loop(i32* %p) {
+define %pair @minimal_loop(i32* %p) {
 entry:
   br label %loop
 loop:
@@ -39,21 +126,40 @@ loop:
 }
 
 ; CHECK-LABEL: .section .custom_section.target_features
-; CHECK-NEXT: .int8 1
+; CHECK-NEXT: .int8 2
 ; CHECK-NEXT: .int8 43
 ; CHECK-NEXT: .int8 10
 ; CHECK-NEXT: .ascii "multivalue"
+; CHECK-NEXT: .int8 43
+; CHECK-NEXT: .int8 9
+; CHECK-NEXT: .ascii "tail-call"
 
 ; OBJ-LABEL:  - Type:            TYPE
 ; OBJ-NEXT:     Signatures:
 ; OBJ-NEXT:       - Index:           0
-; OBJ-NEXT:         ParamTypes:
+; OBJ-NEXT:         ParamTypes:      []
+; OBJ-NEXT:         ReturnTypes:
 ; OBJ-NEXT:           - I32
+; OBJ-NEXT:           - I64
+; OBJ-NEXT:       - Index:           1
+; OBJ-NEXT:         ParamTypes:
 ; OBJ-NEXT:           - I32
+; OBJ-NEXT:           - I64
 ; OBJ-NEXT:         ReturnTypes:
 ; OBJ-NEXT:           - I32
+; OBJ-NEXT:           - I64
+; OBJ-NEXT:       - Index:           2
+; OBJ-NEXT:         ParamTypes:      []
+; OBJ-NEXT:         ReturnTypes:     []
+; OBJ-NEXT:       - Index:           3
+; OBJ-NEXT:         ParamTypes:      []
+; OBJ-NEXT:         ReturnTypes:
 ; OBJ-NEXT:           - I32
-; OBJ-NEXT:       - Index:           1
+; OBJ-NEXT:       - Index:           4
+; OBJ-NEXT:         ParamTypes:      []
+; OBJ-NEXT:         ReturnTypes:
+; OBJ-NEXT:           - I64
+; OBJ-NEXT:       - Index:           5
 ; OBJ-NEXT:         ParamTypes:
 ; OBJ-NEXT:           - I32
 ; OBJ-NEXT:         ReturnTypes:


        


More information about the llvm-commits mailing list