test-run-wasm-module.cc 32.1 KB
Newer Older
1 2 3 4 5 6 7
// Copyright 2015 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 <stdlib.h>
#include <string.h>

8 9
#include <atomic>

10
#include "src/api/api-inl.h"
11
#include "src/objects/objects-inl.h"
12
#include "src/snapshot/code-serializer.h"
13
#include "src/utils/version.h"
14
#include "src/wasm/module-decoder.h"
15
#include "src/wasm/wasm-engine.h"
16
#include "src/wasm/wasm-module-builder.h"
17
#include "src/wasm/wasm-module.h"
18
#include "src/wasm/wasm-objects-inl.h"
19 20
#include "src/wasm/wasm-opcodes.h"
#include "test/cctest/cctest.h"
21
#include "test/common/wasm/flag-utils.h"
22
#include "test/common/wasm/test-signatures.h"
23
#include "test/common/wasm/wasm-macro-gen.h"
24
#include "test/common/wasm/wasm-module-runner.h"
25

26 27 28
namespace v8 {
namespace internal {
namespace wasm {
29
namespace test_run_wasm_module {
30

31 32
using base::ReadLittleEndianValue;
using base::WriteLittleEndianValue;
33 34
using testing::CompileAndInstantiateForTesting;

35
namespace {
36
void Cleanup(Isolate* isolate = CcTest::InitIsolateOnce()) {
37 38 39 40 41 42
  // By sending a low memory notifications, we will try hard to collect all
  // garbage and will therefore also invoke all weak callbacks of actually
  // unreachable persistent handles.
  reinterpret_cast<v8::Isolate*>(isolate)->LowMemoryNotification();
}

43 44 45
void TestModule(Zone* zone, WasmModuleBuilder* builder,
                int32_t expected_result) {
  ZoneBuffer buffer(zone);
46
  builder->WriteTo(&buffer);
47

48
  Isolate* isolate = CcTest::InitIsolateOnce();
49
  HandleScope scope(isolate);
50
  testing::SetupIsolateForWasmModule(isolate);
51 52
  int32_t result =
      testing::CompileAndRunWasmModule(isolate, buffer.begin(), buffer.end());
53 54
  CHECK_EQ(expected_result, result);
}
55

56 57
void TestModuleException(Zone* zone, WasmModuleBuilder* builder) {
  ZoneBuffer buffer(zone);
58
  builder->WriteTo(&buffer);
59 60 61 62 63

  Isolate* isolate = CcTest::InitIsolateOnce();
  HandleScope scope(isolate);
  testing::SetupIsolateForWasmModule(isolate);
  v8::TryCatch try_catch(reinterpret_cast<v8::Isolate*>(isolate));
64
  testing::CompileAndRunWasmModule(isolate, buffer.begin(), buffer.end());
65 66 67 68
  CHECK(try_catch.HasCaught());
  isolate->clear_pending_exception();
}

69
void ExportAsMain(WasmFunctionBuilder* f) {
70
  f->builder()->AddExport(base::CStrVector("main"), f);
71
}
72

73 74 75 76 77 78
#define EMIT_CODE_WITH_END(f, code)  \
  do {                               \
    f->EmitCode(code, sizeof(code)); \
    f->Emit(kExprEnd);               \
  } while (false)

79 80 81
}  // namespace

TEST(Run_WasmModule_Return114) {
82 83 84 85 86 87
  {
    static const int32_t kReturnValue = 114;
    TestSignatures sigs;
    v8::internal::AccountingAllocator allocator;
    Zone zone(&allocator, ZONE_NAME);

88
    WasmModuleBuilder* builder = zone.New<WasmModuleBuilder>(&zone);
89 90
    WasmFunctionBuilder* f = builder->AddFunction(sigs.i_v());
    ExportAsMain(f);
91
    byte code[] = {WASM_I32V_2(kReturnValue)};
92
    EMIT_CODE_WITH_END(f, code);
93 94 95
    TestModule(&zone, builder, kReturnValue);
  }
  Cleanup();
96 97
}

98 99 100 101 102 103 104 105 106 107 108
TEST(Run_WasmModule_CompilationHintsLazy) {
  if (!FLAG_wasm_tier_up || !FLAG_liftoff) return;
  {
    EXPERIMENTAL_FLAG_SCOPE(compilation_hints);

    static const int32_t kReturnValue = 114;
    TestSignatures sigs;
    v8::internal::AccountingAllocator allocator;
    Zone zone(&allocator, ZONE_NAME);

    // Build module with one lazy function.
109
    WasmModuleBuilder* builder = zone.New<WasmModuleBuilder>(&zone);
110 111 112 113 114 115 116 117 118 119
    WasmFunctionBuilder* f = builder->AddFunction(sigs.i_v());
    ExportAsMain(f);
    byte code[] = {WASM_I32V_2(kReturnValue)};
    EMIT_CODE_WITH_END(f, code);
    f->SetCompilationHint(WasmCompilationHintStrategy::kLazy,
                          WasmCompilationHintTier::kBaseline,
                          WasmCompilationHintTier::kOptimized);

    // Compile module. No function is actually compiled as the function is lazy.
    ZoneBuffer buffer(&zone);
120
    builder->WriteTo(&buffer);
121 122 123 124 125 126 127 128 129
    Isolate* isolate = CcTest::InitIsolateOnce();
    HandleScope scope(isolate);
    testing::SetupIsolateForWasmModule(isolate);
    ErrorThrower thrower(isolate, "CompileAndRunWasmModule");
    MaybeHandle<WasmModuleObject> module = testing::CompileForTesting(
        isolate, &thrower, ModuleWireBytes(buffer.begin(), buffer.end()));
    CHECK(!module.is_null());

    // Lazy function was not invoked and therefore not compiled yet.
130
    static const int kFuncIndex = 0;
131
    NativeModule* native_module = module.ToHandleChecked()->native_module();
132 133 134
    CHECK(!native_module->HasCode(kFuncIndex));
    auto* compilation_state = native_module->compilation_state();
    CHECK(compilation_state->baseline_compilation_finished());
135 136

    // Instantiate and invoke function.
137 138
    MaybeHandle<WasmInstanceObject> instance = GetWasmEngine()->SyncInstantiate(
        isolate, &thrower, module.ToHandleChecked(), {}, {});
139
    CHECK(!instance.is_null());
140 141
    int32_t result = testing::CallWasmFunctionForTesting(
        isolate, instance.ToHandleChecked(), "main", 0, nullptr);
142 143 144
    CHECK_EQ(kReturnValue, result);

    // Lazy function was invoked and therefore compiled.
145
    CHECK(native_module->HasCode(kFuncIndex));
146
    WasmCodeRefScope code_ref_scope;
147
    ExecutionTier actual_tier = native_module->GetCode(kFuncIndex)->tier();
148
    static_assert(ExecutionTier::kLiftoff < ExecutionTier::kTurbofan,
149 150 151
                  "Assume an order on execution tiers");
    ExecutionTier baseline_tier = ExecutionTier::kLiftoff;
    CHECK_LE(baseline_tier, actual_tier);
152
    CHECK(compilation_state->baseline_compilation_finished());
153 154 155 156 157 158 159 160 161 162 163 164 165 166 167
  }
  Cleanup();
}

TEST(Run_WasmModule_CompilationHintsNoTiering) {
  if (!FLAG_wasm_tier_up || !FLAG_liftoff) return;
  {
    EXPERIMENTAL_FLAG_SCOPE(compilation_hints);

    static const int32_t kReturnValue = 114;
    TestSignatures sigs;
    v8::internal::AccountingAllocator allocator;
    Zone zone(&allocator, ZONE_NAME);

    // Build module with regularly compiled function (no tiering).
168
    WasmModuleBuilder* builder = zone.New<WasmModuleBuilder>(&zone);
169 170 171 172 173 174 175 176 177 178
    WasmFunctionBuilder* f = builder->AddFunction(sigs.i_v());
    ExportAsMain(f);
    byte code[] = {WASM_I32V_2(kReturnValue)};
    EMIT_CODE_WITH_END(f, code);
    f->SetCompilationHint(WasmCompilationHintStrategy::kEager,
                          WasmCompilationHintTier::kBaseline,
                          WasmCompilationHintTier::kBaseline);

    // Compile module.
    ZoneBuffer buffer(&zone);
179
    builder->WriteTo(&buffer);
180 181 182 183 184 185 186 187 188
    Isolate* isolate = CcTest::InitIsolateOnce();
    HandleScope scope(isolate);
    testing::SetupIsolateForWasmModule(isolate);
    ErrorThrower thrower(isolate, "CompileAndRunWasmModule");
    MaybeHandle<WasmModuleObject> module = testing::CompileForTesting(
        isolate, &thrower, ModuleWireBytes(buffer.begin(), buffer.end()));
    CHECK(!module.is_null());

    // Synchronous compilation finished and no tiering units were initialized.
189
    static const int kFuncIndex = 0;
190
    NativeModule* native_module = module.ToHandleChecked()->native_module();
191
    CHECK(native_module->HasCode(kFuncIndex));
192
    ExecutionTier expected_tier = ExecutionTier::kLiftoff;
193 194
    WasmCodeRefScope code_ref_scope;
    ExecutionTier actual_tier = native_module->GetCode(kFuncIndex)->tier();
195
    CHECK_EQ(expected_tier, actual_tier);
196 197 198
    auto* compilation_state = native_module->compilation_state();
    CHECK(compilation_state->baseline_compilation_finished());
    CHECK(compilation_state->top_tier_compilation_finished());
199 200 201 202 203
  }
  Cleanup();
}

TEST(Run_WasmModule_CompilationHintsTierUp) {
204
  FlagScope<bool> no_wasm_dynamic_tiering(&FLAG_wasm_dynamic_tiering, false);
205 206 207 208 209 210 211 212 213 214
  if (!FLAG_wasm_tier_up || !FLAG_liftoff) return;
  {
    EXPERIMENTAL_FLAG_SCOPE(compilation_hints);

    static const int32_t kReturnValue = 114;
    TestSignatures sigs;
    v8::internal::AccountingAllocator allocator;
    Zone zone(&allocator, ZONE_NAME);

    // Build module with tiering compilation hint.
215
    WasmModuleBuilder* builder = zone.New<WasmModuleBuilder>(&zone);
216 217 218 219 220 221 222 223 224 225
    WasmFunctionBuilder* f = builder->AddFunction(sigs.i_v());
    ExportAsMain(f);
    byte code[] = {WASM_I32V_2(kReturnValue)};
    EMIT_CODE_WITH_END(f, code);
    f->SetCompilationHint(WasmCompilationHintStrategy::kEager,
                          WasmCompilationHintTier::kBaseline,
                          WasmCompilationHintTier::kOptimized);

    // Compile module.
    ZoneBuffer buffer(&zone);
226
    builder->WriteTo(&buffer);
227 228 229 230 231 232 233 234
    Isolate* isolate = CcTest::InitIsolateOnce();
    HandleScope scope(isolate);
    testing::SetupIsolateForWasmModule(isolate);
    ErrorThrower thrower(isolate, "CompileAndRunWasmModule");
    MaybeHandle<WasmModuleObject> module = testing::CompileForTesting(
        isolate, &thrower, ModuleWireBytes(buffer.begin(), buffer.end()));
    CHECK(!module.is_null());

235 236
    // Expect baseline or top tier code.
    static const int kFuncIndex = 0;
237
    NativeModule* native_module = module.ToHandleChecked()->native_module();
238
    auto* compilation_state = native_module->compilation_state();
239
    static_assert(ExecutionTier::kLiftoff < ExecutionTier::kTurbofan,
240 241
                  "Assume an order on execution tiers");
    ExecutionTier baseline_tier = ExecutionTier::kLiftoff;
242 243 244 245 246 247 248 249 250 251 252 253 254
    {
      CHECK(native_module->HasCode(kFuncIndex));
      WasmCodeRefScope code_ref_scope;
      ExecutionTier actual_tier = native_module->GetCode(kFuncIndex)->tier();
      CHECK_LE(baseline_tier, actual_tier);
      CHECK(compilation_state->baseline_compilation_finished());
    }

    // Busy wait for top tier compilation to finish.
    while (!compilation_state->top_tier_compilation_finished()) {
    }

    // Expect top tier code.
255
    ExecutionTier top_tier = ExecutionTier::kTurbofan;
256 257 258 259 260 261 262 263
    {
      CHECK(native_module->HasCode(kFuncIndex));
      WasmCodeRefScope code_ref_scope;
      ExecutionTier actual_tier = native_module->GetCode(kFuncIndex)->tier();
      CHECK_EQ(top_tier, actual_tier);
      CHECK(compilation_state->baseline_compilation_finished());
      CHECK(compilation_state->top_tier_compilation_finished());
    }
264 265 266 267
  }
  Cleanup();
}

268
TEST(Run_WasmModule_CompilationHintsLazyBaselineEagerTopTier) {
269
  FlagScope<bool> no_wasm_dynamic_tiering(&FLAG_wasm_dynamic_tiering, false);
270 271 272 273 274 275 276 277 278 279
  if (!FLAG_wasm_tier_up || !FLAG_liftoff) return;
  {
    EXPERIMENTAL_FLAG_SCOPE(compilation_hints);

    static const int32_t kReturnValue = 114;
    TestSignatures sigs;
    v8::internal::AccountingAllocator allocator;
    Zone zone(&allocator, ZONE_NAME);

    // Build module with tiering compilation hint.
280
    WasmModuleBuilder* builder = zone.New<WasmModuleBuilder>(&zone);
281 282 283 284 285 286 287 288 289 290 291
    WasmFunctionBuilder* f = builder->AddFunction(sigs.i_v());
    ExportAsMain(f);
    byte code[] = {WASM_I32V_2(kReturnValue)};
    EMIT_CODE_WITH_END(f, code);
    f->SetCompilationHint(
        WasmCompilationHintStrategy::kLazyBaselineEagerTopTier,
        WasmCompilationHintTier::kBaseline,
        WasmCompilationHintTier::kOptimized);

    // Compile module.
    ZoneBuffer buffer(&zone);
292
    builder->WriteTo(&buffer);
293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308
    Isolate* isolate = CcTest::InitIsolateOnce();
    HandleScope scope(isolate);
    testing::SetupIsolateForWasmModule(isolate);
    ErrorThrower thrower(isolate, "CompileAndRunWasmModule");
    MaybeHandle<WasmModuleObject> module = testing::CompileForTesting(
        isolate, &thrower, ModuleWireBytes(buffer.begin(), buffer.end()));
    CHECK(!module.is_null());

    NativeModule* native_module = module.ToHandleChecked()->native_module();
    auto* compilation_state = native_module->compilation_state();

    // Busy wait for top tier compilation to finish.
    while (!compilation_state->top_tier_compilation_finished()) {
    }

    // Expect top tier code.
309
    static_assert(ExecutionTier::kLiftoff < ExecutionTier::kTurbofan,
310 311 312 313 314 315 316 317 318 319 320 321 322 323 324
                  "Assume an order on execution tiers");
    static const int kFuncIndex = 0;
    ExecutionTier top_tier = ExecutionTier::kTurbofan;
    {
      CHECK(native_module->HasCode(kFuncIndex));
      WasmCodeRefScope code_ref_scope;
      ExecutionTier actual_tier = native_module->GetCode(kFuncIndex)->tier();
      CHECK_EQ(top_tier, actual_tier);
      CHECK(compilation_state->baseline_compilation_finished());
      CHECK(compilation_state->top_tier_compilation_finished());
    }
  }
  Cleanup();
}

325
TEST(Run_WasmModule_CallAdd) {
326 327 328 329
  {
    v8::internal::AccountingAllocator allocator;
    Zone zone(&allocator, ZONE_NAME);
    TestSignatures sigs;
330

331
    WasmModuleBuilder* builder = zone.New<WasmModuleBuilder>(&zone);
332

333 334 335 336
    WasmFunctionBuilder* f1 = builder->AddFunction(sigs.i_ii());
    uint16_t param1 = 0;
    uint16_t param2 = 1;
    byte code1[] = {
337
        WASM_I32_ADD(WASM_LOCAL_GET(param1), WASM_LOCAL_GET(param2))};
338
    EMIT_CODE_WITH_END(f1, code1);
339

340
    WasmFunctionBuilder* f2 = builder->AddFunction(sigs.i_v());
341

342 343
    ExportAsMain(f2);
    byte code2[] = {
344
        WASM_CALL_FUNCTION(f1->func_index(), WASM_I32V_2(77), WASM_I32V_1(22))};
345
    EMIT_CODE_WITH_END(f2, code2);
346 347 348
    TestModule(&zone, builder, 99);
  }
  Cleanup();
349 350 351
}

TEST(Run_WasmModule_ReadLoadedDataSegment) {
352 353 354 355 356 357
  {
    static const byte kDataSegmentDest0 = 12;
    v8::internal::AccountingAllocator allocator;
    Zone zone(&allocator, ZONE_NAME);
    TestSignatures sigs;

358
    WasmModuleBuilder* builder = zone.New<WasmModuleBuilder>(&zone);
359 360 361 362
    WasmFunctionBuilder* f = builder->AddFunction(sigs.i_v());

    ExportAsMain(f);
    byte code[] = {
363
        WASM_LOAD_MEM(MachineType::Int32(), WASM_I32V_1(kDataSegmentDest0))};
364
    EMIT_CODE_WITH_END(f, code);
365
    byte data[] = {0xAA, 0xBB, 0xCC, 0xDD};
366
    builder->AddDataSegment(data, sizeof(data), kDataSegmentDest0);
367
    TestModule(&zone, builder, 0xDDCCBBAA);
368 369
  }
  Cleanup();
370 371 372
}

TEST(Run_WasmModule_CheckMemoryIsZero) {
373 374 375 376 377 378
  {
    static const int kCheckSize = 16 * 1024;
    v8::internal::AccountingAllocator allocator;
    Zone zone(&allocator, ZONE_NAME);
    TestSignatures sigs;

379
    WasmModuleBuilder* builder = zone.New<WasmModuleBuilder>(&zone);
380 381
    WasmFunctionBuilder* f = builder->AddFunction(sigs.i_v());

382
    uint16_t localIndex = f->AddLocal(kWasmI32);
383 384 385
    ExportAsMain(f);
    byte code[] = {WASM_BLOCK_I(
        WASM_WHILE(
386
            WASM_I32_LTS(WASM_LOCAL_GET(localIndex), WASM_I32V_3(kCheckSize)),
387
            WASM_IF_ELSE(
388
                WASM_LOAD_MEM(MachineType::Int32(), WASM_LOCAL_GET(localIndex)),
389 390 391
                WASM_BRV(3, WASM_I32V_1(-1)),
                WASM_INC_LOCAL_BY(localIndex, 4))),
        WASM_I32V_1(11))};
392
    EMIT_CODE_WITH_END(f, code);
393 394 395
    TestModule(&zone, builder, 11);
  }
  Cleanup();
396 397 398
}

TEST(Run_WasmModule_CallMain_recursive) {
399 400 401 402 403
  {
    v8::internal::AccountingAllocator allocator;
    Zone zone(&allocator, ZONE_NAME);
    TestSignatures sigs;

404
    WasmModuleBuilder* builder = zone.New<WasmModuleBuilder>(&zone);
405 406
    WasmFunctionBuilder* f = builder->AddFunction(sigs.i_v());

407
    uint16_t localIndex = f->AddLocal(kWasmI32);
408 409
    ExportAsMain(f);
    byte code[] = {
410
        WASM_LOCAL_SET(localIndex,
411
                       WASM_LOAD_MEM(MachineType::Int32(), WASM_ZERO)),
412
        WASM_IF_ELSE_I(WASM_I32_LTS(WASM_LOCAL_GET(localIndex), WASM_I32V_1(5)),
413 414 415
                       WASM_SEQ(WASM_STORE_MEM(MachineType::Int32(), WASM_ZERO,
                                               WASM_INC_LOCAL(localIndex)),
                                WASM_CALL_FUNCTION0(0)),
416
                       WASM_I32V_1(55))};
417
    EMIT_CODE_WITH_END(f, code);
418 419 420
    TestModule(&zone, builder, 55);
  }
  Cleanup();
421 422 423
}

TEST(Run_WasmModule_Global) {
424 425 426 427 428
  {
    v8::internal::AccountingAllocator allocator;
    Zone zone(&allocator, ZONE_NAME);
    TestSignatures sigs;

429
    WasmModuleBuilder* builder = zone.New<WasmModuleBuilder>(&zone);
430 431
    uint32_t global1 = builder->AddGlobal(kWasmI32);
    uint32_t global2 = builder->AddGlobal(kWasmI32);
432 433
    WasmFunctionBuilder* f1 = builder->AddFunction(sigs.i_v());
    byte code1[] = {
434
        WASM_I32_ADD(WASM_GLOBAL_GET(global1), WASM_GLOBAL_GET(global2))};
435
    EMIT_CODE_WITH_END(f1, code1);
436 437
    WasmFunctionBuilder* f2 = builder->AddFunction(sigs.i_v());
    ExportAsMain(f2);
438 439
    byte code2[] = {WASM_GLOBAL_SET(global1, WASM_I32V_1(56)),
                    WASM_GLOBAL_SET(global2, WASM_I32V_1(41)),
440
                    WASM_RETURN(WASM_CALL_FUNCTION0(f1->func_index()))};
441
    EMIT_CODE_WITH_END(f2, code2);
442 443 444
    TestModule(&zone, builder, 97);
  }
  Cleanup();
445
}
446

447
TEST(MemorySize) {
448 449 450 451 452 453 454
  {
    // Initial memory size is 16, see wasm-module-builder.cc
    static const int kExpectedValue = 16;
    TestSignatures sigs;
    v8::internal::AccountingAllocator allocator;
    Zone zone(&allocator, ZONE_NAME);

455
    WasmModuleBuilder* builder = zone.New<WasmModuleBuilder>(&zone);
456 457 458
    WasmFunctionBuilder* f = builder->AddFunction(sigs.i_v());
    ExportAsMain(f);
    byte code[] = {WASM_MEMORY_SIZE};
459
    EMIT_CODE_WITH_END(f, code);
460 461 462
    TestModule(&zone, builder, kExpectedValue);
  }
  Cleanup();
463 464
}

465
TEST(Run_WasmModule_MemSize_GrowMem) {
466
  {
467
    // Initial memory size = 16 + MemoryGrow(10)
468 469 470 471 472
    static const int kExpectedValue = 26;
    TestSignatures sigs;
    v8::internal::AccountingAllocator allocator;
    Zone zone(&allocator, ZONE_NAME);

473
    WasmModuleBuilder* builder = zone.New<WasmModuleBuilder>(&zone);
474 475
    WasmFunctionBuilder* f = builder->AddFunction(sigs.i_v());
    ExportAsMain(f);
476
    byte code[] = {WASM_MEMORY_GROW(WASM_I32V_1(10)), WASM_DROP,
477
                   WASM_MEMORY_SIZE};
478
    EMIT_CODE_WITH_END(f, code);
479 480 481
    TestModule(&zone, builder, kExpectedValue);
  }
  Cleanup();
482 483
}

484
TEST(MemoryGrowZero) {
485 486 487 488 489 490 491
  {
    // Initial memory size is 16, see wasm-module-builder.cc
    static const int kExpectedValue = 16;
    TestSignatures sigs;
    v8::internal::AccountingAllocator allocator;
    Zone zone(&allocator, ZONE_NAME);

492
    WasmModuleBuilder* builder = zone.New<WasmModuleBuilder>(&zone);
493 494
    WasmFunctionBuilder* f = builder->AddFunction(sigs.i_v());
    ExportAsMain(f);
495
    byte code[] = {WASM_MEMORY_GROW(WASM_I32V(0))};
496
    EMIT_CODE_WITH_END(f, code);
497 498 499
    TestModule(&zone, builder, kExpectedValue);
  }
  Cleanup();
500 501
}

502 503
class InterruptThread : public v8::base::Thread {
 public:
504
  explicit InterruptThread(Isolate* isolate, std::atomic<int32_t>* memory)
505 506 507 508 509 510 511
      : Thread(Options("TestInterruptLoop")),
        isolate_(isolate),
        memory_(memory) {}

  static void OnInterrupt(v8::Isolate* isolate, void* data) {
    int32_t* m = reinterpret_cast<int32_t*>(data);
    // Set the interrupt location to 0 to break the loop in {TestInterruptLoop}.
512
    Address ptr = reinterpret_cast<Address>(&m[interrupt_location_]);
513
    WriteLittleEndianValue<int32_t>(ptr, interrupt_value_);
514 515
  }

516
  void Run() override {
517
    // Wait for the main thread to write the signal value.
518 519
    int32_t val = 0;
    do {
520
      val = memory_[0].load(std::memory_order_relaxed);
521
      val = ReadLittleEndianValue<int32_t>(reinterpret_cast<Address>(&val));
522
    } while (val != signal_value_);
523
    isolate_->RequestInterrupt(&OnInterrupt, memory_);
524 525 526
  }

  Isolate* isolate_;
527
  std::atomic<int32_t>* memory_;
528 529 530 531 532 533
  static const int32_t interrupt_location_ = 10;
  static const int32_t interrupt_value_ = 154;
  static const int32_t signal_value_ = 1221;
};

TEST(TestInterruptLoop) {
534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554
  {
    // Do not dump the module of this test because it contains an infinite loop.
    if (FLAG_dump_wasm_module) return;

    // This test tests that WebAssembly loops can be interrupted, i.e. that if
    // an
    // InterruptCallback is registered by {Isolate::RequestInterrupt}, then the
    // InterruptCallback is eventually called even if a loop in WebAssembly code
    // is executed.
    // Test setup:
    // The main thread executes a WebAssembly function with a loop. In the loop
    // {signal_value_} is written to memory to signal a helper thread that the
    // main thread reached the loop in the WebAssembly program. When the helper
    // thread reads {signal_value_} from memory, it registers the
    // InterruptCallback. Upon exeution, the InterruptCallback write into the
    // WebAssemblyMemory to end the loop in the WebAssembly program.
    TestSignatures sigs;
    Isolate* isolate = CcTest::InitIsolateOnce();
    v8::internal::AccountingAllocator allocator;
    Zone zone(&allocator, ZONE_NAME);

555
    WasmModuleBuilder* builder = zone.New<WasmModuleBuilder>(&zone);
556 557 558 559
    WasmFunctionBuilder* f = builder->AddFunction(sigs.i_v());
    ExportAsMain(f);
    byte code[] = {
        WASM_LOOP(
560 561 562 563 564 565
            WASM_IF(WASM_NOT(WASM_LOAD_MEM(
                        MachineType::Int32(),
                        WASM_I32V(InterruptThread::interrupt_location_ * 4))),
                    WASM_STORE_MEM(MachineType::Int32(), WASM_ZERO,
                                   WASM_I32V(InterruptThread::signal_value_)),
                    WASM_BR(1))),
566
        WASM_I32V(121)};
567
    EMIT_CODE_WITH_END(f, code);
568
    ZoneBuffer buffer(&zone);
569
    builder->WriteTo(&buffer);
570

571 572 573
    HandleScope scope(isolate);
    testing::SetupIsolateForWasmModule(isolate);
    ErrorThrower thrower(isolate, "Test");
574
    const Handle<WasmInstanceObject> instance =
575 576
        CompileAndInstantiateForTesting(
            isolate, &thrower, ModuleWireBytes(buffer.begin(), buffer.end()))
577
            .ToHandleChecked();
578

579
    Handle<JSArrayBuffer> memory(instance->memory_object().array_buffer(),
580
                                 isolate);
581 582
    std::atomic<int32_t>* memory_array =
        reinterpret_cast<std::atomic<int32_t>*>(memory->backing_store());
583 584

    InterruptThread thread(isolate, memory_array);
585
    CHECK(thread.Start());
586
    testing::CallWasmFunctionForTesting(isolate, instance, "main", 0, nullptr);
587 588
    Address address = reinterpret_cast<Address>(
        &memory_array[InterruptThread::interrupt_location_]);
589
    CHECK_EQ(InterruptThread::interrupt_value_,
590
             ReadLittleEndianValue<int32_t>(address));
591 592
  }
  Cleanup();
593 594
}

595
TEST(Run_WasmModule_MemoryGrowInIf) {
596 597 598 599
  {
    TestSignatures sigs;
    v8::internal::AccountingAllocator allocator;
    Zone zone(&allocator, ZONE_NAME);
600
    WasmModuleBuilder* builder = zone.New<WasmModuleBuilder>(&zone);
601 602
    WasmFunctionBuilder* f = builder->AddFunction(sigs.i_v());
    ExportAsMain(f);
603
    byte code[] = {WASM_IF_ELSE_I(WASM_I32V(0), WASM_MEMORY_GROW(WASM_I32V(1)),
604
                                  WASM_I32V(12))};
605
    EMIT_CODE_WITH_END(f, code);
606 607 608
    TestModule(&zone, builder, 12);
  }
  Cleanup();
609
}
610 611

TEST(Run_WasmModule_GrowMemOobOffset) {
612 613
  {
    static const int kPageSize = 0x10000;
614
    // Initial memory size = 16 + MemoryGrow(10)
615
    static const int index = kPageSize * 17 + 4;
616
    int value = 0xACED;
617 618 619 620
    TestSignatures sigs;
    v8::internal::AccountingAllocator allocator;
    Zone zone(&allocator, ZONE_NAME);

621
    WasmModuleBuilder* builder = zone.New<WasmModuleBuilder>(&zone);
622 623
    WasmFunctionBuilder* f = builder->AddFunction(sigs.i_v());
    ExportAsMain(f);
624
    byte code[] = {WASM_MEMORY_GROW(WASM_I32V_1(1)),
625 626
                   WASM_STORE_MEM(MachineType::Int32(), WASM_I32V(index),
                                  WASM_I32V(value))};
627
    EMIT_CODE_WITH_END(f, code);
628 629 630
    TestModuleException(&zone, builder);
  }
  Cleanup();
631 632 633
}

TEST(Run_WasmModule_GrowMemOobFixedIndex) {
634 635
  {
    static const int kPageSize = 0x10000;
636
    // Initial memory size = 16 + MemoryGrow(10)
637
    static const int index = kPageSize * 26 + 4;
638
    int value = 0xACED;
639 640 641
    TestSignatures sigs;
    Isolate* isolate = CcTest::InitIsolateOnce();
    Zone zone(isolate->allocator(), ZONE_NAME);
642

643
    WasmModuleBuilder* builder = zone.New<WasmModuleBuilder>(&zone);
644 645
    WasmFunctionBuilder* f = builder->AddFunction(sigs.i_i());
    ExportAsMain(f);
646
    byte code[] = {WASM_MEMORY_GROW(WASM_LOCAL_GET(0)), WASM_DROP,
647 648 649
                   WASM_STORE_MEM(MachineType::Int32(), WASM_I32V(index),
                                  WASM_I32V(value)),
                   WASM_LOAD_MEM(MachineType::Int32(), WASM_I32V(index))};
650
    EMIT_CODE_WITH_END(f, code);
651

652 653
    HandleScope scope(isolate);
    ZoneBuffer buffer(&zone);
654
    builder->WriteTo(&buffer);
655
    testing::SetupIsolateForWasmModule(isolate);
656

657
    ErrorThrower thrower(isolate, "Test");
658
    Handle<WasmInstanceObject> instance =
659 660
        CompileAndInstantiateForTesting(
            isolate, &thrower, ModuleWireBytes(buffer.begin(), buffer.end()))
661
            .ToHandleChecked();
662 663 664 665 666 667

    // Initial memory size is 16 pages, should trap till index > MemSize on
    // consecutive GrowMem calls
    for (uint32_t i = 1; i < 5; i++) {
      Handle<Object> params[1] = {Handle<Object>(Smi::FromInt(i), isolate)};
      v8::TryCatch try_catch(reinterpret_cast<v8::Isolate*>(isolate));
668
      testing::CallWasmFunctionForTesting(isolate, instance, "main", 1, params);
669 670 671
      CHECK(try_catch.HasCaught());
      isolate->clear_pending_exception();
    }
672

673
    Handle<Object> params[1] = {Handle<Object>(Smi::FromInt(1), isolate)};
674 675
    int32_t result = testing::CallWasmFunctionForTesting(isolate, instance,
                                                         "main", 1, params);
676
    CHECK_EQ(0xACED, result);
677
  }
678
  Cleanup();
679 680 681
}

TEST(Run_WasmModule_GrowMemOobVariableIndex) {
682 683
  {
    static const int kPageSize = 0x10000;
684
    int value = 0xACED;
685 686 687 688
    TestSignatures sigs;
    Isolate* isolate = CcTest::InitIsolateOnce();
    v8::internal::AccountingAllocator allocator;
    Zone zone(&allocator, ZONE_NAME);
689

690
    WasmModuleBuilder* builder = zone.New<WasmModuleBuilder>(&zone);
691 692
    WasmFunctionBuilder* f = builder->AddFunction(sigs.i_i());
    ExportAsMain(f);
693
    byte code[] = {WASM_MEMORY_GROW(WASM_I32V_1(1)), WASM_DROP,
694
                   WASM_STORE_MEM(MachineType::Int32(), WASM_LOCAL_GET(0),
695
                                  WASM_I32V(value)),
696
                   WASM_LOAD_MEM(MachineType::Int32(), WASM_LOCAL_GET(0))};
697
    EMIT_CODE_WITH_END(f, code);
698

699 700
    HandleScope scope(isolate);
    ZoneBuffer buffer(&zone);
701
    builder->WriteTo(&buffer);
702
    testing::SetupIsolateForWasmModule(isolate);
703

704
    ErrorThrower thrower(isolate, "Test");
705
    Handle<WasmInstanceObject> instance =
706 707
        CompileAndInstantiateForTesting(
            isolate, &thrower, ModuleWireBytes(buffer.begin(), buffer.end()))
708
            .ToHandleChecked();
709 710 711 712 713 714 715

    // Initial memory size is 16 pages, should trap till index > MemSize on
    // consecutive GrowMem calls
    for (int i = 1; i < 5; i++) {
      Handle<Object> params[1] = {
          Handle<Object>(Smi::FromInt((16 + i) * kPageSize - 3), isolate)};
      v8::TryCatch try_catch(reinterpret_cast<v8::Isolate*>(isolate));
716
      testing::CallWasmFunctionForTesting(isolate, instance, "main", 1, params);
717 718 719 720 721 722 723
      CHECK(try_catch.HasCaught());
      isolate->clear_pending_exception();
    }

    for (int i = 1; i < 5; i++) {
      Handle<Object> params[1] = {
          Handle<Object>(Smi::FromInt((20 + i) * kPageSize - 4), isolate)};
724 725
      int32_t result = testing::CallWasmFunctionForTesting(isolate, instance,
                                                           "main", 1, params);
726
      CHECK_EQ(0xACED, result);
727
    }
728 729

    v8::TryCatch try_catch(reinterpret_cast<v8::Isolate*>(isolate));
730 731
    Handle<Object> params[1] = {
        Handle<Object>(Smi::FromInt(25 * kPageSize), isolate)};
732
    testing::CallWasmFunctionForTesting(isolate, instance, "main", 1, params);
733 734 735
    CHECK(try_catch.HasCaught());
    isolate->clear_pending_exception();
  }
736
  Cleanup();
737
}
738 739

TEST(Run_WasmModule_Global_init) {
740 741 742 743
  {
    v8::internal::AccountingAllocator allocator;
    Zone zone(&allocator, ZONE_NAME);
    TestSignatures sigs;
744

745
    WasmModuleBuilder* builder = zone.New<WasmModuleBuilder>(&zone);
746
    uint32_t global1 =
747
        builder->AddGlobal(kWasmI32, false, WasmInitExpr(777777));
748
    uint32_t global2 =
749
        builder->AddGlobal(kWasmI32, false, WasmInitExpr(222222));
750 751
    WasmFunctionBuilder* f1 = builder->AddFunction(sigs.i_v());
    byte code[] = {
752
        WASM_I32_ADD(WASM_GLOBAL_GET(global1), WASM_GLOBAL_GET(global2))};
753
    EMIT_CODE_WITH_END(f1, code);
754
    ExportAsMain(f1);
755
    TestModule(&zone, builder, 999999);
756
  }
757 758
  Cleanup();
}
759

760
template <typename CType>
761
static void RunWasmModuleGlobalInitTest(ValueType type, CType expected) {
762 763 764 765
  {
    v8::internal::AccountingAllocator allocator;
    Zone zone(&allocator, ZONE_NAME);
    TestSignatures sigs;
766

767
    ValueType types[] = {type};
768 769 770 771
    FunctionSig sig(1, 0, types);

    for (int padding = 0; padding < 5; padding++) {
      // Test with a simple initializer
772
      WasmModuleBuilder* builder = zone.New<WasmModuleBuilder>(&zone);
773 774

      for (int i = 0; i < padding; i++) {  // pad global before
775
        builder->AddGlobal(kWasmI32, false, WasmInitExpr(i + 20000));
776
      }
777
      uint32_t global = builder->AddGlobal(type, false, WasmInitExpr(expected));
778
      for (int i = 0; i < padding; i++) {  // pad global after
779
        builder->AddGlobal(kWasmI32, false, WasmInitExpr(i + 30000));
780 781 782
      }

      WasmFunctionBuilder* f1 = builder->AddFunction(&sig);
783
      byte code[] = {WASM_GLOBAL_GET(global)};
784
      EMIT_CODE_WITH_END(f1, code);
785 786
      ExportAsMain(f1);
      TestModule(&zone, builder, expected);
787 788
    }
  }
789
  Cleanup();
790 791 792
}

TEST(Run_WasmModule_Global_i32) {
793 794
  RunWasmModuleGlobalInitTest<int32_t>(kWasmI32, -983489);
  RunWasmModuleGlobalInitTest<int32_t>(kWasmI32, 11223344);
795 796 797
}

TEST(Run_WasmModule_Global_f32) {
798 799
  RunWasmModuleGlobalInitTest<float>(kWasmF32, -983.9f);
  RunWasmModuleGlobalInitTest<float>(kWasmF32, 1122.99f);
800 801 802
}

TEST(Run_WasmModule_Global_f64) {
803 804
  RunWasmModuleGlobalInitTest<double>(kWasmF64, -833.9);
  RunWasmModuleGlobalInitTest<double>(kWasmF64, 86374.25);
805
}
806 807 808 809 810 811 812 813 814 815

TEST(InitDataAtTheUpperLimit) {
  {
    Isolate* isolate = CcTest::InitIsolateOnce();
    HandleScope scope(isolate);
    testing::SetupIsolateForWasmModule(isolate);

    ErrorThrower thrower(isolate, "Run_WasmModule_InitDataAtTheUpperLimit");

    const byte data[] = {
816 817 818 819
        WASM_MODULE_HEADER,   // --
        kMemorySectionCode,   // --
        U32V_1(4),            // section size
        ENTRY_COUNT(1),       // --
820
        kWithMaximum,         // --
821 822 823 824 825 826
        1,                    // initial size
        2,                    // maximum size
        kDataSectionCode,     // --
        U32V_1(9),            // section size
        ENTRY_COUNT(1),       // --
        0,                    // linear memory index
827
        WASM_I32V_3(0xFFFF),  // destination offset
828 829 830 831 832
        kExprEnd,
        U32V_1(1),  // source size
        'c'         // data bytes
    };

833 834
    CompileAndInstantiateForTesting(
        isolate, &thrower, ModuleWireBytes(data, data + arraysize(data)));
835
    if (thrower.error()) {
836
      thrower.Reify()->Print();
837
      FATAL("compile or instantiate error");
838 839 840 841
    }
  }
  Cleanup();
}
842 843 844 845 846 847 848 849 850 851

TEST(EmptyMemoryNonEmptyDataSegment) {
  {
    Isolate* isolate = CcTest::InitIsolateOnce();
    HandleScope scope(isolate);
    testing::SetupIsolateForWasmModule(isolate);

    ErrorThrower thrower(isolate, "Run_WasmModule_InitDataAtTheUpperLimit");

    const byte data[] = {
852 853 854 855
        WASM_MODULE_HEADER,  // --
        kMemorySectionCode,  // --
        U32V_1(4),           // section size
        ENTRY_COUNT(1),      // --
856
        kWithMaximum,        // --
857 858 859 860 861 862 863
        0,                   // initial size
        0,                   // maximum size
        kDataSectionCode,    // --
        U32V_1(7),           // section size
        ENTRY_COUNT(1),      // --
        0,                   // linear memory index
        WASM_I32V_1(8),      // destination offset
864 865 866 867 868
        kExprEnd,
        U32V_1(1),  // source size
        'c'         // data bytes
    };

869 870
    CompileAndInstantiateForTesting(
        isolate, &thrower, ModuleWireBytes(data, data + arraysize(data)));
871 872 873 874 875 876 877 878 879 880 881 882 883 884 885
    // It should not be possible to instantiate this module.
    CHECK(thrower.error());
  }
  Cleanup();
}

TEST(EmptyMemoryEmptyDataSegment) {
  {
    Isolate* isolate = CcTest::InitIsolateOnce();
    HandleScope scope(isolate);
    testing::SetupIsolateForWasmModule(isolate);

    ErrorThrower thrower(isolate, "Run_WasmModule_InitDataAtTheUpperLimit");

    const byte data[] = {
886 887 888 889
        WASM_MODULE_HEADER,  // --
        kMemorySectionCode,  // --
        U32V_1(4),           // section size
        ENTRY_COUNT(1),      // --
890
        kWithMaximum,        // --
891 892 893 894 895 896 897
        0,                   // initial size
        0,                   // maximum size
        kDataSectionCode,    // --
        U32V_1(6),           // section size
        ENTRY_COUNT(1),      // --
        0,                   // linear memory index
        WASM_I32V_1(0),      // destination offset
898 899 900 901
        kExprEnd,
        U32V_1(0),  // source size
    };

902 903
    CompileAndInstantiateForTesting(
        isolate, &thrower, ModuleWireBytes(data, data + arraysize(data)));
904 905 906 907 908 909
    // It should be possible to instantiate this module.
    CHECK(!thrower.error());
  }
  Cleanup();
}

910 911
#undef EMIT_CODE_WITH_END

912
}  // namespace test_run_wasm_module
913 914 915
}  // namespace wasm
}  // namespace internal
}  // namespace v8