runtime-wasm.cc 28.5 KB
Newer Older
1 2 3 4
// 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.

5
#include "src/base/memory.h"
6
#include "src/common/assert-scope.h"
7
#include "src/common/message-template.h"
8
#include "src/compiler/wasm-compiler.h"
9
#include "src/debug/debug.h"
10 11
#include "src/execution/arguments-inl.h"
#include "src/execution/frame-constants.h"
12
#include "src/execution/frames.h"
13
#include "src/heap/factory.h"
14
#include "src/logging/counters.h"
15
#include "src/numbers/conversions.h"
16
#include "src/objects/objects-inl.h"
17
#include "src/runtime/runtime-utils.h"
eholk's avatar
eholk committed
18
#include "src/trap-handler/trap-handler.h"
19
#include "src/wasm/module-compiler.h"
20
#include "src/wasm/stacks.h"
21
#include "src/wasm/value-type.h"
22
#include "src/wasm/wasm-code-manager.h"
23
#include "src/wasm/wasm-constants.h"
24
#include "src/wasm/wasm-debug.h"
25
#include "src/wasm/wasm-engine.h"
26
#include "src/wasm/wasm-objects.h"
27
#include "src/wasm/wasm-subtyping.h"
28
#include "src/wasm/wasm-value.h"
29 30 31 32

namespace v8 {
namespace internal {

33
namespace {
34

35
template <typename FrameType>
36 37
class FrameFinder {
 public:
38 39 40
  explicit FrameFinder(Isolate* isolate,
                       std::initializer_list<StackFrame::Type>
                           skipped_frame_types = {StackFrame::EXIT})
41
      : frame_iterator_(isolate, isolate->thread_local_top()) {
42 43 44 45
    // We skip at least one frame.
    DCHECK_LT(0, skipped_frame_types.size());

    for (auto type : skipped_frame_types) {
46 47 48 49 50 51 52 53 54 55 56 57 58 59
      DCHECK_EQ(type, frame_iterator_.frame()->type());
      USE(type);
      frame_iterator_.Advance();
    }
    // Type check the frame where the iterator stopped now.
    DCHECK_NOT_NULL(frame());
  }

  FrameType* frame() { return FrameType::cast(frame_iterator_.frame()); }

 private:
  StackFrameIterator frame_iterator_;
};

60
WasmInstanceObject GetWasmInstanceOnStackTop(Isolate* isolate) {
61
  return FrameFinder<WasmFrame>(isolate).frame()->wasm_instance();
62 63
}

64
Context GetNativeContextFromWasmInstanceOnStackTop(Isolate* isolate) {
65
  return GetWasmInstanceOnStackTop(isolate).native_context();
66
}
67

68
class V8_NODISCARD ClearThreadInWasmScope {
69
 public:
70
  explicit ClearThreadInWasmScope(Isolate* isolate) : isolate_(isolate) {
71 72
    DCHECK_IMPLIES(trap_handler::IsTrapHandlerEnabled(),
                   trap_handler::IsThreadInWasm());
73
    trap_handler::ClearThreadInWasm();
74 75
  }
  ~ClearThreadInWasmScope() {
76 77
    DCHECK_IMPLIES(trap_handler::IsTrapHandlerEnabled(),
                   !trap_handler::IsThreadInWasm());
78 79 80 81 82
    if (!isolate_->has_pending_exception()) {
      trap_handler::SetThreadInWasm();
    }
    // Otherwise we only want to set the flag if the exception is caught in
    // wasm. This is handled by the unwinder.
83
  }
84 85 86

 private:
  Isolate* isolate_;
87 88
};

89
Object ThrowWasmError(Isolate* isolate, MessageTemplate message) {
90 91 92 93
  Handle<JSObject> error_obj = isolate->factory()->NewWasmRuntimeError(message);
  JSObject::AddProperty(isolate, error_obj,
                        isolate->factory()->wasm_uncatchable_symbol(),
                        isolate->factory()->true_value(), NONE);
94 95
  return isolate->Throw(*error_obj);
}
96 97
}  // namespace

98
RUNTIME_FUNCTION(Runtime_WasmIsValidRefValue) {
99
  // This code is called from wrappers, so the "thread is wasm" flag is not set.
100 101
  DCHECK_IMPLIES(trap_handler::IsTrapHandlerEnabled(),
                 !trap_handler::IsThreadInWasm());
102
  HandleScope scope(isolate);
103
  DCHECK_EQ(3, args.length());
104 105
  // 'raw_instance' can be either a WasmInstanceObject or undefined.
  CONVERT_ARG_HANDLE_CHECKED(Object, raw_instance, 0)
106 107 108 109
  CONVERT_ARG_HANDLE_CHECKED(Object, value, 1);
  // Make sure ValueType fits properly in a Smi.
  STATIC_ASSERT(wasm::ValueType::kLastUsedBit + 1 <= kSmiValueSize);
  CONVERT_SMI_ARG_CHECKED(raw_type, 2);
110

111 112 113 114 115
  const wasm::WasmModule* module =
      raw_instance->IsWasmInstanceObject()
          ? Handle<WasmInstanceObject>::cast(raw_instance)->module()
          : nullptr;

116 117 118
  wasm::ValueType type = wasm::ValueType::FromRawBitField(raw_type);
  const char* error_message;

119 120
  bool result = internal::wasm::TypecheckJSObject(isolate, module, value, type,
                                                  &error_message);
121
  return Smi::FromInt(result);
122 123
}

124
RUNTIME_FUNCTION(Runtime_WasmMemoryGrow) {
125
  ClearThreadInWasmScope flag_scope(isolate);
126
  HandleScope scope(isolate);
127 128
  DCHECK_EQ(2, args.length());
  CONVERT_ARG_HANDLE_CHECKED(WasmInstanceObject, instance, 0);
129
  // {delta_pages} is checked to be a positive smi in the WasmMemoryGrow builtin
130 131
  // which calls this runtime function.
  CONVERT_UINT32_ARG_CHECKED(delta_pages, 1);
132

133 134
  int ret = WasmMemoryObject::Grow(
      isolate, handle(instance->memory_object(), isolate), delta_pages);
135
  // The WasmMemoryGrow builtin which calls this runtime function expects us to
136
  // always return a Smi.
137
  DCHECK(!isolate->has_pending_exception());
138
  return Smi::FromInt(ret);
139
}
140

141
RUNTIME_FUNCTION(Runtime_ThrowWasmError) {
142
  ClearThreadInWasmScope flag_scope(isolate);
143
  HandleScope scope(isolate);
144 145
  DCHECK_EQ(1, args.length());
  CONVERT_SMI_ARG_CHECKED(message_id, 0);
146
  return ThrowWasmError(isolate, MessageTemplateFromInt(message_id));
147 148
}

149
RUNTIME_FUNCTION(Runtime_ThrowWasmStackOverflow) {
150
  ClearThreadInWasmScope clear_wasm_flag(isolate);
151 152 153 154 155
  SealHandleScope shs(isolate);
  DCHECK_LE(0, args.length());
  return isolate->StackOverflow();
}

156
RUNTIME_FUNCTION(Runtime_WasmThrowJSTypeError) {
157 158 159 160 161
  // The caller may be wasm or JS. Only clear the thread_in_wasm flag if the
  // caller is wasm, and let the unwinder set it back depending on the handler.
  if (trap_handler::IsTrapHandlerEnabled() && trap_handler::IsThreadInWasm()) {
    trap_handler::ClearThreadInWasm();
  }
162 163 164
  HandleScope scope(isolate);
  DCHECK_EQ(0, args.length());
  THROW_NEW_ERROR_RETURN_FAILURE(
165
      isolate, NewTypeError(MessageTemplate::kWasmTrapJSTypeError));
166
}
jpp's avatar
jpp committed
167

168
RUNTIME_FUNCTION(Runtime_WasmThrow) {
169
  ClearThreadInWasmScope clear_wasm_flag(isolate);
170
  HandleScope scope(isolate);
171
  DCHECK_EQ(2, args.length());
172 173
  isolate->set_context(GetNativeContextFromWasmInstanceOnStackTop(isolate));

174 175 176 177 178
  CONVERT_ARG_CHECKED(WasmExceptionTag, tag_raw, 0);
  CONVERT_ARG_CHECKED(FixedArray, values_raw, 1);
  // TODO(wasm): Manually box because parameters are not visited yet.
  Handle<WasmExceptionTag> tag(tag_raw, isolate);
  Handle<FixedArray> values(values_raw, isolate);
179 180
  Handle<WasmExceptionPackage> exception =
      WasmExceptionPackage::New(isolate, tag, values);
181
  wasm::GetWasmEngine()->SampleThrowEvent(isolate);
182
  return isolate->Throw(*exception);
183 184 185
}

RUNTIME_FUNCTION(Runtime_WasmReThrow) {
186
  ClearThreadInWasmScope clear_wasm_flag(isolate);
187 188
  HandleScope scope(isolate);
  DCHECK_EQ(1, args.length());
189
  wasm::GetWasmEngine()->SampleRethrowEvent(isolate);
190 191 192
  return isolate->ReThrow(args[0]);
}

193
RUNTIME_FUNCTION(Runtime_WasmStackGuard) {
194
  ClearThreadInWasmScope wasm_flag(isolate);
195 196 197 198 199 200 201 202 203 204
  SealHandleScope shs(isolate);
  DCHECK_EQ(0, args.length());

  // Check if this is a real stack overflow.
  StackLimitCheck check(isolate);
  if (check.JsHasOverflowed()) return isolate->StackOverflow();

  return isolate->stack_guard()->HandleInterrupts();
}

205
RUNTIME_FUNCTION(Runtime_WasmCompileLazy) {
206
  ClearThreadInWasmScope wasm_flag(isolate);
207
  HandleScope scope(isolate);
208
  DCHECK_EQ(2, args.length());
209
  CONVERT_ARG_HANDLE_CHECKED(WasmInstanceObject, instance, 0);
210
  CONVERT_SMI_ARG_CHECKED(func_index, 1);
211

212
#ifdef DEBUG
213
  FrameFinder<WasmCompileLazyFrame> frame_finder(isolate);
214
  DCHECK_EQ(*instance, frame_finder.frame()->wasm_instance());
215 216
#endif

217 218
  DCHECK(isolate->context().is_null());
  isolate->set_context(instance->native_context());
219
  bool success = wasm::CompileLazy(isolate, instance, func_index);
220 221 222 223
  if (!success) {
    DCHECK(isolate->has_pending_exception());
    return ReadOnlyRoots(isolate).exception();
  }
224

225
  Address entrypoint =
226 227
      instance->module_object().native_module()->GetCallTargetForFunction(
          func_index);
228

229
  return Object(entrypoint);
230 231
}

232 233
namespace {
void ReplaceWrapper(Isolate* isolate, Handle<WasmInstanceObject> instance,
234
                    int function_index, Handle<CodeT> wrapper_code) {
235 236
  Handle<WasmInternalFunction> internal =
      WasmInstanceObject::GetWasmInternalFunction(isolate, instance,
237 238
                                                  function_index)
          .ToHandleChecked();
239 240
  Handle<WasmExternalFunction> exported_function =
      handle(WasmExternalFunction::cast(internal->external()), isolate);
241
  exported_function->set_code(*wrapper_code, kReleaseStore);
242 243 244 245 246 247
  WasmExportedFunctionData function_data =
      exported_function->shared().wasm_exported_function_data();
  function_data.set_wrapper_code(*wrapper_code);
}
}  // namespace

248 249 250 251 252 253 254 255 256 257
RUNTIME_FUNCTION(Runtime_WasmCompileWrapper) {
  HandleScope scope(isolate);
  DCHECK_EQ(2, args.length());
  CONVERT_ARG_HANDLE_CHECKED(WasmInstanceObject, instance, 0);
  CONVERT_ARG_HANDLE_CHECKED(WasmExportedFunctionData, function_data, 1);
  DCHECK(isolate->context().is_null());
  isolate->set_context(instance->native_context());

  const wasm::WasmModule* module = instance->module();
  const int function_index = function_data->function_index();
258
  const wasm::WasmFunction& function = module->functions[function_index];
259 260
  const wasm::FunctionSig* sig = function.sig;

261 262 263 264
  // The start function is not guaranteed to be registered as
  // an exported function (although it is called as one).
  // If there is no entry for the start function,
  // the tier-up is abandoned.
265 266 267
  if (WasmInstanceObject::GetWasmInternalFunction(isolate, instance,
                                                  function_index)
          .is_null()) {
268 269 270 271
    DCHECK_EQ(function_index, module->start_function_index);
    return ReadOnlyRoots(isolate).undefined_value();
  }

272
  Handle<CodeT> wrapper_code = ToCodeT(
273
      wasm::JSToWasmWrapperCompilationUnit::CompileSpecificJSToWasmWrapper(
274 275
          isolate, sig, module),
      isolate);
276

277 278 279 280
  // Replace the wrapper for the function that triggered the tier-up.
  // This is to verify that the wrapper is replaced, even if the function
  // is implicitly exported and is not part of the export_table.
  ReplaceWrapper(isolate, instance, function_index, wrapper_code);
281

282 283 284 285 286 287 288
  // Iterate over all exports to replace eagerly the wrapper for all functions
  // that share the signature of the function that tiered up.
  for (wasm::WasmExport exp : module->export_table) {
    if (exp.kind != wasm::kExternalFunction) {
      continue;
    }
    int index = static_cast<int>(exp.index);
289 290
    const wasm::WasmFunction& exp_function = module->functions[index];
    if (exp_function.sig == sig && index != function_index) {
291 292 293
      ReplaceWrapper(isolate, instance, index, wrapper_code);
    }
  }
294

295 296 297
  return ReadOnlyRoots(isolate).undefined_value();
}

298
RUNTIME_FUNCTION(Runtime_WasmTriggerTierUp) {
299
  ClearThreadInWasmScope clear_wasm_flag(isolate);
300 301 302 303
  HandleScope scope(isolate);
  DCHECK_EQ(1, args.length());
  CONVERT_ARG_HANDLE_CHECKED(WasmInstanceObject, instance, 0);

304 305 306 307 308 309
  // We're reusing this interrupt mechanism to interrupt long-running loops.
  StackLimitCheck check(isolate);
  DCHECK(!check.JsHasOverflowed());
  if (check.InterruptRequested()) {
    Object result = isolate->stack_guard()->HandleInterrupts();
    if (result.IsException()) return result;
310 311
  }

312
  FrameFinder<WasmFrame> frame_finder(isolate);
313 314 315
  int func_index = frame_finder.frame()->function_index();
  auto* native_module = instance->module_object().native_module();

316
  wasm::TriggerTierUp(isolate, native_module, func_index, instance);
317 318 319 320

  return ReadOnlyRoots(isolate).undefined_value();
}

321
RUNTIME_FUNCTION(Runtime_WasmAtomicNotify) {
322
  ClearThreadInWasmScope clear_wasm_flag(isolate);
323 324 325
  HandleScope scope(isolate);
  DCHECK_EQ(3, args.length());
  CONVERT_ARG_HANDLE_CHECKED(WasmInstanceObject, instance, 0);
326 327
  CONVERT_DOUBLE_ARG_CHECKED(offset_double, 1);
  uintptr_t offset = static_cast<uintptr_t>(offset_double);
328
  CONVERT_NUMBER_CHECKED(uint32_t, count, Uint32, args[2]);
329 330 331 332 333 334
  Handle<JSArrayBuffer> array_buffer{instance->memory_object().array_buffer(),
                                     isolate};
  // Should have trapped if address was OOB.
  DCHECK_LT(offset, array_buffer->byte_length());
  if (!array_buffer->is_shared()) return Smi::FromInt(0);
  return FutexEmulation::Wake(array_buffer, offset, count);
335 336
}

337
RUNTIME_FUNCTION(Runtime_WasmI32AtomicWait) {
338
  ClearThreadInWasmScope clear_wasm_flag(isolate);
339 340 341
  HandleScope scope(isolate);
  DCHECK_EQ(4, args.length());
  CONVERT_ARG_HANDLE_CHECKED(WasmInstanceObject, instance, 0);
342 343
  CONVERT_DOUBLE_ARG_CHECKED(offset_double, 1);
  uintptr_t offset = static_cast<uintptr_t>(offset_double);
344
  CONVERT_NUMBER_CHECKED(int32_t, expected_value, Int32, args[2]);
345 346
  CONVERT_ARG_HANDLE_CHECKED(BigInt, timeout_ns, 3);

347 348 349 350
  Handle<JSArrayBuffer> array_buffer{instance->memory_object().array_buffer(),
                                     isolate};
  // Should have trapped if address was OOB.
  DCHECK_LT(offset, array_buffer->byte_length());
351

352 353
  // Trap if memory is not shared, or wait is not allowed on the isolate
  if (!array_buffer->is_shared() || !isolate->allow_atomics_wait()) {
354 355
    return ThrowWasmError(isolate, MessageTemplate::kAtomicsWaitNotAllowed);
  }
356
  return FutexEmulation::WaitWasm32(isolate, array_buffer, offset,
357
                                    expected_value, timeout_ns->AsInt64());
358 359 360
}

RUNTIME_FUNCTION(Runtime_WasmI64AtomicWait) {
361
  ClearThreadInWasmScope clear_wasm_flag(isolate);
362
  HandleScope scope(isolate);
363
  DCHECK_EQ(4, args.length());
364
  CONVERT_ARG_HANDLE_CHECKED(WasmInstanceObject, instance, 0);
365 366
  CONVERT_DOUBLE_ARG_CHECKED(offset_double, 1);
  uintptr_t offset = static_cast<uintptr_t>(offset_double);
367 368 369
  CONVERT_ARG_HANDLE_CHECKED(BigInt, expected_value, 2);
  CONVERT_ARG_HANDLE_CHECKED(BigInt, timeout_ns, 3);

370 371 372 373
  Handle<JSArrayBuffer> array_buffer{instance->memory_object().array_buffer(),
                                     isolate};
  // Should have trapped if address was OOB.
  DCHECK_LT(offset, array_buffer->byte_length());
374

375 376
  // Trap if memory is not shared, or if wait is not allowed on the isolate
  if (!array_buffer->is_shared() || !isolate->allow_atomics_wait()) {
377 378
    return ThrowWasmError(isolate, MessageTemplate::kAtomicsWaitNotAllowed);
  }
379
  return FutexEmulation::WaitWasm64(isolate, array_buffer, offset,
380 381
                                    expected_value->AsInt64(),
                                    timeout_ns->AsInt64());
382 383
}

384 385 386 387 388 389 390 391
namespace {
Object ThrowTableOutOfBounds(Isolate* isolate,
                             Handle<WasmInstanceObject> instance) {
  // Handle out-of-bounds access here in the runtime call, rather
  // than having the lower-level layers deal with JS exceptions.
  if (isolate->context().is_null()) {
    isolate->set_context(instance->native_context());
  }
392
  return ThrowWasmError(isolate, MessageTemplate::kWasmTrapTableOutOfBounds);
393 394 395
}
}  // namespace

396
RUNTIME_FUNCTION(Runtime_WasmRefFunc) {
397
  ClearThreadInWasmScope flag_scope(isolate);
398
  HandleScope scope(isolate);
399 400 401
  DCHECK_EQ(2, args.length());
  CONVERT_ARG_HANDLE_CHECKED(WasmInstanceObject, instance, 0);
  CONVERT_UINT32_ARG_CHECKED(function_index, 1);
402

403 404
  return *WasmInstanceObject::GetOrCreateWasmInternalFunction(isolate, instance,
                                                              function_index);
405 406
}

407
RUNTIME_FUNCTION(Runtime_WasmFunctionTableGet) {
408
  ClearThreadInWasmScope flag_scope(isolate);
409 410 411 412 413
  HandleScope scope(isolate);
  DCHECK_EQ(3, args.length());
  CONVERT_ARG_HANDLE_CHECKED(WasmInstanceObject, instance, 0);
  CONVERT_UINT32_ARG_CHECKED(table_index, 1);
  CONVERT_UINT32_ARG_CHECKED(entry_index, 2);
414
  DCHECK_LT(table_index, instance->tables().length());
415
  auto table = handle(
416
      WasmTableObject::cast(instance->tables().get(table_index)), isolate);
417
  // We only use the runtime call for lazily initialized function references.
418 419 420 421 422
  DCHECK(
      table->instance().IsUndefined()
          ? table->type() == wasm::kWasmFuncRef
          : IsSubtypeOf(table->type(), wasm::kWasmFuncRef,
                        WasmInstanceObject::cast(table->instance()).module()));
423 424 425 426 427 428 429 430 431

  if (!WasmTableObject::IsInBounds(isolate, table, entry_index)) {
    return ThrowWasmError(isolate, MessageTemplate::kWasmTrapTableOutOfBounds);
  }

  return *WasmTableObject::Get(isolate, table, entry_index);
}

RUNTIME_FUNCTION(Runtime_WasmFunctionTableSet) {
432
  ClearThreadInWasmScope flag_scope(isolate);
433 434 435 436 437 438
  HandleScope scope(isolate);
  DCHECK_EQ(4, args.length());
  CONVERT_ARG_HANDLE_CHECKED(WasmInstanceObject, instance, 0);
  CONVERT_UINT32_ARG_CHECKED(table_index, 1);
  CONVERT_UINT32_ARG_CHECKED(entry_index, 2);
  CONVERT_ARG_CHECKED(Object, element_raw, 3);
439
  // TODO(wasm): Manually box because parameters are not visited yet.
440
  Handle<Object> element(element_raw, isolate);
441
  DCHECK_LT(table_index, instance->tables().length());
442
  auto table = handle(
443
      WasmTableObject::cast(instance->tables().get(table_index)), isolate);
444
  // We only use the runtime call for function references.
445 446 447 448 449
  DCHECK(
      table->instance().IsUndefined()
          ? table->type() == wasm::kWasmFuncRef
          : IsSubtypeOf(table->type(), wasm::kWasmFuncRef,
                        WasmInstanceObject::cast(table->instance()).module()));
450 451 452 453 454 455 456 457

  if (!WasmTableObject::IsInBounds(isolate, table, entry_index)) {
    return ThrowWasmError(isolate, MessageTemplate::kWasmTrapTableOutOfBounds);
  }
  WasmTableObject::Set(isolate, table, entry_index, element);
  return ReadOnlyRoots(isolate).undefined_value();
}

458
RUNTIME_FUNCTION(Runtime_WasmTableInit) {
459
  ClearThreadInWasmScope flag_scope(isolate);
460
  HandleScope scope(isolate);
461 462 463 464
  DCHECK_EQ(6, args.length());
  CONVERT_ARG_HANDLE_CHECKED(WasmInstanceObject, instance, 0);
  CONVERT_UINT32_ARG_CHECKED(table_index, 1);
  CONVERT_UINT32_ARG_CHECKED(elem_segment_index, 2);
465 466 467
  static_assert(
      wasm::kV8MaxWasmTableSize < kSmiMaxValue,
      "Make sure clamping to Smi range doesn't make an invalid call valid");
468 469 470
  CONVERT_UINT32_ARG_CHECKED(dst, 3);
  CONVERT_UINT32_ARG_CHECKED(src, 4);
  CONVERT_UINT32_ARG_CHECKED(count, 5);
471

472
  DCHECK(!isolate->context().is_null());
473

474 475 476 477
  bool oob = !WasmInstanceObject::InitTableEntries(
      isolate, instance, table_index, elem_segment_index, dst, src, count);
  if (oob) return ThrowTableOutOfBounds(isolate, instance);
  return ReadOnlyRoots(isolate).undefined_value();
478 479 480
}

RUNTIME_FUNCTION(Runtime_WasmTableCopy) {
481
  ClearThreadInWasmScope flag_scope(isolate);
482
  HandleScope scope(isolate);
483 484 485 486
  DCHECK_EQ(6, args.length());
  CONVERT_ARG_HANDLE_CHECKED(WasmInstanceObject, instance, 0);
  CONVERT_UINT32_ARG_CHECKED(table_dst_index, 1);
  CONVERT_UINT32_ARG_CHECKED(table_src_index, 2);
487 488 489
  static_assert(
      wasm::kV8MaxWasmTableSize < kSmiMaxValue,
      "Make sure clamping to Smi range doesn't make an invalid call valid");
490 491 492 493 494
  CONVERT_UINT32_ARG_CHECKED(dst, 3);
  CONVERT_UINT32_ARG_CHECKED(src, 4);
  CONVERT_UINT32_ARG_CHECKED(count, 5);

  DCHECK(!isolate->context().is_null());
495 496

  bool oob = !WasmInstanceObject::CopyTableEntries(
497
      isolate, instance, table_dst_index, table_src_index, dst, src, count);
498
  if (oob) return ThrowTableOutOfBounds(isolate, instance);
499
  return ReadOnlyRoots(isolate).undefined_value();
500
}
501 502

RUNTIME_FUNCTION(Runtime_WasmTableGrow) {
503
  ClearThreadInWasmScope flag_scope(isolate);
504
  HandleScope scope(isolate);
505 506 507 508
  DCHECK_EQ(4, args.length());
  CONVERT_ARG_HANDLE_CHECKED(WasmInstanceObject, instance, 0);
  CONVERT_UINT32_ARG_CHECKED(table_index, 1);
  CONVERT_ARG_CHECKED(Object, value_raw, 2);
509
  // TODO(wasm): Manually box because parameters are not visited yet.
510
  Handle<Object> value(value_raw, isolate);
511
  CONVERT_UINT32_ARG_CHECKED(delta, 3);
512 513

  Handle<WasmTableObject> table(
514
      WasmTableObject::cast(instance->tables().get(table_index)), isolate);
515 516 517 518
  int result = WasmTableObject::Grow(isolate, table, delta, value);

  return Smi::FromInt(result);
}
519 520

RUNTIME_FUNCTION(Runtime_WasmTableFill) {
521
  ClearThreadInWasmScope flag_scope(isolate);
522
  HandleScope scope(isolate);
523 524 525 526 527
  DCHECK_EQ(5, args.length());
  CONVERT_ARG_HANDLE_CHECKED(WasmInstanceObject, instance, 0);
  CONVERT_UINT32_ARG_CHECKED(table_index, 1);
  CONVERT_UINT32_ARG_CHECKED(start, 2);
  CONVERT_ARG_CHECKED(Object, value_raw, 3);
528
  // TODO(wasm): Manually box because parameters are not visited yet.
529
  Handle<Object> value(value_raw, isolate);
530
  CONVERT_UINT32_ARG_CHECKED(count, 4);
531 532

  Handle<WasmTableObject> table(
533
      WasmTableObject::cast(instance->tables().get(table_index)), isolate);
534

535
  uint32_t table_size = table->current_length();
536

537
  if (start > table_size) {
538 539 540 541 542 543 544 545 546
    return ThrowTableOutOfBounds(isolate, instance);
  }

  // Even when table.fill goes out-of-bounds, as many entries as possible are
  // put into the table. Only afterwards we trap.
  uint32_t fill_count = std::min(count, table_size - start);
  if (fill_count < count) {
    return ThrowTableOutOfBounds(isolate, instance);
  }
547 548
  WasmTableObject::Fill(isolate, table, start, value, fill_count);

549 550
  return ReadOnlyRoots(isolate).undefined_value();
}
551

552
RUNTIME_FUNCTION(Runtime_WasmDebugBreak) {
553
  ClearThreadInWasmScope flag_scope(isolate);
554 555
  HandleScope scope(isolate);
  DCHECK_EQ(0, args.length());
556 557
  FrameFinder<WasmFrame> frame_finder(
      isolate, {StackFrame::EXIT, StackFrame::WASM_DEBUG_BREAK});
558
  WasmFrame* frame = frame_finder.frame();
559 560 561
  auto instance = handle(frame->wasm_instance(), isolate);
  auto script = handle(instance->module_object().script(), isolate);
  auto* debug_info = instance->module_object().native_module()->GetDebugInfo();
562
  isolate->set_context(instance->native_context());
563

564 565 566
  // Stepping can repeatedly create code, and code GC requires stack guards to
  // be executed on all involved isolates. Proactively do this here.
  StackLimitCheck check(isolate);
567 568 569 570 571 572 573
  if (check.InterruptRequested()) {
    Object interrupt_object = isolate->stack_guard()->HandleInterrupts();
    // Interrupt handling can create an exception, including the
    // termination exception.
    if (interrupt_object.IsException(isolate)) return interrupt_object;
    DCHECK(interrupt_object.IsUndefined(isolate));
  }
574

575 576 577
  // Enter the debugger.
  DebugScope debug_scope(isolate->debug());

578
  // Check for instrumentation breakpoint.
579
  DCHECK_EQ(script->break_on_entry(), !!instance->break_on_entry());
580 581
  if (script->break_on_entry()) {
    MaybeHandle<FixedArray> maybe_on_entry_breakpoints =
582 583 584
        WasmScript::CheckBreakPoints(isolate, script,
                                     WasmScript::kOnEntryBreakpointPosition,
                                     frame->id());
585 586 587 588 589
    script->set_break_on_entry(false);
    // Update the "break_on_entry" flag on all live instances.
    i::WeakArrayList weak_instance_list = script->wasm_weak_instance_list();
    for (int i = 0; i < weak_instance_list.length(); ++i) {
      if (weak_instance_list.Get(i)->IsCleared()) continue;
590 591
      i::WasmInstanceObject::cast(weak_instance_list.Get(i)->GetHeapObject())
          .set_break_on_entry(false);
592 593 594 595 596 597 598 599 600 601 602 603 604
    }
    DCHECK(!instance->break_on_entry());
    Handle<FixedArray> on_entry_breakpoints;
    if (maybe_on_entry_breakpoints.ToHandle(&on_entry_breakpoints)) {
      debug_info->ClearStepping(isolate);
      StepAction step_action = isolate->debug()->last_step_action();
      isolate->debug()->ClearStepping();
      isolate->debug()->OnDebugBreak(on_entry_breakpoints, step_action);
      // Don't process regular breakpoints.
      return ReadOnlyRoots(isolate).undefined_value();
    }
  }

605
  if (debug_info->IsStepping(frame)) {
606
    debug_info->ClearStepping(isolate);
607
    StepAction step_action = isolate->debug()->last_step_action();
608
    isolate->debug()->ClearStepping();
609
    isolate->debug()->OnDebugBreak(isolate->factory()->empty_fixed_array(),
610
                                   step_action);
611
    return ReadOnlyRoots(isolate).undefined_value();
612 613
  }

614
  // Check whether we hit a breakpoint.
615
  Handle<FixedArray> breakpoints;
616 617
  if (WasmScript::CheckBreakPoints(isolate, script, frame->position(),
                                   frame->id())
618
          .ToHandle(&breakpoints)) {
619
    debug_info->ClearStepping(isolate);
620
    StepAction step_action = isolate->debug()->last_step_action();
621
    isolate->debug()->ClearStepping();
622
    if (isolate->debug()->break_points_active()) {
623
      // We hit one or several breakpoints. Notify the debug listeners.
624
      isolate->debug()->OnDebugBreak(breakpoints, step_action);
625
    }
626
    return ReadOnlyRoots(isolate).undefined_value();
627
  }
628

629 630 631 632 633
  // We did not hit a breakpoint. If we are in stepping code, but the user did
  // not request stepping, clear this (to save further calls into this runtime
  // function).
  debug_info->ClearStepping(frame);

634
  return ReadOnlyRoots(isolate).undefined_value();
635 636
}

637
RUNTIME_FUNCTION(Runtime_WasmAllocateRtt) {
638
  ClearThreadInWasmScope flag_scope(isolate);
639
  HandleScope scope(isolate);
640
  DCHECK_EQ(3, args.length());
641 642
  CONVERT_UINT32_ARG_CHECKED(type_index, 0);
  CONVERT_ARG_HANDLE_CHECKED(Map, parent, 1);
643
  CONVERT_SMI_ARG_CHECKED(raw_mode, 2);
644 645
  Handle<WasmInstanceObject> instance(GetWasmInstanceOnStackTop(isolate),
                                      isolate);
646 647
  return *wasm::AllocateSubRtt(isolate, instance, type_index, parent,
                               static_cast<WasmRttSubMode>(raw_mode));
648 649
}

650 651 652 653 654 655 656 657
namespace {
inline void* ArrayElementAddress(Handle<WasmArray> array, uint32_t index,
                                 int element_size_bytes) {
  return reinterpret_cast<void*>(array->ptr() + WasmArray::kHeaderSize -
                                 kHeapObjectTag + index * element_size_bytes);
}
}  // namespace

658
// Assumes copy ranges are in-bounds and copy length > 0.
659 660 661 662 663 664 665 666 667
RUNTIME_FUNCTION(Runtime_WasmArrayCopy) {
  ClearThreadInWasmScope flag_scope(isolate);
  HandleScope scope(isolate);
  DCHECK_EQ(5, args.length());
  CONVERT_ARG_HANDLE_CHECKED(WasmArray, dst_array, 0);
  CONVERT_UINT32_ARG_CHECKED(dst_index, 1);
  CONVERT_ARG_HANDLE_CHECKED(WasmArray, src_array, 2);
  CONVERT_UINT32_ARG_CHECKED(src_index, 3);
  CONVERT_UINT32_ARG_CHECKED(length, 4);
668
  DCHECK_GT(length, 0);
669 670
  bool overlapping_ranges =
      dst_array->ptr() == src_array->ptr() &&
671 672
      (dst_index < src_index ? dst_index + length > src_index
                             : src_index + length > dst_index);
673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697
  wasm::ValueType element_type = src_array->type()->element_type();
  if (element_type.is_reference()) {
    ObjectSlot dst_slot = dst_array->ElementSlot(dst_index);
    ObjectSlot src_slot = src_array->ElementSlot(src_index);
    if (overlapping_ranges) {
      isolate->heap()->MoveRange(*dst_array, dst_slot, src_slot, length,
                                 UPDATE_WRITE_BARRIER);
    } else {
      isolate->heap()->CopyRange(*dst_array, dst_slot, src_slot, length,
                                 UPDATE_WRITE_BARRIER);
    }
  } else {
    int element_size_bytes = element_type.element_size_bytes();
    void* dst = ArrayElementAddress(dst_array, dst_index, element_size_bytes);
    void* src = ArrayElementAddress(src_array, src_index, element_size_bytes);
    size_t copy_size = length * element_size_bytes;
    if (overlapping_ranges) {
      MemMove(dst, src, copy_size);
    } else {
      MemCopy(dst, src, copy_size);
    }
  }
  return ReadOnlyRoots(isolate).undefined_value();
}

698 699 700 701 702 703 704 705
namespace {
// Synchronize the stack limit with the active continuation for stack-switching.
// This can be done before or after changing the stack pointer itself, as long
// as we update both before the next stack check.
// {StackGuard::SetStackLimit} doesn't update the value of the jslimit if it
// contains a sentinel value, and it is also thread-safe. So if an interrupt is
// requested before, during or after this call, it will be preserved and handled
// at the next stack check.
706 707 708 709
void SyncStackLimit(Isolate* isolate) {
  DisallowGarbageCollection no_gc;
  auto continuation = WasmContinuationObject::cast(
      *isolate->roots_table().slot(RootIndex::kActiveContinuation));
710 711
  auto stack = Managed<wasm::StackMemory>::cast(continuation.stack()).get();
  uintptr_t limit = reinterpret_cast<uintptr_t>(stack->jmpbuf()->stack_limit);
712 713 714 715 716 717
  isolate->stack_guard()->SetStackLimit(limit);
}
}  // namespace

// Allocate a new continuation, and prepare for stack switching by updating the
// active continuation and setting the stack limit.
718 719 720
RUNTIME_FUNCTION(Runtime_WasmAllocateContinuation) {
  CHECK(FLAG_experimental_wasm_stack_switching);
  HandleScope scope(isolate);
721 722 723 724 725
  auto parent =
      handle(WasmContinuationObject::cast(
                 *isolate->roots_table().slot(RootIndex::kActiveContinuation)),
             isolate);
  auto target = WasmContinuationObject::New(isolate, *parent);
726 727 728
  auto target_stack =
      Managed<wasm::StackMemory>::cast(target->stack()).get().get();
  isolate->wasm_stacks()->Add(target_stack);
729 730
  isolate->roots_table().slot(RootIndex::kActiveContinuation).store(*target);
  SyncStackLimit(isolate);
731 732 733
  return *target;
}

734 735 736
// Update the stack limit after a stack switch, and preserve pending interrupts.
RUNTIME_FUNCTION(Runtime_WasmSyncStackLimit) {
  CHECK(FLAG_experimental_wasm_stack_switching);
737
  SyncStackLimit(isolate);
738 739 740
  return ReadOnlyRoots(isolate).undefined_value();
}

741 742
}  // namespace internal
}  // namespace v8