wasm-run-utils.cc 24.9 KB
Newer Older
1 2 3 4 5 6
// Copyright 2017 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.

#include "test/cctest/wasm/wasm-run-utils.h"

7 8
#include "src/codegen/assembler-inl.h"
#include "src/diagnostics/code-tracer.h"
9
#include "src/heap/heap-inl.h"
10
#include "src/wasm/graph-builder-interface.h"
11
#include "src/wasm/leb-helper.h"
12
#include "src/wasm/module-compiler.h"
13
#include "src/wasm/wasm-engine.h"
14
#include "src/wasm/wasm-import-wrapper-cache.h"
15
#include "src/wasm/wasm-objects-inl.h"
16
#include "src/wasm/wasm-opcodes.h"
17

18 19 20 21
namespace v8 {
namespace internal {
namespace wasm {

22
TestingModuleBuilder::TestingModuleBuilder(
23
    Zone* zone, ManuallyImportedJSFunction* maybe_import,
24 25
    TestExecutionTier tier, RuntimeExceptionSupport exception_support,
    Isolate* isolate)
26
    : test_module_(std::make_shared<WasmModule>()),
27
      isolate_(isolate ? isolate : CcTest::InitIsolateOnce()),
28
      enabled_features_(WasmFeatures::FromIsolate(isolate_)),
29
      execution_tier_(tier),
30
      runtime_exception_support_(exception_support) {
31
  WasmJs::Install(isolate_, true);
32
  test_module_->untagged_globals_buffer_size = kMaxGlobalsSize;
33
  memset(globals_data_, 0, sizeof(globals_data_));
34 35 36 37

  uint32_t maybe_import_index = 0;
  if (maybe_import) {
    // Manually add an imported function before any other functions.
38
    // This must happen before the instance object is created, since the
39
    // instance object allocates import entries.
40
    maybe_import_index = AddFunction(maybe_import->sig, nullptr, kImport);
41 42 43
    DCHECK_EQ(0, maybe_import_index);
  }

44
  instance_object_ = InitInstanceObject();
45 46
  Handle<FixedArray> tables(isolate_->factory()->NewFixedArray(0));
  instance_object_->set_tables(*tables);
47 48

  if (maybe_import) {
49
    // Manually compile an import wrapper and insert it into the instance.
50
    CodeSpaceMemoryModificationScope modification_scope(isolate_->heap());
51
    auto resolved = compiler::ResolveWasmImportCall(
52 53
        maybe_import->js_function, maybe_import->sig,
        instance_object_->module(), enabled_features_);
54 55
    compiler::WasmImportCallKind kind = resolved.first;
    Handle<JSReceiver> callable = resolved.second;
56 57
    WasmImportWrapperCache::ModificationScope cache_scope(
        native_module_->import_wrapper_cache());
58 59 60
    WasmImportWrapperCache::CacheKey key(
        kind, maybe_import->sig,
        static_cast<int>(maybe_import->sig->parameter_count()));
61 62 63 64
    auto import_wrapper = cache_scope[key];
    if (import_wrapper == nullptr) {
      import_wrapper = CompileImportWrapper(
          isolate_->wasm_engine(), native_module_, isolate_->counters(), kind,
65 66
          maybe_import->sig,
          static_cast<int>(maybe_import->sig->parameter_count()), &cache_scope);
67
    }
68

69
    ImportedFunctionEntry(instance_object_, maybe_import_index)
70
        .SetWasmToJs(isolate_, callable, import_wrapper);
71 72
  }

73
  if (tier == TestExecutionTier::kInterpreter) {
74
    interpreter_ = std::make_unique<WasmInterpreter>(
75
        isolate_, test_module_.get(),
76
        ModuleWireBytes{native_module_->wire_bytes()}, instance_object_);
77 78 79
  }
}

80 81 82 83 84 85
TestingModuleBuilder::~TestingModuleBuilder() {
  // When the native module dies and is erased from the cache, it is expected to
  // have either valid bytes or no bytes at all.
  native_module_->SetWireBytes({});
}

86
byte* TestingModuleBuilder::AddMemory(uint32_t size, SharedFlag shared) {
87
  CHECK(!test_module_->has_memory);
88 89
  CHECK_NULL(mem_start_);
  CHECK_EQ(0, mem_size_);
90
  DCHECK(!instance_object_->has_memory_object());
91 92 93 94
  uint32_t initial_pages = RoundUp(size, kWasmPageSize) / kWasmPageSize;
  uint32_t maximum_pages = (test_module_->maximum_pages != 0)
                               ? test_module_->maximum_pages
                               : initial_pages;
95
  test_module_->has_memory = true;
96

97
  // Create the WasmMemoryObject.
98
  Handle<WasmMemoryObject> memory_object =
99 100
      WasmMemoryObject::New(isolate_, initial_pages, maximum_pages, shared)
          .ToHandleChecked();
101
  instance_object_->set_memory_object(*memory_object);
102 103 104 105 106 107

  mem_start_ =
      reinterpret_cast<byte*>(memory_object->array_buffer().backing_store());
  mem_size_ = size;
  CHECK(size == 0 || mem_start_);

108
  WasmMemoryObject::AddInstance(isolate_, memory_object, instance_object_);
109 110 111 112
  // TODO(wasm): Delete the following two lines when test-run-wasm will use a
  // multiple of kPageSize as memory size. At the moment, the effect of these
  // two lines is used to shrink the memory for testing purposes.
  instance_object_->SetRawMemory(mem_start_, mem_size_);
113 114 115
  return mem_start_;
}

116 117
uint32_t TestingModuleBuilder::AddFunction(const FunctionSig* sig,
                                           const char* name,
118
                                           FunctionType type) {
119
  if (test_module_->functions.size() == 0) {
120 121
    // TODO(titzer): Reserving space here to avoid the underlying WasmFunction
    // structs from moving.
122
    test_module_->functions.reserve(kMaxFunctions);
123
  }
124
  uint32_t index = static_cast<uint32_t>(test_module_->functions.size());
125 126 127 128 129 130 131
  test_module_->functions.push_back({sig,      // sig
                                     index,    // func_index
                                     0,        // sig_index
                                     {0, 0},   // code
                                     false,    // imported
                                     false,    // exported
                                     false});  // declared
132 133 134 135 136 137 138 139 140 141
  if (type == kImport) {
    DCHECK_EQ(0, test_module_->num_declared_functions);
    ++test_module_->num_imported_functions;
    test_module_->functions.back().imported = true;
  } else {
    ++test_module_->num_declared_functions;
  }
  DCHECK_EQ(test_module_->functions.size(),
            test_module_->num_imported_functions +
                test_module_->num_declared_functions);
142 143
  if (name) {
    Vector<const byte> name_vec = Vector<const byte>::cast(CStrVector(name));
144
    test_module_->lazily_generated_names.AddForTesting(
145
        index, {AddBytes(name_vec), static_cast<uint32_t>(name_vec.length())});
146 147
  }
  if (interpreter_) {
148
    interpreter_->AddFunctionForTesting(&test_module_->functions.back());
149 150 151 152 153
  }
  DCHECK_LT(index, kMaxFunctions);  // limited for testing.
  return index;
}

154 155 156 157 158 159
void TestingModuleBuilder::FreezeSignatureMapAndInitializeWrapperCache() {
  if (test_module_->signature_map.is_frozen()) return;
  test_module_->signature_map.Freeze();
  size_t max_num_sigs = MaxNumExportWrappers(test_module_.get());
  Handle<FixedArray> export_wrappers =
      isolate_->factory()->NewFixedArray(static_cast<int>(max_num_sigs));
160
  instance_object_->module_object().set_export_wrappers(*export_wrappers);
161 162
}

163
Handle<JSFunction> TestingModuleBuilder::WrapCode(uint32_t index) {
164
  CHECK(!interpreter_);
165
  FreezeSignatureMapAndInitializeWrapperCache();
166
  return WasmInstanceObject::GetOrCreateWasmExternalFunction(
167
      isolate_, instance_object(), index);
168 169
}

170
void TestingModuleBuilder::AddIndirectFunctionTable(
171 172
    const uint16_t* function_indexes, uint32_t table_size,
    ValueType table_type) {
173
  Handle<WasmInstanceObject> instance = instance_object();
174
  uint32_t table_index = static_cast<uint32_t>(test_module_->tables.size());
175 176
  test_module_->tables.emplace_back();
  WasmTable& table = test_module_->tables.back();
177 178 179
  table.initial_size = table_size;
  table.maximum_size = table_size;
  table.has_maximum_size = true;
180
  table.type = table_type;
181 182 183 184 185 186 187 188 189 190 191 192 193 194 195

  {
    // Allocate the indirect function table.
    Handle<FixedArray> old_tables =
        table_index == 0
            ? isolate_->factory()->empty_fixed_array()
            : handle(instance_object_->indirect_function_tables(), isolate_);
    Handle<FixedArray> new_tables =
        isolate_->factory()->CopyFixedArrayAndGrow(old_tables, 1);
    Handle<WasmIndirectFunctionTable> table_obj =
        WasmIndirectFunctionTable::New(isolate_, table.initial_size);
    new_tables->set(table_index, *table_obj);
    instance_object_->set_indirect_function_tables(*new_tables);
  }

196
  WasmInstanceObject::EnsureIndirectFunctionTableWithMinimumSize(
197
      instance_object(), table_index, table_size);
198
  Handle<WasmTableObject> table_obj =
199
      WasmTableObject::New(isolate_, instance, table.type, table.initial_size,
200 201 202 203 204 205 206 207 208
                           table.has_maximum_size, table.maximum_size, nullptr);

  WasmTableObject::AddDispatchTable(isolate_, table_obj, instance_object_,
                                    table_index);

  if (function_indexes) {
    for (uint32_t i = 0; i < table_size; ++i) {
      WasmFunction& function = test_module_->functions[function_indexes[i]];
      int sig_id = test_module_->signature_map.Find(*function.sig);
209
      IndirectFunctionTableEntry(instance, table_index, i)
210 211 212 213
          .Set(sig_id, instance, function.func_index);
      WasmTableObject::SetFunctionTablePlaceholder(
          isolate_, table_obj, i, instance_object_, function_indexes[i]);
    }
214
  }
215 216

  Handle<FixedArray> old_tables(instance_object_->tables(), isolate_);
217 218
  Handle<FixedArray> new_tables =
      isolate_->factory()->CopyFixedArrayAndGrow(old_tables, 1);
219 220
  new_tables->set(old_tables->length(), *table_obj);
  instance_object_->set_tables(*new_tables);
221 222 223
}

uint32_t TestingModuleBuilder::AddBytes(Vector<const byte> bytes) {
224 225
  Vector<const uint8_t> old_bytes = native_module_->wire_bytes();
  uint32_t old_size = static_cast<uint32_t>(old_bytes.size());
226 227 228
  // 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;
229
  size_t new_size = bytes_offset + bytes.size();
230
  OwnedVector<uint8_t> new_bytes = OwnedVector<uint8_t>::New(new_size);
231
  if (old_size > 0) {
232
    memcpy(new_bytes.start(), old_bytes.begin(), old_size);
233 234 235 236
  } else {
    // Set the unused byte. It is never decoded, but the bytes are used as the
    // key in the native module cache.
    new_bytes[0] = 0;
237
  }
238
  memcpy(new_bytes.start() + bytes_offset, bytes.begin(), bytes.length());
239
  native_module_->SetWireBytes(std::move(new_bytes));
240 241 242
  return bytes_offset;
}

243
uint32_t TestingModuleBuilder::AddException(const FunctionSig* sig) {
244 245 246 247 248 249 250 251 252 253 254
  DCHECK_EQ(0, sig->return_count());
  uint32_t index = static_cast<uint32_t>(test_module_->exceptions.size());
  test_module_->exceptions.push_back(WasmException{sig});
  Handle<WasmExceptionTag> tag = WasmExceptionTag::New(isolate_, index);
  Handle<FixedArray> table(instance_object_->exceptions_table(), isolate_);
  table = isolate_->factory()->CopyFixedArrayAndGrow(table, 1);
  instance_object_->set_exceptions_table(*table);
  table->set(index, *tag);
  return index;
}

255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276
uint32_t TestingModuleBuilder::AddPassiveDataSegment(Vector<const byte> bytes) {
  uint32_t index = static_cast<uint32_t>(test_module_->data_segments.size());
  DCHECK_EQ(index, test_module_->data_segments.size());
  DCHECK_EQ(index, data_segment_starts_.size());
  DCHECK_EQ(index, data_segment_sizes_.size());

  // Add a passive data segment. This isn't used by function compilation, but
  // but it keeps the index in sync. The data segment's source will not be
  // correct, since we don't store data in the module wire bytes.
  test_module_->data_segments.emplace_back();

  // The num_declared_data_segments (from the DataCount section) is used
  // to validate the segment index, during function compilation.
  test_module_->num_declared_data_segments = index + 1;

  Address old_data_address =
      reinterpret_cast<Address>(data_segment_data_.data());
  size_t old_data_size = data_segment_data_.size();
  data_segment_data_.resize(old_data_size + bytes.length());
  Address new_data_address =
      reinterpret_cast<Address>(data_segment_data_.data());

277
  memcpy(data_segment_data_.data() + old_data_size, bytes.begin(),
278 279 280 281 282 283 284 285 286 287 288 289 290 291 292
         bytes.length());

  // The data_segment_data_ offset may have moved, so update all the starts.
  for (Address& start : data_segment_starts_) {
    start += new_data_address - old_data_address;
  }
  data_segment_starts_.push_back(new_data_address + old_data_size);
  data_segment_sizes_.push_back(bytes.length());

  // The vector pointers may have moved, so update the instance object.
  instance_object_->set_data_segment_starts(data_segment_starts_.data());
  instance_object_->set_data_segment_sizes(data_segment_sizes_.data());
  return index;
}

293 294 295 296 297
uint32_t TestingModuleBuilder::AddPassiveElementSegment(
    const std::vector<uint32_t>& entries) {
  uint32_t index = static_cast<uint32_t>(test_module_->elem_segments.size());
  DCHECK_EQ(index, dropped_elem_segments_.size());

298
  test_module_->elem_segments.emplace_back(kWasmFuncRef, false);
299
  auto& elem_segment = test_module_->elem_segments.back();
300 301 302
  for (uint32_t entry : entries) {
    elem_segment.entries.push_back(WasmInitExpr::RefFuncConst(entry));
  }
303 304 305 306 307 308 309

  // The vector pointers may have moved, so update the instance object.
  dropped_elem_segments_.push_back(0);
  instance_object_->set_dropped_elem_segments(dropped_elem_segments_.data());
  return index;
}

310
CompilationEnv TestingModuleBuilder::CreateCompilationEnv() {
311 312 313 314
  // This is a hack so we don't need to call
  // trap_handler::IsTrapHandlerEnabled().
  const bool is_trap_handler_enabled =
      V8_TRAP_HANDLER_SUPPORTED && i::FLAG_wasm_trap_handler;
315
  return {test_module_.get(),
316
          is_trap_handler_enabled ? kUseTrapHandler : kNoTrapHandler,
317
          runtime_exception_support_, enabled_features_};
318 319 320
}

const WasmGlobal* TestingModuleBuilder::AddGlobal(ValueType type) {
321
  byte size = type.element_size_bytes();
322
  global_offset = (global_offset + size - 1) & ~(size - 1);  // align
323
  test_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 &test_module_->globals.back();
329 330 331
}

Handle<WasmInstanceObject> TestingModuleBuilder::InitInstanceObject() {
332
  const bool kUsesLiftoff = true;
333
  size_t code_size_estimate =
334 335
      wasm::WasmCodeManager::EstimateNativeModuleCodeSize(test_module_.get(),
                                                          kUsesLiftoff);
336
  auto native_module = isolate_->wasm_engine()->NewNativeModule(
337
      isolate_, enabled_features_, test_module_, code_size_estimate);
338
  native_module->SetWireBytes(OwnedVector<const uint8_t>());
339
  native_module->compilation_state()->set_compilation_id(0);
340 341 342
  constexpr Vector<const char> kNoSourceUrl{"", 0};
  Handle<Script> script = isolate_->wasm_engine()->GetOrCreateScript(
      isolate_, native_module, kNoSourceUrl);
343

344
  Handle<WasmModuleObject> module_object =
345
      WasmModuleObject::New(isolate_, std::move(native_module), script);
346 347 348
  // This method is called when we initialize TestEnvironment. We don't
  // have a memory yet, so we won't create it here. We'll update the
  // interpreter when we get a memory. We do have globals, though.
349
  native_module_ = module_object->native_module();
350
  native_module_->ReserveCodeTableForTesting(kMaxFunctions);
351

352
  auto instance = WasmInstanceObject::New(isolate_, module_object);
353
  instance->set_exceptions_table(*isolate_->factory()->empty_fixed_array());
354
  instance->set_globals_start(globals_data_);
355
  return instance;
356 357
}

358
void TestBuildingGraphWithBuilder(compiler::WasmGraphBuilder* builder,
359
                                  Zone* zone, const FunctionSig* sig,
360
                                  const byte* start, const byte* end) {
361 362
  WasmFeatures unused_detected_features;
  FunctionBody body(sig, 0, start, end);
363
  std::vector<compiler::WasmLoopInfo> loops;
364
  DecodeResult result =
365
      BuildTFGraph(zone->allocator(), WasmFeatures::All(), nullptr, builder,
366
                   &unused_detected_features, body, &loops, nullptr);
367
  if (result.failed()) {
368
#ifdef DEBUG
369 370 371
    if (!FLAG_trace_wasm_decoder) {
      // Retry the compilation with the tracing flag on, to help in debugging.
      FLAG_trace_wasm_decoder = true;
372 373 374
      result =
          BuildTFGraph(zone->allocator(), WasmFeatures::All(), nullptr, builder,
                       &unused_detected_features, body, &loops, nullptr);
375
    }
376
#endif
377

378 379
    FATAL("Verification failed; pc = +%x, msg = %s", result.error().offset(),
          result.error().message().c_str());
380
  }
381
  builder->LowerInt64(compiler::WasmGraphBuilder::kCalledFromWasm);
382 383
}

384
void TestBuildingGraph(Zone* zone, compiler::JSGraph* jsgraph,
385
                       CompilationEnv* module, const FunctionSig* sig,
386 387
                       compiler::SourcePositionTable* source_position_table,
                       const byte* start, const byte* end) {
388 389
  compiler::WasmGraphBuilder builder(module, zone, jsgraph, sig,
                                     source_position_table);
390
  TestBuildingGraphWithBuilder(&builder, zone, sig, start, end);
391 392 393
}

WasmFunctionWrapper::WasmFunctionWrapper(Zone* zone, int num_params)
394 395 396 397
    : GraphAndBuilders(zone),
      inner_code_node_(nullptr),
      context_address_(nullptr),
      signature_(nullptr) {
398 399 400 401 402 403 404 405 406 407
  // One additional parameter for the pointer to the return value memory.
  Signature<MachineType>::Builder sig_builder(zone, 1, num_params + 1);

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

408
void WasmFunctionWrapper::Init(CallDescriptor* call_descriptor,
409 410
                               MachineType return_type,
                               Vector<MachineType> param_types) {
411
  DCHECK_NOT_NULL(call_descriptor);
412 413 414 415
  DCHECK_EQ(signature_->parameter_count(), param_types.length() + 1);

  // Create the TF graph for the wrapper.

416 417
  // Function, context_address, effect, and control.
  Node** parameters = zone()->NewArray<Node*>(param_types.length() + 4);
418 419 420 421
  int start_value_output_count =
      static_cast<int>(signature_->parameter_count()) + 1;
  graph()->SetStart(
      graph()->NewNode(common()->Start(start_value_output_count)));
422 423 424 425 426 427 428
  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_;

429 430 431 432
  // Dummy node that gets replaced in SetContextAddress.
  context_address_ = graph()->NewNode(IntPtrConstant(0));
  parameters[parameter_count++] = context_address_;

433 434 435 436 437 438 439 440 441 442 443 444
  int param_idx = 0;
  for (MachineType t : param_types) {
    DCHECK_NE(MachineType::None(), t);
    parameters[parameter_count] = graph()->NewNode(
        machine()->Load(t),
        graph()->NewNode(common()->Parameter(param_idx++), graph()->start()),
        graph()->NewNode(common()->Int32Constant(0)), effect, graph()->start());
    effect = parameters[parameter_count++];
  }

  parameters[parameter_count++] = effect;
  parameters[parameter_count++] = graph()->start();
445 446
  Node* call = graph()->NewNode(common()->Call(call_descriptor),
                                parameter_count, parameters);
447 448 449 450

  if (!return_type.IsNone()) {
    effect = graph()->NewNode(
        machine()->Store(compiler::StoreRepresentation(
451 452
            return_type.representation(),
            compiler::WriteBarrierKind::kNoWriteBarrier)),
453 454 455 456 457 458 459 460 461 462 463 464 465
        graph()->NewNode(common()->Parameter(param_types.length()),
                         graph()->start()),
        graph()->NewNode(common()->Int32Constant(0)), call, effect,
        graph()->start());
  }
  Node* zero = graph()->NewNode(common()->Int32Constant(0));
  Node* r = graph()->NewNode(
      common()->Return(), zero,
      graph()->NewNode(common()->Int32Constant(WASM_WRAPPER_RETURN_VALUE)),
      effect, graph()->start());
  graph()->SetEnd(graph()->NewNode(common()->End(1), r));
}

466
Handle<Code> WasmFunctionWrapper::GetWrapperCode(Isolate* isolate) {
467 468
  Handle<Code> code;
  if (!code_.ToHandle(&code)) {
469 470
    auto call_descriptor = compiler::Linkage::GetSimplifiedCDescriptor(
        zone(), signature_, CallDescriptor::kInitializeRootRegister);
471

472
    if (kSystemPointerSize == 4) {
473 474 475 476 477 478 479 480 481
      size_t num_params = signature_->parameter_count();
      // One additional parameter for the pointer of the return value.
      Signature<MachineRepresentation>::Builder rep_builder(zone(), 1,
                                                            num_params + 1);

      rep_builder.AddReturn(MachineRepresentation::kWord32);
      for (size_t i = 0; i < num_params + 1; i++) {
        rep_builder.AddParam(MachineRepresentation::kWord32);
      }
482 483
      compiler::Int64Lowering r(graph(), machine(), common(), simplified(),
                                zone(), rep_builder.Build());
484 485 486
      r.LowerGraph();
    }

487
    OptimizedCompilationInfo info(ArrayVector("testing"), graph()->zone(),
488
                                  CodeKind::C_WASM_ENTRY);
489
    code_ = compiler::Pipeline::GenerateCodeForTesting(
490
        &info, isolate, call_descriptor, graph(),
491
        AssemblerOptions::Default(isolate));
492
    code = code_.ToHandleChecked();
493 494
#ifdef ENABLE_DISASSEMBLER
    if (FLAG_print_opt_code) {
495 496 497
      CodeTracer::Scope tracing_scope(isolate->GetCodeTracer());
      OFStream os(tracing_scope.file());

498
      code->Disassemble("wasm wrapper", os, isolate);
499 500 501 502
    }
#endif
  }

503
  return code;
504 505
}

506 507 508
// This struct is just a type tag for Zone::NewArray<T>(size_t) call.
struct WasmFunctionCompilerBuffer {};

509 510 511
void WasmFunctionCompiler::Build(const byte* start, const byte* end) {
  size_t locals_size = local_decls.Size();
  size_t total_size = end - start + locals_size + 1;
512
  byte* buffer = zone()->NewArray<byte, WasmFunctionCompilerBuffer>(total_size);
513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528
  // Prepend the local decls to the code.
  local_decls.Emit(buffer);
  // Emit the code.
  memcpy(buffer + locals_size, start, end - start);
  // Append an extra end opcode.
  buffer[total_size - 1] = kExprEnd;

  start = buffer;
  end = buffer + total_size;

  CHECK_GE(kMaxInt, end - start);
  int len = static_cast<int>(end - start);
  function_->code = {builder_->AddBytes(Vector<const byte>(start, len)),
                     static_cast<uint32_t>(len)};

  if (interpreter_) {
529
    // Add the code to the interpreter; do not generate compiled code.
530
    interpreter_->SetFunctionCodeForTesting(function_, start, end);
531
    return;
532 533
  }

534 535
  Vector<const uint8_t> wire_bytes = builder_->instance_object()
                                         ->module_object()
536
                                         .native_module()
537
                                         ->wire_bytes();
538

539
  CompilationEnv env = builder_->CreateCompilationEnv();
540
  ScopedVector<uint8_t> func_wire_bytes(function_->code.length());
541
  memcpy(func_wire_bytes.begin(), wire_bytes.begin() + function_->code.offset(),
542 543 544
         func_wire_bytes.length());

  FunctionBody func_body{function_->sig, function_->code.offset(),
545
                         func_wire_bytes.begin(), func_wire_bytes.end()};
546
  NativeModule* native_module =
547
      builder_->instance_object()->module_object().native_module();
548 549
  ForDebugging for_debugging =
      native_module->IsTieredDown() ? kForDebugging : kNoDebugging;
550
  WasmCompilationUnit unit(function_->func_index, builder_->execution_tier(),
551
                           for_debugging);
552
  WasmFeatures unused_detected_features;
553
  WasmCompilationResult result = unit.ExecuteCompilation(
554 555
      isolate()->wasm_engine(), &env,
      native_module->compilation_state()->GetWireBytesStorage(),
556
      isolate()->counters(), &unused_detected_features);
557 558
  WasmCode* code = native_module->PublishCode(
      native_module->AddCompiledCode(std::move(result)));
559
  DCHECK_NOT_NULL(code);
560 561
  DisallowGarbageCollection no_gc;
  Script script = builder_->instance_object()->module_object().script();
562
  std::unique_ptr<char[]> source_url = String::cast(script.name()).ToCString();
563 564 565
  if (WasmCode::ShouldBeLogged(isolate())) {
    code->LogCode(isolate(), source_url.get(), script.id());
  }
566 567
}

568
WasmFunctionCompiler::WasmFunctionCompiler(Zone* zone, const FunctionSig* sig,
569 570
                                           TestingModuleBuilder* builder,
                                           const char* name)
571 572 573 574 575 576 577 578
    : GraphAndBuilders(zone),
      jsgraph(builder->isolate(), this->graph(), this->common(), nullptr,
              nullptr, this->machine()),
      sig(sig),
      descriptor_(nullptr),
      builder_(builder),
      local_decls(zone, sig),
      source_position_table_(this->graph()),
579
      interpreter_(builder->interpreter()) {
580
  // Get a new function from the testing module.
581
  int index = builder->AddFunction(sig, name, TestingModuleBuilder::kWasm);
582 583 584
  function_ = builder_->GetFunctionAt(index);
}

585
WasmFunctionCompiler::~WasmFunctionCompiler() = default;
586

587 588 589
/* static */
FunctionSig* WasmRunnerBase::CreateSig(Zone* zone, MachineType return_type,
                                       Vector<MachineType> param_types) {
590 591 592 593
  int return_count = return_type.IsNone() ? 0 : 1;
  int param_count = param_types.length();

  // Allocate storage array in zone.
594
  ValueType* sig_types = zone->NewArray<ValueType>(return_count + param_count);
595 596 597 598

  // Convert machine types to local types, and check that there are no
  // MachineType::None()'s in the parameters.
  int idx = 0;
599
  if (return_count) sig_types[idx++] = ValueType::For(return_type);
600 601
  for (MachineType param : param_types) {
    CHECK_NE(MachineType::None(), param);
602
    sig_types[idx++] = ValueType::For(param);
603
  }
604
  return zone->New<FunctionSig>(return_count, param_count, sig_types);
605 606 607 608 609 610 611 612
}

// static
bool WasmRunnerBase::trap_happened;

}  // namespace wasm
}  // namespace internal
}  // namespace v8