Commit 9174bb4d authored by Frederik Gossen's avatar Frederik Gossen Committed by Commit Bot

[wasm] Refactoring for a Wasm Interpreter as a First-class Tier

Added a new compilation unit for Wasm interpreter in order to make it
a first-class tier in the future. Adapted Wasm interpreter usage to
work with the new interface. The new compilation unit is currently
not used.

Change-Id: Ib9e1d0dc6ca1b03467cc43059f03ce153bb96400
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1514734
Commit-Queue: Frederik Gossen <frgossen@google.com>
Reviewed-by: 's avatarMichael Starzinger <mstarzinger@chromium.org>
Cr-Commit-Position: refs/heads/master@{#60183}
parent c420b34f
......@@ -2160,16 +2160,21 @@ MaybeHandle<Code> Pipeline::GenerateCodeForCodeStub(
}
// static
wasm::WasmCode* Pipeline::GenerateCodeForWasmNativeStub(
wasm::WasmCompilationResult Pipeline::GenerateCodeForWasmNativeStub(
wasm::WasmEngine* wasm_engine, CallDescriptor* call_descriptor,
MachineGraph* mcgraph, Code::Kind kind, int wasm_kind,
const char* debug_name, const AssemblerOptions& options,
wasm::NativeModule* native_module, SourcePositionTable* source_positions) {
SourcePositionTable* source_positions) {
Graph* graph = mcgraph->graph();
OptimizedCompilationInfo info(CStrVector(debug_name), graph->zone(), kind);
// Construct a pipeline for scheduling and code generation.
ZoneStats zone_stats(wasm_engine->allocator());
NodeOriginTable* node_positions = new (graph->zone()) NodeOriginTable(graph);
// {instruction_buffer} must live longer than {PipelineData}, since
// {PipelineData} will reference the {instruction_buffer} via the
// {AssemblerBuffer} of the {Assembler} contained in the {CodeGenerator}.
std::unique_ptr<wasm::WasmInstructionBuffer> instruction_buffer =
wasm::WasmInstructionBuffer::New();
PipelineData data(&zone_stats, wasm_engine, &info, mcgraph, nullptr,
source_positions, node_positions, options);
std::unique_ptr<PipelineStatistics> pipeline_statistics;
......@@ -2205,22 +2210,21 @@ wasm::WasmCode* Pipeline::GenerateCodeForWasmNativeStub(
pipeline.ComputeScheduledGraph();
Linkage linkage(call_descriptor);
if (!pipeline.SelectInstructions(&linkage)) return nullptr;
pipeline.AssembleCode(&linkage);
CHECK(pipeline.SelectInstructions(&linkage));
pipeline.AssembleCode(&linkage, instruction_buffer->CreateView());
CodeGenerator* code_generator = pipeline.code_generator();
CodeDesc code_desc;
wasm::WasmCompilationResult result;
code_generator->tasm()->GetCode(
nullptr, &code_desc, code_generator->safepoint_table_builder(),
nullptr, &result.code_desc, code_generator->safepoint_table_builder(),
static_cast<int>(code_generator->GetHandlerTableOffset()));
result.instr_buffer = instruction_buffer->ReleaseBuffer();
result.source_positions = code_generator->GetSourcePositionTable();
result.protected_instructions = code_generator->GetProtectedInstructions();
result.frame_slot_count = code_generator->frame()->GetTotalFrameSlotCount();
result.tagged_parameter_slots = call_descriptor->GetTaggedParameterSlots();
wasm::WasmCode* code = native_module->AddCode(
wasm::WasmCode::kAnonymousFuncIndex, code_desc,
code_generator->frame()->GetTotalFrameSlotCount(),
call_descriptor->GetTaggedParameterSlots(),
code_generator->GetProtectedInstructions(),
code_generator->GetSourcePositionTable(),
static_cast<wasm::WasmCode::Kind>(wasm_kind), wasm::WasmCode::kOther);
DCHECK(result.succeeded());
if (info.trace_turbo_json_enabled()) {
TurboJsonFile json_of(&info, std::ios_base::app);
......@@ -2228,9 +2232,9 @@ wasm::WasmCode* Pipeline::GenerateCodeForWasmNativeStub(
#ifdef ENABLE_DISASSEMBLER
std::stringstream disassembler_stream;
Disassembler::Decode(
nullptr, &disassembler_stream, code->instructions().start(),
code->instructions().start() + code->safepoint_table_offset(),
CodeReference(code));
nullptr, &disassembler_stream, result.code_desc.buffer,
result.code_desc.buffer + result.code_desc.safepoint_table_offset,
CodeReference(&result.code_desc));
for (auto const c : disassembler_stream.str()) {
json_of << AsEscapedUC16ForJSON(c);
}
......@@ -2247,7 +2251,7 @@ wasm::WasmCode* Pipeline::GenerateCodeForWasmNativeStub(
<< " using Turbofan" << std::endl;
}
return code;
return result;
}
// static
......@@ -2474,7 +2478,7 @@ void Pipeline::GenerateCodeForWasmFunction(
std::stringstream disassembler_stream;
Disassembler::Decode(
nullptr, &disassembler_stream, result->code_desc.buffer,
result->code_desc.buffer + result->code_desc.instr_size,
result->code_desc.buffer + result->code_desc.safepoint_table_offset,
CodeReference(&result->code_desc));
for (auto const c : disassembler_stream.str()) {
json_of << AsEscapedUC16ForJSON(c);
......
......@@ -22,7 +22,7 @@ class RegisterConfiguration;
namespace wasm {
struct FunctionBody;
class NativeModule;
class WasmCode;
struct WasmCompilationResult;
class WasmEngine;
struct WasmModule;
} // namespace wasm
......@@ -54,11 +54,10 @@ class Pipeline : public AllStatic {
int function_index);
// Run the pipeline on a machine graph and generate code.
static wasm::WasmCode* GenerateCodeForWasmNativeStub(
static wasm::WasmCompilationResult GenerateCodeForWasmNativeStub(
wasm::WasmEngine* wasm_engine, CallDescriptor* call_descriptor,
MachineGraph* mcgraph, Code::Kind kind, int wasm_kind,
const char* debug_name, const AssemblerOptions& assembler_options,
wasm::NativeModule* native_module,
SourcePositionTable* source_positions = nullptr);
// Run the pipeline on a machine graph and generate code.
......
......@@ -41,7 +41,7 @@
#include "src/tracing/trace-event.h"
#include "src/trap-handler/trap-handler.h"
#include "src/vector.h"
#include "src/wasm/function-body-decoder.h"
#include "src/wasm/function-body-decoder-impl.h"
#include "src/wasm/function-compiler.h"
#include "src/wasm/graph-builder-interface.h"
#include "src/wasm/jump-table-assembler.h"
......@@ -5813,22 +5813,22 @@ wasm::WasmCode* CompileWasmMathIntrinsic(wasm::WasmEngine* wasm_engine,
builder.set_instance_node(builder.Param(wasm::kWasmInstanceParameterIndex));
// Generate either a unop or a binop.
Node* result = nullptr;
Node* node = nullptr;
const char* debug_name = "WasmMathIntrinsic";
auto opcode = GetMathIntrinsicOpcode(kind, &debug_name);
switch (sig->parameter_count()) {
case 1:
result = builder.Unop(opcode, builder.Param(1));
node = builder.Unop(opcode, builder.Param(1));
break;
case 2:
result = builder.Binop(opcode, builder.Param(1), builder.Param(2));
node = builder.Binop(opcode, builder.Param(1), builder.Param(2));
break;
default:
UNREACHABLE();
break;
}
builder.Return(result);
builder.Return(node);
// Run the compiler pipeline to generate machine code.
auto call_descriptor = GetWasmCallDescriptor(&zone, sig);
......@@ -5836,10 +5836,16 @@ wasm::WasmCode* CompileWasmMathIntrinsic(wasm::WasmEngine* wasm_engine,
call_descriptor = GetI32WasmCallDescriptor(&zone, call_descriptor);
}
wasm::WasmCode* wasm_code = Pipeline::GenerateCodeForWasmNativeStub(
wasm::WasmCompilationResult result = Pipeline::GenerateCodeForWasmNativeStub(
wasm_engine, call_descriptor, mcgraph, Code::WASM_FUNCTION,
wasm::WasmCode::kFunction, debug_name, WasmStubAssemblerOptions(),
native_module, source_positions);
source_positions);
wasm::WasmCode* wasm_code = native_module->AddCode(
wasm::WasmCode::kAnonymousFuncIndex, result.code_desc,
result.frame_slot_count, result.tagged_parameter_slots,
std::move(result.protected_instructions),
std::move(result.source_positions), wasm::WasmCode::kFunction,
wasm::WasmCode::kOther);
CHECK_NOT_NULL(wasm_code);
// TODO(titzer): add counters for math intrinsic code size / allocation
......@@ -5897,19 +5903,25 @@ wasm::WasmCode* CompileWasmImportCallWrapper(wasm::WasmEngine* wasm_engine,
if (machine.Is32()) {
incoming = GetI32WasmCallDescriptor(&zone, incoming);
}
wasm::WasmCode* wasm_code = Pipeline::GenerateCodeForWasmNativeStub(
wasm::WasmCompilationResult result = Pipeline::GenerateCodeForWasmNativeStub(
wasm_engine, incoming, &jsgraph, Code::WASM_TO_JS_FUNCTION,
wasm::WasmCode::kWasmToJsWrapper, func_name, WasmStubAssemblerOptions(),
native_module, source_position_table);
source_position_table);
wasm::WasmCode* wasm_code = native_module->AddCode(
wasm::WasmCode::kAnonymousFuncIndex, result.code_desc,
result.frame_slot_count, result.tagged_parameter_slots,
std::move(result.protected_instructions),
std::move(result.source_positions), wasm::WasmCode::kWasmToJsWrapper,
wasm::WasmCode::kOther);
CHECK_NOT_NULL(wasm_code);
return wasm_code;
}
wasm::WasmCode* CompileWasmInterpreterEntry(wasm::WasmEngine* wasm_engine,
wasm::NativeModule* native_module,
uint32_t func_index,
wasm::FunctionSig* sig) {
wasm::WasmCompilationResult CompileWasmInterpreterEntry(
wasm::WasmEngine* wasm_engine, const wasm::WasmFeatures& enabled_features,
uint32_t func_index, wasm::FunctionSig* sig) {
//----------------------------------------------------------------------------
// Create the Graph
//----------------------------------------------------------------------------
......@@ -5927,7 +5939,7 @@ wasm::WasmCode* CompileWasmInterpreterEntry(wasm::WasmEngine* wasm_engine,
WasmWrapperGraphBuilder builder(&zone, &jsgraph, sig, nullptr,
StubCallMode::kCallWasmRuntimeStub,
native_module->enabled_features());
enabled_features);
builder.set_control_ptr(&control);
builder.set_effect_ptr(&effect);
builder.BuildWasmInterpreterEntry(func_index);
......@@ -5942,13 +5954,12 @@ wasm::WasmCode* CompileWasmInterpreterEntry(wasm::WasmEngine* wasm_engine,
func_name.Truncate(
SNPrintF(func_name, "wasm-interpreter-entry#%d", func_index));
wasm::WasmCode* wasm_code = Pipeline::GenerateCodeForWasmNativeStub(
wasm::WasmCompilationResult result = Pipeline::GenerateCodeForWasmNativeStub(
wasm_engine, incoming, &jsgraph, Code::WASM_INTERPRETER_ENTRY,
wasm::WasmCode::kInterpreterEntry, func_name.start(),
WasmStubAssemblerOptions(), native_module);
CHECK_NOT_NULL(wasm_code);
WasmStubAssemblerOptions());
return wasm_code;
return result;
}
MaybeHandle<Code> CompileCWasmEntry(Isolate* isolate, wasm::FunctionSig* sig) {
......@@ -6142,6 +6153,26 @@ wasm::WasmCompilationResult TurbofanWasmCompilationUnit::ExecuteCompilation(
return std::move(*info.ReleaseWasmCompilationResult());
}
wasm::WasmCompilationResult InterpreterCompilationUnit::ExecuteCompilation(
wasm::CompilationEnv* env, const wasm::FunctionBody& func_body,
Counters* counters, wasm::WasmFeatures* detected) {
Zone zone(wasm_unit_->wasm_engine_->allocator(), ZONE_NAME);
const wasm::WasmModule* module = env ? env->module : nullptr;
wasm::WasmFullDecoder<wasm::Decoder::kValidate, wasm::EmptyInterface> decoder(
&zone, module, env->enabled_features, detected, func_body);
decoder.Decode();
if (decoder.failed()) {
return wasm::WasmCompilationResult{decoder.error()};
}
wasm::WasmCompilationResult result = CompileWasmInterpreterEntry(
wasm_unit_->wasm_engine_, env->enabled_features, wasm_unit_->func_index_,
func_body.sig);
DCHECK(result.succeeded());
return result;
}
namespace {
// Helper for allocating either an GP or FP reg, or the next stack slot.
class LinkageLocationAllocator {
......
......@@ -69,9 +69,25 @@ class TurbofanWasmCompilationUnit {
DISALLOW_COPY_AND_ASSIGN(TurbofanWasmCompilationUnit);
};
// Calls to WASM imports are handled in several different ways, depending
// on the type of the target function/callable and whether the signature
// matches the argument arity.
class InterpreterCompilationUnit final {
public:
explicit InterpreterCompilationUnit(wasm::WasmCompilationUnit* wasm_unit)
: wasm_unit_(wasm_unit) {}
wasm::WasmCompilationResult ExecuteCompilation(wasm::CompilationEnv*,
const wasm::FunctionBody&,
Counters*,
wasm::WasmFeatures* detected);
private:
wasm::WasmCompilationUnit* const wasm_unit_;
DISALLOW_COPY_AND_ASSIGN(InterpreterCompilationUnit);
};
// Calls to WASM imports are handled in several different ways, depending on the
// type of the target function/callable and whether the signature matches the
// argument arity.
enum class WasmImportCallKind : uint8_t {
kLinkError, // static WASM->WASM type error
kRuntimeTypeError, // runtime WASM->JS type error
......@@ -129,10 +145,9 @@ V8_EXPORT_PRIVATE MaybeHandle<Code> CompileJSToWasmWrapper(Isolate*,
// Compiles a stub that redirects a call to a wasm function to the wasm
// interpreter. It's ABI compatible with the compiled wasm function.
wasm::WasmCode* CompileWasmInterpreterEntry(wasm::WasmEngine*,
wasm::NativeModule*,
uint32_t func_index,
wasm::FunctionSig*);
wasm::WasmCompilationResult CompileWasmInterpreterEntry(
wasm::WasmEngine*, const wasm::WasmFeatures& enabled_features,
uint32_t func_index, wasm::FunctionSig*);
enum CWasmEntryParameters {
kCodeEntry,
......
......@@ -82,8 +82,8 @@ class WasmInstructionBufferImpl {
OwnedVector<uint8_t> buffer_ =
OwnedVector<uint8_t>::New(AssemblerBase::kMinimalBufferSize);
// While the buffer is grown, we need to temporarily also keep the old
// buffer alive.
// While the buffer is grown, we need to temporarily also keep the old buffer
// alive.
OwnedVector<uint8_t> old_buffer_;
};
......@@ -173,7 +173,9 @@ WasmCompilationResult WasmCompilationUnit::ExecuteCompilation(
detected);
break;
case ExecutionTier::kInterpreter:
UNREACHABLE(); // TODO(titzer): compile interpreter entry stub.
result = interpreter_unit_->ExecuteCompilation(env, func_body, counters,
detected);
break;
}
if (result.succeeded()) {
......@@ -198,32 +200,41 @@ WasmCode* WasmCompilationUnit::Publish(WasmCompilationResult result,
? WasmCode::kLiftoff
: WasmCode::kTurbofan;
DCHECK_EQ(result.code_desc.buffer, result.instr_buffer.get());
WasmCode::Kind code_kind = executed_tier_ == ExecutionTier::kInterpreter
? WasmCode::Kind::kInterpreterEntry
: WasmCode::Kind::kFunction;
WasmCode* code = native_module->AddCode(
func_index_, result.code_desc, result.frame_slot_count,
result.tagged_parameter_slots, std::move(result.protected_instructions),
std::move(result.source_positions), WasmCode::kFunction, code_tier);
std::move(result.source_positions), code_kind, code_tier);
return code;
}
void WasmCompilationUnit::SwitchTier(ExecutionTier new_tier) {
// This method is being called in the constructor, where neither
// {liftoff_unit_} nor {turbofan_unit_} are set, or to switch tier from
// kLiftoff to kTurbofan, in which case {liftoff_unit_} is already set.
// {liftoff_unit_} nor {turbofan_unit_} nor {interpreter_unit_} are set, or to
// switch tier from kLiftoff to kTurbofan, in which case {liftoff_unit_} is
// already set.
executed_tier_ = new_tier;
switch (new_tier) {
case ExecutionTier::kBaseline:
DCHECK(!turbofan_unit_);
DCHECK(!liftoff_unit_);
DCHECK(!interpreter_unit_);
liftoff_unit_.reset(new LiftoffCompilationUnit(this));
return;
case ExecutionTier::kOptimized:
DCHECK(!turbofan_unit_);
DCHECK(!interpreter_unit_);
liftoff_unit_.reset();
turbofan_unit_.reset(new compiler::TurbofanWasmCompilationUnit(this));
return;
case ExecutionTier::kInterpreter:
UNREACHABLE(); // TODO(titzer): allow compiling interpreter entry stub.
DCHECK(!turbofan_unit_);
DCHECK(!liftoff_unit_);
DCHECK(!interpreter_unit_);
interpreter_unit_.reset(new compiler::InterpreterCompilationUnit(this));
return;
}
UNREACHABLE();
}
......
......@@ -20,6 +20,7 @@ class AssemblerBuffer;
class Counters;
namespace compiler {
class InterpreterCompilationUnit;
class Pipeline;
class TurbofanWasmCompilationUnit;
} // namespace compiler
......@@ -97,6 +98,7 @@ class WasmCompilationUnit final {
private:
friend class LiftoffCompilationUnit;
friend class compiler::TurbofanWasmCompilationUnit;
friend class compiler::InterpreterCompilationUnit;
WasmEngine* const wasm_engine_;
const int func_index_;
......@@ -107,6 +109,8 @@ class WasmCompilationUnit final {
std::unique_ptr<LiftoffCompilationUnit> liftoff_unit_;
// TurbofanWasmCompilationUnit, set if {tier_ == kTurbofan}.
std::unique_ptr<compiler::TurbofanWasmCompilationUnit> turbofan_unit_;
// InterpreterCompilationUnit, set if {tier_ == kInterpreter}.
std::unique_ptr<compiler::InterpreterCompilationUnit> interpreter_unit_;
void SwitchTier(ExecutionTier new_tier);
......
......@@ -580,12 +580,24 @@ void WasmDebugInfo::RedirectToInterpreter(Handle<WasmDebugInfo> debug_info,
DCHECK_GT(module->functions.size(), func_index);
if (!interpreted_functions->get(func_index)->IsUndefined(isolate)) continue;
wasm::WasmCode* wasm_new_code = compiler::CompileWasmInterpreterEntry(
isolate->wasm_engine(), native_module, func_index,
wasm::WasmCompilationResult result = compiler::CompileWasmInterpreterEntry(
isolate->wasm_engine(), native_module->enabled_features(), func_index,
module->functions[func_index].sig);
native_module->PublishInterpreterEntry(wasm_new_code, func_index);
// The interpreter entry is added with {kAnonymousFuncIndex} so that it will
// not replace existing code in the code table, only the jump table is
// redirected by {PublishInterpreterEntry}.
wasm::WasmCode* wasm_code = native_module->AddCode(
wasm::WasmCode::kAnonymousFuncIndex, result.code_desc,
result.frame_slot_count, result.tagged_parameter_slots,
std::move(result.protected_instructions),
std::move(result.source_positions), wasm::WasmCode::kInterpreterEntry,
wasm::WasmCode::kOther);
DCHECK_NOT_NULL(wasm_code);
native_module->PublishInterpreterEntry(wasm_code, func_index);
Handle<Foreign> foreign_holder = isolate->factory()->NewForeign(
wasm_new_code->instruction_start(), AllocationType::kOld);
wasm_code->instruction_start(), AllocationType::kOld);
interpreted_functions->set(func_index, *foreign_holder);
}
}
......
......@@ -16,7 +16,7 @@ class WasmInstanceObject;
namespace wasm {
// forward declarations.
// Forward declarations.
struct ModuleWireBytes;
struct WasmFunction;
struct WasmModule;
......
......@@ -150,8 +150,15 @@ Handle<JSFunction> TestingModuleBuilder::WrapCode(uint32_t index) {
// Patch the jump table to call the interpreter for this function. This is
// only needed for functions with a wrapper. Other functions never get
// called through the jump table.
wasm::WasmCode* wasm_new_code = compiler::CompileWasmInterpreterEntry(
isolate_->wasm_engine(), native_module_, index, sig);
wasm::WasmCompilationResult result = compiler::CompileWasmInterpreterEntry(
isolate_->wasm_engine(), native_module_->enabled_features(), index,
sig);
wasm::WasmCode* wasm_new_code = native_module_->AddCode(
wasm::WasmCode::kAnonymousFuncIndex, result.code_desc,
result.frame_slot_count, result.tagged_parameter_slots,
std::move(result.protected_instructions),
std::move(result.source_positions), wasm::WasmCode::kInterpreterEntry,
wasm::WasmCode::kOther);
native_module_->PublishInterpreterEntry(wasm_new_code, index);
}
return ret;
......
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