blob: a92c9ff50a760618dfabaea6cbe1f3152774edeb [file] [log] [blame]
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00001// Copyright 2016 the V8 project authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#ifndef WASM_RUN_UTILS_H
6#define WASM_RUN_UTILS_H
7
8#include <stdint.h>
9#include <stdlib.h>
10#include <string.h>
11
12#include "src/base/utils/random-number-generator.h"
13
14#include "src/compiler/graph-visualizer.h"
Ben Murdoch097c5b22016-05-18 11:27:45 +010015#include "src/compiler/int64-lowering.h"
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000016#include "src/compiler/js-graph.h"
Ben Murdoch097c5b22016-05-18 11:27:45 +010017#include "src/compiler/node.h"
18#include "src/compiler/pipeline.h"
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000019#include "src/compiler/wasm-compiler.h"
Ben Murdochc5610432016-08-08 18:44:38 +010020#include "src/compiler/zone-pool.h"
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000021
22#include "src/wasm/ast-decoder.h"
23#include "src/wasm/wasm-js.h"
Ben Murdochc5610432016-08-08 18:44:38 +010024#include "src/wasm/wasm-macro-gen.h"
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000025#include "src/wasm/wasm-module.h"
26#include "src/wasm/wasm-opcodes.h"
27
Ben Murdoch097c5b22016-05-18 11:27:45 +010028#include "src/zone.h"
29
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000030#include "test/cctest/cctest.h"
Ben Murdoch097c5b22016-05-18 11:27:45 +010031#include "test/cctest/compiler/call-tester.h"
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000032#include "test/cctest/compiler/graph-builder-tester.h"
33
34// TODO(titzer): pull WASM_64 up to a common header.
35#if !V8_TARGET_ARCH_32_BIT || V8_TARGET_ARCH_X64
36#define WASM_64 1
37#else
38#define WASM_64 0
39#endif
40
Ben Murdoch097c5b22016-05-18 11:27:45 +010041static const uint32_t kMaxFunctions = 10;
42
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000043// TODO(titzer): check traps more robustly in tests.
44// Currently, in tests, we just return 0xdeadbeef from the function in which
45// the trap occurs if the runtime context is not available to throw a JavaScript
46// exception.
47#define CHECK_TRAP32(x) \
48 CHECK_EQ(0xdeadbeef, (bit_cast<uint32_t>(x)) & 0xFFFFFFFF)
49#define CHECK_TRAP64(x) \
50 CHECK_EQ(0xdeadbeefdeadbeef, (bit_cast<uint64_t>(x)) & 0xFFFFFFFFFFFFFFFF)
51#define CHECK_TRAP(x) CHECK_TRAP32(x)
52
Ben Murdoch097c5b22016-05-18 11:27:45 +010053#define WASM_RUNNER_MAX_NUM_PARAMETERS 4
54#define WASM_WRAPPER_RETURN_VALUE 8754
55
Ben Murdochda12d292016-06-02 14:46:10 +010056#define BUILD(r, ...) \
57 do { \
58 byte code[] = {__VA_ARGS__}; \
59 r.Build(code, code + arraysize(code)); \
60 } while (false)
61
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000062namespace {
63using namespace v8::base;
64using namespace v8::internal;
65using namespace v8::internal::compiler;
66using namespace v8::internal::wasm;
67
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000068const uint32_t kMaxGlobalsSize = 128;
69
70// A helper for module environments that adds the ability to allocate memory
Ben Murdoch097c5b22016-05-18 11:27:45 +010071// and global variables. Contains a built-in {WasmModule} and
72// {WasmModuleInstance}.
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000073class TestingModule : public ModuleEnv {
74 public:
Ben Murdoch097c5b22016-05-18 11:27:45 +010075 TestingModule() : instance_(&module_), global_offset(0) {
76 module_.shared_isolate = CcTest::InitIsolateOnce();
77 module = &module_;
78 instance = &instance_;
79 instance->module = &module_;
80 instance->globals_start = global_data;
81 instance->globals_size = kMaxGlobalsSize;
82 instance->mem_start = nullptr;
83 instance->mem_size = 0;
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000084 linker = nullptr;
Ben Murdochda12d292016-06-02 14:46:10 +010085 origin = kWasmOrigin;
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000086 memset(global_data, 0, sizeof(global_data));
87 }
88
89 ~TestingModule() {
Ben Murdoch097c5b22016-05-18 11:27:45 +010090 if (instance->mem_start) {
91 free(instance->mem_start);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000092 }
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000093 }
94
95 byte* AddMemory(size_t size) {
Ben Murdoch097c5b22016-05-18 11:27:45 +010096 CHECK_NULL(instance->mem_start);
97 CHECK_EQ(0, instance->mem_size);
98 instance->mem_start = reinterpret_cast<byte*>(malloc(size));
99 CHECK(instance->mem_start);
100 memset(instance->mem_start, 0, size);
101 instance->mem_size = size;
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000102 return raw_mem_start<byte>();
103 }
104
105 template <typename T>
106 T* AddMemoryElems(size_t count) {
107 AddMemory(count * sizeof(T));
108 return raw_mem_start<T>();
109 }
110
111 template <typename T>
112 T* AddGlobal(MachineType mem_type) {
113 WasmGlobal* global = AddGlobal(mem_type);
Ben Murdoch097c5b22016-05-18 11:27:45 +0100114 return reinterpret_cast<T*>(instance->globals_start + global->offset);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000115 }
116
117 byte AddSignature(FunctionSig* sig) {
Ben Murdochda12d292016-06-02 14:46:10 +0100118 module->signatures.push_back(sig);
119 size_t size = module->signatures.size();
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000120 CHECK(size < 127);
121 return static_cast<byte>(size - 1);
122 }
123
124 template <typename T>
125 T* raw_mem_start() {
Ben Murdoch097c5b22016-05-18 11:27:45 +0100126 DCHECK(instance->mem_start);
127 return reinterpret_cast<T*>(instance->mem_start);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000128 }
129
130 template <typename T>
131 T* raw_mem_end() {
Ben Murdoch097c5b22016-05-18 11:27:45 +0100132 DCHECK(instance->mem_start);
133 return reinterpret_cast<T*>(instance->mem_start + instance->mem_size);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000134 }
135
136 template <typename T>
137 T raw_mem_at(int i) {
Ben Murdoch097c5b22016-05-18 11:27:45 +0100138 DCHECK(instance->mem_start);
139 return reinterpret_cast<T*>(instance->mem_start)[i];
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000140 }
141
142 template <typename T>
143 T raw_val_at(int i) {
144 T val;
Ben Murdoch097c5b22016-05-18 11:27:45 +0100145 memcpy(&val, reinterpret_cast<void*>(instance->mem_start + i), sizeof(T));
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000146 return val;
147 }
148
149 // Zero-initialize the memory.
150 void BlankMemory() {
151 byte* raw = raw_mem_start<byte>();
Ben Murdoch097c5b22016-05-18 11:27:45 +0100152 memset(raw, 0, instance->mem_size);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000153 }
154
155 // Pseudo-randomly intialize the memory.
156 void RandomizeMemory(unsigned int seed = 88) {
157 byte* raw = raw_mem_start<byte>();
158 byte* end = raw_mem_end<byte>();
159 v8::base::RandomNumberGenerator rng;
160 rng.SetSeed(seed);
161 rng.NextBytes(raw, end - raw);
162 }
163
Ben Murdochc5610432016-08-08 18:44:38 +0100164 uint32_t AddFunction(FunctionSig* sig, Handle<Code> code) {
Ben Murdochda12d292016-06-02 14:46:10 +0100165 if (module->functions.size() == 0) {
Ben Murdoch097c5b22016-05-18 11:27:45 +0100166 // TODO(titzer): Reserving space here to avoid the underlying WasmFunction
167 // structs from moving.
Ben Murdochda12d292016-06-02 14:46:10 +0100168 module->functions.reserve(kMaxFunctions);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000169 }
Ben Murdochda12d292016-06-02 14:46:10 +0100170 uint32_t index = static_cast<uint32_t>(module->functions.size());
Ben Murdochc5610432016-08-08 18:44:38 +0100171 module->functions.push_back({sig, index, 0, 0, 0, 0, 0, false});
Ben Murdochda12d292016-06-02 14:46:10 +0100172 instance->function_code.push_back(code);
Ben Murdoch097c5b22016-05-18 11:27:45 +0100173 DCHECK_LT(index, kMaxFunctions); // limited for testing.
174 return index;
175 }
176
Ben Murdochc5610432016-08-08 18:44:38 +0100177 uint32_t AddJsFunction(FunctionSig* sig, const char* source) {
178 Handle<JSFunction> jsfunc = Handle<JSFunction>::cast(v8::Utils::OpenHandle(
179 *v8::Local<v8::Function>::Cast(CompileRun(source))));
180 uint32_t index = AddFunction(sig, Handle<Code>::null());
181 Isolate* isolate = module->shared_isolate;
182 WasmName module_name = ArrayVector("test");
183 WasmName function_name;
184 Handle<Code> code = CompileWasmToJSWrapper(isolate, this, jsfunc, sig,
185 module_name, function_name);
186 instance->function_code[index] = code;
187 return index;
188 }
189
190 Handle<JSFunction> WrapCode(uint32_t index) {
191 Isolate* isolate = module->shared_isolate;
192 // Wrap the code so it can be called as a JS function.
193 Handle<String> name = isolate->factory()->NewStringFromStaticChars("main");
194 Handle<JSObject> module_object = Handle<JSObject>(0, isolate);
195 Handle<Code> code = instance->function_code[index];
196 WasmJs::InstallWasmFunctionMap(isolate, isolate->native_context());
197 return compiler::CompileJSToWasmWrapper(isolate, this, name, code,
198 module_object, index);
199 }
200
Ben Murdoch097c5b22016-05-18 11:27:45 +0100201 void SetFunctionCode(uint32_t index, Handle<Code> code) {
Ben Murdochda12d292016-06-02 14:46:10 +0100202 instance->function_code[index] = code;
Ben Murdoch097c5b22016-05-18 11:27:45 +0100203 }
204
205 void AddIndirectFunctionTable(int* functions, int table_size) {
206 Isolate* isolate = module->shared_isolate;
207 Handle<FixedArray> fixed =
208 isolate->factory()->NewFixedArray(2 * table_size);
209 instance->function_table = fixed;
Ben Murdochda12d292016-06-02 14:46:10 +0100210 DCHECK_EQ(0u, module->function_table.size());
Ben Murdoch097c5b22016-05-18 11:27:45 +0100211 for (int i = 0; i < table_size; i++) {
Ben Murdochda12d292016-06-02 14:46:10 +0100212 module->function_table.push_back(functions[i]);
Ben Murdoch097c5b22016-05-18 11:27:45 +0100213 }
214 }
215
216 void PopulateIndirectFunctionTable() {
217 if (instance->function_table.is_null()) return;
Ben Murdochda12d292016-06-02 14:46:10 +0100218 int table_size = static_cast<int>(module->function_table.size());
Ben Murdoch097c5b22016-05-18 11:27:45 +0100219 for (int i = 0; i < table_size; i++) {
Ben Murdochda12d292016-06-02 14:46:10 +0100220 int function_index = module->function_table[i];
221 WasmFunction* function = &module->functions[function_index];
Ben Murdoch097c5b22016-05-18 11:27:45 +0100222 instance->function_table->set(i, Smi::FromInt(function->sig_index));
Ben Murdochda12d292016-06-02 14:46:10 +0100223 instance->function_table->set(i + table_size,
224 *instance->function_code[function_index]);
Ben Murdoch097c5b22016-05-18 11:27:45 +0100225 }
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000226 }
227
228 private:
Ben Murdoch097c5b22016-05-18 11:27:45 +0100229 WasmModule module_;
230 WasmModuleInstance instance_;
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000231 uint32_t global_offset;
Ben Murdoch097c5b22016-05-18 11:27:45 +0100232 V8_ALIGNED(8) byte global_data[kMaxGlobalsSize]; // preallocated global data.
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000233
234 WasmGlobal* AddGlobal(MachineType mem_type) {
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000235 byte size = WasmOpcodes::MemSize(mem_type);
236 global_offset = (global_offset + size - 1) & ~(size - 1); // align
Ben Murdochda12d292016-06-02 14:46:10 +0100237 module->globals.push_back({0, 0, mem_type, global_offset, false});
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000238 global_offset += size;
239 // limit number of globals.
240 CHECK_LT(global_offset, kMaxGlobalsSize);
Ben Murdochda12d292016-06-02 14:46:10 +0100241 return &module->globals.back();
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000242 }
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000243};
244
Ben Murdochda12d292016-06-02 14:46:10 +0100245inline void TestBuildingGraph(Zone* zone, JSGraph* jsgraph, ModuleEnv* module,
Ben Murdochc5610432016-08-08 18:44:38 +0100246 FunctionSig* sig,
247 SourcePositionTable* source_position_table,
248 const byte* start, const byte* end) {
249 compiler::WasmGraphBuilder builder(zone, jsgraph, sig, source_position_table);
Ben Murdochda12d292016-06-02 14:46:10 +0100250 TreeResult result =
251 BuildTFGraph(zone->allocator(), &builder, module, sig, start, end);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000252 if (result.failed()) {
253 ptrdiff_t pc = result.error_pc - result.start;
254 ptrdiff_t pt = result.error_pt - result.start;
255 std::ostringstream str;
256 str << "Verification failed: " << result.error_code << " pc = +" << pc;
257 if (result.error_pt) str << ", pt = +" << pt;
258 str << ", msg = " << result.error_msg.get();
259 FATAL(str.str().c_str());
260 }
Ben Murdoch097c5b22016-05-18 11:27:45 +0100261 builder.Int64LoweringForTesting();
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000262 if (FLAG_trace_turbo_graph) {
263 OFStream os(stdout);
264 os << AsRPO(*jsgraph->graph());
265 }
266}
267
Ben Murdoch097c5b22016-05-18 11:27:45 +0100268template <typename ReturnType>
269class WasmFunctionWrapper : public HandleAndZoneScope,
270 private GraphAndBuilders {
271 public:
272 WasmFunctionWrapper()
273 : GraphAndBuilders(main_zone()),
274 inner_code_node_(nullptr),
275 signature_(nullptr) {
276 // One additional parameter for the pointer to the return value memory.
277 Signature<MachineType>::Builder sig_builder(
278 zone(), 1, WASM_RUNNER_MAX_NUM_PARAMETERS + 1);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000279
Ben Murdoch097c5b22016-05-18 11:27:45 +0100280 sig_builder.AddReturn(MachineType::Int32());
281 for (int i = 0; i < WASM_RUNNER_MAX_NUM_PARAMETERS + 1; i++) {
282 sig_builder.AddParam(MachineType::Pointer());
283 }
284 signature_ = sig_builder.Build();
285 }
286
287 void Init(CallDescriptor* descriptor, MachineType p0 = MachineType::None(),
288 MachineType p1 = MachineType::None(),
289 MachineType p2 = MachineType::None(),
290 MachineType p3 = MachineType::None()) {
291 // Create the TF graph for the wrapper. The wrapper always takes four
292 // pointers as parameters, but may not pass the values of all pointers to
293 // the actual test function.
294
295 // Function, effect, and control.
296 Node** parameters =
297 zone()->template NewArray<Node*>(WASM_RUNNER_MAX_NUM_PARAMETERS + 3);
298 graph()->SetStart(graph()->NewNode(common()->Start(6)));
299 Node* effect = graph()->start();
300 int parameter_count = 0;
301
302 // Dummy node which gets replaced in SetInnerCode.
303 inner_code_node_ = graph()->NewNode(common()->Int32Constant(0));
304 parameters[parameter_count++] = inner_code_node_;
305
306 if (p0 != MachineType::None()) {
307 parameters[parameter_count] = graph()->NewNode(
308 machine()->Load(p0),
309 graph()->NewNode(common()->Parameter(0), graph()->start()),
310 graph()->NewNode(common()->Int32Constant(0)), effect,
311 graph()->start());
312 effect = parameters[parameter_count++];
313 }
314 if (p1 != MachineType::None()) {
315 parameters[parameter_count] = graph()->NewNode(
316 machine()->Load(p0),
317 graph()->NewNode(common()->Parameter(1), graph()->start()),
318 graph()->NewNode(common()->Int32Constant(0)), effect,
319 graph()->start());
320 effect = parameters[parameter_count++];
321 }
322 if (p2 != MachineType::None()) {
323 parameters[parameter_count] = graph()->NewNode(
324 machine()->Load(p0),
325 graph()->NewNode(common()->Parameter(2), graph()->start()),
326 graph()->NewNode(common()->Int32Constant(0)), effect,
327 graph()->start());
328 effect = parameters[parameter_count++];
329 }
330 if (p3 != MachineType::None()) {
331 parameters[parameter_count] = graph()->NewNode(
332 machine()->Load(p0),
333 graph()->NewNode(common()->Parameter(3), graph()->start()),
334 graph()->NewNode(common()->Int32Constant(0)), effect,
335 graph()->start());
336 effect = parameters[parameter_count++];
337 }
338
339 parameters[parameter_count++] = effect;
340 parameters[parameter_count++] = graph()->start();
341 Node* call = graph()->NewNode(common()->Call(descriptor), parameter_count,
342 parameters);
343
344 effect = graph()->NewNode(
345 machine()->Store(
346 StoreRepresentation(MachineTypeForC<ReturnType>().representation(),
347 WriteBarrierKind::kNoWriteBarrier)),
348 graph()->NewNode(common()->Parameter(WASM_RUNNER_MAX_NUM_PARAMETERS),
349 graph()->start()),
350 graph()->NewNode(common()->Int32Constant(0)), call, effect,
351 graph()->start());
352 Node* r = graph()->NewNode(
353 common()->Return(),
354 graph()->NewNode(common()->Int32Constant(WASM_WRAPPER_RETURN_VALUE)),
355 effect, graph()->start());
356 graph()->SetEnd(graph()->NewNode(common()->End(2), r, graph()->start()));
357 }
358
359 void SetInnerCode(Handle<Code> code_handle) {
360 NodeProperties::ChangeOp(inner_code_node_,
361 common()->HeapConstant(code_handle));
362 }
363
364 Handle<Code> GetWrapperCode() {
365 if (code_.is_null()) {
366 Isolate* isolate = CcTest::InitIsolateOnce();
367
368 CallDescriptor* descriptor =
369 Linkage::GetSimplifiedCDescriptor(zone(), signature_, true);
370
371 if (kPointerSize == 4) {
372 // One additional parameter for the pointer of the return value.
373 Signature<MachineRepresentation>::Builder rep_builder(
374 zone(), 1, WASM_RUNNER_MAX_NUM_PARAMETERS + 1);
375
376 rep_builder.AddReturn(MachineRepresentation::kWord32);
377 for (int i = 0; i < WASM_RUNNER_MAX_NUM_PARAMETERS + 1; i++) {
378 rep_builder.AddParam(MachineRepresentation::kWord32);
379 }
380 Int64Lowering r(graph(), machine(), common(), zone(),
381 rep_builder.Build());
382 r.LowerGraph();
383 }
384
Ben Murdochc5610432016-08-08 18:44:38 +0100385 CompilationInfo info(ArrayVector("testing"), isolate, graph()->zone());
Ben Murdoch097c5b22016-05-18 11:27:45 +0100386 code_ =
387 Pipeline::GenerateCodeForTesting(&info, descriptor, graph(), nullptr);
388 CHECK(!code_.is_null());
389#ifdef ENABLE_DISASSEMBLER
390 if (FLAG_print_opt_code) {
391 OFStream os(stdout);
392 code_->Disassemble("wasm wrapper", os);
393 }
394#endif
395 }
396
397 return code_;
398 }
399
400 Signature<MachineType>* signature() const { return signature_; }
401
402 private:
403 Node* inner_code_node_;
404 Handle<Code> code_;
405 Signature<MachineType>* signature_;
406};
407
408// A helper for compiling WASM functions for testing. This class can create a
409// standalone function if {module} is NULL or a function within a
410// {TestingModule}. It contains the internal state for compilation (i.e.
411// TurboFan graph) and, later, interpretation.
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000412class WasmFunctionCompiler : public HandleAndZoneScope,
413 private GraphAndBuilders {
414 public:
Ben Murdochc5610432016-08-08 18:44:38 +0100415 explicit WasmFunctionCompiler(
416 FunctionSig* sig, TestingModule* module,
417 Vector<const char> debug_name = ArrayVector("<WASM UNNAMED>"))
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000418 : GraphAndBuilders(main_zone()),
419 jsgraph(this->isolate(), this->graph(), this->common(), nullptr,
420 nullptr, this->machine()),
Ben Murdochda12d292016-06-02 14:46:10 +0100421 sig(sig),
Ben Murdoch097c5b22016-05-18 11:27:45 +0100422 descriptor_(nullptr),
Ben Murdochc5610432016-08-08 18:44:38 +0100423 testing_module_(module),
424 debug_name_(debug_name),
425 local_decls(main_zone(), sig),
426 source_position_table_(this->graph()) {
Ben Murdoch097c5b22016-05-18 11:27:45 +0100427 if (module) {
428 // Get a new function from the testing module.
429 function_ = nullptr;
430 function_index_ = module->AddFunction(sig, Handle<Code>::null());
431 } else {
432 // Create our own function.
433 function_ = new WasmFunction();
434 function_->sig = sig;
435 function_index_ = 0;
436 }
437 }
438
439 ~WasmFunctionCompiler() {
440 if (function_) delete function_;
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000441 }
442
443 JSGraph jsgraph;
Ben Murdochda12d292016-06-02 14:46:10 +0100444 FunctionSig* sig;
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000445 // The call descriptor is initialized when the function is compiled.
446 CallDescriptor* descriptor_;
Ben Murdoch097c5b22016-05-18 11:27:45 +0100447 TestingModule* testing_module_;
Ben Murdochc5610432016-08-08 18:44:38 +0100448 Vector<const char> debug_name_;
Ben Murdoch097c5b22016-05-18 11:27:45 +0100449 WasmFunction* function_;
450 int function_index_;
Ben Murdochda12d292016-06-02 14:46:10 +0100451 LocalDeclEncoder local_decls;
Ben Murdochc5610432016-08-08 18:44:38 +0100452 SourcePositionTable source_position_table_;
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000453
454 Isolate* isolate() { return main_isolate(); }
455 Graph* graph() const { return main_graph_; }
456 Zone* zone() const { return graph()->zone(); }
457 CommonOperatorBuilder* common() { return &main_common_; }
458 MachineOperatorBuilder* machine() { return &main_machine_; }
Ben Murdoch097c5b22016-05-18 11:27:45 +0100459 void InitializeDescriptor() {
460 if (descriptor_ == nullptr) {
Ben Murdochda12d292016-06-02 14:46:10 +0100461 descriptor_ = testing_module_->GetWasmCallDescriptor(main_zone(), sig);
Ben Murdoch097c5b22016-05-18 11:27:45 +0100462 }
463 }
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000464 CallDescriptor* descriptor() { return descriptor_; }
465
466 void Build(const byte* start, const byte* end) {
Ben Murdoch097c5b22016-05-18 11:27:45 +0100467 // Build the TurboFan graph.
Ben Murdochda12d292016-06-02 14:46:10 +0100468 local_decls.Prepend(&start, &end);
Ben Murdochc5610432016-08-08 18:44:38 +0100469 TestBuildingGraph(main_zone(), &jsgraph, testing_module_, sig,
470 &source_position_table_, start, end);
Ben Murdochda12d292016-06-02 14:46:10 +0100471 delete[] start;
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000472 }
473
474 byte AllocateLocal(LocalType type) {
Ben Murdochc5610432016-08-08 18:44:38 +0100475 uint32_t index = local_decls.AddLocals(1, type);
Ben Murdochda12d292016-06-02 14:46:10 +0100476 byte result = static_cast<byte>(index);
477 DCHECK_EQ(index, result);
478 return result;
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000479 }
480
Ben Murdoch097c5b22016-05-18 11:27:45 +0100481 Handle<Code> Compile() {
482 InitializeDescriptor();
483 CallDescriptor* desc = descriptor_;
484 if (kPointerSize == 4) {
485 desc = testing_module_->GetI32WasmCallDescriptor(this->zone(), desc);
486 }
Ben Murdochc5610432016-08-08 18:44:38 +0100487 CompilationInfo info(debug_name_, this->isolate(), this->zone(),
488 Code::ComputeFlags(Code::WASM_FUNCTION));
489 v8::base::SmartPointer<CompilationJob> job(Pipeline::NewWasmCompilationJob(
490 &info, graph(), desc, &source_position_table_));
491 if (job->OptimizeGraph() != CompilationJob::SUCCEEDED ||
492 job->GenerateCode() != CompilationJob::SUCCEEDED)
493 return Handle<Code>::null();
494
495 Handle<Code> code = info.code();
496
497 // Length is always 2, since usually <wasm_obj, func_index> is stored in the
498 // deopt data. Here, we only store the function index.
499 DCHECK(code->deoptimization_data() == nullptr ||
500 code->deoptimization_data()->length() == 0);
501 Handle<FixedArray> deopt_data =
502 isolate()->factory()->NewFixedArray(2, TENURED);
503 deopt_data->set(1, Smi::FromInt(function_index_));
504 deopt_data->set_length(2);
505 code->set_deoptimization_data(*deopt_data);
506
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000507#ifdef ENABLE_DISASSEMBLER
Ben Murdochc5610432016-08-08 18:44:38 +0100508 if (FLAG_print_opt_code) {
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000509 OFStream os(stdout);
Ben Murdochc5610432016-08-08 18:44:38 +0100510 code->Disassemble("wasm code", os);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000511 }
512#endif
513
Ben Murdochc5610432016-08-08 18:44:38 +0100514 return code;
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000515 }
516
Ben Murdoch097c5b22016-05-18 11:27:45 +0100517 uint32_t CompileAndAdd(uint16_t sig_index = 0) {
518 CHECK(testing_module_);
519 function()->sig_index = sig_index;
520 Handle<Code> code = Compile();
521 testing_module_->SetFunctionCode(function_index_, code);
522 return static_cast<uint32_t>(function_index_);
523 }
524
525 WasmFunction* function() {
526 if (function_) return function_;
Ben Murdochda12d292016-06-02 14:46:10 +0100527 return &testing_module_->module->functions[function_index_];
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000528 }
Ben Murdochc5610432016-08-08 18:44:38 +0100529
530 // Set the context, such that e.g. runtime functions can be called.
531 void SetModuleContext() {
532 if (!testing_module_->instance->context.is_null()) {
533 CHECK(testing_module_->instance->context.is_identical_to(
534 main_isolate()->native_context()));
535 return;
536 }
537 testing_module_->instance->context = main_isolate()->native_context();
538 }
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000539};
540
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000541// A helper class to build graphs from Wasm bytecode, generate machine
542// code, and run that code.
543template <typename ReturnType>
544class WasmRunner {
545 public:
546 WasmRunner(MachineType p0 = MachineType::None(),
547 MachineType p1 = MachineType::None(),
548 MachineType p2 = MachineType::None(),
549 MachineType p3 = MachineType::None())
Ben Murdochda12d292016-06-02 14:46:10 +0100550 : zone(&allocator_),
551 compiled_(false),
Ben Murdoch097c5b22016-05-18 11:27:45 +0100552 signature_(MachineTypeForC<ReturnType>() == MachineType::None() ? 0 : 1,
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000553 GetParameterCount(p0, p1, p2, p3), storage_),
Ben Murdoch097c5b22016-05-18 11:27:45 +0100554 compiler_(&signature_, nullptr) {
555 InitSigStorage(p0, p1, p2, p3);
556 }
557
558 WasmRunner(TestingModule* module, MachineType p0 = MachineType::None(),
559 MachineType p1 = MachineType::None(),
560 MachineType p2 = MachineType::None(),
561 MachineType p3 = MachineType::None())
Ben Murdochda12d292016-06-02 14:46:10 +0100562 : zone(&allocator_),
563 compiled_(false),
Ben Murdoch097c5b22016-05-18 11:27:45 +0100564 signature_(MachineTypeForC<ReturnType>() == MachineType::None() ? 0 : 1,
565 GetParameterCount(p0, p1, p2, p3), storage_),
566 compiler_(&signature_, module) {
567 DCHECK(module);
568 InitSigStorage(p0, p1, p2, p3);
569 }
570
571 void InitSigStorage(MachineType p0, MachineType p1, MachineType p2,
572 MachineType p3) {
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000573 int index = 0;
574 MachineType ret = MachineTypeForC<ReturnType>();
575 if (ret != MachineType::None()) {
576 storage_[index++] = WasmOpcodes::LocalTypeFor(ret);
577 }
578 if (p0 != MachineType::None())
579 storage_[index++] = WasmOpcodes::LocalTypeFor(p0);
580 if (p1 != MachineType::None())
581 storage_[index++] = WasmOpcodes::LocalTypeFor(p1);
582 if (p2 != MachineType::None())
583 storage_[index++] = WasmOpcodes::LocalTypeFor(p2);
584 if (p3 != MachineType::None())
585 storage_[index++] = WasmOpcodes::LocalTypeFor(p3);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000586
Ben Murdoch097c5b22016-05-18 11:27:45 +0100587 compiler_.InitializeDescriptor();
588 wrapper_.Init(compiler_.descriptor(), p0, p1, p2, p3);
589 }
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000590
Ben Murdoch097c5b22016-05-18 11:27:45 +0100591 // Builds a graph from the given Wasm code and generates the machine
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000592 // code and call wrapper for that graph. This method must not be called
593 // more than once.
594 void Build(const byte* start, const byte* end) {
Ben Murdoch097c5b22016-05-18 11:27:45 +0100595 CHECK(!compiled_);
596 compiled_ = true;
597
598 // Build the TF graph within the compiler.
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000599 compiler_.Build(start, end);
600 // Generate code.
Ben Murdoch097c5b22016-05-18 11:27:45 +0100601 Handle<Code> code = compiler_.Compile();
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000602
Ben Murdoch097c5b22016-05-18 11:27:45 +0100603 if (compiler_.testing_module_) {
604 // Update the table of function code in the module.
605 compiler_.testing_module_->SetFunctionCode(compiler_.function_index_,
606 code);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000607 }
608
Ben Murdoch097c5b22016-05-18 11:27:45 +0100609 wrapper_.SetInnerCode(code);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000610 }
611
Ben Murdoch097c5b22016-05-18 11:27:45 +0100612 ReturnType Call() { return Call(0, 0, 0, 0); }
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000613
614 template <typename P0>
615 ReturnType Call(P0 p0) {
Ben Murdoch097c5b22016-05-18 11:27:45 +0100616 return Call(p0, 0, 0, 0);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000617 }
618
619 template <typename P0, typename P1>
620 ReturnType Call(P0 p0, P1 p1) {
Ben Murdoch097c5b22016-05-18 11:27:45 +0100621 return Call(p0, p1, 0, 0);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000622 }
623
624 template <typename P0, typename P1, typename P2>
625 ReturnType Call(P0 p0, P1 p1, P2 p2) {
Ben Murdoch097c5b22016-05-18 11:27:45 +0100626 return Call(p0, p1, p2, 0);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000627 }
628
629 template <typename P0, typename P1, typename P2, typename P3>
630 ReturnType Call(P0 p0, P1 p1, P2 p2, P3 p3) {
Ben Murdoch097c5b22016-05-18 11:27:45 +0100631 CodeRunner<int32_t> runner(CcTest::InitIsolateOnce(),
632 wrapper_.GetWrapperCode(), wrapper_.signature());
633 ReturnType return_value;
634 int32_t result = runner.Call<void*, void*, void*, void*, void*>(
635 &p0, &p1, &p2, &p3, &return_value);
636 CHECK_EQ(WASM_WRAPPER_RETURN_VALUE, result);
637 return return_value;
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000638 }
639
Ben Murdochda12d292016-06-02 14:46:10 +0100640 byte AllocateLocal(LocalType type) { return compiler_.AllocateLocal(type); }
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000641
Ben Murdoch097c5b22016-05-18 11:27:45 +0100642 protected:
Ben Murdochda12d292016-06-02 14:46:10 +0100643 v8::base::AccountingAllocator allocator_;
Ben Murdoch097c5b22016-05-18 11:27:45 +0100644 Zone zone;
645 bool compiled_;
646 LocalType storage_[WASM_RUNNER_MAX_NUM_PARAMETERS];
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000647 FunctionSig signature_;
648 WasmFunctionCompiler compiler_;
Ben Murdoch097c5b22016-05-18 11:27:45 +0100649 WasmFunctionWrapper<ReturnType> wrapper_;
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000650
651 static size_t GetParameterCount(MachineType p0, MachineType p1,
652 MachineType p2, MachineType p3) {
653 if (p0 == MachineType::None()) return 0;
654 if (p1 == MachineType::None()) return 1;
655 if (p2 == MachineType::None()) return 2;
656 if (p3 == MachineType::None()) return 3;
657 return 4;
658 }
659};
660
Ben Murdochc5610432016-08-08 18:44:38 +0100661// A macro to define tests that run in different engine configurations.
662// Currently only supports compiled tests, but a future
663// RunWasmInterpreted_##name version will allow each test to also run in the
664// interpreter.
665#define WASM_EXEC_TEST(name) \
666 void RunWasm_##name(); \
667 TEST(RunWasmCompiled_##name) { RunWasm_##name(); } \
668 void RunWasm_##name()
669
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000670} // namespace
671
672#endif