runtime-wasm.cc 26.1 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/message-template.h"
7
#include "src/compiler/wasm-compiler.h"
8
#include "src/debug/debug.h"
9 10
#include "src/execution/arguments-inl.h"
#include "src/execution/frame-constants.h"
11
#include "src/execution/frames.h"
12
#include "src/heap/factory.h"
13
#include "src/logging/counters.h"
14
#include "src/numbers/conversions.h"
15
#include "src/objects/objects-inl.h"
16
#include "src/runtime/runtime-utils.h"
eholk's avatar
eholk committed
17
#include "src/trap-handler/trap-handler.h"
18
#include "src/wasm/module-compiler.h"
19
#include "src/wasm/value-type.h"
20
#include "src/wasm/wasm-code-manager.h"
21
#include "src/wasm/wasm-constants.h"
22
#include "src/wasm/wasm-debug.h"
23
#include "src/wasm/wasm-engine.h"
24
#include "src/wasm/wasm-objects.h"
25
#include "src/wasm/wasm-subtyping.h"
26
#include "src/wasm/wasm-value.h"
27 28 29 30

namespace v8 {
namespace internal {

31
namespace {
32

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

    for (auto type : skipped_frame_types) {
44 45 46 47 48 49 50 51 52 53 54 55 56 57
      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_;
};

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

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

66
class V8_NODISCARD ClearThreadInWasmScope {
67
 public:
68
  explicit ClearThreadInWasmScope(Isolate* isolate) : isolate_(isolate) {
69 70
    DCHECK_IMPLIES(trap_handler::IsTrapHandlerEnabled(),
                   trap_handler::IsThreadInWasm());
71
    trap_handler::ClearThreadInWasm();
72 73
  }
  ~ClearThreadInWasmScope() {
74 75
    DCHECK_IMPLIES(trap_handler::IsTrapHandlerEnabled(),
                   !trap_handler::IsThreadInWasm());
76 77 78 79 80
    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.
81
  }
82 83 84

 private:
  Isolate* isolate_;
85 86
};

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

96
RUNTIME_FUNCTION(Runtime_WasmIsValidRefValue) {
97
  // This code is called from wrappers, so the "thread is wasm" flag is not set.
98 99
  DCHECK_IMPLIES(trap_handler::IsTrapHandlerEnabled(),
                 !trap_handler::IsThreadInWasm());
100
  HandleScope scope(isolate);
101 102 103 104 105 106
  DCHECK_EQ(3, args.length());
  CONVERT_ARG_HANDLE_CHECKED(WasmInstanceObject, instance, 0)
  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);
107

108 109 110
  wasm::ValueType type = wasm::ValueType::FromRawBitField(raw_type);
  const char* error_message;

111 112
  bool result = internal::wasm::TypecheckJSObject(isolate, instance->module(),
                                                  value, type, &error_message);
113
  return Smi::FromInt(result);
114 115
}

116
RUNTIME_FUNCTION(Runtime_WasmMemoryGrow) {
117
  ClearThreadInWasmScope flag_scope(isolate);
118
  HandleScope scope(isolate);
119 120
  DCHECK_EQ(2, args.length());
  CONVERT_ARG_HANDLE_CHECKED(WasmInstanceObject, instance, 0);
121
  // {delta_pages} is checked to be a positive smi in the WasmMemoryGrow builtin
122 123
  // which calls this runtime function.
  CONVERT_UINT32_ARG_CHECKED(delta_pages, 1);
124

125 126
  int ret = WasmMemoryObject::Grow(
      isolate, handle(instance->memory_object(), isolate), delta_pages);
127
  // The WasmMemoryGrow builtin which calls this runtime function expects us to
128
  // always return a Smi.
129
  DCHECK(!isolate->has_pending_exception());
130
  return Smi::FromInt(ret);
131
}
132

133
RUNTIME_FUNCTION(Runtime_ThrowWasmError) {
134
  ClearThreadInWasmScope flag_scope(isolate);
135
  HandleScope scope(isolate);
136 137
  DCHECK_EQ(1, args.length());
  CONVERT_SMI_ARG_CHECKED(message_id, 0);
138
  return ThrowWasmError(isolate, MessageTemplateFromInt(message_id));
139 140
}

141
RUNTIME_FUNCTION(Runtime_ThrowWasmStackOverflow) {
142
  ClearThreadInWasmScope clear_wasm_flag(isolate);
143 144 145 146 147
  SealHandleScope shs(isolate);
  DCHECK_LE(0, args.length());
  return isolate->StackOverflow();
}

148
RUNTIME_FUNCTION(Runtime_WasmThrowJSTypeError) {
149 150 151 152 153
  // 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();
  }
154 155 156
  HandleScope scope(isolate);
  DCHECK_EQ(0, args.length());
  THROW_NEW_ERROR_RETURN_FAILURE(
157
      isolate, NewTypeError(MessageTemplate::kWasmTrapJSTypeError));
158
}
jpp's avatar
jpp committed
159

160
RUNTIME_FUNCTION(Runtime_WasmThrow) {
161
  ClearThreadInWasmScope clear_wasm_flag(isolate);
162
  HandleScope scope(isolate);
163
  DCHECK_EQ(2, args.length());
164 165
  isolate->set_context(GetNativeContextFromWasmInstanceOnStackTop(isolate));

166 167 168 169 170
  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);
171 172
  Handle<WasmExceptionPackage> exception =
      WasmExceptionPackage::New(isolate, tag, values);
173
  wasm::GetWasmEngine()->SampleThrowEvent(isolate);
174
  return isolate->Throw(*exception);
175 176 177
}

RUNTIME_FUNCTION(Runtime_WasmReThrow) {
178
  ClearThreadInWasmScope clear_wasm_flag(isolate);
179 180
  HandleScope scope(isolate);
  DCHECK_EQ(1, args.length());
181
  wasm::GetWasmEngine()->SampleRethrowEvent(isolate);
182 183 184
  return isolate->ReThrow(args[0]);
}

185
RUNTIME_FUNCTION(Runtime_WasmStackGuard) {
186
  ClearThreadInWasmScope wasm_flag(isolate);
187 188 189 190 191 192 193 194 195 196
  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();
}

197
RUNTIME_FUNCTION(Runtime_WasmCompileLazy) {
198
  ClearThreadInWasmScope wasm_flag(isolate);
199
  HandleScope scope(isolate);
200
  DCHECK_EQ(2, args.length());
201
  CONVERT_ARG_HANDLE_CHECKED(WasmInstanceObject, instance, 0);
202
  CONVERT_SMI_ARG_CHECKED(func_index, 1);
203

204
#ifdef DEBUG
205
  FrameFinder<WasmCompileLazyFrame> frame_finder(isolate);
206
  DCHECK_EQ(*instance, frame_finder.frame()->wasm_instance());
207 208
#endif

209 210
  DCHECK(isolate->context().is_null());
  isolate->set_context(instance->native_context());
211 212
  Handle<WasmModuleObject> module_object{instance->module_object(), isolate};
  bool success = wasm::CompileLazy(isolate, module_object, func_index);
213 214 215 216
  if (!success) {
    DCHECK(isolate->has_pending_exception());
    return ReadOnlyRoots(isolate).exception();
  }
217

218 219
  Address entrypoint =
      module_object->native_module()->GetCallTargetForFunction(func_index);
220

221
  return Object(entrypoint);
222 223
}

224 225 226 227 228 229 230
namespace {
void ReplaceWrapper(Isolate* isolate, Handle<WasmInstanceObject> instance,
                    int function_index, Handle<Code> wrapper_code) {
  Handle<WasmExternalFunction> exported_function =
      WasmInstanceObject::GetWasmExternalFunction(isolate, instance,
                                                  function_index)
          .ToHandleChecked();
231
  exported_function->set_code(*wrapper_code, kReleaseStore);
232 233 234 235 236 237
  WasmExportedFunctionData function_data =
      exported_function->shared().wasm_exported_function_data();
  function_data.set_wrapper_code(*wrapper_code);
}
}  // namespace

238 239 240 241 242 243 244 245 246 247
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();
248
  const wasm::WasmFunction& function = module->functions[function_index];
249 250
  const wasm::FunctionSig* sig = function.sig;

251 252 253 254 255
  // 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.
  MaybeHandle<WasmExternalFunction> maybe_exported_function =
256 257
      WasmInstanceObject::GetWasmExternalFunction(isolate, instance,
                                                  function_index);
258 259
  Handle<WasmExternalFunction> exported_function;
  if (!maybe_exported_function.ToHandle(&exported_function)) {
260 261 262 263
    DCHECK_EQ(function_index, module->start_function_index);
    return ReadOnlyRoots(isolate).undefined_value();
  }

264
  Handle<Code> wrapper_code =
265 266 267
      wasm::JSToWasmWrapperCompilationUnit::CompileSpecificJSToWasmWrapper(
          isolate, sig, module);

268 269 270 271
  // 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);
272

273 274 275 276 277 278 279
  // 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);
280 281
    const wasm::WasmFunction& exp_function = module->functions[index];
    if (exp_function.sig == sig && index != function_index) {
282 283 284
      ReplaceWrapper(isolate, instance, index, wrapper_code);
    }
  }
285

286 287 288
  return ReadOnlyRoots(isolate).undefined_value();
}

289 290 291 292 293
RUNTIME_FUNCTION(Runtime_WasmTriggerTierUp) {
  HandleScope scope(isolate);
  DCHECK_EQ(1, args.length());
  CONVERT_ARG_HANDLE_CHECKED(WasmInstanceObject, instance, 0);

294
  FrameFinder<WasmFrame> frame_finder(isolate);
295 296 297 298 299 300 301 302
  int func_index = frame_finder.frame()->function_index();
  auto* native_module = instance->module_object().native_module();

  wasm::TriggerTierUp(isolate, native_module, func_index);

  return ReadOnlyRoots(isolate).undefined_value();
}

303
RUNTIME_FUNCTION(Runtime_WasmAtomicNotify) {
304
  ClearThreadInWasmScope clear_wasm_flag(isolate);
305 306 307
  HandleScope scope(isolate);
  DCHECK_EQ(3, args.length());
  CONVERT_ARG_HANDLE_CHECKED(WasmInstanceObject, instance, 0);
308 309
  CONVERT_DOUBLE_ARG_CHECKED(offset_double, 1);
  uintptr_t offset = static_cast<uintptr_t>(offset_double);
310
  CONVERT_NUMBER_CHECKED(uint32_t, count, Uint32, args[2]);
311 312 313 314 315 316
  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);
317 318
}

319
RUNTIME_FUNCTION(Runtime_WasmI32AtomicWait) {
320
  ClearThreadInWasmScope clear_wasm_flag(isolate);
321 322 323
  HandleScope scope(isolate);
  DCHECK_EQ(4, args.length());
  CONVERT_ARG_HANDLE_CHECKED(WasmInstanceObject, instance, 0);
324 325
  CONVERT_DOUBLE_ARG_CHECKED(offset_double, 1);
  uintptr_t offset = static_cast<uintptr_t>(offset_double);
326
  CONVERT_NUMBER_CHECKED(int32_t, expected_value, Int32, args[2]);
327 328
  CONVERT_ARG_HANDLE_CHECKED(BigInt, timeout_ns, 3);

329 330 331 332
  Handle<JSArrayBuffer> array_buffer{instance->memory_object().array_buffer(),
                                     isolate};
  // Should have trapped if address was OOB.
  DCHECK_LT(offset, array_buffer->byte_length());
333

334 335
  // Trap if memory is not shared, or wait is not allowed on the isolate
  if (!array_buffer->is_shared() || !isolate->allow_atomics_wait()) {
336 337
    return ThrowWasmError(isolate, MessageTemplate::kAtomicsWaitNotAllowed);
  }
338
  return FutexEmulation::WaitWasm32(isolate, array_buffer, offset,
339
                                    expected_value, timeout_ns->AsInt64());
340 341 342
}

RUNTIME_FUNCTION(Runtime_WasmI64AtomicWait) {
343
  ClearThreadInWasmScope clear_wasm_flag(isolate);
344
  HandleScope scope(isolate);
345
  DCHECK_EQ(4, args.length());
346
  CONVERT_ARG_HANDLE_CHECKED(WasmInstanceObject, instance, 0);
347 348
  CONVERT_DOUBLE_ARG_CHECKED(offset_double, 1);
  uintptr_t offset = static_cast<uintptr_t>(offset_double);
349 350 351
  CONVERT_ARG_HANDLE_CHECKED(BigInt, expected_value, 2);
  CONVERT_ARG_HANDLE_CHECKED(BigInt, timeout_ns, 3);

352 353 354 355
  Handle<JSArrayBuffer> array_buffer{instance->memory_object().array_buffer(),
                                     isolate};
  // Should have trapped if address was OOB.
  DCHECK_LT(offset, array_buffer->byte_length());
356

357 358
  // Trap if memory is not shared, or if wait is not allowed on the isolate
  if (!array_buffer->is_shared() || !isolate->allow_atomics_wait()) {
359 360
    return ThrowWasmError(isolate, MessageTemplate::kAtomicsWaitNotAllowed);
  }
361
  return FutexEmulation::WaitWasm64(isolate, array_buffer, offset,
362 363
                                    expected_value->AsInt64(),
                                    timeout_ns->AsInt64());
364 365
}

366 367 368 369 370 371 372 373
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());
  }
374
  return ThrowWasmError(isolate, MessageTemplate::kWasmTrapTableOutOfBounds);
375 376 377
}
}  // namespace

378
RUNTIME_FUNCTION(Runtime_WasmRefFunc) {
379
  ClearThreadInWasmScope flag_scope(isolate);
380
  HandleScope scope(isolate);
381 382 383
  DCHECK_EQ(2, args.length());
  CONVERT_ARG_HANDLE_CHECKED(WasmInstanceObject, instance, 0);
  CONVERT_UINT32_ARG_CHECKED(function_index, 1);
384

385 386
  Handle<WasmExternalFunction> function =
      WasmInstanceObject::GetOrCreateWasmExternalFunction(isolate, instance,
387 388 389 390 391
                                                          function_index);

  return *function;
}

392
RUNTIME_FUNCTION(Runtime_WasmFunctionTableGet) {
393
  ClearThreadInWasmScope flag_scope(isolate);
394 395 396 397 398
  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);
399
  DCHECK_LT(table_index, instance->tables().length());
400
  auto table = handle(
401
      WasmTableObject::cast(instance->tables().get(table_index)), isolate);
402
  // We only use the runtime call for lazily initialized function references.
403 404 405 406 407
  DCHECK(
      table->instance().IsUndefined()
          ? table->type() == wasm::kWasmFuncRef
          : IsSubtypeOf(table->type(), wasm::kWasmFuncRef,
                        WasmInstanceObject::cast(table->instance()).module()));
408 409 410 411 412 413 414 415 416

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

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

RUNTIME_FUNCTION(Runtime_WasmFunctionTableSet) {
417
  ClearThreadInWasmScope flag_scope(isolate);
418 419 420 421 422 423
  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);
424
  // TODO(wasm): Manually box because parameters are not visited yet.
425
  Handle<Object> element(element_raw, isolate);
426
  DCHECK_LT(table_index, instance->tables().length());
427
  auto table = handle(
428
      WasmTableObject::cast(instance->tables().get(table_index)), isolate);
429
  // We only use the runtime call for function references.
430 431 432 433 434
  DCHECK(
      table->instance().IsUndefined()
          ? table->type() == wasm::kWasmFuncRef
          : IsSubtypeOf(table->type(), wasm::kWasmFuncRef,
                        WasmInstanceObject::cast(table->instance()).module()));
435 436 437 438 439 440 441 442

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

443
RUNTIME_FUNCTION(Runtime_WasmTableInit) {
444
  ClearThreadInWasmScope flag_scope(isolate);
445
  HandleScope scope(isolate);
446 447 448 449
  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);
450 451 452
  static_assert(
      wasm::kV8MaxWasmTableSize < kSmiMaxValue,
      "Make sure clamping to Smi range doesn't make an invalid call valid");
453 454 455
  CONVERT_UINT32_ARG_CHECKED(dst, 3);
  CONVERT_UINT32_ARG_CHECKED(src, 4);
  CONVERT_UINT32_ARG_CHECKED(count, 5);
456

457
  DCHECK(!isolate->context().is_null());
458

459 460 461 462
  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();
463 464 465
}

RUNTIME_FUNCTION(Runtime_WasmTableCopy) {
466
  ClearThreadInWasmScope flag_scope(isolate);
467
  HandleScope scope(isolate);
468 469 470 471
  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);
472 473 474
  static_assert(
      wasm::kV8MaxWasmTableSize < kSmiMaxValue,
      "Make sure clamping to Smi range doesn't make an invalid call valid");
475 476 477 478 479
  CONVERT_UINT32_ARG_CHECKED(dst, 3);
  CONVERT_UINT32_ARG_CHECKED(src, 4);
  CONVERT_UINT32_ARG_CHECKED(count, 5);

  DCHECK(!isolate->context().is_null());
480 481

  bool oob = !WasmInstanceObject::CopyTableEntries(
482
      isolate, instance, table_dst_index, table_src_index, dst, src, count);
483
  if (oob) return ThrowTableOutOfBounds(isolate, instance);
484
  return ReadOnlyRoots(isolate).undefined_value();
485
}
486 487

RUNTIME_FUNCTION(Runtime_WasmTableGrow) {
488
  ClearThreadInWasmScope flag_scope(isolate);
489
  HandleScope scope(isolate);
490 491 492 493
  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);
494
  // TODO(wasm): Manually box because parameters are not visited yet.
495
  Handle<Object> value(value_raw, isolate);
496
  CONVERT_UINT32_ARG_CHECKED(delta, 3);
497 498

  Handle<WasmTableObject> table(
499
      WasmTableObject::cast(instance->tables().get(table_index)), isolate);
500 501 502 503
  int result = WasmTableObject::Grow(isolate, table, delta, value);

  return Smi::FromInt(result);
}
504 505

RUNTIME_FUNCTION(Runtime_WasmTableFill) {
506
  ClearThreadInWasmScope flag_scope(isolate);
507
  HandleScope scope(isolate);
508 509 510 511 512
  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);
513
  // TODO(wasm): Manually box because parameters are not visited yet.
514
  Handle<Object> value(value_raw, isolate);
515
  CONVERT_UINT32_ARG_CHECKED(count, 4);
516 517

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

520
  uint32_t table_size = table->current_length();
521

522
  if (start > table_size) {
523 524 525 526 527 528 529 530 531
    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);
  }
532 533
  WasmTableObject::Fill(isolate, table, start, value, fill_count);

534 535
  return ReadOnlyRoots(isolate).undefined_value();
}
536

537
RUNTIME_FUNCTION(Runtime_WasmDebugBreak) {
538
  ClearThreadInWasmScope flag_scope(isolate);
539 540
  HandleScope scope(isolate);
  DCHECK_EQ(0, args.length());
541 542
  FrameFinder<WasmFrame> frame_finder(
      isolate, {StackFrame::EXIT, StackFrame::WASM_DEBUG_BREAK});
543
  WasmFrame* frame = frame_finder.frame();
544 545 546
  auto instance = handle(frame->wasm_instance(), isolate);
  auto script = handle(instance->module_object().script(), isolate);
  auto* debug_info = instance->module_object().native_module()->GetDebugInfo();
547
  isolate->set_context(instance->native_context());
548

549 550 551
  // 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);
552 553 554 555 556 557 558
  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));
  }
559

560 561 562
  // Enter the debugger.
  DebugScope debug_scope(isolate->debug());

563
  // Check for instrumentation breakpoint.
564
  DCHECK_EQ(script->break_on_entry(), !!instance->break_on_entry());
565 566
  if (script->break_on_entry()) {
    MaybeHandle<FixedArray> maybe_on_entry_breakpoints =
567 568 569
        WasmScript::CheckBreakPoints(isolate, script,
                                     WasmScript::kOnEntryBreakpointPosition,
                                     frame->id());
570 571 572 573 574
    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;
575 576
      i::WasmInstanceObject::cast(weak_instance_list.Get(i)->GetHeapObject())
          .set_break_on_entry(false);
577 578 579 580 581 582 583 584 585 586 587 588 589
    }
    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();
    }
  }

590
  if (debug_info->IsStepping(frame)) {
591
    debug_info->ClearStepping(isolate);
592
    StepAction step_action = isolate->debug()->last_step_action();
593
    isolate->debug()->ClearStepping();
594
    isolate->debug()->OnDebugBreak(isolate->factory()->empty_fixed_array(),
595
                                   step_action);
596
    return ReadOnlyRoots(isolate).undefined_value();
597 598
  }

599
  // Check whether we hit a breakpoint.
600
  Handle<FixedArray> breakpoints;
601 602
  if (WasmScript::CheckBreakPoints(isolate, script, frame->position(),
                                   frame->id())
603
          .ToHandle(&breakpoints)) {
604
    debug_info->ClearStepping(isolate);
605
    StepAction step_action = isolate->debug()->last_step_action();
606
    isolate->debug()->ClearStepping();
607
    if (isolate->debug()->break_points_active()) {
608
      // We hit one or several breakpoints. Notify the debug listeners.
609
      isolate->debug()->OnDebugBreak(breakpoints, step_action);
610
    }
611
    return ReadOnlyRoots(isolate).undefined_value();
612
  }
613

614 615 616 617 618
  // 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);

619
  return ReadOnlyRoots(isolate).undefined_value();
620 621
}

622
RUNTIME_FUNCTION(Runtime_WasmAllocateRtt) {
623
  ClearThreadInWasmScope flag_scope(isolate);
624
  HandleScope scope(isolate);
625
  DCHECK_EQ(3, args.length());
626 627
  CONVERT_UINT32_ARG_CHECKED(type_index, 0);
  CONVERT_ARG_HANDLE_CHECKED(Map, parent, 1);
628
  CONVERT_SMI_ARG_CHECKED(raw_mode, 2);
629 630
  Handle<WasmInstanceObject> instance(GetWasmInstanceOnStackTop(isolate),
                                      isolate);
631 632
  return *wasm::AllocateSubRtt(isolate, instance, type_index, parent,
                               static_cast<WasmRttSubMode>(raw_mode));
633 634
}

635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654
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

// Assumes copy ranges are in-bounds.
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);
  bool overlapping_ranges =
      dst_array->ptr() == src_array->ptr() &&
655 656
      (dst_index < src_index ? dst_index + length > src_index
                             : src_index + length > dst_index);
657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681
  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();
}

682 683
}  // namespace internal
}  // namespace v8