Commit a2efde46 authored by clemensh's avatar clemensh Committed by Commit bot

[wasm] Add support for compiling WASM_INTERPRETER_ENTRY stubs

Also, add a runtime function to call the interpreter, passing a
stack-allocated buffer holding the arguments.
The WASM_INTERPRETER_ENTRY stub allocates the stack slot for the
arguments, fills it, and calls to the wasm interpreter.
It's abi is compatible with WASM functions, such that we can just
replace a call to a WASM_FUNCTION with a call to
WASM_INTERPRETER_ENTRY.
See tracking bug to get the overall picture.

BUG=v8:5822
R=titzer@chromium.org

Review-Url: https://codereview.chromium.org/2619803004
Cr-Commit-Position: refs/heads/master@{#42242}
parent 9e60e7ac
......@@ -36,6 +36,7 @@
#include "src/wasm/function-body-decoder.h"
#include "src/wasm/wasm-limits.h"
#include "src/wasm/wasm-module.h"
#include "src/wasm/wasm-objects.h"
#include "src/wasm/wasm-opcodes.h"
#include "src/wasm/wasm-text.h"
......@@ -70,9 +71,6 @@ Node* BuildCallToRuntime(Runtime::FunctionId f, JSGraph* jsgraph,
Handle<Context> context, Node** parameters,
int parameter_count, Node** effect_ptr,
Node* control) {
// At the moment we only allow 2 parameters. If more parameters are needed,
// then the size of {inputs} below has to be increased accordingly.
DCHECK(parameter_count <= 2);
const Runtime::Function* fun = Runtime::FunctionForId(f);
CallDescriptor* desc = Linkage::GetRuntimeCallDescriptor(
jsgraph->zone(), f, fun->nargs, Operator::kNoProperties,
......@@ -80,7 +78,11 @@ Node* BuildCallToRuntime(Runtime::FunctionId f, JSGraph* jsgraph,
// CEntryStubConstant nodes have to be created and cached in the main
// thread. At the moment this is only done for CEntryStubConstant(1).
DCHECK_EQ(1, fun->result_size);
Node* inputs[8];
// At the moment we only allow 2 parameters. If more parameters are needed,
// increase this constant accordingly.
static const int kMaxParams = 3;
DCHECK_GE(kMaxParams, parameter_count);
Node* inputs[kMaxParams + 6];
int count = 0;
inputs[count++] = jsgraph->CEntryStubConstant(fun->result_size);
for (int i = 0; i < parameter_count; i++) {
......@@ -2927,6 +2929,101 @@ void WasmGraphBuilder::BuildWasmToJSWrapper(Handle<JSReceiver> target,
Return(val);
}
void WasmGraphBuilder::BuildWasmInterpreterEntry(
uint32_t function_index, wasm::FunctionSig* sig,
Handle<WasmInstanceObject> instance) {
int wasm_count = static_cast<int>(sig->parameter_count());
int param_count = jsgraph()->machine()->Is64()
? wasm_count
: Int64Lowering::GetParameterCountAfterLowering(sig);
// Build the start and the parameter nodes.
Node* start = Start(param_count + 3);
*effect_ = start;
*control_ = start;
// Compute size for the argument buffer.
int args_size_bytes = 0;
for (int i = 0; i < wasm_count; i++) {
args_size_bytes += 1 << ElementSizeLog2Of(sig->GetParam(i));
}
// The return value is also passed via this buffer:
DCHECK_GE(1, sig->return_count());
int return_size_bytes =
sig->return_count() == 0 ? 0 : 1 << ElementSizeLog2Of(sig->GetReturn(0));
// Get a stack slot for the arguments.
Node* arg_buffer = graph()->NewNode(jsgraph()->machine()->StackSlot(
std::max(args_size_bytes, return_size_bytes)));
// Now store all our arguments to the buffer.
int param_index = 0;
int offset = 0;
for (int i = 0; i < wasm_count; i++) {
Node* param = Param(param_index++);
bool is_i64_as_two_params =
jsgraph()->machine()->Is32() && sig->GetParam(i) == wasm::kWasmI64;
MachineRepresentation param_rep =
is_i64_as_two_params ? wasm::kWasmI32 : sig->GetParam(i);
StoreRepresentation store_rep(param_rep, WriteBarrierKind::kNoWriteBarrier);
*effect_ =
graph()->NewNode(jsgraph()->machine()->Store(store_rep), arg_buffer,
Int32Constant(offset), param, *effect_, *control_);
offset += 1 << ElementSizeLog2Of(param_rep);
// TODO(clemensh): Respect endianess here. Might need to swap upper and
// lower word.
if (is_i64_as_two_params) {
// Also store the upper half.
param = Param(param_index++);
StoreRepresentation store_rep(wasm::kWasmI32,
WriteBarrierKind::kNoWriteBarrier);
*effect_ =
graph()->NewNode(jsgraph()->machine()->Store(store_rep), arg_buffer,
Int32Constant(offset), param, *effect_, *control_);
offset += 1 << ElementSizeLog2Of(wasm::kWasmI32);
}
}
DCHECK_EQ(param_count, param_index);
DCHECK_EQ(args_size_bytes, offset);
// We are passing the raw arg_buffer here. To the GC and other parts, it looks
// like a Smi (lowest bit not set). In the runtime function however, don't
// call Smi::value on it, but just cast it to a byte pointer.
Node* parameters[] = {
jsgraph()->HeapConstant(instance), // wasm instance
jsgraph()->SmiConstant(function_index), // function index
arg_buffer, // argument buffer
};
BuildCallToRuntime(Runtime::kWasmRunInterpreter, jsgraph(),
jsgraph()->isolate()->native_context(), parameters,
arraysize(parameters), effect_, *control_);
// Read back the return value.
if (jsgraph()->machine()->Is32() && sig->return_count() > 0 &&
sig->GetReturn() == wasm::kWasmI64) {
MachineType load_rep = wasm::WasmOpcodes::MachineTypeFor(wasm::kWasmI32);
Node* lower =
graph()->NewNode(jsgraph()->machine()->Load(load_rep), arg_buffer,
Int32Constant(0), *effect_, *control_);
Node* upper =
graph()->NewNode(jsgraph()->machine()->Load(load_rep), arg_buffer,
Int32Constant(sizeof(int32_t)), *effect_, *control_);
Return(upper, lower);
} else {
Node* val;
if (sig->return_count() == 0) {
val = Int32Constant(0);
} else {
MachineType load_rep =
wasm::WasmOpcodes::MachineTypeFor(sig->GetReturn());
val = graph()->NewNode(jsgraph()->machine()->Load(load_rep), arg_buffer,
Int32Constant(0), *effect_, *control_);
}
Return(val);
}
}
Node* WasmGraphBuilder::MemBuffer(uint32_t offset) {
DCHECK(module_ && module_->instance);
if (offset == 0) {
......@@ -3508,6 +3605,72 @@ Handle<Code> CompileWasmToJSWrapper(Isolate* isolate, Handle<JSReceiver> target,
return code;
}
Handle<Code> CompileWasmInterpreterEntry(Isolate* isolate, uint32_t func_index,
wasm::FunctionSig* sig,
Handle<WasmInstanceObject> instance) {
//----------------------------------------------------------------------------
// Create the Graph
//----------------------------------------------------------------------------
Zone zone(isolate->allocator(), ZONE_NAME);
Graph graph(&zone);
CommonOperatorBuilder common(&zone);
MachineOperatorBuilder machine(&zone);
JSGraph jsgraph(isolate, &graph, &common, nullptr, nullptr, &machine);
Node* control = nullptr;
Node* effect = nullptr;
WasmGraphBuilder builder(nullptr, &zone, &jsgraph, sig);
builder.set_control_ptr(&control);
builder.set_effect_ptr(&effect);
builder.BuildWasmInterpreterEntry(func_index, sig, instance);
Handle<Code> code = Handle<Code>::null();
{
if (FLAG_trace_turbo_graph) { // Simple textual RPO.
OFStream os(stdout);
os << "-- Wasm to interpreter graph -- " << std::endl;
os << AsRPO(graph);
}
// Schedule and compile to machine code.
CallDescriptor* incoming =
wasm::ModuleEnv::GetWasmCallDescriptor(&zone, sig);
if (machine.Is32()) {
incoming = wasm::ModuleEnv::GetI32WasmCallDescriptor(&zone, incoming);
}
Code::Flags flags = Code::ComputeFlags(Code::WASM_INTERPRETER_ENTRY);
EmbeddedVector<char, 32> debug_name;
int name_len = SNPrintF(debug_name, "wasm-to-interpreter#%d", func_index);
DCHECK(name_len > 0 && name_len < debug_name.length());
debug_name.Truncate(name_len);
DCHECK_EQ('\0', debug_name.start()[debug_name.length()]);
CompilationInfo info(debug_name, isolate, &zone, flags);
code = Pipeline::GenerateCodeForTesting(&info, incoming, &graph, nullptr);
#ifdef ENABLE_DISASSEMBLER
if (FLAG_print_opt_code && !code.is_null()) {
OFStream os(stdout);
code->Disassemble(debug_name.start(), os);
}
#endif
if (isolate->logger()->is_logging_code_events() ||
isolate->is_profiling()) {
RecordFunctionCompilation(CodeEventListener::FUNCTION_TAG, isolate, code,
"wasm-to-interpreter", func_index,
wasm::WasmName("module"), debug_name);
}
}
Handle<FixedArray> deopt_data = isolate->factory()->NewFixedArray(1, TENURED);
Handle<WeakCell> weak_instance = isolate->factory()->NewWeakCell(instance);
deopt_data->set(0, *weak_instance);
code->set_deoptimization_data(*deopt_data);
return code;
}
SourcePositionTable* WasmCompilationUnit::BuildGraphForWasmFunction(
double* decode_ms) {
base::ElapsedTimer decode_timer;
......
......@@ -102,6 +102,12 @@ Handle<Code> CompileJSToWasmWrapper(Isolate* isolate,
const wasm::WasmModule* module,
Handle<Code> wasm_code, uint32_t index);
// Compiles a stub that redirects a call to a wasm function to the wasm
// interpreter. It's ABI compatible with the compiled wasm function.
Handle<Code> CompileWasmInterpreterEntry(Isolate* isolate, uint32_t func_index,
wasm::FunctionSig* sig,
Handle<WasmInstanceObject> instance);
// Abstracts details of building TurboFan graph nodes for WASM to separate
// the WASM decoder from the internal details of TurboFan.
class WasmTrapHelper;
......@@ -182,6 +188,8 @@ class WasmGraphBuilder {
void BuildJSToWasmWrapper(Handle<Code> wasm_code, wasm::FunctionSig* sig);
void BuildWasmToJSWrapper(Handle<JSReceiver> target, wasm::FunctionSig* sig);
void BuildWasmInterpreterEntry(uint32_t func_index, wasm::FunctionSig* sig,
Handle<WasmInstanceObject> instance);
Node* ToJS(Node* node, wasm::ValueType type);
Node* FromJS(Node* node, Node* context, wasm::ValueType type);
......
......@@ -152,5 +152,28 @@ RUNTIME_FUNCTION(Runtime_WasmGetCaughtExceptionValue) {
return exception;
}
RUNTIME_FUNCTION(Runtime_WasmRunInterpreter) {
DCHECK(args.length() == 3);
HandleScope scope(isolate);
CONVERT_ARG_HANDLE_CHECKED(JSObject, instance_obj, 0);
CONVERT_NUMBER_CHECKED(int32_t, func_index, Int32, args[1]);
CONVERT_ARG_HANDLE_CHECKED(Object, arg_buffer_obj, 2);
CHECK(WasmInstanceObject::IsWasmInstanceObject(*instance_obj));
// The arg buffer is the raw pointer to the caller's stack. It looks like a
// Smi (lowest bit not set, as checked by IsSmi), but is no valid Smi. We just
// cast it back to the raw pointer.
CHECK(!arg_buffer_obj->IsHeapObject());
CHECK(arg_buffer_obj->IsSmi());
uint8_t* arg_buffer = reinterpret_cast<uint8_t*>(*arg_buffer_obj);
Handle<WasmInstanceObject> instance =
Handle<WasmInstanceObject>::cast(instance_obj);
Handle<WasmDebugInfo> debug_info =
WasmInstanceObject::GetOrCreateDebugInfo(instance);
WasmDebugInfo::RunInterpreter(debug_info, func_index, arg_buffer);
return isolate->heap()->undefined_value();
}
} // namespace internal
} // namespace v8
......@@ -943,7 +943,8 @@ namespace internal {
F(ThrowWasmTrapRemByZero, 0, 1) \
F(ThrowWasmTrapFloatUnrepresentable, 0, 1) \
F(ThrowWasmTrapFuncInvalid, 0, 1) \
F(ThrowWasmTrapFuncSigMismatch, 0, 1)
F(ThrowWasmTrapFuncSigMismatch, 0, 1) \
F(WasmRunInterpreter, 3, 1)
#define FOR_EACH_INTRINSIC_RETURN_PAIR(F) \
F(LoadLookupSlotForCall, 1, 2)
......
......@@ -36,3 +36,9 @@ WasmDebugInfo *WasmDebugInfo::cast(Object *object) {
WasmInstanceObject *WasmDebugInfo::wasm_instance() {
return WasmInstanceObject::cast(get(kInstance));
}
void WasmDebugInfo::RunInterpreter(Handle<WasmDebugInfo> debug_info,
int func_index, uint8_t *arg_buffer) {
// TODO(clemensh): Implement this.
UNIMPLEMENTED();
}
......@@ -383,6 +383,9 @@ class WasmDebugInfo : public FixedArray {
static bool IsDebugInfo(Object*);
static WasmDebugInfo* cast(Object*);
static void RunInterpreter(Handle<WasmDebugInfo>, int func_index,
uint8_t* arg_buffer);
DECLARE_GETTER(wasm_instance, WasmInstanceObject);
};
......
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