2016-01-14 09:48:45 +00:00
|
|
|
// Copyright 2016 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.
|
|
|
|
|
|
|
|
#ifndef WASM_RUN_UTILS_H
|
|
|
|
#define WASM_RUN_UTILS_H
|
|
|
|
|
|
|
|
#include <stdint.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
|
|
|
|
#include "src/base/utils/random-number-generator.h"
|
|
|
|
|
|
|
|
#include "src/compiler/graph-visualizer.h"
|
2016-02-04 09:40:55 +00:00
|
|
|
#include "src/compiler/int64-lowering.h"
|
2016-01-14 09:48:45 +00:00
|
|
|
#include "src/compiler/js-graph.h"
|
2016-02-18 09:55:25 +00:00
|
|
|
#include "src/compiler/node.h"
|
|
|
|
#include "src/compiler/pipeline.h"
|
2016-01-14 09:48:45 +00:00
|
|
|
#include "src/compiler/wasm-compiler.h"
|
2016-04-26 12:46:03 +00:00
|
|
|
#include "src/compiler/zone-pool.h"
|
2016-01-14 09:48:45 +00:00
|
|
|
|
|
|
|
#include "src/wasm/ast-decoder.h"
|
|
|
|
#include "src/wasm/wasm-js.h"
|
2016-04-29 12:43:14 +00:00
|
|
|
#include "src/wasm/wasm-macro-gen.h"
|
2016-01-14 09:48:45 +00:00
|
|
|
#include "src/wasm/wasm-module.h"
|
|
|
|
#include "src/wasm/wasm-opcodes.h"
|
|
|
|
|
2016-02-18 09:55:25 +00:00
|
|
|
#include "src/zone.h"
|
|
|
|
|
2016-01-14 09:48:45 +00:00
|
|
|
#include "test/cctest/cctest.h"
|
2016-02-18 09:55:25 +00:00
|
|
|
#include "test/cctest/compiler/call-tester.h"
|
2016-01-14 09:48:45 +00:00
|
|
|
#include "test/cctest/compiler/graph-builder-tester.h"
|
|
|
|
|
|
|
|
// TODO(titzer): pull WASM_64 up to a common header.
|
|
|
|
#if !V8_TARGET_ARCH_32_BIT || V8_TARGET_ARCH_X64
|
|
|
|
#define WASM_64 1
|
|
|
|
#else
|
|
|
|
#define WASM_64 0
|
|
|
|
#endif
|
|
|
|
|
2016-02-18 15:58:02 +00:00
|
|
|
static const uint32_t kMaxFunctions = 10;
|
|
|
|
|
2016-01-14 09:48:45 +00:00
|
|
|
// TODO(titzer): check traps more robustly in tests.
|
|
|
|
// Currently, in tests, we just return 0xdeadbeef from the function in which
|
|
|
|
// the trap occurs if the runtime context is not available to throw a JavaScript
|
|
|
|
// exception.
|
|
|
|
#define CHECK_TRAP32(x) \
|
|
|
|
CHECK_EQ(0xdeadbeef, (bit_cast<uint32_t>(x)) & 0xFFFFFFFF)
|
|
|
|
#define CHECK_TRAP64(x) \
|
|
|
|
CHECK_EQ(0xdeadbeefdeadbeef, (bit_cast<uint64_t>(x)) & 0xFFFFFFFFFFFFFFFF)
|
|
|
|
#define CHECK_TRAP(x) CHECK_TRAP32(x)
|
|
|
|
|
2016-02-18 15:18:41 +00:00
|
|
|
#define WASM_RUNNER_MAX_NUM_PARAMETERS 4
|
2016-02-18 09:55:25 +00:00
|
|
|
#define WASM_WRAPPER_RETURN_VALUE 8754
|
|
|
|
|
2016-02-23 16:30:27 +00:00
|
|
|
#define BUILD(r, ...) \
|
|
|
|
do { \
|
|
|
|
byte code[] = {__VA_ARGS__}; \
|
|
|
|
r.Build(code, code + arraysize(code)); \
|
|
|
|
} while (false)
|
|
|
|
|
2016-01-14 09:48:45 +00:00
|
|
|
namespace {
|
|
|
|
using namespace v8::base;
|
|
|
|
using namespace v8::internal;
|
|
|
|
using namespace v8::internal::compiler;
|
|
|
|
using namespace v8::internal::wasm;
|
|
|
|
|
|
|
|
const uint32_t kMaxGlobalsSize = 128;
|
|
|
|
|
|
|
|
// A helper for module environments that adds the ability to allocate memory
|
2016-02-18 15:58:02 +00:00
|
|
|
// and global variables. Contains a built-in {WasmModule} and
|
|
|
|
// {WasmModuleInstance}.
|
2016-01-14 09:48:45 +00:00
|
|
|
class TestingModule : public ModuleEnv {
|
|
|
|
public:
|
2016-02-18 15:58:02 +00:00
|
|
|
TestingModule() : instance_(&module_), global_offset(0) {
|
|
|
|
module_.shared_isolate = CcTest::InitIsolateOnce();
|
|
|
|
module = &module_;
|
2016-01-27 11:04:02 +00:00
|
|
|
instance = &instance_;
|
2016-02-18 15:58:02 +00:00
|
|
|
instance->module = &module_;
|
2016-01-27 11:04:02 +00:00
|
|
|
instance->globals_start = global_data;
|
|
|
|
instance->globals_size = kMaxGlobalsSize;
|
|
|
|
instance->mem_start = nullptr;
|
|
|
|
instance->mem_size = 0;
|
2016-01-14 09:48:45 +00:00
|
|
|
linker = nullptr;
|
2016-02-27 20:38:55 +00:00
|
|
|
origin = kWasmOrigin;
|
2016-01-14 09:48:45 +00:00
|
|
|
memset(global_data, 0, sizeof(global_data));
|
|
|
|
}
|
|
|
|
|
|
|
|
~TestingModule() {
|
2016-01-27 11:04:02 +00:00
|
|
|
if (instance->mem_start) {
|
|
|
|
free(instance->mem_start);
|
2016-01-14 09:48:45 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
byte* AddMemory(size_t size) {
|
2016-01-27 11:04:02 +00:00
|
|
|
CHECK_NULL(instance->mem_start);
|
|
|
|
CHECK_EQ(0, instance->mem_size);
|
|
|
|
instance->mem_start = reinterpret_cast<byte*>(malloc(size));
|
|
|
|
CHECK(instance->mem_start);
|
|
|
|
memset(instance->mem_start, 0, size);
|
|
|
|
instance->mem_size = size;
|
2016-01-14 09:48:45 +00:00
|
|
|
return raw_mem_start<byte>();
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename T>
|
|
|
|
T* AddMemoryElems(size_t count) {
|
|
|
|
AddMemory(count * sizeof(T));
|
|
|
|
return raw_mem_start<T>();
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename T>
|
|
|
|
T* AddGlobal(MachineType mem_type) {
|
|
|
|
WasmGlobal* global = AddGlobal(mem_type);
|
2016-01-27 11:04:02 +00:00
|
|
|
return reinterpret_cast<T*>(instance->globals_start + global->offset);
|
2016-01-14 09:48:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
byte AddSignature(FunctionSig* sig) {
|
2016-02-28 19:04:10 +00:00
|
|
|
module->signatures.push_back(sig);
|
|
|
|
size_t size = module->signatures.size();
|
2016-01-14 09:48:45 +00:00
|
|
|
CHECK(size < 127);
|
|
|
|
return static_cast<byte>(size - 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename T>
|
|
|
|
T* raw_mem_start() {
|
2016-01-27 11:04:02 +00:00
|
|
|
DCHECK(instance->mem_start);
|
|
|
|
return reinterpret_cast<T*>(instance->mem_start);
|
2016-01-14 09:48:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
template <typename T>
|
|
|
|
T* raw_mem_end() {
|
2016-01-27 11:04:02 +00:00
|
|
|
DCHECK(instance->mem_start);
|
|
|
|
return reinterpret_cast<T*>(instance->mem_start + instance->mem_size);
|
2016-01-14 09:48:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
template <typename T>
|
|
|
|
T raw_mem_at(int i) {
|
2016-01-27 11:04:02 +00:00
|
|
|
DCHECK(instance->mem_start);
|
|
|
|
return reinterpret_cast<T*>(instance->mem_start)[i];
|
2016-01-14 09:48:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
template <typename T>
|
|
|
|
T raw_val_at(int i) {
|
|
|
|
T val;
|
2016-01-27 11:04:02 +00:00
|
|
|
memcpy(&val, reinterpret_cast<void*>(instance->mem_start + i), sizeof(T));
|
2016-01-14 09:48:45 +00:00
|
|
|
return val;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Zero-initialize the memory.
|
|
|
|
void BlankMemory() {
|
|
|
|
byte* raw = raw_mem_start<byte>();
|
2016-01-27 11:04:02 +00:00
|
|
|
memset(raw, 0, instance->mem_size);
|
2016-01-14 09:48:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Pseudo-randomly intialize the memory.
|
|
|
|
void RandomizeMemory(unsigned int seed = 88) {
|
|
|
|
byte* raw = raw_mem_start<byte>();
|
|
|
|
byte* end = raw_mem_end<byte>();
|
|
|
|
v8::base::RandomNumberGenerator rng;
|
|
|
|
rng.SetSeed(seed);
|
|
|
|
rng.NextBytes(raw, end - raw);
|
|
|
|
}
|
|
|
|
|
2016-04-19 12:23:00 +00:00
|
|
|
uint32_t AddFunction(FunctionSig* sig, Handle<Code> code) {
|
2016-02-28 19:04:10 +00:00
|
|
|
if (module->functions.size() == 0) {
|
2016-02-18 15:58:02 +00:00
|
|
|
// TODO(titzer): Reserving space here to avoid the underlying WasmFunction
|
|
|
|
// structs from moving.
|
2016-02-28 19:04:10 +00:00
|
|
|
module->functions.reserve(kMaxFunctions);
|
2016-01-14 09:48:45 +00:00
|
|
|
}
|
2016-02-28 19:04:10 +00:00
|
|
|
uint32_t index = static_cast<uint32_t>(module->functions.size());
|
|
|
|
module->functions.push_back(
|
2016-03-09 18:55:27 +00:00
|
|
|
{sig, index, 0, 0, 0, 0, 0, 0, 0, 0, 0, false, false});
|
2016-02-28 19:04:10 +00:00
|
|
|
instance->function_code.push_back(code);
|
2016-02-18 15:58:02 +00:00
|
|
|
DCHECK_LT(index, kMaxFunctions); // limited for testing.
|
|
|
|
return index;
|
|
|
|
}
|
|
|
|
|
2016-04-19 12:23:00 +00:00
|
|
|
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());
|
|
|
|
Isolate* isolate = module->shared_isolate;
|
2016-04-26 14:47:01 +00:00
|
|
|
WasmName module_name = ArrayVector("test");
|
|
|
|
WasmName function_name;
|
2016-04-19 12:23:00 +00:00
|
|
|
Handle<Code> code = CompileWasmToJSWrapper(isolate, this, jsfunc, sig,
|
|
|
|
module_name, function_name);
|
|
|
|
instance->function_code[index] = code;
|
|
|
|
return index;
|
|
|
|
}
|
|
|
|
|
|
|
|
Handle<JSFunction> WrapCode(uint32_t index) {
|
|
|
|
Isolate* isolate = module->shared_isolate;
|
|
|
|
// Wrap the code so it can be called as a JS function.
|
|
|
|
Handle<String> name = isolate->factory()->NewStringFromStaticChars("main");
|
|
|
|
Handle<JSObject> module_object = Handle<JSObject>(0, isolate);
|
|
|
|
Handle<Code> code = instance->function_code[index];
|
|
|
|
WasmJs::InstallWasmFunctionMap(isolate, isolate->native_context());
|
|
|
|
return compiler::CompileJSToWasmWrapper(isolate, this, name, code,
|
|
|
|
module_object, index);
|
|
|
|
}
|
|
|
|
|
2016-02-18 15:58:02 +00:00
|
|
|
void SetFunctionCode(uint32_t index, Handle<Code> code) {
|
2016-02-28 19:04:10 +00:00
|
|
|
instance->function_code[index] = code;
|
2016-01-14 09:48:45 +00:00
|
|
|
}
|
|
|
|
|
2016-01-21 16:10:53 +00:00
|
|
|
void AddIndirectFunctionTable(int* functions, int table_size) {
|
|
|
|
Isolate* isolate = module->shared_isolate;
|
|
|
|
Handle<FixedArray> fixed =
|
|
|
|
isolate->factory()->NewFixedArray(2 * table_size);
|
2016-01-27 11:04:02 +00:00
|
|
|
instance->function_table = fixed;
|
2016-02-28 19:04:10 +00:00
|
|
|
DCHECK_EQ(0u, module->function_table.size());
|
2016-01-21 16:10:53 +00:00
|
|
|
for (int i = 0; i < table_size; i++) {
|
2016-02-28 19:04:10 +00:00
|
|
|
module->function_table.push_back(functions[i]);
|
2016-01-21 16:10:53 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void PopulateIndirectFunctionTable() {
|
2016-01-27 11:04:02 +00:00
|
|
|
if (instance->function_table.is_null()) return;
|
2016-02-28 19:04:10 +00:00
|
|
|
int table_size = static_cast<int>(module->function_table.size());
|
2016-01-21 16:10:53 +00:00
|
|
|
for (int i = 0; i < table_size; i++) {
|
2016-02-28 19:04:10 +00:00
|
|
|
int function_index = module->function_table[i];
|
|
|
|
WasmFunction* function = &module->functions[function_index];
|
2016-01-27 11:04:02 +00:00
|
|
|
instance->function_table->set(i, Smi::FromInt(function->sig_index));
|
2016-02-28 19:04:10 +00:00
|
|
|
instance->function_table->set(i + table_size,
|
|
|
|
*instance->function_code[function_index]);
|
2016-01-21 16:10:53 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-01-14 09:48:45 +00:00
|
|
|
private:
|
2016-02-18 15:58:02 +00:00
|
|
|
WasmModule module_;
|
2016-01-27 11:04:02 +00:00
|
|
|
WasmModuleInstance instance_;
|
2016-01-14 09:48:45 +00:00
|
|
|
uint32_t global_offset;
|
2016-02-02 12:44:58 +00:00
|
|
|
V8_ALIGNED(8) byte global_data[kMaxGlobalsSize]; // preallocated global data.
|
2016-01-14 09:48:45 +00:00
|
|
|
|
|
|
|
WasmGlobal* AddGlobal(MachineType mem_type) {
|
|
|
|
byte size = WasmOpcodes::MemSize(mem_type);
|
|
|
|
global_offset = (global_offset + size - 1) & ~(size - 1); // align
|
2016-03-09 18:55:27 +00:00
|
|
|
module->globals.push_back({0, 0, mem_type, global_offset, false});
|
2016-01-14 09:48:45 +00:00
|
|
|
global_offset += size;
|
|
|
|
// limit number of globals.
|
|
|
|
CHECK_LT(global_offset, kMaxGlobalsSize);
|
2016-02-28 19:04:10 +00:00
|
|
|
return &module->globals.back();
|
2016-01-14 09:48:45 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2016-03-07 21:04:07 +00:00
|
|
|
inline void TestBuildingGraph(Zone* zone, JSGraph* jsgraph, ModuleEnv* module,
|
2016-04-26 12:46:03 +00:00
|
|
|
FunctionSig* sig,
|
|
|
|
SourcePositionTable* source_position_table,
|
|
|
|
const byte* start, const byte* end) {
|
|
|
|
compiler::WasmGraphBuilder builder(zone, jsgraph, sig, source_position_table);
|
2016-04-01 10:00:30 +00:00
|
|
|
TreeResult result =
|
|
|
|
BuildTFGraph(zone->allocator(), &builder, module, sig, start, end);
|
2016-01-14 09:48:45 +00:00
|
|
|
if (result.failed()) {
|
|
|
|
ptrdiff_t pc = result.error_pc - result.start;
|
|
|
|
ptrdiff_t pt = result.error_pt - result.start;
|
|
|
|
std::ostringstream str;
|
|
|
|
str << "Verification failed: " << result.error_code << " pc = +" << pc;
|
|
|
|
if (result.error_pt) str << ", pt = +" << pt;
|
|
|
|
str << ", msg = " << result.error_msg.get();
|
|
|
|
FATAL(str.str().c_str());
|
|
|
|
}
|
2016-02-04 09:40:55 +00:00
|
|
|
builder.Int64LoweringForTesting();
|
2016-01-14 09:48:45 +00:00
|
|
|
if (FLAG_trace_turbo_graph) {
|
|
|
|
OFStream os(stdout);
|
|
|
|
os << AsRPO(*jsgraph->graph());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-02-18 09:55:25 +00:00
|
|
|
template <typename ReturnType>
|
|
|
|
class WasmFunctionWrapper : public HandleAndZoneScope,
|
|
|
|
private GraphAndBuilders {
|
|
|
|
public:
|
|
|
|
WasmFunctionWrapper()
|
|
|
|
: GraphAndBuilders(main_zone()),
|
|
|
|
inner_code_node_(nullptr),
|
|
|
|
signature_(nullptr) {
|
2016-02-18 15:18:41 +00:00
|
|
|
// One additional parameter for the pointer to the return value memory.
|
|
|
|
Signature<MachineType>::Builder sig_builder(
|
|
|
|
zone(), 1, WASM_RUNNER_MAX_NUM_PARAMETERS + 1);
|
2016-02-18 09:55:25 +00:00
|
|
|
|
|
|
|
sig_builder.AddReturn(MachineType::Int32());
|
2016-02-18 15:18:41 +00:00
|
|
|
for (int i = 0; i < WASM_RUNNER_MAX_NUM_PARAMETERS + 1; i++) {
|
2016-02-18 09:55:25 +00:00
|
|
|
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.
|
|
|
|
|
|
|
|
// Function, effect, and control.
|
2016-02-18 15:18:41 +00:00
|
|
|
Node** parameters =
|
|
|
|
zone()->template NewArray<Node*>(WASM_RUNNER_MAX_NUM_PARAMETERS + 3);
|
2016-02-18 09:55:25 +00:00
|
|
|
graph()->SetStart(graph()->NewNode(common()->Start(6)));
|
|
|
|
Node* effect = graph()->start();
|
|
|
|
int parameter_count = 0;
|
|
|
|
|
|
|
|
// Dummy node which gets replaced in SetInnerCode.
|
|
|
|
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(p0),
|
|
|
|
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(p0),
|
|
|
|
graph()->NewNode(common()->Parameter(2), graph()->start()),
|
|
|
|
graph()->NewNode(common()->Int32Constant(0)), effect,
|
|
|
|
graph()->start());
|
|
|
|
effect = parameters[parameter_count++];
|
|
|
|
}
|
|
|
|
if (p3 != MachineType::None()) {
|
|
|
|
parameters[parameter_count] = graph()->NewNode(
|
|
|
|
machine()->Load(p0),
|
|
|
|
graph()->NewNode(common()->Parameter(3), graph()->start()),
|
|
|
|
graph()->NewNode(common()->Int32Constant(0)), effect,
|
|
|
|
graph()->start());
|
|
|
|
effect = parameters[parameter_count++];
|
|
|
|
}
|
|
|
|
|
|
|
|
parameters[parameter_count++] = effect;
|
|
|
|
parameters[parameter_count++] = graph()->start();
|
|
|
|
Node* call = graph()->NewNode(common()->Call(descriptor), parameter_count,
|
|
|
|
parameters);
|
|
|
|
|
|
|
|
effect = graph()->NewNode(
|
|
|
|
machine()->Store(
|
|
|
|
StoreRepresentation(MachineTypeForC<ReturnType>().representation(),
|
|
|
|
WriteBarrierKind::kNoWriteBarrier)),
|
2016-02-18 15:18:41 +00:00
|
|
|
graph()->NewNode(common()->Parameter(WASM_RUNNER_MAX_NUM_PARAMETERS),
|
|
|
|
graph()->start()),
|
2016-02-18 09:55:25 +00:00
|
|
|
graph()->NewNode(common()->Int32Constant(0)), call, effect,
|
|
|
|
graph()->start());
|
|
|
|
Node* r = graph()->NewNode(
|
|
|
|
common()->Return(),
|
|
|
|
graph()->NewNode(common()->Int32Constant(WASM_WRAPPER_RETURN_VALUE)),
|
|
|
|
effect, graph()->start());
|
|
|
|
graph()->SetEnd(graph()->NewNode(common()->End(2), r, graph()->start()));
|
|
|
|
}
|
|
|
|
|
|
|
|
void SetInnerCode(Handle<Code> code_handle) {
|
|
|
|
NodeProperties::ChangeOp(inner_code_node_,
|
|
|
|
common()->HeapConstant(code_handle));
|
|
|
|
}
|
|
|
|
|
|
|
|
Handle<Code> GetWrapperCode() {
|
|
|
|
if (code_.is_null()) {
|
|
|
|
Isolate* isolate = CcTest::InitIsolateOnce();
|
|
|
|
|
|
|
|
CallDescriptor* descriptor =
|
|
|
|
Linkage::GetSimplifiedCDescriptor(zone(), signature_, true);
|
|
|
|
|
2016-02-18 15:18:41 +00:00
|
|
|
if (kPointerSize == 4) {
|
|
|
|
// One additional parameter for the pointer of the return value.
|
|
|
|
Signature<MachineRepresentation>::Builder rep_builder(
|
|
|
|
zone(), 1, WASM_RUNNER_MAX_NUM_PARAMETERS + 1);
|
|
|
|
|
|
|
|
rep_builder.AddReturn(MachineRepresentation::kWord32);
|
|
|
|
for (int i = 0; i < WASM_RUNNER_MAX_NUM_PARAMETERS + 1; i++) {
|
|
|
|
rep_builder.AddParam(MachineRepresentation::kWord32);
|
|
|
|
}
|
|
|
|
Int64Lowering r(graph(), machine(), common(), zone(),
|
|
|
|
rep_builder.Build());
|
|
|
|
r.LowerGraph();
|
|
|
|
}
|
|
|
|
|
2016-04-26 14:36:35 +00:00
|
|
|
CompilationInfo info(ArrayVector("testing"), isolate, graph()->zone());
|
2016-02-18 09:55:25 +00:00
|
|
|
code_ =
|
|
|
|
Pipeline::GenerateCodeForTesting(&info, descriptor, graph(), nullptr);
|
|
|
|
CHECK(!code_.is_null());
|
|
|
|
#ifdef ENABLE_DISASSEMBLER
|
|
|
|
if (FLAG_print_opt_code) {
|
|
|
|
OFStream os(stdout);
|
|
|
|
code_->Disassemble("wasm wrapper", os);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
return code_;
|
|
|
|
}
|
|
|
|
|
|
|
|
Signature<MachineType>* signature() const { return signature_; }
|
|
|
|
|
|
|
|
private:
|
|
|
|
Node* inner_code_node_;
|
|
|
|
Handle<Code> code_;
|
|
|
|
Signature<MachineType>* signature_;
|
|
|
|
};
|
2016-01-14 09:48:45 +00:00
|
|
|
|
2016-02-18 15:58:02 +00:00
|
|
|
// 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, later, interpretation.
|
2016-01-14 09:48:45 +00:00
|
|
|
class WasmFunctionCompiler : public HandleAndZoneScope,
|
|
|
|
private GraphAndBuilders {
|
|
|
|
public:
|
2016-04-26 14:36:35 +00:00
|
|
|
explicit WasmFunctionCompiler(
|
|
|
|
FunctionSig* sig, TestingModule* module,
|
|
|
|
Vector<const char> debug_name = ArrayVector("<WASM UNNAMED>"))
|
2016-01-14 09:48:45 +00:00
|
|
|
: GraphAndBuilders(main_zone()),
|
|
|
|
jsgraph(this->isolate(), this->graph(), this->common(), nullptr,
|
|
|
|
nullptr, this->machine()),
|
2016-03-07 21:04:07 +00:00
|
|
|
sig(sig),
|
2016-02-18 15:58:02 +00:00
|
|
|
descriptor_(nullptr),
|
2016-04-20 15:09:36 +00:00
|
|
|
testing_module_(module),
|
2016-04-26 12:46:03 +00:00
|
|
|
debug_name_(debug_name),
|
|
|
|
source_position_table_(this->graph()) {
|
2016-02-18 15:58:02 +00:00
|
|
|
if (module) {
|
|
|
|
// Get a new function from the testing module.
|
|
|
|
function_ = nullptr;
|
|
|
|
function_index_ = module->AddFunction(sig, Handle<Code>::null());
|
|
|
|
} else {
|
|
|
|
// Create our own function.
|
|
|
|
function_ = new WasmFunction();
|
|
|
|
function_->sig = sig;
|
|
|
|
function_index_ = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
~WasmFunctionCompiler() {
|
|
|
|
if (function_) delete function_;
|
2016-01-14 09:48:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
JSGraph jsgraph;
|
2016-03-07 21:04:07 +00:00
|
|
|
FunctionSig* sig;
|
2016-01-14 09:48:45 +00:00
|
|
|
// The call descriptor is initialized when the function is compiled.
|
|
|
|
CallDescriptor* descriptor_;
|
2016-02-18 15:58:02 +00:00
|
|
|
TestingModule* testing_module_;
|
2016-04-26 14:36:35 +00:00
|
|
|
Vector<const char> debug_name_;
|
2016-02-18 15:58:02 +00:00
|
|
|
WasmFunction* function_;
|
|
|
|
int function_index_;
|
2016-03-07 21:04:07 +00:00
|
|
|
LocalDeclEncoder local_decls;
|
2016-04-26 12:46:03 +00:00
|
|
|
SourcePositionTable source_position_table_;
|
2016-01-14 09:48:45 +00:00
|
|
|
|
|
|
|
Isolate* isolate() { return main_isolate(); }
|
|
|
|
Graph* graph() const { return main_graph_; }
|
|
|
|
Zone* zone() const { return graph()->zone(); }
|
|
|
|
CommonOperatorBuilder* common() { return &main_common_; }
|
|
|
|
MachineOperatorBuilder* machine() { return &main_machine_; }
|
2016-02-18 09:55:25 +00:00
|
|
|
void InitializeDescriptor() {
|
|
|
|
if (descriptor_ == nullptr) {
|
2016-03-07 21:04:07 +00:00
|
|
|
descriptor_ = testing_module_->GetWasmCallDescriptor(main_zone(), sig);
|
2016-02-18 09:55:25 +00:00
|
|
|
}
|
|
|
|
}
|
2016-01-14 09:48:45 +00:00
|
|
|
CallDescriptor* descriptor() { return descriptor_; }
|
|
|
|
|
|
|
|
void Build(const byte* start, const byte* end) {
|
2016-02-18 15:58:02 +00:00
|
|
|
// Build the TurboFan graph.
|
2016-03-07 21:04:07 +00:00
|
|
|
local_decls.Prepend(&start, &end);
|
2016-04-26 12:46:03 +00:00
|
|
|
TestBuildingGraph(main_zone(), &jsgraph, testing_module_, sig,
|
|
|
|
&source_position_table_, start, end);
|
2016-03-07 21:04:07 +00:00
|
|
|
delete[] start;
|
2016-01-14 09:48:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
byte AllocateLocal(LocalType type) {
|
2016-03-07 21:04:07 +00:00
|
|
|
uint32_t index = local_decls.AddLocals(1, type, sig);
|
|
|
|
byte result = static_cast<byte>(index);
|
|
|
|
DCHECK_EQ(index, result);
|
|
|
|
return result;
|
2016-01-14 09:48:45 +00:00
|
|
|
}
|
|
|
|
|
2016-02-18 15:58:02 +00:00
|
|
|
Handle<Code> Compile() {
|
2016-02-18 09:55:25 +00:00
|
|
|
InitializeDescriptor();
|
2016-02-18 15:18:41 +00:00
|
|
|
CallDescriptor* desc = descriptor_;
|
|
|
|
if (kPointerSize == 4) {
|
2016-02-18 15:58:02 +00:00
|
|
|
desc = testing_module_->GetI32WasmCallDescriptor(this->zone(), desc);
|
2016-02-18 15:18:41 +00:00
|
|
|
}
|
2016-04-20 15:09:36 +00:00
|
|
|
CompilationInfo info(debug_name_, this->isolate(), this->zone(),
|
2016-04-19 12:54:10 +00:00
|
|
|
Code::ComputeFlags(Code::WASM_FUNCTION));
|
2016-04-27 17:54:16 +00:00
|
|
|
v8::base::SmartPointer<CompilationJob> job(Pipeline::NewWasmCompilationJob(
|
|
|
|
&info, graph(), desc, &source_position_table_));
|
2016-04-29 12:24:58 +00:00
|
|
|
if (job->OptimizeGraph() != CompilationJob::SUCCEEDED ||
|
|
|
|
job->GenerateCode() != 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 store <func_name, undef> instead.
|
|
|
|
DCHECK(code->deoptimization_data() == nullptr ||
|
|
|
|
code->deoptimization_data()->length() == 0);
|
|
|
|
Handle<FixedArray> deopt_data =
|
|
|
|
isolate()->factory()->NewFixedArray(2, TENURED);
|
|
|
|
if (debug_name_.start() != nullptr) {
|
|
|
|
MaybeHandle<String> maybe_name =
|
|
|
|
isolate()->factory()->NewStringFromUtf8(debug_name_, TENURED);
|
|
|
|
if (!maybe_name.is_null())
|
|
|
|
deopt_data->set(0, *maybe_name.ToHandleChecked());
|
2016-04-26 12:46:03 +00:00
|
|
|
}
|
2016-04-29 12:24:58 +00:00
|
|
|
deopt_data->set_length(2);
|
|
|
|
code->set_deoptimization_data(*deopt_data);
|
|
|
|
|
2016-01-14 09:48:45 +00:00
|
|
|
#ifdef ENABLE_DISASSEMBLER
|
2016-04-29 12:24:58 +00:00
|
|
|
if (FLAG_print_opt_code) {
|
2016-01-14 09:48:45 +00:00
|
|
|
OFStream os(stdout);
|
2016-04-26 12:46:03 +00:00
|
|
|
code->Disassemble("wasm code", os);
|
2016-01-14 09:48:45 +00:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2016-04-26 12:46:03 +00:00
|
|
|
return code;
|
2016-01-14 09:48:45 +00:00
|
|
|
}
|
|
|
|
|
2016-02-18 15:58:02 +00:00
|
|
|
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 static_cast<uint32_t>(function_index_);
|
2016-01-14 09:48:45 +00:00
|
|
|
}
|
|
|
|
|
2016-02-18 15:58:02 +00:00
|
|
|
WasmFunction* function() {
|
|
|
|
if (function_) return function_;
|
2016-02-28 19:04:10 +00:00
|
|
|
return &testing_module_->module->functions[function_index_];
|
2016-02-18 15:58:02 +00:00
|
|
|
}
|
2016-04-20 15:09:36 +00:00
|
|
|
|
|
|
|
// 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();
|
|
|
|
}
|
2016-02-18 15:58:02 +00:00
|
|
|
};
|
2016-01-14 09:48:45 +00:00
|
|
|
|
|
|
|
// A helper class to build graphs from Wasm bytecode, generate machine
|
|
|
|
// code, and run that code.
|
|
|
|
template <typename ReturnType>
|
|
|
|
class WasmRunner {
|
|
|
|
public:
|
|
|
|
WasmRunner(MachineType p0 = MachineType::None(),
|
|
|
|
MachineType p1 = MachineType::None(),
|
|
|
|
MachineType p2 = MachineType::None(),
|
|
|
|
MachineType p3 = MachineType::None())
|
2016-04-01 10:00:30 +00:00
|
|
|
: zone(&allocator_),
|
|
|
|
compiled_(false),
|
2016-02-18 15:58:02 +00:00
|
|
|
signature_(MachineTypeForC<ReturnType>() == MachineType::None() ? 0 : 1,
|
|
|
|
GetParameterCount(p0, p1, p2, p3), storage_),
|
|
|
|
compiler_(&signature_, nullptr) {
|
|
|
|
InitSigStorage(p0, p1, p2, p3);
|
|
|
|
}
|
|
|
|
|
|
|
|
WasmRunner(TestingModule* module, MachineType p0 = MachineType::None(),
|
|
|
|
MachineType p1 = MachineType::None(),
|
|
|
|
MachineType p2 = MachineType::None(),
|
|
|
|
MachineType p3 = MachineType::None())
|
2016-04-01 10:00:30 +00:00
|
|
|
: zone(&allocator_),
|
|
|
|
compiled_(false),
|
2016-02-18 15:58:02 +00:00
|
|
|
signature_(MachineTypeForC<ReturnType>() == MachineType::None() ? 0 : 1,
|
2016-01-14 09:48:45 +00:00
|
|
|
GetParameterCount(p0, p1, p2, p3), storage_),
|
2016-02-18 15:58:02 +00:00
|
|
|
compiler_(&signature_, module) {
|
|
|
|
DCHECK(module);
|
|
|
|
InitSigStorage(p0, p1, p2, p3);
|
|
|
|
}
|
|
|
|
|
|
|
|
void InitSigStorage(MachineType p0, MachineType p1, MachineType p2,
|
|
|
|
MachineType p3) {
|
2016-01-14 09:48:45 +00:00
|
|
|
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);
|
2016-02-18 09:55:25 +00:00
|
|
|
|
|
|
|
compiler_.InitializeDescriptor();
|
|
|
|
wrapper_.Init(compiler_.descriptor(), p0, p1, p2, p3);
|
2016-01-14 09:48:45 +00:00
|
|
|
}
|
|
|
|
|
2016-02-18 15:58:02 +00:00
|
|
|
// Builds a graph from the given Wasm code and generates the machine
|
2016-01-14 09:48:45 +00:00
|
|
|
// code and call wrapper for that graph. This method must not be called
|
|
|
|
// more than once.
|
|
|
|
void Build(const byte* start, const byte* end) {
|
2016-02-18 15:58:02 +00:00
|
|
|
CHECK(!compiled_);
|
|
|
|
compiled_ = true;
|
|
|
|
|
|
|
|
// Build the TF graph within the compiler.
|
2016-01-14 09:48:45 +00:00
|
|
|
compiler_.Build(start, end);
|
|
|
|
// Generate code.
|
2016-02-18 15:58:02 +00:00
|
|
|
Handle<Code> code = compiler_.Compile();
|
|
|
|
|
|
|
|
if (compiler_.testing_module_) {
|
|
|
|
// Update the table of function code in the module.
|
|
|
|
compiler_.testing_module_->SetFunctionCode(compiler_.function_index_,
|
|
|
|
code);
|
|
|
|
}
|
2016-01-14 09:48:45 +00:00
|
|
|
|
2016-02-18 09:55:25 +00:00
|
|
|
wrapper_.SetInnerCode(code);
|
2016-01-14 09:48:45 +00:00
|
|
|
}
|
|
|
|
|
2016-02-18 15:58:02 +00:00
|
|
|
ReturnType Call() { return Call(0, 0, 0, 0); }
|
2016-01-14 09:48:45 +00:00
|
|
|
|
|
|
|
template <typename P0>
|
|
|
|
ReturnType Call(P0 p0) {
|
2016-02-18 15:58:02 +00:00
|
|
|
return Call(p0, 0, 0, 0);
|
2016-01-14 09:48:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
template <typename P0, typename P1>
|
|
|
|
ReturnType Call(P0 p0, P1 p1) {
|
2016-02-18 15:58:02 +00:00
|
|
|
return Call(p0, p1, 0, 0);
|
2016-01-14 09:48:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
template <typename P0, typename P1, typename P2>
|
|
|
|
ReturnType Call(P0 p0, P1 p1, P2 p2) {
|
2016-02-18 15:58:02 +00:00
|
|
|
return Call(p0, p1, p2, 0);
|
2016-01-14 09:48:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
template <typename P0, typename P1, typename P2, typename P3>
|
|
|
|
ReturnType Call(P0 p0, P1 p1, P2 p2, P3 p3) {
|
2016-02-18 09:55:25 +00:00
|
|
|
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;
|
2016-01-14 09:48:45 +00:00
|
|
|
}
|
|
|
|
|
2016-03-07 21:04:07 +00:00
|
|
|
byte AllocateLocal(LocalType type) { return compiler_.AllocateLocal(type); }
|
2016-01-14 09:48:45 +00:00
|
|
|
|
2016-02-18 15:58:02 +00:00
|
|
|
protected:
|
2016-04-01 10:00:30 +00:00
|
|
|
v8::base::AccountingAllocator allocator_;
|
2016-02-18 15:58:02 +00:00
|
|
|
Zone zone;
|
|
|
|
bool compiled_;
|
2016-02-18 15:18:41 +00:00
|
|
|
LocalType storage_[WASM_RUNNER_MAX_NUM_PARAMETERS];
|
2016-01-14 09:48:45 +00:00
|
|
|
FunctionSig signature_;
|
|
|
|
WasmFunctionCompiler compiler_;
|
2016-02-18 09:55:25 +00:00
|
|
|
WasmFunctionWrapper<ReturnType> wrapper_;
|
2016-01-14 09:48:45 +00:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
#endif
|