[llvm] 9d37f5a - [WebAssembly] Implement multivalue call_indirects
Thomas Lively via llvm-commits
llvm-commits at lists.llvm.org
Tue Feb 18 13:49:55 PST 2020
Author: Thomas Lively
Date: 2020-02-18T13:49:46-08:00
New Revision: 9d37f5afac4a3b9194b9001bed84f58ca8bd6c02
URL: https://github.com/llvm/llvm-project/commit/9d37f5afac4a3b9194b9001bed84f58ca8bd6c02
DIFF: https://github.com/llvm/llvm-project/commit/9d37f5afac4a3b9194b9001bed84f58ca8bd6c02.diff
LOG: [WebAssembly] Implement multivalue call_indirects
Summary:
Unlike normal calls, call_indirects have immediate arguments that
caused a MachineVerifier failure without a small tweak to loosen the
verifier's requirements for variadicOpsAreDefs instructions.
One nice thing about the new call_indirects is that they do not need
to participate in the PCALL_INDIRECT mechanism because their post-isel
hook handles moving the function pointer argument and adding the flags
and typeindex arguments itself.
Reviewers: aheejin
Subscribers: dschuff, sbc100, jgravelle-google, hiraditya, sunfish, llvm-commits
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D74191
Added:
Modified:
llvm/lib/CodeGen/MachineVerifier.cpp
llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.h
llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp
llvm/lib/Target/WebAssembly/WebAssemblyInstrCall.td
llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp
llvm/lib/Target/WebAssembly/WebAssemblyUtilities.cpp
llvm/test/CodeGen/WebAssembly/multivalue.ll
Removed:
################################################################################
diff --git a/llvm/lib/CodeGen/MachineVerifier.cpp b/llvm/lib/CodeGen/MachineVerifier.cpp
index bf589a7019e5..8d1c2fed15ad 100644
--- a/llvm/lib/CodeGen/MachineVerifier.cpp
+++ b/llvm/lib/CodeGen/MachineVerifier.cpp
@@ -1595,7 +1595,7 @@ MachineVerifier::visitMachineOperand(const MachineOperand *MO, unsigned MONum) {
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
index f7d1aeb9ec68..bc6363ec7b32 100644
--- a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.h
+++ b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.h
@@ -465,6 +465,8 @@ inline bool isCallDirect(unsigned Opc) {
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 @@ inline bool isCallIndirect(unsigned Opc) {
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 @@ inline bool isCallIndirect(unsigned Opc) {
}
}
-/// 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
index a3b1bc19698b..d62091e9c7e0 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp
@@ -418,12 +418,29 @@ static MachineBasicBlock *LowerCallResults(MachineInstr &CallResults,
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
index 818b6ce0df49..0020fe2c6ed7 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyInstrCall.td
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyInstrCall.td
@@ -73,12 +73,19 @@ defm CALL_RESULTS :
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<i32, I32, "i32.">;
defm "" : CALL<i64, I64, "i64.">;
defm "" : CALL<f32, F32, "f32.">;
diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp
index 59c10243c545..75cde7d3b858 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp
@@ -208,6 +208,7 @@ void WebAssemblyMCInstLower::lower(const MachineInstr *MI,
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);
@@ -229,9 +230,10 @@ void WebAssemblyMCInstLower::lower(const MachineInstr *MI,
MCOp = MCOperand::createReg(WAReg);
break;
}
- case MachineOperand::MO_Immediate:
- if (I < Desc.NumOperands) {
- const MCOperandInfo &Info = Desc.OpInfo[I];
+ case MachineOperand::MO_Immediate: {
+ unsigned DescIndex = I - NumVariadicDefs;
+ if (DescIndex < Desc.NumOperands) {
+ const MCOperandInfo &Info = Desc.OpInfo[DescIndex];
if (Info.OperandType == WebAssembly::OPERAND_TYPEINDEX) {
SmallVector<wasm::ValType, 4> Returns;
SmallVector<wasm::ValType, 4> Params;
@@ -270,6 +272,7 @@ void WebAssemblyMCInstLower::lower(const MachineInstr *MI,
}
MCOp = MCOperand::createImm(MO.getImm());
break;
+ }
case MachineOperand::MO_FPImmediate: {
// TODO: MC converts all floating point immediate operands to double.
// This is fine for numeric values, but may cause NaNs to change bits.
diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyUtilities.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyUtilities.cpp
index 5b777984ffa6..1e044383547b 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyUtilities.cpp
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyUtilities.cpp
@@ -80,7 +80,7 @@ bool WebAssembly::mayThrow(const MachineInstr &MI) {
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 @@ inline const MachineOperand &getCalleeOp(const MachineInstr &MI) {
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
index af601f7cbe92..0e4cb0847640 100644
--- 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 datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
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 @@ loop:
; 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
More information about the llvm-commits
mailing list