wasm-run-utils.h 31.7 KB
Newer Older
1 2 3 4 5 6 7
// 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

8
#include <setjmp.h>
9 10 11
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
12
#include <array>
13 14
#include <memory>

15
#include "src/base/utils/random-number-generator.h"
16
#include "src/zone/accounting-allocator.h"
17

18
#include "src/compiler/compiler-source-position-table.h"
19
#include "src/compiler/graph-visualizer.h"
20
#include "src/compiler/int64-lowering.h"
21
#include "src/compiler/js-graph.h"
22 23
#include "src/compiler/node.h"
#include "src/compiler/pipeline.h"
24
#include "src/compiler/wasm-compiler.h"
25
#include "src/compiler/zone-stats.h"
eholk's avatar
eholk committed
26
#include "src/trap-handler/trap-handler.h"
27
#include "src/wasm/function-body-decoder.h"
28
#include "src/wasm/local-decl-encoder.h"
29
#include "src/wasm/wasm-external-refs.h"
30
#include "src/wasm/wasm-interpreter.h"
31 32
#include "src/wasm/wasm-js.h"
#include "src/wasm/wasm-module.h"
33
#include "src/wasm/wasm-objects.h"
34 35
#include "src/wasm/wasm-opcodes.h"

36
#include "src/zone/zone.h"
37

38
#include "test/cctest/cctest.h"
39
#include "test/cctest/compiler/call-tester.h"
40 41
#include "test/cctest/compiler/graph-builder-tester.h"

42 43
static const uint32_t kMaxFunctions = 10;

44 45
enum WasmExecutionMode { kExecuteInterpreted, kExecuteCompiled };

46 47 48 49 50 51 52 53 54 55
// 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)

56 57
#define WASM_WRAPPER_RETURN_VALUE 8754

58 59 60 61 62 63
#define BUILD(r, ...)                      \
  do {                                     \
    byte code[] = {__VA_ARGS__};           \
    r.Build(code, code + arraysize(code)); \
  } while (false)

64 65 66 67 68 69 70 71 72
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
73
// and global variables. Contains a built-in {WasmModule} and
74
// {WasmInstance}.
75 76
class TestingModule : public ModuleEnv {
 public:
77
  explicit TestingModule(Zone* zone, WasmExecutionMode mode = kExecuteCompiled)
78
      : ModuleEnv(&module_, &instance_),
79
        instance_(&module_),
mtrofin's avatar
mtrofin committed
80
        isolate_(CcTest::InitIsolateOnce()),
81
        global_offset(0),
82
        interpreter_(nullptr) {
83
    WasmJs::Install(isolate_);
84
    instance->module = &module_;
85
    instance->globals_start = global_data;
86
    module_.globals_size = kMaxGlobalsSize;
87 88
    instance->mem_start = nullptr;
    instance->mem_size = 0;
89
    memset(global_data, 0, sizeof(global_data));
90
    instance_object_ = InitInstanceObject();
91 92 93
    if (mode == kExecuteInterpreted) {
      interpreter_ =
          WasmDebugInfo::SetupForTesting(instance_object_, &instance_);
94 95 96
    }
  }

97
  void ChangeOriginToAsmjs() { module_.set_origin(kAsmJsOrigin); }
98

99
  byte* AddMemory(uint32_t size) {
100
    CHECK(!module_.has_memory);
101
    CHECK_NULL(instance->mem_start);
102
    CHECK_EQ(0, instance->mem_size);
103
    DCHECK(!instance_object_->has_memory_buffer());
104
    module_.has_memory = true;
105 106 107 108 109 110 111 112
    bool enable_guard_regions = EnableGuardRegions() && module_.is_wasm();
    uint32_t alloc_size =
        enable_guard_regions ? RoundUp(size, OS::CommitPageSize()) : size;
    Handle<JSArrayBuffer> new_buffer =
        wasm::NewArrayBuffer(isolate_, alloc_size, enable_guard_regions);
    CHECK(!new_buffer.is_null());
    instance_object_->set_memory_buffer(*new_buffer);
    instance->mem_start = reinterpret_cast<byte*>(new_buffer->backing_store());
113
    CHECK(size == 0 || instance->mem_start);
114 115
    memset(instance->mem_start, 0, size);
    instance->mem_size = size;
116
    return instance->mem_start;
117 118 119
  }

  template <typename T>
120
  T* AddMemoryElems(uint32_t count) {
121 122 123 124 125
    AddMemory(count * sizeof(T));
    return raw_mem_start<T>();
  }

  template <typename T>
126
  T* AddGlobal(
127
      ValueType type = WasmOpcodes::ValueTypeFor(MachineTypeForC<T>())) {
128
    const WasmGlobal* global = AddGlobal(type);
129
    return reinterpret_cast<T*>(instance->globals_start + global->offset);
130 131 132
  }

  byte AddSignature(FunctionSig* sig) {
133
    module_.signatures.push_back(sig);
134
    size_t size = module->signatures.size();
135 136 137 138 139 140
    CHECK(size < 127);
    return static_cast<byte>(size - 1);
  }

  template <typename T>
  T* raw_mem_start() {
141 142
    DCHECK(instance->mem_start);
    return reinterpret_cast<T*>(instance->mem_start);
143 144 145 146
  }

  template <typename T>
  T* raw_mem_end() {
147 148
    DCHECK(instance->mem_start);
    return reinterpret_cast<T*>(instance->mem_start + instance->mem_size);
149 150 151 152
  }

  template <typename T>
  T raw_mem_at(int i) {
153
    DCHECK(instance->mem_start);
154
    return ReadMemory(&(reinterpret_cast<T*>(instance->mem_start)[i]));
155 156 157 158
  }

  template <typename T>
  T raw_val_at(int i) {
159 160 161 162 163 164 165 166 167 168 169
    return ReadMemory(reinterpret_cast<T*>(instance->mem_start + i));
  }

  template <typename T>
  void WriteMemory(T* p, T val) {
    WriteLittleEndianValue<T>(p, val);
  }

  template <typename T>
  T ReadMemory(T* p) {
    return ReadLittleEndianValue<T>(p);
170 171 172 173 174
  }

  // Zero-initialize the memory.
  void BlankMemory() {
    byte* raw = raw_mem_start<byte>();
175
    memset(raw, 0, instance->mem_size);
176 177 178 179 180 181 182 183 184 185 186
  }

  // 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);
  }

187 188 189 190
  void SetMaxMemPages(uint32_t max_mem_pages) {
    module_.max_mem_pages = max_mem_pages;
  }

191
  uint32_t AddFunction(FunctionSig* sig, Handle<Code> code, const char* name) {
192
    if (module->functions.size() == 0) {
193 194
      // TODO(titzer): Reserving space here to avoid the underlying WasmFunction
      // structs from moving.
195
      module_.functions.reserve(kMaxFunctions);
196
    }
197
    uint32_t index = static_cast<uint32_t>(module->functions.size());
198
    module_.functions.push_back({sig, index, 0, 0, 0, 0, 0, false, false});
199 200 201 202 203
    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();
    }
204
    instance->function_code.push_back(code);
205
    if (interpreter_) {
206
      interpreter_->AddFunctionForTesting(&module->functions.back());
207
    }
208 209 210 211
    DCHECK_LT(index, kMaxFunctions);  // limited for testing.
    return index;
  }

212 213 214
  uint32_t AddJsFunction(FunctionSig* sig, const char* source) {
    Handle<JSFunction> jsfunc = Handle<JSFunction>::cast(v8::Utils::OpenHandle(
        *v8::Local<v8::Function>::Cast(CompileRun(source))));
215
    uint32_t index = AddFunction(sig, Handle<Code>::null(), nullptr);
216 217
    Handle<Code> code = CompileWasmToJSWrapper(
        isolate_, jsfunc, sig, index, Handle<String>::null(),
218
        Handle<String>::null(), module->get_origin());
219 220 221 222 223 224 225
    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<Code> code = instance->function_code[index];
226
    Handle<Code> ret_code =
227
        compiler::CompileJSToWasmWrapper(isolate_, &module_, code, index);
228
    Handle<JSFunction> ret = WasmExportedFunction::New(
229 230
        isolate_, instance_object(), MaybeHandle<String>(),
        static_cast<int>(index),
231
        static_cast<int>(this->module->functions[index].sig->parameter_count()),
232
        ret_code);
233 234 235 236 237 238 239 240 241 242 243 244

    // Add weak reference to exported functions.
    Handle<WasmCompiledModule> compiled_module(
        instance_object()->compiled_module(), isolate_);
    Handle<FixedArray> old_arr = compiled_module->weak_exported_functions();
    Handle<FixedArray> new_arr =
        isolate_->factory()->NewFixedArray(old_arr->length() + 1);
    old_arr->CopyTo(0, *new_arr, 0, old_arr->length());
    Handle<WeakCell> weak_fn = isolate_->factory()->NewWeakCell(ret);
    new_arr->set(old_arr->length(), *weak_fn);
    compiled_module->set_weak_exported_functions(new_arr);

245
    return ret;
246 247
  }

248
  void SetFunctionCode(uint32_t index, Handle<Code> code) {
249
    instance->function_code[index] = code;
250 251
  }

252 253
  void AddIndirectFunctionTable(uint16_t* function_indexes,
                                uint32_t table_size) {
254
    module_.function_tables.push_back({table_size, table_size, true,
255 256 257
                                       std::vector<int32_t>(), false, false,
                                       SignatureMap()});
    WasmIndirectFunctionTable& table = module_.function_tables.back();
258 259
    table.min_size = table_size;
    table.max_size = table_size;
260
    for (uint32_t i = 0; i < table_size; ++i) {
261 262
      table.values.push_back(function_indexes[i]);
      table.map.FindOrInsert(module_.functions[function_indexes[i]].sig);
263
    }
264

265
    instance->function_tables.push_back(
266 267 268
        isolate_->factory()->NewFixedArray(table_size));
    instance->signature_tables.push_back(
        isolate_->factory()->NewFixedArray(table_size));
269 270 271
  }

  void PopulateIndirectFunctionTable() {
272
    if (interpret()) return;
273
    // Initialize the fixed arrays in instance->function_tables.
274
    for (uint32_t i = 0; i < instance->function_tables.size(); i++) {
275
      WasmIndirectFunctionTable& table = module_.function_tables[i];
276 277
      Handle<FixedArray> function_table = instance->function_tables[i];
      Handle<FixedArray> signature_table = instance->signature_tables[i];
278 279 280
      int table_size = static_cast<int>(table.values.size());
      for (int j = 0; j < table_size; j++) {
        WasmFunction& function = module_.functions[table.values[j]];
281 282
        signature_table->set(j, Smi::FromInt(table.map.Find(function.sig)));
        function_table->set(j, *instance->function_code[function.func_index]);
283
      }
284 285
    }
  }
286

287
  uint32_t AddBytes(Vector<const byte> bytes) {
288 289
    Handle<SeqOneByteString> old_bytes(
        instance_object_->compiled_module()->module_bytes(), isolate_);
290
    uint32_t old_size = static_cast<uint32_t>(old_bytes->length());
291 292 293 294
    // Avoid placing strings at offset 0, this might be interpreted as "not
    // set", e.g. for function names.
    uint32_t bytes_offset = old_size ? old_size : 1;
    ScopedVector<byte> new_bytes(bytes_offset + bytes.length());
295
    memcpy(new_bytes.start(), old_bytes->GetChars(), old_size);
296
    memcpy(new_bytes.start() + bytes_offset, bytes.start(), bytes.length());
297 298
    Handle<SeqOneByteString> new_bytes_str = Handle<SeqOneByteString>::cast(
        isolate_->factory()->NewStringFromOneByte(new_bytes).ToHandleChecked());
299 300
    instance_object_->compiled_module()->shared()->set_module_bytes(
        *new_bytes_str);
301
    return bytes_offset;
302 303
  }

304
  WasmFunction* GetFunctionAt(int index) { return &module_.functions[index]; }
305

306
  WasmInterpreter* interpreter() { return interpreter_; }
307
  bool interpret() { return interpreter_ != nullptr; }
308
  Isolate* isolate() { return isolate_; }
309
  Handle<WasmInstanceObject> instance_object() { return instance_object_; }
310

311
 private:
312
  WasmModule module_;
313
  WasmInstance instance_;
mtrofin's avatar
mtrofin committed
314
  Isolate* isolate_;
315
  uint32_t global_offset;
316
  V8_ALIGNED(8) byte global_data[kMaxGlobalsSize];  // preallocated global data.
317
  WasmInterpreter* interpreter_;
318
  Handle<WasmInstanceObject> instance_object_;
319

320
  const WasmGlobal* AddGlobal(ValueType type) {
321
    byte size = WasmOpcodes::MemSize(WasmOpcodes::MachineTypeFor(type));
322
    global_offset = (global_offset + size - 1) & ~(size - 1);  // align
323
    module_.globals.push_back(
324
        {type, true, WasmInitExpr(), global_offset, false, false});
325 326 327
    global_offset += size;
    // limit number of globals.
    CHECK_LT(global_offset, kMaxGlobalsSize);
328
    return &module->globals.back();
329
  }
330 331

  Handle<WasmInstanceObject> InitInstanceObject() {
332 333
    Handle<SeqOneByteString> empty_string = Handle<SeqOneByteString>::cast(
        isolate_->factory()->NewStringFromOneByte({}).ToHandleChecked());
334 335 336 337
    // The lifetime of the wasm module is tied to this object's, and we cannot
    // rely on the mechanics of Managed<T>.
    Handle<Foreign> module_wrapper =
        isolate_->factory()->NewForeign(reinterpret_cast<Address>(&module));
338 339 340 341 342 343
    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());
344
    Handle<FixedArray> code_table = isolate_->factory()->NewFixedArray(0);
345 346 347 348

    Handle<WasmCompiledModule> compiled_module = WasmCompiledModule::New(
        isolate_, shared_module_data, code_table, MaybeHandle<FixedArray>(),
        MaybeHandle<FixedArray>());
349 350
    Handle<FixedArray> weak_exported = isolate_->factory()->NewFixedArray(0);
    compiled_module->set_weak_exported_functions(weak_exported);
351 352 353
    DCHECK(WasmCompiledModule::IsWasmCompiledModule(*compiled_module));
    return WasmInstanceObject::New(isolate_, compiled_module);
  }
354 355
};

356
inline void TestBuildingGraph(Zone* zone, JSGraph* jsgraph, ModuleEnv* module,
357 358 359
                              FunctionSig* sig,
                              SourcePositionTable* source_position_table,
                              const byte* start, const byte* end) {
360 361
  compiler::WasmGraphBuilder builder(module, zone, jsgraph, sig,
                                     source_position_table);
362
  DecodeResult result =
363
      BuildTFGraph(zone->allocator(), &builder, sig, start, end);
364
  if (result.failed()) {
365 366 367
    if (!FLAG_trace_wasm_decoder) {
      // Retry the compilation with the tracing flag on, to help in debugging.
      FLAG_trace_wasm_decoder = true;
368
      result = BuildTFGraph(zone->allocator(), &builder, sig, start, end);
369 370
    }

371
    uint32_t pc = result.error_offset();
372
    std::ostringstream str;
373
    str << "Verification failed; pc = +" << pc
374
        << ", msg = " << result.error_msg().c_str();
375 376
    FATAL(str.str().c_str());
  }
377
  builder.Int64LoweringForTesting();
378
  if (!CpuFeatures::SupportsWasmSimd128()) {
379 380
    builder.SimdScalarLoweringForTesting();
  }
381 382
}

383
class WasmFunctionWrapper : private GraphAndBuilders {
384
 public:
385 386
  explicit WasmFunctionWrapper(Zone* zone, int num_params)
      : GraphAndBuilders(zone), inner_code_node_(nullptr), signature_(nullptr) {
387
    // One additional parameter for the pointer to the return value memory.
388
    Signature<MachineType>::Builder sig_builder(zone, 1, num_params + 1);
389 390

    sig_builder.AddReturn(MachineType::Int32());
391
    for (int i = 0; i < num_params + 1; i++) {
392 393 394 395 396
      sig_builder.AddParam(MachineType::Pointer());
    }
    signature_ = sig_builder.Build();
  }

397 398 399 400 401 402
  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.
403 404

    // Function, effect, and control.
405
    Node** parameters = zone()->NewArray<Node*>(param_types.length() + 3);
406 407 408 409 410 411 412 413
    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_;

414 415 416
    int param_idx = 0;
    for (MachineType t : param_types) {
      DCHECK_NE(MachineType::None(), t);
417
      parameters[parameter_count] = graph()->NewNode(
418 419
          machine()->Load(t),
          graph()->NewNode(common()->Parameter(param_idx++), graph()->start()),
420 421 422 423 424 425 426 427 428 429
          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);

430 431 432 433 434 435 436 437 438
    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());
    }
439
    Node* zero = graph()->NewNode(common()->Int32Constant(0));
440
    Node* r = graph()->NewNode(
441
        common()->Return(), zero,
442 443
        graph()->NewNode(common()->Int32Constant(WASM_WRAPPER_RETURN_VALUE)),
        effect, graph()->start());
444
    graph()->SetEnd(graph()->NewNode(common()->End(1), r));
445 446
  }

447 448 449 450 451 452 453 454 455
  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);
  }

456 457 458 459 460 461 462 463 464 465 466 467
  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);

468
      if (kPointerSize == 4) {
469
        size_t num_params = signature_->parameter_count();
470
        // One additional parameter for the pointer of the return value.
471 472
        Signature<MachineRepresentation>::Builder rep_builder(zone(), 1,
                                                              num_params + 1);
473 474

        rep_builder.AddReturn(MachineRepresentation::kWord32);
475
        for (size_t i = 0; i < num_params + 1; i++) {
476 477 478 479 480 481 482
          rep_builder.AddParam(MachineRepresentation::kWord32);
        }
        Int64Lowering r(graph(), machine(), common(), zone(),
                        rep_builder.Build());
        r.LowerGraph();
      }

483 484
      CompilationInfo info(ArrayVector("testing"), isolate, graph()->zone(),
                           Code::ComputeFlags(Code::STUB));
485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505
      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_;
};
506

507 508 509 510
// 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 {
511
 public:
512
  Isolate* isolate() { return testing_module_->isolate(); }
513 514 515 516
  Graph* graph() const { return main_graph_; }
  Zone* zone() const { return graph()->zone(); }
  CommonOperatorBuilder* common() { return &main_common_; }
  MachineOperatorBuilder* machine() { return &main_machine_; }
517
  CallDescriptor* descriptor() {
518
    if (descriptor_ == nullptr) {
519
      descriptor_ = testing_module_->GetWasmCallDescriptor(zone(), sig);
520
    }
521
    return descriptor_;
522
  }
523
  uint32_t function_index() { return function_->func_index; }
524 525

  void Build(const byte* start, const byte* end) {
526 527 528 529 530 531 532 533 534 535 536 537
    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;
538 539 540 541 542 543 544

    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;

545 546
    if (interpreter_) {
      // Add the code to the interpreter.
547
      interpreter_->SetFunctionCodeForTesting(function_, start, end);
548
    }
549 550 551 552 553 554

    // 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);
555 556 557 558 559

    // Add to code table.
    Handle<WasmCompiledModule> compiled_module(
        testing_module_->instance_object()->compiled_module(), isolate());
    Handle<FixedArray> code_table = compiled_module->code_table();
560 561 562 563 564
    if (static_cast<int>(function_index()) >= code_table->length()) {
      Handle<FixedArray> new_arr = isolate()->factory()->NewFixedArray(
          static_cast<int>(function_index()) + 1);
      code_table->CopyTo(0, *new_arr, 0, code_table->length());
      code_table = new_arr;
565
      compiled_module->ReplaceCodeTableForTesting(code_table);
566 567 568 569
    }
    DCHECK(code_table->get(static_cast<int>(function_index()))
               ->IsUndefined(isolate()));
    code_table->set(static_cast<int>(function_index()), *code);
570 571 572
    if (trap_handler::UseTrapHandler()) {
      UnpackAndRegisterProtectedInstructions(isolate(), code_table);
    }
573 574
  }

575
  byte AllocateLocal(ValueType type) {
576
    uint32_t index = local_decls.AddLocals(1, type);
577 578 579
    byte result = static_cast<byte>(index);
    DCHECK_EQ(index, result);
    return result;
580 581
  }

582 583 584 585 586 587
  void SetSigIndex(int sig_index) { function_->sig_index = sig_index; }

 private:
  friend class WasmRunnerBase;

  explicit WasmFunctionCompiler(Zone* zone, FunctionSig* sig,
588
                                TestingModule* module, const char* name)
589 590 591 592 593 594 595 596 597 598
      : 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.
599
    int index = module->AddFunction(sig, Handle<Code>::null(), name);
600 601 602
    function_ = testing_module_->GetFunctionAt(index);
  }

603
  Handle<Code> Compile() {
604
    CallDescriptor* desc = descriptor();
605
    if (kPointerSize == 4) {
606
      desc = testing_module_->GetI32WasmCallDescriptor(this->zone(), desc);
607
    }
608 609 610 611
    EmbeddedVector<char, 16> comp_name;
    int comp_name_len = SNPrintF(comp_name, "wasm#%u", this->function_index());
    comp_name.Truncate(comp_name_len);
    CompilationInfo info(comp_name, this->isolate(), this->zone(),
612
                         Code::ComputeFlags(Code::WASM_FUNCTION));
613
    std::unique_ptr<CompilationJob> job(Pipeline::NewWasmCompilationJob(
614
        &info, &jsgraph, desc, &source_position_table_, nullptr, false));
615 616
    if (job->ExecuteJob() != CompilationJob::SUCCEEDED ||
        job->FinalizeJob() != CompilationJob::SUCCEEDED)
617 618 619 620
      return Handle<Code>::null();

    Handle<Code> code = info.code();

621
    // Deopt data holds <WeakCell<wasm_instance>, func_index>.
622 623 624 625
    DCHECK(code->deoptimization_data() == nullptr ||
           code->deoptimization_data()->length() == 0);
    Handle<FixedArray> deopt_data =
        isolate()->factory()->NewFixedArray(2, TENURED);
626 627 628
    Handle<Object> weak_instance =
        isolate()->factory()->NewWeakCell(testing_module_->instance_object());
    deopt_data->set(0, *weak_instance);
629
    deopt_data->set(1, Smi::FromInt(static_cast<int>(function_index())));
630 631
    code->set_deoptimization_data(*deopt_data);

632
#ifdef ENABLE_DISASSEMBLER
633
    if (FLAG_print_opt_code) {
634
      OFStream os(stdout);
635
      code->Disassemble("wasm code", os);
636 637 638
    }
#endif

639
    return code;
640 641
  }

642 643 644 645 646 647 648 649 650 651
  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_;
652 653
};

654
// A helper class to build a module around Wasm bytecode, generate machine
655
// code, and run that code.
656
class WasmRunnerBase : public HandleAndZoneScope {
657
 public:
658 659 660 661
  explicit WasmRunnerBase(WasmExecutionMode execution_mode, int num_params)
      : zone_(&allocator_, ZONE_NAME),
        module_(&zone_, execution_mode),
        wrapper_(&zone_, num_params) {}
662

663
  // Builds a graph from the given Wasm code and generates the machine
664 665 666
  // code and call wrapper for that graph. This method must not be called
  // more than once.
  void Build(const byte* start, const byte* end) {
667 668
    CHECK(!compiled_);
    compiled_ = true;
669 670
    functions_[0]->Build(start, end);
  }
671

672 673 674
  // Resets the state for building the next function.
  // The main function called will always be the first function.
  template <typename ReturnType, typename... ParamTypes>
675 676
  WasmFunctionCompiler& NewFunction(const char* name = nullptr) {
    return NewFunction(CreateSig<ReturnType, ParamTypes...>(), name);
677
  }
678

679 680 681
  // 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.
682 683 684 685
  WasmFunctionCompiler& NewFunction(FunctionSig* sig,
                                    const char* name = nullptr) {
    functions_.emplace_back(
        new WasmFunctionCompiler(&zone_, sig, &module_, name));
686
    return *functions_.back();
687
  }
688

689
  byte AllocateLocal(ValueType type) {
690
    return functions_[0]->AllocateLocal(type);
691 692
  }

693
  uint32_t function_index() { return functions_[0]->function_index(); }
694
  WasmFunction* function() { return functions_[0]->function_; }
695 696 697 698
  WasmInterpreter* interpreter() {
    DCHECK(interpret());
    return functions_[0]->interpreter_;
  }
699 700 701 702 703 704 705 706 707 708
  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;
709
    }
710
    module_.instance->context = main_isolate()->native_context();
711 712
  }

713 714
  bool interpret() { return module_.interpret(); }

715 716 717 718 719 720 721
 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.
722 723
    ValueType* sig_types =
        zone_.NewArray<ValueType>(return_count + param_count);
724 725 726 727

    // Convert machine types to local types, and check that there are no
    // MachineType::None()'s in the parameters.
    int idx = 0;
728
    if (return_count) sig_types[idx++] = WasmOpcodes::ValueTypeFor(return_type);
729 730
    for (MachineType param : param_types) {
      CHECK_NE(MachineType::None(), param);
731
      sig_types[idx++] = WasmOpcodes::ValueTypeFor(param);
732
    }
733
    return new (&zone_) FunctionSig(return_count, param_count, sig_types);
734 735
  }

736 737 738 739 740 741 742
  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);
743
  }
744

745 746 747 748 749 750 751 752 753 754
 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;

 public:
755
  // This field has to be static. Otherwise, gcc complains about the use in
756
  // the lambda context below.
757
  static bool trap_happened;
758
};
759

760 761 762
template <typename ReturnType, typename... ParamTypes>
class WasmRunner : public WasmRunnerBase {
 public:
763 764
  explicit WasmRunner(WasmExecutionMode execution_mode,
                      const char* main_fn_name = "main")
765
      : WasmRunnerBase(execution_mode, sizeof...(ParamTypes)) {
766
    NewFunction<ReturnType, ParamTypes...>(main_fn_name);
767 768 769
    if (!interpret()) {
      wrapper_.Init<ReturnType, ParamTypes...>(functions_[0]->descriptor());
    }
770 771
  }

772 773 774 775
  ReturnType Call(ParamTypes... p) {
    DCHECK(compiled_);
    if (interpret()) return CallInterpreter(p...);

776
    ReturnType return_value = static_cast<ReturnType>(0xdeadbeefdeadbeef);
777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793
    WasmRunnerBase::trap_happened = false;
    auto trap_callback = []() -> void {
      WasmRunnerBase::trap_happened = true;
      set_trap_callback_for_testing(nullptr);
    };
    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(static_cast<void*>(&p)...,
                                 static_cast<void*>(&return_value));
    CHECK_EQ(WASM_WRAPPER_RETURN_VALUE, result);
    return WasmRunnerBase::trap_happened
               ? static_cast<ReturnType>(0xdeadbeefdeadbeef)
               : return_value;
794 795
  }

796
  ReturnType CallInterpreter(ParamTypes... p) {
797 798
    WasmInterpreter::Thread* thread = interpreter()->GetThread(0);
    thread->Reset();
799
    std::array<WasmVal, sizeof...(p)> args{{WasmVal(p)...}};
800
    thread->InitFrame(function(), args.data());
801 802
    if (thread->Run() == WasmInterpreter::FINISHED) {
      WasmVal val = thread->GetReturnValue();
803
      possible_nondeterminism_ |= thread->PossibleNondeterminism();
804
      return val.to<ReturnType>();
805
    } else if (thread->state() == WasmInterpreter::TRAPPED) {
806 807 808 809 810
      // TODO(titzer): return the correct trap code
      int64_t result = 0xdeadbeefdeadbeef;
      return static_cast<ReturnType>(result);
    } else {
      // TODO(titzer): falling off end
811
      return ReturnType{0};
812
    }
813 814 815
  }
};

816
// Declare static variable.
817
bool WasmRunnerBase::trap_happened;
818

819
// A macro to define tests that run in different engine configurations.
820 821 822 823 824
#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)
825

eholk's avatar
eholk committed
826 827 828 829 830 831 832 833 834 835 836 837 838 839
#define WASM_EXEC_TEST_WITH_TRAP(name)                   \
  void RunWasm_##name(WasmExecutionMode execution_mode); \
  TEST(RunWasmCompiled_##name) {                         \
    if (trap_handler::UseTrapHandler()) {                \
      return;                                            \
    }                                                    \
    RunWasm_##name(kExecuteCompiled);                    \
  }                                                      \
  TEST(RunWasmInterpreted_##name) {                      \
    if (trap_handler::UseTrapHandler()) {                \
      return;                                            \
    }                                                    \
    RunWasm_##name(kExecuteInterpreted);                 \
  }                                                      \
840 841
  void RunWasm_##name(WasmExecutionMode execution_mode)

842 843 844 845 846
#define WASM_EXEC_COMPILED_TEST(name)                                \
  void RunWasm_##name(WasmExecutionMode execution_mode);             \
  TEST(RunWasmCompiled_##name) { RunWasm_##name(kExecuteCompiled); } \
  void RunWasm_##name(WasmExecutionMode execution_mode)

847 848 849
}  // namespace

#endif