runtime-wasm.cc 13.8 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/arguments-inl.h"
6
#include "src/compiler/wasm-compiler.h"
7
#include "src/conversions.h"
8
#include "src/counters.h"
9
#include "src/debug/debug.h"
10
#include "src/frame-constants.h"
11
#include "src/heap/factory.h"
12
#include "src/message-template.h"
13
#include "src/objects-inl.h"
14
#include "src/objects/frame-array-inl.h"
15
#include "src/runtime/runtime-utils.h"
eholk's avatar
eholk committed
16
#include "src/trap-handler/trap-handler.h"
17
#include "src/v8memory.h"
18
#include "src/wasm/module-compiler.h"
19
#include "src/wasm/wasm-code-manager.h"
20
#include "src/wasm/wasm-constants.h"
21
#include "src/wasm/wasm-engine.h"
22
#include "src/wasm/wasm-objects.h"
23 24 25 26

namespace v8 {
namespace internal {

27
namespace {
28

29
WasmInstanceObject GetWasmInstanceOnStackTop(Isolate* isolate) {
30 31 32 33
  StackFrameIterator it(isolate, isolate->thread_local_top());
  // On top: C entry stub.
  DCHECK_EQ(StackFrame::EXIT, it.frame()->type());
  it.Advance();
34 35 36
  // Next: the wasm compiled frame.
  DCHECK(it.frame()->is_wasm_compiled());
  WasmCompiledFrame* frame = WasmCompiledFrame::cast(it.frame());
37 38 39
  return frame->wasm_instance();
}

40
Context GetNativeContextFromWasmInstanceOnStackTop(Isolate* isolate) {
41
  return GetWasmInstanceOnStackTop(isolate)->native_context();
42
}
43 44 45

class ClearThreadInWasmScope {
 public:
46 47
  ClearThreadInWasmScope() {
    DCHECK_EQ(trap_handler::IsTrapHandlerEnabled(),
48
              trap_handler::IsThreadInWasm());
49
    trap_handler::ClearThreadInWasm();
50 51 52
  }
  ~ClearThreadInWasmScope() {
    DCHECK(!trap_handler::IsThreadInWasm());
53
    trap_handler::SetThreadInWasm();
54 55 56
  }
};

57 58
}  // namespace

59 60 61 62 63 64 65 66 67 68 69 70 71 72
RUNTIME_FUNCTION(Runtime_WasmIsValidAnyFuncValue) {
  HandleScope scope(isolate);
  DCHECK_EQ(1, args.length());
  CONVERT_ARG_HANDLE_CHECKED(Object, function, 0);

  if (function->IsNull(isolate)) {
    return Smi::FromInt(true);
  }
  if (WasmExportedFunction::IsWasmExportedFunction(*function)) {
    return Smi::FromInt(true);
  }
  return Smi::FromInt(false);
}

73
RUNTIME_FUNCTION(Runtime_WasmMemoryGrow) {
74
  HandleScope scope(isolate);
75 76
  DCHECK_EQ(2, args.length());
  CONVERT_ARG_HANDLE_CHECKED(WasmInstanceObject, instance, 0);
77
  // {delta_pages} is checked to be a positive smi in the WasmMemoryGrow builtin
78 79
  // which calls this runtime function.
  CONVERT_UINT32_ARG_CHECKED(delta_pages, 1);
80

81
  // This runtime function is always being called from wasm code.
82
  ClearThreadInWasmScope flag_scope;
83

84 85
  int ret = WasmMemoryObject::Grow(
      isolate, handle(instance->memory_object(), isolate), delta_pages);
86
  // The WasmMemoryGrow builtin which calls this runtime function expects us to
87 88
  // always return a Smi.
  return Smi::FromInt(ret);
89
}
90

91 92 93
RUNTIME_FUNCTION(Runtime_ThrowWasmError) {
  DCHECK_EQ(1, args.length());
  CONVERT_SMI_ARG_CHECKED(message_id, 0);
94
  ClearThreadInWasmScope clear_wasm_flag;
95

96 97
  HandleScope scope(isolate);
  Handle<Object> error_obj = isolate->factory()->NewWasmRuntimeError(
98
      MessageTemplateFromInt(message_id));
99 100 101
  return isolate->Throw(*error_obj);
}

102 103 104
RUNTIME_FUNCTION(Runtime_ThrowWasmStackOverflow) {
  SealHandleScope shs(isolate);
  DCHECK_LE(0, args.length());
105
  DCHECK(isolate->context().is_null());
106
  isolate->set_context(GetNativeContextFromWasmInstanceOnStackTop(isolate));
107 108 109
  return isolate->StackOverflow();
}

110 111 112 113 114 115
RUNTIME_FUNCTION(Runtime_WasmThrowTypeError) {
  HandleScope scope(isolate);
  DCHECK_EQ(0, args.length());
  THROW_NEW_ERROR_RETURN_FAILURE(
      isolate, NewTypeError(MessageTemplate::kWasmTrapTypeError));
}
jpp's avatar
jpp committed
116

117 118
RUNTIME_FUNCTION(Runtime_WasmThrowCreate) {
  // TODO(kschimpf): Can this be replaced with equivalent TurboFan code/calls.
jpp's avatar
jpp committed
119
  HandleScope scope(isolate);
120
  DCHECK_EQ(2, args.length());
121
  DCHECK(isolate->context().is_null());
122
  isolate->set_context(GetNativeContextFromWasmInstanceOnStackTop(isolate));
123 124 125 126
  CONVERT_ARG_CHECKED(HeapObject, tag_raw, 0);
  CONVERT_SMI_ARG_CHECKED(size, 1);
  // TODO(mstarzinger): Manually box because parameters are not visited yet.
  Handle<Object> tag(tag_raw, isolate);
127
  Handle<Object> exception = isolate->factory()->NewWasmRuntimeError(
128
      MessageTemplate::kWasmExceptionError);
129 130
  CHECK(!Object::SetProperty(isolate, exception,
                             isolate->factory()->wasm_exception_tag_symbol(),
131
                             tag, LanguageMode::kStrict)
132
             .is_null());
133
  Handle<FixedArray> values = isolate->factory()->NewFixedArray(size);
134 135
  CHECK(!Object::SetProperty(isolate, exception,
                             isolate->factory()->wasm_exception_values_symbol(),
136
                             values, LanguageMode::kStrict)
137
             .is_null());
138
  return *exception;
jpp's avatar
jpp committed
139 140
}

141
RUNTIME_FUNCTION(Runtime_WasmExceptionGetTag) {
142
  // TODO(kschimpf): Can this be replaced with equivalent TurboFan code/calls.
143
  HandleScope scope(isolate);
144
  DCHECK_EQ(1, args.length());
145
  DCHECK(isolate->context().is_null());
146
  isolate->set_context(GetNativeContextFromWasmInstanceOnStackTop(isolate));
147 148 149
  CONVERT_ARG_CHECKED(Object, except_obj_raw, 0);
  // TODO(mstarzinger): Manually box because parameters are not visited yet.
  Handle<Object> except_obj(except_obj_raw, isolate);
150
  if (!except_obj.is_null() && except_obj->IsJSReceiver()) {
151
    Handle<JSReceiver> exception(JSReceiver::cast(*except_obj), isolate);
152
    Handle<Object> tag;
153 154
    if (JSReceiver::GetProperty(isolate, exception,
                                isolate->factory()->wasm_exception_tag_symbol())
155
            .ToHandle(&tag)) {
156
      return *tag;
157
    }
158
  }
159
  return ReadOnlyRoots(isolate).undefined_value();
160 161
}

162
RUNTIME_FUNCTION(Runtime_WasmExceptionGetValues) {
163
  // TODO(kschimpf): Can this be replaced with equivalent TurboFan code/calls.
164
  HandleScope scope(isolate);
165
  DCHECK_EQ(1, args.length());
166
  DCHECK(isolate->context().is_null());
167
  isolate->set_context(GetNativeContextFromWasmInstanceOnStackTop(isolate));
168 169 170
  CONVERT_ARG_CHECKED(Object, except_obj_raw, 0);
  // TODO(mstarzinger): Manually box because parameters are not visited yet.
  Handle<Object> except_obj(except_obj_raw, isolate);
171
  if (!except_obj.is_null() && except_obj->IsJSReceiver()) {
172
    Handle<JSReceiver> exception(JSReceiver::cast(*except_obj), isolate);
173
    Handle<Object> values;
174 175 176
    if (JSReceiver::GetProperty(
            isolate, exception,
            isolate->factory()->wasm_exception_values_symbol())
177 178 179
            .ToHandle(&values)) {
      DCHECK(values->IsFixedArray());
      return *values;
180 181
    }
  }
182
  return ReadOnlyRoots(isolate).undefined_value();
183 184
}

185
RUNTIME_FUNCTION(Runtime_WasmRunInterpreter) {
186
  DCHECK_EQ(2, args.length());
187
  HandleScope scope(isolate);
188 189
  CONVERT_NUMBER_CHECKED(int32_t, func_index, Int32, args[0]);
  CONVERT_ARG_HANDLE_CHECKED(Object, arg_buffer_obj, 1);
190 191 192 193 194 195

  // The arg buffer is the raw pointer to the caller's stack. It looks like a
  // Smi (lowest bit not set, as checked by IsSmi), but is no valid Smi. We just
  // cast it back to the raw pointer.
  CHECK(!arg_buffer_obj->IsHeapObject());
  CHECK(arg_buffer_obj->IsSmi());
196
  Address arg_buffer = arg_buffer_obj->ptr();
197

198
  ClearThreadInWasmScope wasm_flag;
199

200 201
  // Find the frame pointer and instance of the interpreter frame on the stack.
  Handle<WasmInstanceObject> instance;
202 203 204 205 206 207 208 209
  Address frame_pointer = 0;
  {
    StackFrameIterator it(isolate, isolate->thread_local_top());
    // On top: C entry stub.
    DCHECK_EQ(StackFrame::EXIT, it.frame()->type());
    it.Advance();
    // Next: the wasm interpreter entry.
    DCHECK_EQ(StackFrame::WASM_INTERPRETER_ENTRY, it.frame()->type());
210 211
    instance = handle(
        WasmInterpreterEntryFrame::cast(it.frame())->wasm_instance(), isolate);
212 213 214
    frame_pointer = it.frame()->fp();
  }

215
  // Set the current isolate's context.
216
  DCHECK(isolate->context().is_null());
217 218
  isolate->set_context(instance->native_context());

219 220 221 222 223 224 225
  // Run the function in the interpreter. Note that neither the {WasmDebugInfo}
  // nor the {InterpreterHandle} have to exist, because interpretation might
  // have been triggered by another Isolate sharing the same WasmEngine.
  Handle<WasmDebugInfo> debug_info =
      WasmInstanceObject::GetOrCreateDebugInfo(instance);
  bool success = WasmDebugInfo::RunInterpreter(
      isolate, debug_info, frame_pointer, func_index, arg_buffer);
226

227 228
  if (!success) {
    DCHECK(isolate->has_pending_exception());
229
    return ReadOnlyRoots(isolate).exception();
230
  }
231
  return ReadOnlyRoots(isolate).undefined_value();
232 233
}

234 235 236
RUNTIME_FUNCTION(Runtime_WasmStackGuard) {
  SealHandleScope shs(isolate);
  DCHECK_EQ(0, args.length());
237 238
  DCHECK(!trap_handler::IsTrapHandlerEnabled() ||
         trap_handler::IsThreadInWasm());
239

240
  ClearThreadInWasmScope wasm_flag;
241 242 243 244 245 246 247 248

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

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

249
RUNTIME_FUNCTION(Runtime_WasmCompileLazy) {
250
  HandleScope scope(isolate);
251
  DCHECK_EQ(2, args.length());
252
  CONVERT_ARG_HANDLE_CHECKED(WasmInstanceObject, instance, 0);
253
  CONVERT_SMI_ARG_CHECKED(func_index, 1);
254

255
  ClearThreadInWasmScope wasm_flag;
256

257 258 259 260 261 262 263 264 265 266
#ifdef DEBUG
  StackFrameIterator it(isolate, isolate->thread_local_top());
  // On top: C entry stub.
  DCHECK_EQ(StackFrame::EXIT, it.frame()->type());
  it.Advance();
  // Next: the wasm lazy compile frame.
  DCHECK_EQ(StackFrame::WASM_COMPILE_LAZY, it.frame()->type());
  DCHECK_EQ(*instance, WasmCompileLazyFrame::cast(it.frame())->wasm_instance());
#endif

267
  Address entrypoint = wasm::CompileLazy(
268
      isolate, instance->module_object()->native_module(), func_index);
269
  return Object(entrypoint);
270 271
}

272 273 274
// Should be called from within a handle scope
Handle<JSArrayBuffer> getSharedArrayBuffer(Handle<WasmInstanceObject> instance,
                                           Isolate* isolate, uint32_t address) {
275 276 277 278 279 280 281 282 283
  DCHECK(instance->has_memory_object());
  Handle<JSArrayBuffer> array_buffer(instance->memory_object()->array_buffer(),
                                     isolate);

  // Validation should have failed if the memory was not shared.
  DCHECK(array_buffer->is_shared());

  // Should have trapped if address was OOB
  DCHECK_LT(address, array_buffer->byte_length());
284 285
  return array_buffer;
}
286

287 288 289 290 291 292 293 294
RUNTIME_FUNCTION(Runtime_WasmAtomicWake) {
  HandleScope scope(isolate);
  DCHECK_EQ(3, args.length());
  CONVERT_ARG_HANDLE_CHECKED(WasmInstanceObject, instance, 0);
  CONVERT_NUMBER_CHECKED(uint32_t, address, Uint32, args[1]);
  CONVERT_NUMBER_CHECKED(uint32_t, count, Uint32, args[2]);
  Handle<JSArrayBuffer> array_buffer =
      getSharedArrayBuffer(instance, isolate, address);
295 296 297
  return FutexEmulation::Wake(array_buffer, address, count);
}

298 299 300 301 302 303 304
double WaitTimeoutInMs(double timeout_ns) {
  return timeout_ns < 0
             ? V8_INFINITY
             : timeout_ns / (base::Time::kNanosecondsPerMicrosecond *
                             base::Time::kMicrosecondsPerMillisecond);
}

305 306 307 308 309 310
RUNTIME_FUNCTION(Runtime_WasmI32AtomicWait) {
  HandleScope scope(isolate);
  DCHECK_EQ(4, args.length());
  CONVERT_ARG_HANDLE_CHECKED(WasmInstanceObject, instance, 0);
  CONVERT_NUMBER_CHECKED(uint32_t, address, Uint32, args[1]);
  CONVERT_NUMBER_CHECKED(int32_t, expected_value, Int32, args[2]);
311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329
  CONVERT_DOUBLE_ARG_CHECKED(timeout_ns, 3);
  double timeout_ms = WaitTimeoutInMs(timeout_ns);
  Handle<JSArrayBuffer> array_buffer =
      getSharedArrayBuffer(instance, isolate, address);
  return FutexEmulation::Wait32(isolate, array_buffer, address, expected_value,
                                timeout_ms);
}

RUNTIME_FUNCTION(Runtime_WasmI64AtomicWait) {
  HandleScope scope(isolate);
  DCHECK_EQ(5, args.length());
  CONVERT_ARG_HANDLE_CHECKED(WasmInstanceObject, instance, 0);
  CONVERT_NUMBER_CHECKED(uint32_t, address, Uint32, args[1]);
  CONVERT_NUMBER_CHECKED(uint32_t, expected_value_high, Uint32, args[2]);
  CONVERT_NUMBER_CHECKED(uint32_t, expected_value_low, Uint32, args[3]);
  CONVERT_DOUBLE_ARG_CHECKED(timeout_ns, 4);
  int64_t expected_value = (static_cast<uint64_t>(expected_value_high) << 32) |
                           static_cast<uint64_t>(expected_value_low);
  double timeout_ms = WaitTimeoutInMs(timeout_ns);
330 331
  Handle<JSArrayBuffer> array_buffer =
      getSharedArrayBuffer(instance, isolate, address);
332 333
  return FutexEmulation::Wait64(isolate, array_buffer, address, expected_value,
                                timeout_ms);
334 335
}

336 337 338
RUNTIME_FUNCTION(Runtime_WasmTableInit) {
  HandleScope scope(isolate);
  DCHECK_EQ(5, args.length());
339 340
  auto instance =
      Handle<WasmInstanceObject>(GetWasmInstanceOnStackTop(isolate), isolate);
341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363
  CONVERT_UINT32_ARG_CHECKED(table_index, 0);
  CONVERT_UINT32_ARG_CHECKED(elem_segment_index, 1);
  CONVERT_UINT32_ARG_CHECKED(dst, 2);
  CONVERT_UINT32_ARG_CHECKED(src, 3);
  CONVERT_UINT32_ARG_CHECKED(size, 4);

  PrintF(
      "TableInit(table_index=%u, elem_segment_index=%u, dst=%u, src=%u, "
      "size=%u)\n",
      table_index, elem_segment_index, dst, src, size);

  USE(instance);
  USE(table_index);
  USE(elem_segment_index);
  USE(dst);
  USE(src);
  USE(size);

  UNREACHABLE();
}

RUNTIME_FUNCTION(Runtime_WasmTableCopy) {
  HandleScope scope(isolate);
364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383
  DCHECK_EQ(4, args.length());
  auto instance =
      Handle<WasmInstanceObject>(GetWasmInstanceOnStackTop(isolate), isolate);
  CONVERT_UINT32_ARG_CHECKED(table_index, 0);
  CONVERT_UINT32_ARG_CHECKED(dst, 1);
  CONVERT_UINT32_ARG_CHECKED(src, 2);
  CONVERT_UINT32_ARG_CHECKED(count, 3);

  bool oob = !WasmInstanceObject::CopyTableEntries(
      isolate, instance, table_index, dst, src, count);
  if (oob) {
    // Handle out-of-bounds access here in the runtime call, rather
    // than having the lower-level layers deal with JS exceptions.
    DCHECK(isolate->context().is_null());
    isolate->set_context(instance->native_context());
    Handle<Object> error_obj = isolate->factory()->NewWasmRuntimeError(
        MessageTemplate::kWasmTrapTableOutOfBounds);
    return isolate->Throw(*error_obj);
  }
  return ReadOnlyRoots(isolate).undefined_value();
384
}
385 386
}  // namespace internal
}  // namespace v8