summaryrefslogtreecommitdiff
path: root/deps/v8/test/cctest/wasm/wasm-run-utils.h
diff options
context:
space:
mode:
authorMichaël Zasso <targos@protonmail.com>2017-03-21 10:16:54 +0100
committerMichaël Zasso <targos@protonmail.com>2017-03-25 09:44:10 +0100
commitc459d8ea5d402c702948c860d9497b2230ff7e8a (patch)
tree56c282fc4d40e5cb613b47cf7be3ea0526ed5b6f /deps/v8/test/cctest/wasm/wasm-run-utils.h
parente0bc5a7361b1d29c3ed034155fd779ce6f44fb13 (diff)
downloadnode-new-c459d8ea5d402c702948c860d9497b2230ff7e8a.tar.gz
deps: update V8 to 5.7.492.69
PR-URL: https://github.com/nodejs/node/pull/11752 Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl> Reviewed-By: Franziska Hinkelmann <franziska.hinkelmann@gmail.com>
Diffstat (limited to 'deps/v8/test/cctest/wasm/wasm-run-utils.h')
-rw-r--r--deps/v8/test/cctest/wasm/wasm-run-utils.h675
1 files changed, 348 insertions, 327 deletions
diff --git a/deps/v8/test/cctest/wasm/wasm-run-utils.h b/deps/v8/test/cctest/wasm/wasm-run-utils.h
index 284b21c7c4..782e7d9635 100644
--- a/deps/v8/test/cctest/wasm/wasm-run-utils.h
+++ b/deps/v8/test/cctest/wasm/wasm-run-utils.h
@@ -5,9 +5,11 @@
#ifndef WASM_RUN_UTILS_H
#define WASM_RUN_UTILS_H
+#include <setjmp.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
+#include <array>
#include <memory>
#include "src/base/utils/random-number-generator.h"
@@ -21,7 +23,8 @@
#include "src/compiler/pipeline.h"
#include "src/compiler/wasm-compiler.h"
#include "src/compiler/zone-stats.h"
-#include "src/wasm/ast-decoder.h"
+#include "src/wasm/function-body-decoder.h"
+#include "src/wasm/wasm-external-refs.h"
#include "src/wasm/wasm-interpreter.h"
#include "src/wasm/wasm-js.h"
#include "src/wasm/wasm-macro-gen.h"
@@ -49,7 +52,6 @@ enum WasmExecutionMode { kExecuteInterpreted, kExecuteCompiled };
CHECK_EQ(0xdeadbeefdeadbeef, (bit_cast<uint64_t>(x)) & 0xFFFFFFFFFFFFFFFF)
#define CHECK_TRAP(x) CHECK_TRAP32(x)
-#define WASM_RUNNER_MAX_NUM_PARAMETERS 4
#define WASM_WRAPPER_RETURN_VALUE 8754
#define BUILD(r, ...) \
@@ -71,23 +73,26 @@ const uint32_t kMaxGlobalsSize = 128;
// {WasmInstance}.
class TestingModule : public ModuleEnv {
public:
- explicit TestingModule(WasmExecutionMode mode = kExecuteCompiled)
- : execution_mode_(mode),
+ explicit TestingModule(Zone* zone, WasmExecutionMode mode = kExecuteCompiled)
+ : ModuleEnv(&module_, &instance_),
+ execution_mode_(mode),
instance_(&module_),
isolate_(CcTest::InitIsolateOnce()),
global_offset(0),
interpreter_(mode == kExecuteInterpreted
- ? new WasmInterpreter(&instance_, &allocator_)
+ ? new WasmInterpreter(
+ ModuleBytesEnv(&module_, &instance_,
+ Vector<const byte>::empty()),
+ zone->allocator())
: nullptr) {
- module = &module_;
- instance = &instance_;
+ WasmJs::Install(isolate_);
instance->module = &module_;
instance->globals_start = global_data;
module_.globals_size = kMaxGlobalsSize;
instance->mem_start = nullptr;
instance->mem_size = 0;
- origin = kWasmOrigin;
memset(global_data, 0, sizeof(global_data));
+ instance_object_ = InitInstanceObject();
}
~TestingModule() {
@@ -97,13 +102,15 @@ class TestingModule : public ModuleEnv {
if (interpreter_) delete interpreter_;
}
- void ChangeOriginToAsmjs() { origin = kAsmJsOrigin; }
+ void ChangeOriginToAsmjs() { module_.origin = kAsmJsOrigin; }
byte* AddMemory(uint32_t size) {
+ CHECK(!module_.has_memory);
CHECK_NULL(instance->mem_start);
- CHECK_EQ(0u, instance->mem_size);
+ CHECK_EQ(0, instance->mem_size);
+ module_.has_memory = true;
instance->mem_start = reinterpret_cast<byte*>(malloc(size));
- CHECK(instance->mem_start);
+ CHECK(size == 0 || instance->mem_start);
memset(instance->mem_start, 0, size);
instance->mem_size = size;
return raw_mem_start<byte>();
@@ -116,7 +123,8 @@ class TestingModule : public ModuleEnv {
}
template <typename T>
- T* AddGlobal(LocalType type) {
+ T* AddGlobal(
+ ValueType type = WasmOpcodes::ValueTypeFor(MachineTypeForC<T>())) {
const WasmGlobal* global = AddGlobal(type);
return reinterpret_cast<T*>(instance->globals_start + global->offset);
}
@@ -176,7 +184,11 @@ class TestingModule : public ModuleEnv {
rng.NextBytes(raw, end - raw);
}
- uint32_t AddFunction(FunctionSig* sig, Handle<Code> code) {
+ void SetMaxMemPages(uint32_t max_mem_pages) {
+ module_.max_mem_pages = max_mem_pages;
+ }
+
+ uint32_t AddFunction(FunctionSig* sig, Handle<Code> code, const char* name) {
if (module->functions.size() == 0) {
// TODO(titzer): Reserving space here to avoid the underlying WasmFunction
// structs from moving.
@@ -184,6 +196,11 @@ class TestingModule : public ModuleEnv {
}
uint32_t index = static_cast<uint32_t>(module->functions.size());
module_.functions.push_back({sig, index, 0, 0, 0, 0, 0, false, false});
+ if (name) {
+ Vector<const byte> name_vec = Vector<const byte>::cast(CStrVector(name));
+ module_.functions.back().name_offset = AddBytes(name_vec);
+ module_.functions.back().name_length = name_vec.length();
+ }
instance->function_code.push_back(code);
if (interpreter_) {
const WasmFunction* function = &module->functions.back();
@@ -197,26 +214,24 @@ class TestingModule : public ModuleEnv {
uint32_t AddJsFunction(FunctionSig* sig, const char* source) {
Handle<JSFunction> jsfunc = Handle<JSFunction>::cast(v8::Utils::OpenHandle(
*v8::Local<v8::Function>::Cast(CompileRun(source))));
- uint32_t index = AddFunction(sig, Handle<Code>::null());
- Handle<Code> code =
- CompileWasmToJSWrapper(isolate_, jsfunc, sig, index,
- Handle<String>::null(), Handle<String>::null());
+ uint32_t index = AddFunction(sig, Handle<Code>::null(), nullptr);
+ Handle<Code> code = CompileWasmToJSWrapper(
+ isolate_, jsfunc, sig, index, Handle<String>::null(),
+ Handle<String>::null(), module->origin);
instance->function_code[index] = code;
return index;
}
Handle<JSFunction> WrapCode(uint32_t index) {
// Wrap the code so it can be called as a JS function.
- Handle<String> name = isolate_->factory()->NewStringFromStaticChars("main");
Handle<WasmInstanceObject> instance_obj(0, isolate_);
Handle<Code> code = instance->function_code[index];
- WasmJs::InstallWasmMapsIfNeeded(isolate_, isolate_->native_context());
Handle<Code> ret_code =
- compiler::CompileJSToWasmWrapper(isolate_, this, code, index);
+ compiler::CompileJSToWasmWrapper(isolate_, &module_, code, index);
Handle<JSFunction> ret = WasmExportedFunction::New(
- isolate_, instance_obj, name, ret_code,
+ isolate_, instance_obj, MaybeHandle<String>(), static_cast<int>(index),
static_cast<int>(this->module->functions[index].sig->parameter_count()),
- static_cast<int>(index));
+ ret_code);
return ret;
}
@@ -238,40 +253,59 @@ class TestingModule : public ModuleEnv {
}
instance->function_tables.push_back(
- isolate_->factory()->NewFixedArray(table_size * 2));
+ isolate_->factory()->NewFixedArray(table_size));
+ instance->signature_tables.push_back(
+ isolate_->factory()->NewFixedArray(table_size));
}
void PopulateIndirectFunctionTable() {
+ if (execution_mode_ == kExecuteInterpreted) return;
// Initialize the fixed arrays in instance->function_tables.
for (uint32_t i = 0; i < instance->function_tables.size(); i++) {
WasmIndirectFunctionTable& table = module_.function_tables[i];
- Handle<FixedArray> array = instance->function_tables[i];
+ Handle<FixedArray> function_table = instance->function_tables[i];
+ Handle<FixedArray> signature_table = instance->signature_tables[i];
int table_size = static_cast<int>(table.values.size());
for (int j = 0; j < table_size; j++) {
WasmFunction& function = module_.functions[table.values[j]];
- array->set(j, Smi::FromInt(table.map.Find(function.sig)));
- array->set(j + table_size,
- *instance->function_code[function.func_index]);
+ signature_table->set(j, Smi::FromInt(table.map.Find(function.sig)));
+ function_table->set(j, *instance->function_code[function.func_index]);
}
}
}
+ uint32_t AddBytes(Vector<const byte> bytes) {
+ Handle<SeqOneByteString> old_bytes(
+ instance_object_->compiled_module()->module_bytes(), isolate_);
+ uint32_t old_size = static_cast<uint32_t>(old_bytes->length());
+ ScopedVector<byte> new_bytes(old_size + bytes.length());
+ memcpy(new_bytes.start(), old_bytes->GetChars(), old_size);
+ memcpy(new_bytes.start() + old_size, bytes.start(), bytes.length());
+ Handle<SeqOneByteString> new_bytes_str = Handle<SeqOneByteString>::cast(
+ isolate_->factory()->NewStringFromOneByte(new_bytes).ToHandleChecked());
+ instance_object_->compiled_module()->shared()->set_module_bytes(
+ *new_bytes_str);
+ return old_size;
+ }
+
WasmFunction* GetFunctionAt(int index) { return &module_.functions[index]; }
WasmInterpreter* interpreter() { return interpreter_; }
WasmExecutionMode execution_mode() { return execution_mode_; }
+ Isolate* isolate() { return isolate_; }
+ Handle<WasmInstanceObject> instance_object() { return instance_object_; }
private:
WasmExecutionMode execution_mode_;
WasmModule module_;
WasmInstance instance_;
Isolate* isolate_;
- v8::internal::AccountingAllocator allocator_;
uint32_t global_offset;
V8_ALIGNED(8) byte global_data[kMaxGlobalsSize]; // preallocated global data.
WasmInterpreter* interpreter_;
+ Handle<WasmInstanceObject> instance_object_;
- const WasmGlobal* AddGlobal(LocalType type) {
+ const WasmGlobal* AddGlobal(ValueType type) {
byte size = WasmOpcodes::MemSize(WasmOpcodes::MachineTypeFor(type));
global_offset = (global_offset + size - 1) & ~(size - 1); // align
module_.globals.push_back(
@@ -281,21 +315,43 @@ class TestingModule : public ModuleEnv {
CHECK_LT(global_offset, kMaxGlobalsSize);
return &module->globals.back();
}
+
+ Handle<WasmInstanceObject> InitInstanceObject() {
+ Handle<SeqOneByteString> empty_string = Handle<SeqOneByteString>::cast(
+ isolate_->factory()->NewStringFromOneByte({}).ToHandleChecked());
+ Handle<Managed<wasm::WasmModule>> module_wrapper =
+ Managed<wasm::WasmModule>::New(isolate_, &module_, false);
+ Handle<Script> script =
+ isolate_->factory()->NewScript(isolate_->factory()->empty_string());
+ script->set_type(Script::TYPE_WASM);
+ Handle<WasmSharedModuleData> shared_module_data =
+ WasmSharedModuleData::New(isolate_, module_wrapper, empty_string,
+ script, Handle<ByteArray>::null());
+ Handle<WasmCompiledModule> compiled_module =
+ WasmCompiledModule::New(isolate_, shared_module_data);
+ // Minimally initialize the compiled module such that IsWasmCompiledModule
+ // passes.
+ // If tests need more (correct) information, add it later.
+ compiled_module->set_min_mem_pages(0);
+ compiled_module->set_max_mem_pages(Smi::kMaxValue);
+ DCHECK(WasmCompiledModule::IsWasmCompiledModule(*compiled_module));
+ return WasmInstanceObject::New(isolate_, compiled_module);
+ }
};
inline void TestBuildingGraph(Zone* zone, JSGraph* jsgraph, ModuleEnv* module,
FunctionSig* sig,
SourcePositionTable* source_position_table,
const byte* start, const byte* end) {
- compiler::WasmGraphBuilder builder(zone, jsgraph, sig, source_position_table);
+ compiler::WasmGraphBuilder builder(module, zone, jsgraph, sig,
+ source_position_table);
DecodeResult result =
- BuildTFGraph(zone->allocator(), &builder, module, sig, start, end);
+ BuildTFGraph(zone->allocator(), &builder, sig, start, end);
if (result.failed()) {
if (!FLAG_trace_wasm_decoder) {
// Retry the compilation with the tracing flag on, to help in debugging.
FLAG_trace_wasm_decoder = true;
- result =
- BuildTFGraph(zone->allocator(), &builder, module, sig, start, end);
+ result = BuildTFGraph(zone->allocator(), &builder, sig, start, end);
}
ptrdiff_t pc = result.error_pc - result.start;
@@ -307,39 +363,34 @@ inline void TestBuildingGraph(Zone* zone, JSGraph* jsgraph, ModuleEnv* module,
FATAL(str.str().c_str());
}
builder.Int64LoweringForTesting();
- builder.SimdScalarLoweringForTesting();
+ if (!CpuFeatures::SupportsSimd128()) {
+ builder.SimdScalarLoweringForTesting();
+ }
}
-template <typename ReturnType>
-class WasmFunctionWrapper : public HandleAndZoneScope,
- private GraphAndBuilders {
+class WasmFunctionWrapper : private GraphAndBuilders {
public:
- WasmFunctionWrapper()
- : GraphAndBuilders(main_zone()),
- inner_code_node_(nullptr),
- signature_(nullptr) {
+ explicit WasmFunctionWrapper(Zone* zone, int num_params)
+ : GraphAndBuilders(zone), inner_code_node_(nullptr), signature_(nullptr) {
// One additional parameter for the pointer to the return value memory.
- Signature<MachineType>::Builder sig_builder(
- zone(), 1, WASM_RUNNER_MAX_NUM_PARAMETERS + 1);
+ Signature<MachineType>::Builder sig_builder(zone, 1, num_params + 1);
sig_builder.AddReturn(MachineType::Int32());
- for (int i = 0; i < WASM_RUNNER_MAX_NUM_PARAMETERS + 1; i++) {
+ for (int i = 0; i < num_params + 1; i++) {
sig_builder.AddParam(MachineType::Pointer());
}
signature_ = sig_builder.Build();
}
- void Init(CallDescriptor* descriptor, MachineType p0 = MachineType::None(),
- MachineType p1 = MachineType::None(),
- MachineType p2 = MachineType::None(),
- MachineType p3 = MachineType::None()) {
- // Create the TF graph for the wrapper. The wrapper always takes four
- // pointers as parameters, but may not pass the values of all pointers to
- // the actual test function.
+ void Init(CallDescriptor* descriptor, MachineType return_type,
+ Vector<MachineType> param_types) {
+ DCHECK_NOT_NULL(descriptor);
+ DCHECK_EQ(signature_->parameter_count(), param_types.length() + 1);
+
+ // Create the TF graph for the wrapper.
// Function, effect, and control.
- Node** parameters =
- zone()->template NewArray<Node*>(WASM_RUNNER_MAX_NUM_PARAMETERS + 3);
+ Node** parameters = zone()->NewArray<Node*>(param_types.length() + 3);
graph()->SetStart(graph()->NewNode(common()->Start(6)));
Node* effect = graph()->start();
int parameter_count = 0;
@@ -348,34 +399,12 @@ class WasmFunctionWrapper : public HandleAndZoneScope,
inner_code_node_ = graph()->NewNode(common()->Int32Constant(0));
parameters[parameter_count++] = inner_code_node_;
- if (p0 != MachineType::None()) {
- parameters[parameter_count] = graph()->NewNode(
- machine()->Load(p0),
- graph()->NewNode(common()->Parameter(0), graph()->start()),
- graph()->NewNode(common()->Int32Constant(0)), effect,
- graph()->start());
- effect = parameters[parameter_count++];
- }
- if (p1 != MachineType::None()) {
- parameters[parameter_count] = graph()->NewNode(
- machine()->Load(p1),
- graph()->NewNode(common()->Parameter(1), graph()->start()),
- graph()->NewNode(common()->Int32Constant(0)), effect,
- graph()->start());
- effect = parameters[parameter_count++];
- }
- if (p2 != MachineType::None()) {
- parameters[parameter_count] = graph()->NewNode(
- machine()->Load(p2),
- graph()->NewNode(common()->Parameter(2), graph()->start()),
- graph()->NewNode(common()->Int32Constant(0)), effect,
- graph()->start());
- effect = parameters[parameter_count++];
- }
- if (p3 != MachineType::None()) {
+ int param_idx = 0;
+ for (MachineType t : param_types) {
+ DCHECK_NE(MachineType::None(), t);
parameters[parameter_count] = graph()->NewNode(
- machine()->Load(p3),
- graph()->NewNode(common()->Parameter(3), graph()->start()),
+ machine()->Load(t),
+ graph()->NewNode(common()->Parameter(param_idx++), graph()->start()),
graph()->NewNode(common()->Int32Constant(0)), effect,
graph()->start());
effect = parameters[parameter_count++];
@@ -386,14 +415,15 @@ class WasmFunctionWrapper : public HandleAndZoneScope,
Node* call = graph()->NewNode(common()->Call(descriptor), parameter_count,
parameters);
- effect = graph()->NewNode(
- machine()->Store(
- StoreRepresentation(MachineTypeForC<ReturnType>().representation(),
- WriteBarrierKind::kNoWriteBarrier)),
- graph()->NewNode(common()->Parameter(WASM_RUNNER_MAX_NUM_PARAMETERS),
- graph()->start()),
- graph()->NewNode(common()->Int32Constant(0)), call, effect,
- graph()->start());
+ if (!return_type.IsNone()) {
+ effect = graph()->NewNode(
+ machine()->Store(StoreRepresentation(
+ return_type.representation(), WriteBarrierKind::kNoWriteBarrier)),
+ graph()->NewNode(common()->Parameter(param_types.length()),
+ graph()->start()),
+ graph()->NewNode(common()->Int32Constant(0)), call, effect,
+ graph()->start());
+ }
Node* zero = graph()->NewNode(common()->Int32Constant(0));
Node* r = graph()->NewNode(
common()->Return(), zero,
@@ -402,6 +432,15 @@ class WasmFunctionWrapper : public HandleAndZoneScope,
graph()->SetEnd(graph()->NewNode(common()->End(2), r, graph()->start()));
}
+ template <typename ReturnType, typename... ParamTypes>
+ void Init(CallDescriptor* descriptor) {
+ std::array<MachineType, sizeof...(ParamTypes)> param_machine_types{
+ {MachineTypeForC<ParamTypes>()...}};
+ Vector<MachineType> param_vec(param_machine_types.data(),
+ param_machine_types.size());
+ Init(descriptor, MachineTypeForC<ReturnType>(), param_vec);
+ }
+
void SetInnerCode(Handle<Code> code_handle) {
NodeProperties::ChangeOp(inner_code_node_,
common()->HeapConstant(code_handle));
@@ -415,12 +454,13 @@ class WasmFunctionWrapper : public HandleAndZoneScope,
Linkage::GetSimplifiedCDescriptor(zone(), signature_, true);
if (kPointerSize == 4) {
+ size_t num_params = signature_->parameter_count();
// One additional parameter for the pointer of the return value.
- Signature<MachineRepresentation>::Builder rep_builder(
- zone(), 1, WASM_RUNNER_MAX_NUM_PARAMETERS + 1);
+ Signature<MachineRepresentation>::Builder rep_builder(zone(), 1,
+ num_params + 1);
rep_builder.AddReturn(MachineRepresentation::kWord32);
- for (int i = 0; i < WASM_RUNNER_MAX_NUM_PARAMETERS + 1; i++) {
+ for (size_t i = 0; i < num_params + 1; i++) {
rep_builder.AddParam(MachineRepresentation::kWord32);
}
Int64Lowering r(graph(), machine(), common(), zone(),
@@ -452,129 +492,108 @@ class WasmFunctionWrapper : public HandleAndZoneScope,
Signature<MachineType>* signature_;
};
-// A helper for compiling WASM functions for testing. This class can create a
-// standalone function if {module} is NULL or a function within a
-// {TestingModule}. It contains the internal state for compilation (i.e.
-// TurboFan graph) and interpretation (by adding to the interpreter manually).
-class WasmFunctionCompiler : public HandleAndZoneScope,
- private GraphAndBuilders {
+// A helper for compiling WASM functions for testing.
+// It contains the internal state for compilation (i.e. TurboFan graph) and
+// interpretation (by adding to the interpreter manually).
+class WasmFunctionCompiler : private GraphAndBuilders {
public:
- explicit WasmFunctionCompiler(
- FunctionSig* sig, WasmExecutionMode mode,
- Vector<const char> debug_name = ArrayVector("<WASM UNNAMED>"))
- : GraphAndBuilders(main_zone()),
- execution_mode_(mode),
- jsgraph(this->isolate(), this->graph(), this->common(), nullptr,
- nullptr, this->machine()),
- sig(sig),
- descriptor_(nullptr),
- testing_module_(nullptr),
- debug_name_(debug_name),
- local_decls(main_zone(), sig),
- source_position_table_(this->graph()),
- interpreter_(nullptr) {
- // Create our own function.
- function_ = new WasmFunction();
- function_->sig = sig;
- function_->func_index = 0;
- function_->sig_index = 0;
- if (mode == kExecuteInterpreted) {
- interpreter_ = new WasmInterpreter(nullptr, zone()->allocator());
- int index = interpreter_->AddFunctionForTesting(function_);
- CHECK_EQ(0, index);
- }
- }
-
- explicit WasmFunctionCompiler(
- FunctionSig* sig, TestingModule* module,
- Vector<const char> debug_name = ArrayVector("<WASM UNNAMED>"))
- : GraphAndBuilders(main_zone()),
- execution_mode_(module->execution_mode()),
- jsgraph(this->isolate(), this->graph(), this->common(), nullptr,
- nullptr, this->machine()),
- sig(sig),
- descriptor_(nullptr),
- testing_module_(module),
- debug_name_(debug_name),
- local_decls(main_zone(), sig),
- source_position_table_(this->graph()),
- interpreter_(module->interpreter()) {
- // Get a new function from the testing module.
- int index = module->AddFunction(sig, Handle<Code>::null());
- function_ = testing_module_->GetFunctionAt(index);
- }
-
- ~WasmFunctionCompiler() {
- if (testing_module_) return; // testing module owns the below things.
- delete function_;
- if (interpreter_) delete interpreter_;
- }
-
- WasmExecutionMode execution_mode_;
- JSGraph jsgraph;
- FunctionSig* sig;
- // The call descriptor is initialized when the function is compiled.
- CallDescriptor* descriptor_;
- TestingModule* testing_module_;
- Vector<const char> debug_name_;
- WasmFunction* function_;
- LocalDeclEncoder local_decls;
- SourcePositionTable source_position_table_;
- WasmInterpreter* interpreter_;
-
- Isolate* isolate() { return main_isolate(); }
+ Isolate* isolate() { return testing_module_->isolate(); }
Graph* graph() const { return main_graph_; }
Zone* zone() const { return graph()->zone(); }
CommonOperatorBuilder* common() { return &main_common_; }
MachineOperatorBuilder* machine() { return &main_machine_; }
- void InitializeDescriptor() {
+ CallDescriptor* descriptor() {
if (descriptor_ == nullptr) {
- descriptor_ = testing_module_->GetWasmCallDescriptor(main_zone(), sig);
+ descriptor_ = testing_module_->GetWasmCallDescriptor(zone(), sig);
}
+ return descriptor_;
}
- CallDescriptor* descriptor() { return descriptor_; }
uint32_t function_index() { return function_->func_index; }
void Build(const byte* start, const byte* end) {
- // Build the TurboFan graph.
- local_decls.Prepend(main_zone(), &start, &end);
- TestBuildingGraph(main_zone(), &jsgraph, testing_module_, sig,
- &source_position_table_, start, end);
+ size_t locals_size = local_decls.Size();
+ size_t total_size = end - start + locals_size + 1;
+ byte* buffer = static_cast<byte*>(zone()->New(total_size));
+ // Prepend the local decls to the code.
+ local_decls.Emit(buffer);
+ // Emit the code.
+ memcpy(buffer + locals_size, start, end - start);
+ // Append an extra end opcode.
+ buffer[total_size - 1] = kExprEnd;
+
+ start = buffer;
+ end = buffer + total_size;
+
+ CHECK_GE(kMaxInt, end - start);
+ int len = static_cast<int>(end - start);
+ function_->code_start_offset =
+ testing_module_->AddBytes(Vector<const byte>(start, len));
+ function_->code_end_offset = function_->code_start_offset + len;
+
if (interpreter_) {
// Add the code to the interpreter.
CHECK(interpreter_->SetFunctionCodeForTesting(function_, start, end));
+ return;
}
+
+ // Build the TurboFan graph.
+ TestBuildingGraph(zone(), &jsgraph, testing_module_, sig,
+ &source_position_table_, start, end);
+ Handle<Code> code = Compile();
+ testing_module_->SetFunctionCode(function_index(), code);
}
- byte AllocateLocal(LocalType type) {
+ byte AllocateLocal(ValueType type) {
uint32_t index = local_decls.AddLocals(1, type);
byte result = static_cast<byte>(index);
DCHECK_EQ(index, result);
return result;
}
+ void SetSigIndex(int sig_index) { function_->sig_index = sig_index; }
+
+ private:
+ friend class WasmRunnerBase;
+
+ explicit WasmFunctionCompiler(Zone* zone, FunctionSig* sig,
+ TestingModule* module, const char* name)
+ : GraphAndBuilders(zone),
+ jsgraph(module->isolate(), this->graph(), this->common(), nullptr,
+ nullptr, this->machine()),
+ sig(sig),
+ descriptor_(nullptr),
+ testing_module_(module),
+ local_decls(zone, sig),
+ source_position_table_(this->graph()),
+ interpreter_(module->interpreter()) {
+ // Get a new function from the testing module.
+ int index = module->AddFunction(sig, Handle<Code>::null(), name);
+ function_ = testing_module_->GetFunctionAt(index);
+ }
+
Handle<Code> Compile() {
- InitializeDescriptor();
- CallDescriptor* desc = descriptor_;
+ CallDescriptor* desc = descriptor();
if (kPointerSize == 4) {
desc = testing_module_->GetI32WasmCallDescriptor(this->zone(), desc);
}
- CompilationInfo info(debug_name_, this->isolate(), this->zone(),
+ CompilationInfo info(CStrVector("wasm"), this->isolate(), this->zone(),
Code::ComputeFlags(Code::WASM_FUNCTION));
std::unique_ptr<CompilationJob> job(Pipeline::NewWasmCompilationJob(
- &info, graph(), desc, &source_position_table_));
+ &info, &jsgraph, desc, &source_position_table_, nullptr, false));
if (job->ExecuteJob() != CompilationJob::SUCCEEDED ||
job->FinalizeJob() != CompilationJob::SUCCEEDED)
return Handle<Code>::null();
Handle<Code> code = info.code();
- // Length is always 2, since usually <wasm_obj, func_index> is stored in
- // the deopt data. Here, we only store the function index.
+ // Deopt data holds <WeakCell<wasm_instance>, func_index>.
DCHECK(code->deoptimization_data() == nullptr ||
code->deoptimization_data()->length() == 0);
Handle<FixedArray> deopt_data =
isolate()->factory()->NewFixedArray(2, TENURED);
+ Handle<Object> weak_instance =
+ isolate()->factory()->NewWeakCell(testing_module_->instance_object());
+ deopt_data->set(0, *weak_instance);
deopt_data->set(1, Smi::FromInt(static_cast<int>(function_index())));
deopt_data->set_length(2);
code->set_deoptimization_data(*deopt_data);
@@ -589,76 +608,26 @@ class WasmFunctionCompiler : public HandleAndZoneScope,
return code;
}
- uint32_t CompileAndAdd(uint16_t sig_index = 0) {
- CHECK(testing_module_);
- function_->sig_index = sig_index;
- Handle<Code> code = Compile();
- testing_module_->SetFunctionCode(function_index(), code);
- return function_index();
- }
-
- // Set the context, such that e.g. runtime functions can be called.
- void SetModuleContext() {
- if (!testing_module_->instance->context.is_null()) {
- CHECK(testing_module_->instance->context.is_identical_to(
- main_isolate()->native_context()));
- return;
- }
- testing_module_->instance->context = main_isolate()->native_context();
- }
+ JSGraph jsgraph;
+ FunctionSig* sig;
+ // The call descriptor is initialized when the function is compiled.
+ CallDescriptor* descriptor_;
+ TestingModule* testing_module_;
+ Vector<const char> debug_name_;
+ WasmFunction* function_;
+ LocalDeclEncoder local_decls;
+ SourcePositionTable source_position_table_;
+ WasmInterpreter* interpreter_;
};
-// A helper class to build graphs from Wasm bytecode, generate machine
+// A helper class to build a module around Wasm bytecode, generate machine
// code, and run that code.
-template <typename ReturnType>
-class WasmRunner {
+class WasmRunnerBase : public HandleAndZoneScope {
public:
- WasmRunner(WasmExecutionMode execution_mode,
- MachineType p0 = MachineType::None(),
- MachineType p1 = MachineType::None(),
- MachineType p2 = MachineType::None(),
- MachineType p3 = MachineType::None())
- : zone(&allocator_, ZONE_NAME),
- compiled_(false),
- signature_(MachineTypeForC<ReturnType>() == MachineType::None() ? 0 : 1,
- GetParameterCount(p0, p1, p2, p3), storage_),
- compiler_(&signature_, execution_mode) {
- InitSigStorage(p0, p1, p2, p3);
- }
-
- WasmRunner(TestingModule* module, MachineType p0 = MachineType::None(),
- MachineType p1 = MachineType::None(),
- MachineType p2 = MachineType::None(),
- MachineType p3 = MachineType::None())
- : zone(&allocator_, ZONE_NAME),
- compiled_(false),
- signature_(MachineTypeForC<ReturnType>() == MachineType::None() ? 0 : 1,
- GetParameterCount(p0, p1, p2, p3), storage_),
- compiler_(&signature_, module),
- possible_nondeterminism_(false) {
- DCHECK(module);
- InitSigStorage(p0, p1, p2, p3);
- }
-
- void InitSigStorage(MachineType p0, MachineType p1, MachineType p2,
- MachineType p3) {
- int index = 0;
- MachineType ret = MachineTypeForC<ReturnType>();
- if (ret != MachineType::None()) {
- storage_[index++] = WasmOpcodes::LocalTypeFor(ret);
- }
- if (p0 != MachineType::None())
- storage_[index++] = WasmOpcodes::LocalTypeFor(p0);
- if (p1 != MachineType::None())
- storage_[index++] = WasmOpcodes::LocalTypeFor(p1);
- if (p2 != MachineType::None())
- storage_[index++] = WasmOpcodes::LocalTypeFor(p2);
- if (p3 != MachineType::None())
- storage_[index++] = WasmOpcodes::LocalTypeFor(p3);
-
- compiler_.InitializeDescriptor();
- wrapper_.Init(compiler_.descriptor(), p0, p1, p2, p3);
- }
+ explicit WasmRunnerBase(WasmExecutionMode execution_mode, int num_params)
+ : zone_(&allocator_, ZONE_NAME),
+ module_(&zone_, execution_mode),
+ wrapper_(&zone_, num_params) {}
// Builds a graph from the given Wasm code and generates the machine
// code and call wrapper for that graph. This method must not be called
@@ -666,83 +635,125 @@ class WasmRunner {
void Build(const byte* start, const byte* end) {
CHECK(!compiled_);
compiled_ = true;
- compiler_.Build(start, end);
+ functions_[0]->Build(start, end);
+ }
- if (!interpret()) {
- // Compile machine code and install it into the module.
- Handle<Code> code = compiler_.Compile();
+ // Resets the state for building the next function.
+ // The main function called will always be the first function.
+ template <typename ReturnType, typename... ParamTypes>
+ WasmFunctionCompiler& NewFunction(const char* name = nullptr) {
+ return NewFunction(CreateSig<ReturnType, ParamTypes...>(), name);
+ }
- if (compiler_.testing_module_) {
- // Update the table of function code in the module.
- compiler_.testing_module_->SetFunctionCode(
- compiler_.function_->func_index, code);
- }
+ // Resets the state for building the next function.
+ // The main function called will be the last generated function.
+ // Returns the index of the previously built function.
+ WasmFunctionCompiler& NewFunction(FunctionSig* sig,
+ const char* name = nullptr) {
+ functions_.emplace_back(
+ new WasmFunctionCompiler(&zone_, sig, &module_, name));
+ return *functions_.back();
+ }
- wrapper_.SetInnerCode(code);
- }
+ byte AllocateLocal(ValueType type) {
+ return functions_[0]->AllocateLocal(type);
}
- ReturnType Call() {
- if (interpret()) {
- return CallInterpreter(Vector<WasmVal>(nullptr, 0));
- } else {
- return Call(0, 0, 0, 0);
+ WasmFunction* function() { return functions_[0]->function_; }
+ WasmInterpreter* interpreter() { return functions_[0]->interpreter_; }
+ bool possible_nondeterminism() { return possible_nondeterminism_; }
+ TestingModule& module() { return module_; }
+ Zone* zone() { return &zone_; }
+
+ // Set the context, such that e.g. runtime functions can be called.
+ void SetModuleContext() {
+ if (!module_.instance->context.is_null()) {
+ CHECK(module_.instance->context.is_identical_to(
+ main_isolate()->native_context()));
+ return;
}
+ module_.instance->context = main_isolate()->native_context();
}
- template <typename P0>
- ReturnType Call(P0 p0) {
- if (interpret()) {
- WasmVal args[] = {WasmVal(p0)};
- return CallInterpreter(ArrayVector(args));
- } else {
- return Call(p0, 0, 0, 0);
+ private:
+ FunctionSig* CreateSig(MachineType return_type,
+ Vector<MachineType> param_types) {
+ int return_count = return_type.IsNone() ? 0 : 1;
+ int param_count = param_types.length();
+
+ // Allocate storage array in zone.
+ ValueType* sig_types =
+ zone_.NewArray<ValueType>(return_count + param_count);
+
+ // Convert machine types to local types, and check that there are no
+ // MachineType::None()'s in the parameters.
+ int idx = 0;
+ if (return_count) sig_types[idx++] = WasmOpcodes::ValueTypeFor(return_type);
+ for (MachineType param : param_types) {
+ CHECK_NE(MachineType::None(), param);
+ sig_types[idx++] = WasmOpcodes::ValueTypeFor(param);
}
+ return new (&zone_) FunctionSig(return_count, param_count, sig_types);
}
- template <typename P0, typename P1>
- ReturnType Call(P0 p0, P1 p1) {
- if (interpret()) {
- WasmVal args[] = {WasmVal(p0), WasmVal(p1)};
- return CallInterpreter(ArrayVector(args));
- } else {
- return Call(p0, p1, 0, 0);
- }
+ template <typename ReturnType, typename... ParamTypes>
+ FunctionSig* CreateSig() {
+ std::array<MachineType, sizeof...(ParamTypes)> param_machine_types{
+ {MachineTypeForC<ParamTypes>()...}};
+ Vector<MachineType> param_vec(param_machine_types.data(),
+ param_machine_types.size());
+ return CreateSig(MachineTypeForC<ReturnType>(), param_vec);
}
- template <typename P0, typename P1, typename P2>
- ReturnType Call(P0 p0, P1 p1, P2 p2) {
- if (interpret()) {
- WasmVal args[] = {WasmVal(p0), WasmVal(p1), WasmVal(p2)};
- return CallInterpreter(ArrayVector(args));
- } else {
- return Call(p0, p1, p2, 0);
+ protected:
+ v8::internal::AccountingAllocator allocator_;
+ Zone zone_;
+ TestingModule module_;
+ std::vector<std::unique_ptr<WasmFunctionCompiler>> functions_;
+ WasmFunctionWrapper wrapper_;
+ bool compiled_ = false;
+ bool possible_nondeterminism_ = false;
+
+ bool interpret() { return module_.execution_mode() == kExecuteInterpreted; }
+
+ public:
+ // This field has to be static. Otherwise, gcc complains about the using in
+ // the lambda context below.
+ static jmp_buf jump_buffer;
+};
+
+template <typename ReturnType, typename... ParamTypes>
+class WasmRunner : public WasmRunnerBase {
+ public:
+ explicit WasmRunner(WasmExecutionMode execution_mode,
+ const char* main_fn_name = "main")
+ : WasmRunnerBase(execution_mode, sizeof...(ParamTypes)) {
+ NewFunction<ReturnType, ParamTypes...>(main_fn_name);
+ if (!interpret()) {
+ wrapper_.Init<ReturnType, ParamTypes...>(functions_[0]->descriptor());
}
}
- template <typename P0, typename P1, typename P2, typename P3>
- ReturnType Call(P0 p0, P1 p1, P2 p2, P3 p3) {
- if (interpret()) {
- WasmVal args[] = {WasmVal(p0), WasmVal(p1), WasmVal(p2), WasmVal(p3)};
- return CallInterpreter(ArrayVector(args));
- } else {
- CodeRunner<int32_t> runner(CcTest::InitIsolateOnce(),
- wrapper_.GetWrapperCode(),
- wrapper_.signature());
- ReturnType return_value;
- int32_t result = runner.Call<void*, void*, void*, void*, void*>(
- &p0, &p1, &p2, &p3, &return_value);
- CHECK_EQ(WASM_WRAPPER_RETURN_VALUE, result);
- return return_value;
+ ReturnType Call(ParamTypes... p) {
+ DCHECK(compiled_);
+ if (interpret()) return CallInterpreter(p...);
+
+ // Use setjmp/longjmp to deal with traps in WebAssembly code.
+ ReturnType return_value = static_cast<ReturnType>(0xdeadbeefdeadbeef);
+ static int setjmp_ret;
+ setjmp_ret = setjmp(WasmRunnerBase::jump_buffer);
+ // setjmp returns 0 on the first return, 1 (passed to longjmp) after trap.
+ if (setjmp_ret == 0) {
+ DoCall(static_cast<void*>(&p)..., static_cast<void*>(&return_value));
}
+ return return_value;
}
- ReturnType CallInterpreter(Vector<WasmVal> args) {
- CHECK_EQ(args.length(),
- static_cast<int>(compiler_.function_->sig->parameter_count()));
+ ReturnType CallInterpreter(ParamTypes... p) {
WasmInterpreter::Thread* thread = interpreter()->GetThread(0);
thread->Reset();
- thread->PushFrame(compiler_.function_, args.start());
+ std::array<WasmVal, sizeof...(p)> args{{WasmVal(p)...}};
+ thread->PushFrame(function(), args.data());
if (thread->Run() == WasmInterpreter::FINISHED) {
WasmVal val = thread->GetReturnValue();
possible_nondeterminism_ |= thread->PossibleNondeterminism();
@@ -753,49 +764,59 @@ class WasmRunner {
return static_cast<ReturnType>(result);
} else {
// TODO(titzer): falling off end
- ReturnType val = 0;
- return val;
+ return ReturnType{0};
}
}
- byte AllocateLocal(LocalType type) { return compiler_.AllocateLocal(type); }
-
- WasmFunction* function() { return compiler_.function_; }
- WasmInterpreter* interpreter() { return compiler_.interpreter_; }
- bool possible_nondeterminism() { return possible_nondeterminism_; }
-
- protected:
- v8::internal::AccountingAllocator allocator_;
- Zone zone;
- bool compiled_;
- LocalType storage_[WASM_RUNNER_MAX_NUM_PARAMETERS];
- FunctionSig signature_;
- WasmFunctionCompiler compiler_;
- WasmFunctionWrapper<ReturnType> wrapper_;
- bool possible_nondeterminism_;
-
- bool interpret() { return compiler_.execution_mode_ == kExecuteInterpreted; }
-
- static size_t GetParameterCount(MachineType p0, MachineType p1,
- MachineType p2, MachineType p3) {
- if (p0 == MachineType::None()) return 0;
- if (p1 == MachineType::None()) return 1;
- if (p2 == MachineType::None()) return 2;
- if (p3 == MachineType::None()) return 3;
- return 4;
+ private:
+ // Don't inline this function. The setjmp above should be followed immediately
+ // by a call.
+ template <typename... Ptrs>
+ V8_NOINLINE void DoCall(Ptrs... ptrs) {
+ auto trap_callback = []() -> void {
+ set_trap_callback_for_testing(nullptr);
+ longjmp(WasmRunnerBase::jump_buffer, 1);
+ };
+ set_trap_callback_for_testing(trap_callback);
+
+ wrapper_.SetInnerCode(
+ module_.GetFunctionCode(functions_[0]->function_index()));
+ CodeRunner<int32_t> runner(CcTest::InitIsolateOnce(),
+ wrapper_.GetWrapperCode(), wrapper_.signature());
+ int32_t result = runner.Call(ptrs...);
+ // If we arrive here, no trap happened.
+ CHECK_EQ(WASM_WRAPPER_RETURN_VALUE, result);
}
};
+// Declare static variable.
+jmp_buf WasmRunnerBase::jump_buffer;
+
// A macro to define tests that run in different engine configurations.
-// Currently only supports compiled tests, but a future
-// RunWasmInterpreted_##name version will allow each test to also run in the
-// interpreter.
#define WASM_EXEC_TEST(name) \
void RunWasm_##name(WasmExecutionMode execution_mode); \
TEST(RunWasmCompiled_##name) { RunWasm_##name(kExecuteCompiled); } \
TEST(RunWasmInterpreted_##name) { RunWasm_##name(kExecuteInterpreted); } \
void RunWasm_##name(WasmExecutionMode execution_mode)
+#define WASM_EXEC_TEST_WITH_TRAP(name) \
+ void RunWasm_##name(WasmExecutionMode execution_mode); \
+ TEST(RunWasmCompiled_##name) { RunWasm_##name(kExecuteCompiled); } \
+ void RunWasm_##name(WasmExecutionMode execution_mode); \
+ TEST(RunWasmCompiledWithTrapIf_##name) { \
+ bool trap_if = FLAG_wasm_trap_if; \
+ FLAG_wasm_trap_if = true; \
+ RunWasm_##name(kExecuteCompiled); \
+ FLAG_wasm_trap_if = trap_if; \
+ } \
+ TEST(RunWasmInterpreted_##name) { RunWasm_##name(kExecuteInterpreted); } \
+ void RunWasm_##name(WasmExecutionMode execution_mode)
+
+#define WASM_EXEC_COMPILED_TEST(name) \
+ void RunWasm_##name(WasmExecutionMode execution_mode); \
+ TEST(RunWasmCompiled_##name) { RunWasm_##name(kExecuteCompiled); } \
+ void RunWasm_##name(WasmExecutionMode execution_mode)
+
} // namespace
#endif