Commit d735882d authored by Francis McCabe's avatar Francis McCabe Committed by Commit Bot

[wasm] ReturnCall Implementation (TurboFan)

Implement ReturnCall functionality for TurboFan compiler.

Bug: v8:7431
Change-Id: I1e20473a9b3eba9ee48c0c11f89029356dd9b9eb
Cq-Include-Trybots: luci.v8.try:v8_linux64_ubsan_rel_ng
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1467344
Commit-Queue: Francis McCabe <fgm@chromium.org>
Reviewed-by: 's avatarClemens Hammacher <clemensh@chromium.org>
Reviewed-by: 's avatarAndreas Haas <ahaas@chromium.org>
Cr-Commit-Position: refs/heads/master@{#60103}
parent afbfd756
......@@ -2599,11 +2599,9 @@ Node* WasmGraphBuilder::BuildCCall(MachineSignature* sig, Node* function,
return SetEffect(graph()->NewNode(op, arraysize(call_args), call_args));
}
Node* WasmGraphBuilder::BuildWasmCall(wasm::FunctionSig* sig, Node** args,
Node*** rets,
Node* WasmGraphBuilder::BuildCallNode(wasm::FunctionSig* sig, Node** args,
wasm::WasmCodePosition position,
Node* instance_node,
UseRetpoline use_retpoline) {
Node* instance_node, const Operator* op) {
if (instance_node == nullptr) {
DCHECK_NOT_NULL(instance_node_);
instance_node = instance_node_.get();
......@@ -2624,13 +2622,23 @@ Node* WasmGraphBuilder::BuildWasmCall(wasm::FunctionSig* sig, Node** args,
args[params + 2] = Effect();
args[params + 3] = Control();
auto call_descriptor =
GetWasmCallDescriptor(mcgraph()->zone(), sig, use_retpoline);
const Operator* op = mcgraph()->common()->Call(call_descriptor);
Node* call = SetEffect(graph()->NewNode(op, static_cast<int>(count), args));
DCHECK(position == wasm::kNoCodePosition || position > 0);
if (position > 0) SetSourcePosition(call, position);
return call;
}
Node* WasmGraphBuilder::BuildWasmCall(wasm::FunctionSig* sig, Node** args,
Node*** rets,
wasm::WasmCodePosition position,
Node* instance_node,
UseRetpoline use_retpoline) {
auto call_descriptor =
GetWasmCallDescriptor(mcgraph()->zone(), sig, use_retpoline);
const Operator* op = mcgraph()->common()->Call(call_descriptor);
Node* call = BuildCallNode(sig, args, position, instance_node, op);
size_t ret_count = sig->return_count();
if (ret_count == 0) return call; // No return value.
......@@ -2648,10 +2656,25 @@ Node* WasmGraphBuilder::BuildWasmCall(wasm::FunctionSig* sig, Node** args,
return call;
}
Node* WasmGraphBuilder::BuildWasmReturnCall(wasm::FunctionSig* sig, Node** args,
wasm::WasmCodePosition position,
Node* instance_node,
UseRetpoline use_retpoline) {
auto call_descriptor =
GetWasmCallDescriptor(mcgraph()->zone(), sig, use_retpoline);
const Operator* op = mcgraph()->common()->TailCall(call_descriptor);
Node* call = BuildCallNode(sig, args, position, instance_node, op);
MergeControlToEnd(mcgraph(), call);
return call;
}
Node* WasmGraphBuilder::BuildImportCall(wasm::FunctionSig* sig, Node** args,
Node*** rets,
wasm::WasmCodePosition position,
int func_index) {
int func_index,
IsReturnCall continuation) {
// Load the imported function refs array from the instance.
Node* imported_function_refs =
LOAD_INSTANCE_FIELD(ImportedFunctionRefs, MachineType::TaggedPointer());
......@@ -2666,14 +2689,23 @@ Node* WasmGraphBuilder::BuildImportCall(wasm::FunctionSig* sig, Node** args,
mcgraph()->Int32Constant(func_index * kSystemPointerSize), Effect(),
Control()));
args[0] = target_node;
return BuildWasmCall(sig, args, rets, position, ref_node,
untrusted_code_mitigations_ ? kRetpoline : kNoRetpoline);
const UseRetpoline use_retpoline =
untrusted_code_mitigations_ ? kRetpoline : kNoRetpoline;
switch (continuation) {
case kCallContinues:
return BuildWasmCall(sig, args, rets, position, ref_node, use_retpoline);
case kReturnCall:
DCHECK_NULL(rets);
return BuildWasmReturnCall(sig, args, position, ref_node, use_retpoline);
}
}
Node* WasmGraphBuilder::BuildImportCall(wasm::FunctionSig* sig, Node** args,
Node*** rets,
wasm::WasmCodePosition position,
Node* func_index) {
Node* func_index,
IsReturnCall continuation) {
// Load the imported function refs array from the instance.
Node* imported_function_refs =
LOAD_INSTANCE_FIELD(ImportedFunctionRefs, MachineType::TaggedPointer());
......@@ -2708,8 +2740,16 @@ Node* WasmGraphBuilder::BuildImportCall(wasm::FunctionSig* sig, Node** args,
mcgraph()->machine()->Load(MachineType::Pointer()), imported_targets,
func_index_times_pointersize, Effect(), Control()));
args[0] = target_node;
return BuildWasmCall(sig, args, rets, position, ref_node,
untrusted_code_mitigations_ ? kRetpoline : kNoRetpoline);
const UseRetpoline use_retpoline =
untrusted_code_mitigations_ ? kRetpoline : kNoRetpoline;
switch (continuation) {
case kCallContinues:
return BuildWasmCall(sig, args, rets, position, ref_node, use_retpoline);
case kReturnCall:
DCHECK_NULL(rets);
return BuildWasmReturnCall(sig, args, position, ref_node, use_retpoline);
}
}
Node* WasmGraphBuilder::CallDirect(uint32_t index, Node** args, Node*** rets,
......@@ -2719,7 +2759,7 @@ Node* WasmGraphBuilder::CallDirect(uint32_t index, Node** args, Node*** rets,
if (env_ && index < env_->module->num_imported_functions) {
// Call to an imported function.
return BuildImportCall(sig, args, rets, position, index);
return BuildImportCall(sig, args, rets, position, index, kCallContinues);
}
// A direct call to a wasm function defined in this module.
......@@ -2733,6 +2773,13 @@ Node* WasmGraphBuilder::CallDirect(uint32_t index, Node** args, Node*** rets,
Node* WasmGraphBuilder::CallIndirect(uint32_t sig_index, Node** args,
Node*** rets,
wasm::WasmCodePosition position) {
return BuildIndirectCall(sig_index, args, rets, position, kCallContinues);
}
Node* WasmGraphBuilder::BuildIndirectCall(uint32_t sig_index, Node** args,
Node*** rets,
wasm::WasmCodePosition position,
IsReturnCall continuation) {
DCHECK_NOT_NULL(args[0]);
DCHECK_NOT_NULL(env_);
......@@ -2812,8 +2859,41 @@ Node* WasmGraphBuilder::CallIndirect(uint32_t sig_index, Node** args,
intptr_scaled_key, Effect(), Control()));
args[0] = target;
const UseRetpoline use_retpoline =
untrusted_code_mitigations_ ? kRetpoline : kNoRetpoline;
switch (continuation) {
case kCallContinues:
return BuildWasmCall(sig, args, rets, position, target_instance,
untrusted_code_mitigations_ ? kRetpoline : kNoRetpoline);
use_retpoline);
case kReturnCall:
return BuildWasmReturnCall(sig, args, position, target_instance,
use_retpoline);
}
}
Node* WasmGraphBuilder::ReturnCall(uint32_t index, Node** args,
wasm::WasmCodePosition position) {
DCHECK_NULL(args[0]);
wasm::FunctionSig* sig = env_->module->functions[index].sig;
if (env_ && index < env_->module->num_imported_functions) {
// Return Call to an imported function.
return BuildImportCall(sig, args, nullptr, position, index, kReturnCall);
}
// A direct tail call to a wasm function defined in this module.
// Just encode the function index. This will be patched during code
// generation.
Address code = static_cast<Address>(index);
args[0] = mcgraph()->RelocatableIntPtrConstant(code, RelocInfo::WASM_CALL);
return BuildWasmReturnCall(sig, args, position, nullptr, kNoRetpoline);
}
Node* WasmGraphBuilder::ReturnCallIndirect(uint32_t sig_index, Node** args,
wasm::WasmCodePosition position) {
return BuildIndirectCall(sig_index, args, nullptr, position, kReturnCall);
}
Node* WasmGraphBuilder::BuildI32Rol(Node* left, Node* right) {
......@@ -5118,7 +5198,8 @@ class WasmWrapperGraphBuilder : public WasmGraphBuilder {
// Load function index from {WasmExportedFunctionData}.
Node* function_index =
BuildLoadFunctionIndexFromExportedFunctionData(function_data);
BuildImportCall(sig_, args, &rets, wasm::kNoCodePosition, function_index);
BuildImportCall(sig_, args, &rets, wasm::kNoCodePosition, function_index,
kCallContinues);
} else {
// Call to a wasm function defined in this module.
// The call target is the jump table slot for that function.
......
......@@ -270,6 +270,11 @@ class WasmGraphBuilder {
Node* CallIndirect(uint32_t index, Node** args, Node*** rets,
wasm::WasmCodePosition position);
Node* ReturnCall(uint32_t index, Node** args,
wasm::WasmCodePosition position);
Node* ReturnCallIndirect(uint32_t index, Node** args,
wasm::WasmCodePosition position);
Node* Invert(Node* node);
Node* GetGlobal(uint32_t index);
......@@ -465,15 +470,28 @@ class WasmGraphBuilder {
Node* MaskShiftCount32(Node* node);
Node* MaskShiftCount64(Node* node);
enum IsReturnCall : bool { kReturnCall = true, kCallContinues = false };
template <typename... Args>
Node* BuildCCall(MachineSignature* sig, Node* function, Args... args);
Node* BuildCallNode(wasm::FunctionSig* sig, Node** args,
wasm::WasmCodePosition position, Node* instance_node,
const Operator* op);
Node* BuildIndirectCall(uint32_t sig_index, Node** args, Node*** rets,
wasm::WasmCodePosition position,
IsReturnCall continuation);
Node* BuildWasmCall(wasm::FunctionSig* sig, Node** args, Node*** rets,
wasm::WasmCodePosition position, Node* instance_node,
UseRetpoline use_retpoline);
Node* BuildWasmReturnCall(wasm::FunctionSig* sig, Node** args,
wasm::WasmCodePosition position,
Node* instance_node, UseRetpoline use_retpoline);
Node* BuildImportCall(wasm::FunctionSig* sig, Node** args, Node*** rets,
wasm::WasmCodePosition position, int func_index);
wasm::WasmCodePosition position, int func_index,
IsReturnCall continuation);
Node* BuildImportCall(wasm::FunctionSig* sig, Node** args, Node*** rets,
wasm::WasmCodePosition position, Node* func_index);
wasm::WasmCodePosition position, Node* func_index,
IsReturnCall continuation);
Node* BuildF32CopySign(Node* left, Node* right);
Node* BuildF64CopySign(Node* left, Node* right);
......
......@@ -1683,7 +1683,7 @@ void Assembler::jmp(Address entry, RelocInfo::Mode rmode) {
EnsureSpace ensure_space(this);
DCHECK(!RelocInfo::IsCodeTarget(rmode));
EMIT(0xE9);
if (RelocInfo::IsRuntimeEntry(rmode)) {
if (RelocInfo::IsRuntimeEntry(rmode) || RelocInfo::IsWasmCall(rmode)) {
emit(entry, rmode);
} else {
emit(entry - (reinterpret_cast<Address>(pc_) + sizeof(int32_t)), rmode);
......
......@@ -408,7 +408,7 @@ class WasmGraphBuildingInterface {
void ReturnCall(FullDecoder* decoder,
const CallFunctionImmediate<validate>& imm,
const Value args[]) {
UNIMPLEMENTED();
DoReturnCall(decoder, nullptr, imm.sig, imm.index, args);
}
void CallIndirect(FullDecoder* decoder, const Value& index,
......@@ -420,7 +420,7 @@ class WasmGraphBuildingInterface {
void ReturnCallIndirect(FullDecoder* decoder, const Value& index,
const CallIndirectImmediate<validate>& imm,
const Value args[]) {
UNIMPLEMENTED();
DoReturnCall(decoder, index.node, imm.sig, imm.sig_index, args);
}
void SimdOp(FullDecoder* decoder, WasmOpcode opcode, Vector<Value> args,
......@@ -872,6 +872,21 @@ class WasmGraphBuildingInterface {
// reload mem_size and mem_start.
LoadContextIntoSsa(ssa_env_);
}
void DoReturnCall(FullDecoder* decoder, TFNode* index_node, FunctionSig* sig,
uint32_t index, const Value args[]) {
int param_count = static_cast<int>(sig->parameter_count());
TFNode** arg_nodes = builder_->Buffer(param_count + 1);
arg_nodes[0] = index_node;
for (int i = 0; i < param_count; ++i) {
arg_nodes[i + 1] = args[i].node;
}
if (index_node) {
BUILD(ReturnCallIndirect, index, arg_nodes, decoder->position());
} else {
BUILD(ReturnCall, index, arg_nodes, decoder->position());
}
}
};
} // namespace
......
......@@ -512,6 +512,64 @@ WASM_EXEC_TEST(Run_JSSelectAlign_10) {
RunJSSelectAlignTest(execution_tier, 10, 10);
}
// Set up a test with an import, so we can return call it.
// Create a javascript function that returns left or right arguments
// depending on the value of the third argument
// function (a,b,c){ if(c)return a; return b; }
void RunPickerTest(ExecutionTier tier, bool indirect) {
EXPERIMENTAL_FLAG_SCOPE(return_call);
Isolate* isolate = CcTest::InitIsolateOnce();
HandleScope scope(isolate);
TestSignatures sigs;
const char* source = "(function(a,b,c) { if(c)return a; return b; })";
Handle<JSFunction> js_function =
Handle<JSFunction>::cast(v8::Utils::OpenHandle(
*v8::Local<v8::Function>::Cast(CompileRun(source))));
ManuallyImportedJSFunction import = {sigs.i_iii(), js_function};
WasmRunner<int32_t, int32_t> r(tier, &import);
const uint32_t js_index = 0;
const int32_t left = -2;
const int32_t right = 3;
WasmFunctionCompiler& rc_fn = r.NewFunction(sigs.i_i(), "rc");
if (indirect) {
r.builder().AddSignature(sigs.i_iii());
uint16_t indirect_function_table[] = {static_cast<uint16_t>(js_index)};
r.builder().AddIndirectFunctionTable(indirect_function_table,
arraysize(indirect_function_table));
r.builder().PopulateIndirectFunctionTable();
BUILD(rc_fn,
WASM_RETURN_CALL_INDIRECT(0, WASM_I32V(js_index), WASM_I32V(left),
WASM_I32V(right), WASM_GET_LOCAL(0)));
} else {
BUILD(rc_fn,
WASM_RETURN_CALL_FUNCTION(js_index, WASM_I32V(left), WASM_I32V(right),
WASM_GET_LOCAL(0)));
}
Handle<Object> args_left[] = {isolate->factory()->NewNumber(1)};
r.CheckCallViaJS(left, rc_fn.function_index(), args_left, 1);
Handle<Object> args_right[] = {isolate->factory()->NewNumber(0)};
r.CheckCallViaJS(right, rc_fn.function_index(), args_right, 1);
}
WASM_EXEC_TEST(Run_ReturnCallImportedFunction) {
RunPickerTest(execution_tier, false);
}
WASM_EXEC_TEST(Run_ReturnCallIndirectImportedFunction) {
RunPickerTest(execution_tier, true);
}
#undef ADD_CODE
} // namespace wasm
......
......@@ -6,6 +6,7 @@
#include <stdlib.h>
#include <string.h>
#include "src/api-inl.h"
#include "src/assembler-inl.h"
#include "src/base/overflowing-math.h"
#include "src/base/platform/elapsed-timer.h"
......@@ -2326,6 +2327,257 @@ WASM_EXEC_TEST(Call_Float64Sub) {
}
}
template <typename T>
static T factorial(T v) {
T expected = 1;
for (T i = v; i > 1; i--) {
expected *= i;
}
return expected;
}
template <typename T>
static T sum_1_to_n(T v) {
return v * (v + 1) / 2;
}
// We use unsigned arithmetic because of ubsan validation.
WASM_EXEC_TEST(Regular_Factorial) {
WasmRunner<uint32_t, uint32_t> r(execution_tier);
WasmFunctionCompiler& fact_aux_fn =
r.NewFunction<uint32_t, uint32_t, uint32_t>("fact_aux");
BUILD(r, WASM_CALL_FUNCTION(fact_aux_fn.function_index(), WASM_GET_LOCAL(0),
WASM_I32V(1)));
BUILD(fact_aux_fn,
WASM_IF_ELSE_I(
WASM_I32_LES(WASM_GET_LOCAL(0), WASM_I32V(1)), WASM_GET_LOCAL(1),
WASM_CALL_FUNCTION(
fact_aux_fn.function_index(),
WASM_I32_SUB(WASM_GET_LOCAL(0), WASM_I32V(1)),
WASM_I32_MUL(WASM_GET_LOCAL(0), WASM_GET_LOCAL(1)))));
uint32_t test_values[] = {1, 2, 5, 10, 20};
for (uint32_t v : test_values) {
CHECK_EQ(factorial(v), r.Call(v));
}
}
// Tail-recursive variation on factorial:
// fact(N) => f(N,1).
//
// f(N,X) where N=<1 => X
// f(N,X) => f(N-1,X*N).
WASM_EXEC_TEST(ReturnCall_Factorial) {
EXPERIMENTAL_FLAG_SCOPE(return_call);
// Run in bounded amount of stack - 8kb.
FlagScope<int32_t> stack_size(&v8::internal::FLAG_stack_size, 8);
WasmRunner<uint32_t, uint32_t> r(execution_tier);
WasmFunctionCompiler& fact_aux_fn =
r.NewFunction<uint32_t, uint32_t, uint32_t>("fact_aux");
BUILD(r, WASM_RETURN_CALL_FUNCTION(fact_aux_fn.function_index(),
WASM_GET_LOCAL(0), WASM_I32V(1)));
BUILD(fact_aux_fn,
WASM_IF_ELSE_I(
WASM_I32_LES(WASM_GET_LOCAL(0), WASM_I32V(1)), WASM_GET_LOCAL(1),
WASM_RETURN_CALL_FUNCTION(
fact_aux_fn.function_index(),
WASM_I32_SUB(WASM_GET_LOCAL(0), WASM_I32V(1)),
WASM_I32_MUL(WASM_GET_LOCAL(0), WASM_GET_LOCAL(1)))));
uint32_t test_values[] = {1, 2, 5, 10, 20, 2000};
for (uint32_t v : test_values) {
CHECK_EQ(factorial<uint32_t>(v), r.Call(v));
}
}
// Mutually recursive factorial mixing it up
// f(0,X)=>X
// f(N,X) => g(X*N,N-1)
// g(X,0) => X.
// g(X,N) => f(N-1,X*N).
WASM_EXEC_TEST(ReturnCall_MutualFactorial) {
EXPERIMENTAL_FLAG_SCOPE(return_call);
// Run in bounded amount of stack - 8kb.
FlagScope<int32_t> stack_size(&v8::internal::FLAG_stack_size, 8);
WasmRunner<uint32_t, uint32_t> r(execution_tier);
WasmFunctionCompiler& f_fn = r.NewFunction<uint32_t, uint32_t, uint32_t>("f");
WasmFunctionCompiler& g_fn = r.NewFunction<uint32_t, uint32_t, uint32_t>("g");
BUILD(r, WASM_RETURN_CALL_FUNCTION(f_fn.function_index(), WASM_GET_LOCAL(0),
WASM_I32V(1)));
BUILD(f_fn,
WASM_IF_ELSE_I(WASM_I32_LES(WASM_GET_LOCAL(0), WASM_I32V(1)),
WASM_GET_LOCAL(1),
WASM_RETURN_CALL_FUNCTION(
g_fn.function_index(),
WASM_I32_MUL(WASM_GET_LOCAL(0), WASM_GET_LOCAL(1)),
WASM_I32_SUB(WASM_GET_LOCAL(0), WASM_I32V(1)))));
BUILD(g_fn,
WASM_IF_ELSE_I(
WASM_I32_LES(WASM_GET_LOCAL(1), WASM_I32V(1)), WASM_GET_LOCAL(0),
WASM_RETURN_CALL_FUNCTION(
f_fn.function_index(),
WASM_I32_SUB(WASM_GET_LOCAL(1), WASM_I32V(1)),
WASM_I32_MUL(WASM_GET_LOCAL(1), WASM_GET_LOCAL(0)))));
uint32_t test_values[] = {1, 2, 5, 10, 20, 2000};
for (uint32_t v : test_values) {
CHECK_EQ(factorial(v), r.Call(v));
}
}
// Indirect variant of factorial. Pass the function ID as an argument:
// fact(N) => f(N,1,f).
//
// f(N,X,_) where N=<1 => X
// f(N,X,F) => F(N-1,X*N,F).
WASM_EXEC_TEST(ReturnCall_IndirectFactorial) {
EXPERIMENTAL_FLAG_SCOPE(return_call);
// Run in bounded amount of stack - 8kb.
FlagScope<int32_t> stack_size(&v8::internal::FLAG_stack_size, 8);
WasmRunner<uint32_t, uint32_t> r(execution_tier);
TestSignatures sigs;
WasmFunctionCompiler& f_ind_fn = r.NewFunction(sigs.i_iii(), "f_ind");
uint32_t sig_index = r.builder().AddSignature(sigs.i_iii());
f_ind_fn.SetSigIndex(sig_index);
// Function table.
uint16_t indirect_function_table[] = {
static_cast<uint16_t>(f_ind_fn.function_index())};
const int f_ind_index = 0;
r.builder().AddIndirectFunctionTable(indirect_function_table,
arraysize(indirect_function_table));
r.builder().PopulateIndirectFunctionTable();
BUILD(r,
WASM_RETURN_CALL_FUNCTION(f_ind_fn.function_index(), WASM_GET_LOCAL(0),
WASM_I32V(1), WASM_I32V(f_ind_index)));
BUILD(f_ind_fn,
WASM_IF_ELSE_I(WASM_I32_LES(WASM_GET_LOCAL(0), WASM_I32V(1)),
WASM_GET_LOCAL(1),
WASM_RETURN_CALL_INDIRECT(
sig_index, WASM_GET_LOCAL(2),
WASM_I32_SUB(WASM_GET_LOCAL(0), WASM_I32V(1)),
WASM_I32_MUL(WASM_GET_LOCAL(0), WASM_GET_LOCAL(1)),
WASM_GET_LOCAL(2))));
uint32_t test_values[] = {1, 2, 5, 10, 10000};
for (uint32_t v : test_values) {
CHECK_EQ(factorial(v), r.Call(v));
}
}
// This is 'more stable' (does not degenerate so quickly) than factorial
// sum(N,k) where N<1 =>k.
// sum(N,k) => sum(N-1,k+N).
WASM_EXEC_TEST(ReturnCall_Sum) {
EXPERIMENTAL_FLAG_SCOPE(return_call);
// Run in bounded amount of stack - 8kb.
FlagScope<int32_t> stack_size(&v8::internal::FLAG_stack_size, 8);
WasmRunner<int32_t, int32_t> r(execution_tier);
TestSignatures sigs;
WasmFunctionCompiler& sum_aux_fn = r.NewFunction(sigs.i_ii(), "sum_aux");
BUILD(r, WASM_RETURN_CALL_FUNCTION(sum_aux_fn.function_index(),
WASM_GET_LOCAL(0), WASM_I32V(0)));
BUILD(sum_aux_fn,
WASM_IF_ELSE_I(
WASM_I32_LTS(WASM_GET_LOCAL(0), WASM_I32V(1)), WASM_GET_LOCAL(1),
WASM_RETURN_CALL_FUNCTION(
sum_aux_fn.function_index(),
WASM_I32_SUB(WASM_GET_LOCAL(0), WASM_I32V(1)),
WASM_I32_ADD(WASM_GET_LOCAL(0), WASM_GET_LOCAL(1)))));
int32_t test_values[] = {1, 2, 5, 10, 1000};
for (int32_t v : test_values) {
CHECK_EQ(sum_1_to_n(v), r.Call(v));
}
}
// 'Bouncing' mutual recursive sum with different #s of arguments
// b1(N,k) where N<1 =>k.
// b1(N,k) => b2(N-1,N,k+N).
// b2(N,_,k) where N<1 =>k.
// b2(N,l,k) => b3(N-1,N,l,k+N).
// b3(N,_,_,k) where N<1 =>k.
// b3(N,_,_,k) => b1(N-1,k+N).
WASM_EXEC_TEST(ReturnCall_Bounce_Sum) {
EXPERIMENTAL_FLAG_SCOPE(return_call);
// Run in bounded amount of stack - 8kb.
FlagScope<int32_t> stack_size(&v8::internal::FLAG_stack_size, 8);
WasmRunner<int32_t, int32_t> r(execution_tier);
TestSignatures sigs;
WasmFunctionCompiler& b1_fn = r.NewFunction(sigs.i_ii(), "b1");
WasmFunctionCompiler& b2_fn = r.NewFunction(sigs.i_iii(), "b2");
WasmFunctionCompiler& b3_fn =
r.NewFunction<int32_t, int32_t, int32_t, int32_t, int32_t>("b3");
BUILD(r, WASM_RETURN_CALL_FUNCTION(b1_fn.function_index(), WASM_GET_LOCAL(0),
WASM_I32V(0)));
BUILD(
b1_fn,
WASM_IF_ELSE_I(
WASM_I32_LTS(WASM_GET_LOCAL(0), WASM_I32V(1)), WASM_GET_LOCAL(1),
WASM_RETURN_CALL_FUNCTION(
b2_fn.function_index(),
WASM_I32_SUB(WASM_GET_LOCAL(0), WASM_I32V(1)), WASM_GET_LOCAL(0),
WASM_I32_ADD(WASM_GET_LOCAL(0), WASM_GET_LOCAL(1)))));
BUILD(b2_fn,
WASM_IF_ELSE_I(
WASM_I32_LTS(WASM_GET_LOCAL(0), WASM_I32V(1)), WASM_GET_LOCAL(2),
WASM_RETURN_CALL_FUNCTION(
b3_fn.function_index(),
WASM_I32_SUB(WASM_GET_LOCAL(0), WASM_I32V(1)),
WASM_GET_LOCAL(0), WASM_GET_LOCAL(1),
WASM_I32_ADD(WASM_GET_LOCAL(0), WASM_GET_LOCAL(2)))));
BUILD(b3_fn,
WASM_IF_ELSE_I(
WASM_I32_LTS(WASM_GET_LOCAL(0), WASM_I32V(1)), WASM_GET_LOCAL(3),
WASM_RETURN_CALL_FUNCTION(
b1_fn.function_index(),
WASM_I32_SUB(WASM_GET_LOCAL(0), WASM_I32V(1)),
WASM_I32_ADD(WASM_GET_LOCAL(0), WASM_GET_LOCAL(3)))));
int32_t test_values[] = {1, 2, 5, 10, 1000};
for (int32_t v : test_values) {
CHECK_EQ(sum_1_to_n(v), r.Call(v));
}
}
#define ADD_CODE(vec, ...) \
do { \
byte __buf[] = {__VA_ARGS__}; \
......
......@@ -407,6 +407,7 @@ class WasmRunnerBase : public HandleAndZoneScope {
WasmFunctionWrapper wrapper_;
bool compiled_ = false;
bool possible_nondeterminism_ = false;
int32_t main_fn_index_ = 0;
public:
// This field has to be static. Otherwise, gcc complains about the use in
......@@ -425,9 +426,13 @@ class WasmRunner : public WasmRunnerBase {
LowerSimd lower_simd = kNoLowerSimd)
: WasmRunnerBase(maybe_import, execution_tier, sizeof...(ParamTypes),
runtime_exception_support, lower_simd) {
WasmFunctionCompiler& main_fn =
NewFunction<ReturnType, ParamTypes...>(main_fn_name);
// Non-zero if there is an import.
main_fn_index_ = main_fn.function_index();
if (!interpret()) {
wrapper_.Init<ReturnType, ParamTypes...>(functions_[0]->descriptor());
wrapper_.Init<ReturnType, ParamTypes...>(main_fn.descriptor());
}
}
......@@ -448,7 +453,7 @@ class WasmRunner : public WasmRunnerBase {
};
set_trap_callback_for_testing(trap_callback);
wrapper_.SetInnerCode(builder_.GetFunctionCode(0));
wrapper_.SetInnerCode(builder_.GetFunctionCode(main_fn_index_));
wrapper_.SetInstance(builder_.instance_object());
builder_.SetExecutable();
Handle<Code> wrapper_code = wrapper_.GetWrapperCode();
......@@ -520,9 +525,8 @@ class WasmRunner : public WasmRunnerBase {
void CheckCallViaJS(double expected, ParamTypes... p) {
Isolate* isolate = builder_.isolate();
uint32_t function_index = function()->func_index;
Handle<Object> buffer[] = {isolate->factory()->NewNumber(p)...};
CheckCallViaJS(expected, function_index, buffer, sizeof...(p));
CheckCallViaJS(expected, function()->func_index, buffer, sizeof...(p));
}
Handle<Code> GetWrapperCode() { return wrapper_.GetWrapperCode(); }
......
// Copyright 2019 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --expose-wasm --experimental-wasm-return-call --stack-size=64
load("test/mjsunit/wasm/wasm-module-builder.js");
(function TestFactorialReturnCall() {
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
const sig_i_iii = builder.addType(kSig_i_iii);
// construct the code for the function
// f_aux(N,X) where N=<1 => X
// f_aux(N,X) => f_aux(N-1,X*N)
let fact_aux = builder.addFunction("fact_aux",kSig_i_ii);
fact_aux.addBody([
kExprGetLocal, 0, kExprI32Const, 1, kExprI32LeS,
kExprIf, kWasmI32,
kExprGetLocal, 1,
kExprElse,
kExprGetLocal, 0,
kExprI32Const, 1,
kExprI32Sub,
kExprGetLocal, 0,
kExprGetLocal, 1,
kExprI32Mul,
kExprReturnCall, fact_aux.index,
kExprEnd
]);
//main(N)=>fact_aux(N,1)
let main = builder.addFunction("main", kSig_i_i)
.addBody([
kExprGetLocal, 0,
kExprI32Const, 1,
kExprReturnCall,0
]).exportFunc();
let module = builder.instantiate();
print(" --three--");
assertEquals(6, module.exports.main(3));
print(" --four--");
assertEquals(24, module.exports.main(4));
})();
(function TestIndirectFactorialReturnCall() {
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
const sig_i_iii = builder.addType(kSig_i_iii);
// construct the code for the function
// fact(N) => f_ind(N,1,f).
//
// f_ind(N,X,_) where N=<1 => X
// f_ind(N,X,F) => F(N-1,X*N,F).
let f_ind = builder.addFunction("f_ind",kSig_i_iii).
addBody([
kExprGetLocal, 0, kExprI32Const, 1, kExprI32LeS,
kExprIf, kWasmI32,
kExprGetLocal, 1,
kExprElse,
kExprGetLocal, 0,
kExprI32Const, 1,
kExprI32Sub,
kExprGetLocal, 0,
kExprGetLocal, 1,
kExprI32Mul,
kExprGetLocal, 2,
kExprGetLocal, 2,
kExprReturnCallIndirect, sig_i_iii, kTableZero,
kExprEnd
]);
//main(N)=>fact_aux(N,1)
let main = builder.addFunction("main", kSig_i_i)
.addBody([
kExprGetLocal, 0,
kExprI32Const, 1,
kExprI32Const, f_ind.index,
kExprReturnCall, f_ind.index
]).exportFunc();
builder.appendToTable([f_ind.index, main.index]);
let module = builder.instantiate();
print(" --three--");
assertEquals(6, module.exports.main(3));
print(" --four--");
assertEquals(24, module.exports.main(4));
})();
(function TestImportReturnCall() {
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
const sig_i_iii = builder.addType(kSig_i_iii);
let pick = builder.addImport("q", "pick", sig_i_iii);
let main = builder.addFunction("main", kSig_i_iii)
.addBody([
kExprGetLocal, 1,
kExprGetLocal, 2,
kExprGetLocal, 0,
kExprReturnCall, pick
])
.exportFunc();
let module = builder.instantiate({q: {
pick: function(a, b, c) { return c ? a : b; }}});
print(" --left--");
assertEquals(-2, module.exports.main(1, -2, 3));
print(" --right--");
assertEquals(3, module.exports.main(0, -2, 3));
})();
(function TestImportIndirectReturnCall() {
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
const sig_i_iii = builder.addType(kSig_i_iii);
let pick = builder.addImport("q", "pick", sig_i_iii);
const tableIndex = 3; // Arbitrary location of import
builder.addElementSegment(tableIndex,false,[pick]);
let main = builder.addFunction("main", kSig_i_iii)
.addBody([
kExprGetLocal, 1,
kExprGetLocal, 2,
kExprGetLocal, 0,
kExprI32Const, tableIndex,
kExprReturnCallIndirect, sig_i_iii, kTableZero
])
.exportFunc();
builder.appendToTable([pick, main.index]);
let module = builder.instantiate({q: {
pick: function(a, b, c) { return c ? a : b; }
}});
print(" --left--");
assertEquals(-2, module.exports.main(1, -2, 3));
print(" --right--");
assertEquals(3, module.exports.main(0, -2, 3));
})();
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment