diff --git a/llvm/lib/CodeGen/MachineVerifier.cpp b/llvm/lib/CodeGen/MachineVerifier.cpp --- a/llvm/lib/CodeGen/MachineVerifier.cpp +++ b/llvm/lib/CodeGen/MachineVerifier.cpp @@ -1595,7 +1595,7 @@ bool IsOptional = MI->isVariadic() && MONum == MCID.getNumOperands() - 1; if (!IsOptional) { if (MO->isReg()) { - if (MO->isDef() && !MCOI.isOptionalDef()) + if (MO->isDef() && !MCOI.isOptionalDef() && !MCID.variadicOpsAreDefs()) report("Explicit operand marked as def", MO, MONum); if (MO->isImplicit()) report("Explicit operand marked as implicit", MO, MONum); diff --git a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.h b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.h --- a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.h +++ b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.h @@ -465,6 +465,8 @@ case WebAssembly::CALL_v2f64_S: case WebAssembly::CALL_exnref: case WebAssembly::CALL_exnref_S: + case WebAssembly::CALL: + case WebAssembly::CALL_S: case WebAssembly::RET_CALL: case WebAssembly::RET_CALL_S: return true; @@ -499,6 +501,8 @@ case WebAssembly::CALL_INDIRECT_v2f64_S: case WebAssembly::CALL_INDIRECT_exnref: case WebAssembly::CALL_INDIRECT_exnref_S: + case WebAssembly::CALL_INDIRECT: + case WebAssembly::CALL_INDIRECT_S: case WebAssembly::RET_CALL_INDIRECT: case WebAssembly::RET_CALL_INDIRECT_S: return true; @@ -507,72 +511,6 @@ } } -/// Returns the operand number of a callee, assuming the argument is a call -/// instruction. -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"); - } -} - inline bool isMarker(unsigned Opc) { switch (Opc) { case WebAssembly::BLOCK: diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp --- a/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp @@ -418,12 +418,29 @@ assert(CallParams.getOpcode() == WebAssembly::CALL_PARAMS); assert(CallResults.getOpcode() == WebAssembly::CALL_RESULTS); + bool IsIndirect = CallParams.getOperand(0).isReg(); + unsigned CallOp = IsIndirect ? WebAssembly::CALL_INDIRECT : WebAssembly::CALL; + MachineFunction &MF = *BB->getParent(); - const MCInstrDesc &MCID = TII.get(WebAssembly::CALL); + const MCInstrDesc &MCID = TII.get(CallOp); MachineInstrBuilder MIB(MF, MF.CreateMachineInstr(MCID, DL)); + // Move the function pointer to the end of the arguments for indirect calls + if (IsIndirect) { + auto FnPtr = CallParams.getOperand(0); + CallParams.RemoveOperand(0); + CallParams.addOperand(FnPtr); + } + for (auto Def : CallResults.defs()) MIB.add(Def); + + // Add placeholders for the type index and immediate flags + if (IsIndirect) { + MIB.addImm(0); + MIB.addImm(0); + } + for (auto Use : CallParams.uses()) MIB.add(Use); diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyInstrCall.td b/llvm/lib/Target/WebAssembly/WebAssemblyInstrCall.td --- a/llvm/lib/Target/WebAssembly/WebAssemblyInstrCall.td +++ b/llvm/lib/Target/WebAssembly/WebAssemblyInstrCall.td @@ -73,12 +73,19 @@ let Uses = [SP32, SP64], isCall = 1 in { -// TODO: Add an indirect version of the variadic call, delete CALL_* +// TODO: 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>; +let variadicOpsAreDefs = 1 in +defm CALL_INDIRECT : + I<(outs), (ins TypeIndex:$type, i32imm:$flags, variable_ops), + (outs), (ins TypeIndex:$type, i32imm:$flags), [], + "call_indirect", "call_indirect\t$type", 0x11>; + defm "" : CALL; defm "" : CALL; defm "" : CALL; diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp --- a/llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp @@ -208,6 +208,7 @@ OutMI.setOpcode(MI->getOpcode()); const MCInstrDesc &Desc = MI->getDesc(); + unsigned NumVariadicDefs = MI->getNumExplicitDefs() - Desc.getNumDefs(); for (unsigned I = 0, E = MI->getNumOperands(); I != E; ++I) { const MachineOperand &MO = MI->getOperand(I); @@ -230,8 +231,8 @@ break; } case MachineOperand::MO_Immediate: - if (I < Desc.NumOperands) { - const MCOperandInfo &Info = Desc.OpInfo[I]; + if (I - NumVariadicDefs < Desc.NumOperands) { + const MCOperandInfo &Info = Desc.OpInfo[I - NumVariadicDefs]; if (Info.OperandType == WebAssembly::OPERAND_TYPEINDEX) { SmallVector Returns; SmallVector Params; diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyUtilities.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyUtilities.cpp --- a/llvm/lib/Target/WebAssembly/WebAssemblyUtilities.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyUtilities.cpp @@ -80,7 +80,7 @@ return true; } -inline const MachineOperand &getCalleeOp(const MachineInstr &MI) { +const MachineOperand &WebAssembly::getCalleeOp(const MachineInstr &MI) { switch (MI.getOpcode()) { case WebAssembly::CALL_VOID: case WebAssembly::CALL_VOID_S: @@ -138,7 +138,10 @@ return MI.getOperand(1); case WebAssembly::CALL: case WebAssembly::CALL_S: - return MI.getOperand(MI.getNumDefs()); + return MI.getOperand(MI.getNumExplicitDefs()); + case WebAssembly::CALL_INDIRECT: + case WebAssembly::CALL_INDIRECT_S: + return MI.getOperand(MI.getNumOperands() - 1); default: llvm_unreachable("Not a call instruction"); } diff --git a/llvm/test/CodeGen/WebAssembly/multivalue.ll b/llvm/test/CodeGen/WebAssembly/multivalue.ll --- a/llvm/test/CodeGen/WebAssembly/multivalue.ll +++ b/llvm/test/CodeGen/WebAssembly/multivalue.ll @@ -1,4 +1,4 @@ -; RUN: llc < %s -asm-verbose=false -verify-machineinstrs -disable-wasm-fallthrough-return-opt -wasm-disable-explicit-locals -wasm-keep-registers -mattr=+multivalue,+tail-call | FileCheck %s +; RUN: llc < %s -asm-verbose=false -verify-machineinstrs -mattr=+multivalue,+tail-call | FileCheck %s ; RUN: llc < %s --filetype=obj -mattr=+multivalue,+tail-call | obj2yaml | FileCheck %s --check-prefix OBJ ; Test that the multivalue calls, returns, function types, and block @@ -8,109 +8,82 @@ target triple = "wasm32-unknown-unknown" %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]]{{$}} +; CHECK-NEXT: i32.const 42{{$}} +; CHECK-NEXT: i64.const 42{{$}} +; CHECK-NEXT: end_function{{$}} define %pair @pair_const() { ret %pair { i32 42, i64 42 } } -; CHECK-LABEL: packed_pair_const: -; CHECK-NEXT: .functype packed_pair_const () -> (i32, i64) -; CHECK-NEXT: i32.const $push[[L0:[0-9]+]]=, 42{{$}} -; CHECK-NEXT: i64.const $push[[L1:[0-9]+]]=, 42{{$}} -; CHECK-NEXT: return $pop[[L0]], $pop[[L1]]{{$}} -define %packed_pair @packed_pair_const() { - ret %packed_pair <{ i32 42, i64 42 }> -} - ; CHECK-LABEL: pair_ident: ; CHECK-NEXT: .functype pair_ident (i32, i64) -> (i32, i64) -; CHECK-NEXT: return $0, $1{{$}} +; CHECK-NEXT: local.get 0{{$}} +; CHECK-NEXT: local.get 1{{$}} +; CHECK-NEXT: end_function{{$}} define %pair @pair_ident(%pair %p) { ret %pair %p } -; CHECK-LABEL: packed_pair_ident: -; CHECK-NEXT: .functype packed_pair_ident (i32, i64) -> (i32, i64) -; CHECK-NEXT: return $0, $1{{$}} -define %packed_pair @packed_pair_ident(%packed_pair %p) { - ret %packed_pair %p -} - -;; TODO: Multivalue calls are a WIP and do not necessarily produce -;; correct output. For now, just check that they don't cause any -;; crashes. +;; 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 () -> () define void @pair_call() { %p = call %pair @pair_const() ret void } -define void @packed_pair_call() { - %p = call %packed_pair @packed_pair_const() - ret void -} - +; CHECK-LABEL: pair_call_return: +; CHECK-NEXT: .functype pair_call_return () -> (i32, i64) define %pair @pair_call_return() { %p = call %pair @pair_const() ret %pair %p } -define %packed_pair @packed_pair_call_return() { - %p = call %packed_pair @packed_pair_const() - ret %packed_pair %p +; CHECK-LABEL: pair_call_indirect: +; CHECK-NEXT: .functype pair_call_indirect (i32) -> (i32, i64) +; CHECK: call_indirect () -> (i32, i64){{$}} +define %pair @pair_call_indirect(%pair()* %f) { + %p = call %pair %f() + ret %pair %p } +; CHECK-LABEL: pair_tail_call: +; CHECK-NEXT: .functype pair_tail_call () -> (i32, i64) define %pair @pair_tail_call() { %p = musttail call %pair @pair_const() ret %pair %p } -define %packed_pair @packed_pair_tail_call() { - %p = musttail call %packed_pair @packed_pair_const() - ret %packed_pair %p -} - +; CHECK-LABEL: pair_call_return_first: +; CHECK-NEXT: .functype pair_call_return_first () -> (i32) define i32 @pair_call_return_first() { %p = call %pair @pair_const() %v = extractvalue %pair %p, 0 ret i32 %v } -define i32 @packed_pair_call_return_first() { - %p = call %packed_pair @packed_pair_const() - %v = extractvalue %packed_pair %p, 0 - ret i32 %v -} - +; CHECK-LABEL: pair_call_return_second: +; CHECK-NEXT: .functype pair_call_return_second () -> (i64) define i64 @pair_call_return_second() { %p = call %pair @pair_const() %v = extractvalue %pair %p, 1 ret i64 %v } -define i64 @packed_pair_call_return_second() { - %p = call %packed_pair @packed_pair_const() - %v = extractvalue %packed_pair %p, 1 - ret i64 %v -} +; CHECK-LABEL: pair_pass_through: +; CHECK-NEXT: .functype pair_pass_through (i32, i64) -> (i32, i64) 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: @@ -152,16 +125,16 @@ ; OBJ-NEXT: ParamTypes: [] ; OBJ-NEXT: ReturnTypes: [] ; OBJ-NEXT: - Index: 3 -; OBJ-NEXT: ParamTypes: [] +; OBJ-NEXT: ParamTypes: +; OBJ-NEXT: - I32 ; OBJ-NEXT: ReturnTypes: ; OBJ-NEXT: - I32 +; OBJ-NEXT: - I64 ; 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: - Index: 5 +; OBJ-NEXT: ParamTypes: [] ; OBJ-NEXT: ReturnTypes: -; OBJ-NEXT: - I32 ; OBJ-NEXT: - I64