Source code
Revision control
Copy as Markdown
Other Tools
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* vim: set ts=8 sts=2 et sw=2 tw=80:
*
* Copyright 2017 Mozilla Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "wasm/WasmBuiltins.h"
#include "mozilla/Atomics.h"
#include "mozilla/ScopeExit.h"
#include "fdlibm.h"
#include "jslibmath.h"
#include "jsmath.h"
#include "jit/AtomicOperations.h"
#include "jit/InlinableNatives.h"
#include "jit/JitRuntime.h"
#include "jit/MacroAssembler.h"
#include "jit/ProcessExecutableMemory.h"
#include "jit/Simulator.h"
#include "js/experimental/JitInfo.h" // JSJitInfo
#include "js/friend/ErrorMessages.h" // js::GetErrorMessage, JSMSG_*
#include "js/friend/StackLimits.h" // js::AutoCheckRecursionLimit
#include "threading/Mutex.h"
#include "util/Memory.h"
#include "util/Poison.h"
#include "vm/BigIntType.h"
#include "vm/ErrorObject.h"
#include "wasm/WasmCodegenTypes.h"
#include "wasm/WasmDebug.h"
#include "wasm/WasmDebugFrame.h"
#include "wasm/WasmGcObject.h"
#include "wasm/WasmInstance.h"
#include "wasm/WasmPI.h"
#include "wasm/WasmStubs.h"
#include "debugger/DebugAPI-inl.h"
#include "vm/ErrorObject-inl.h"
#include "vm/JSContext-inl.h"
#include "vm/Stack-inl.h"
#include "wasm/WasmInstance-inl.h"
using namespace js;
using namespace jit;
using namespace wasm;
using mozilla::EnumeratedArray;
using mozilla::HashGeneric;
using mozilla::MakeEnumeratedRange;
using mozilla::Maybe;
using mozilla::Nothing;
using mozilla::Some;
static const unsigned BUILTIN_THUNK_LIFO_SIZE = 64 * 1024;
// ============================================================================
// WebAssembly builtin C++ functions called from wasm code to implement internal
// wasm operations: type descriptions.
// Some abbreviations, for the sake of conciseness.
#define _F64 MIRType::Double
#define _F32 MIRType::Float32
#define _I32 MIRType::Int32
#define _I64 MIRType::Int64
#define _PTR MIRType::Pointer
#define _RoN MIRType::WasmAnyRef
#define _VOID MIRType::None
#define _END MIRType::None
#define _Infallible FailureMode::Infallible
#define _FailOnNegI32 FailureMode::FailOnNegI32
#define _FailOnMaxI32 FailureMode::FailOnMaxI32
#define _FailOnNullPtr FailureMode::FailOnNullPtr
#define _FailOnInvalidRef FailureMode::FailOnInvalidRef
namespace js {
namespace wasm {
constexpr SymbolicAddressSignature SASigSinNativeD = {
SymbolicAddress::SinNativeD, _F64, _Infallible, 1, {_F64, _END}};
constexpr SymbolicAddressSignature SASigSinFdlibmD = {
SymbolicAddress::SinFdlibmD, _F64, _Infallible, 1, {_F64, _END}};
constexpr SymbolicAddressSignature SASigCosNativeD = {
SymbolicAddress::CosNativeD, _F64, _Infallible, 1, {_F64, _END}};
constexpr SymbolicAddressSignature SASigCosFdlibmD = {
SymbolicAddress::CosFdlibmD, _F64, _Infallible, 1, {_F64, _END}};
constexpr SymbolicAddressSignature SASigTanNativeD = {
SymbolicAddress::TanNativeD, _F64, _Infallible, 1, {_F64, _END}};
constexpr SymbolicAddressSignature SASigTanFdlibmD = {
SymbolicAddress::TanFdlibmD, _F64, _Infallible, 1, {_F64, _END}};
constexpr SymbolicAddressSignature SASigASinD = {
SymbolicAddress::ASinD, _F64, _Infallible, 1, {_F64, _END}};
constexpr SymbolicAddressSignature SASigACosD = {
SymbolicAddress::ACosD, _F64, _Infallible, 1, {_F64, _END}};
constexpr SymbolicAddressSignature SASigATanD = {
SymbolicAddress::ATanD, _F64, _Infallible, 1, {_F64, _END}};
constexpr SymbolicAddressSignature SASigCeilD = {
SymbolicAddress::CeilD, _F64, _Infallible, 1, {_F64, _END}};
constexpr SymbolicAddressSignature SASigCeilF = {
SymbolicAddress::CeilF, _F32, _Infallible, 1, {_F32, _END}};
constexpr SymbolicAddressSignature SASigFloorD = {
SymbolicAddress::FloorD, _F64, _Infallible, 1, {_F64, _END}};
constexpr SymbolicAddressSignature SASigFloorF = {
SymbolicAddress::FloorF, _F32, _Infallible, 1, {_F32, _END}};
constexpr SymbolicAddressSignature SASigTruncD = {
SymbolicAddress::TruncD, _F64, _Infallible, 1, {_F64, _END}};
constexpr SymbolicAddressSignature SASigTruncF = {
SymbolicAddress::TruncF, _F32, _Infallible, 1, {_F32, _END}};
constexpr SymbolicAddressSignature SASigNearbyIntD = {
SymbolicAddress::NearbyIntD, _F64, _Infallible, 1, {_F64, _END}};
constexpr SymbolicAddressSignature SASigNearbyIntF = {
SymbolicAddress::NearbyIntF, _F32, _Infallible, 1, {_F32, _END}};
constexpr SymbolicAddressSignature SASigExpD = {
SymbolicAddress::ExpD, _F64, _Infallible, 1, {_F64, _END}};
constexpr SymbolicAddressSignature SASigLogD = {
SymbolicAddress::LogD, _F64, _Infallible, 1, {_F64, _END}};
constexpr SymbolicAddressSignature SASigPowD = {
SymbolicAddress::PowD, _F64, _Infallible, 2, {_F64, _F64, _END}};
constexpr SymbolicAddressSignature SASigATan2D = {
SymbolicAddress::ATan2D, _F64, _Infallible, 2, {_F64, _F64, _END}};
constexpr SymbolicAddressSignature SASigMemoryGrowM32 = {
SymbolicAddress::MemoryGrowM32,
_I32,
_Infallible,
3,
{_PTR, _I32, _I32, _END}};
constexpr SymbolicAddressSignature SASigMemoryGrowM64 = {
SymbolicAddress::MemoryGrowM64,
_I64,
_Infallible,
3,
{_PTR, _I64, _I32, _END}};
constexpr SymbolicAddressSignature SASigMemorySizeM32 = {
SymbolicAddress::MemorySizeM32, _I32, _Infallible, 2, {_PTR, _I32, _END}};
constexpr SymbolicAddressSignature SASigMemorySizeM64 = {
SymbolicAddress::MemorySizeM64, _I64, _Infallible, 2, {_PTR, _I32, _END}};
constexpr SymbolicAddressSignature SASigWaitI32M32 = {
SymbolicAddress::WaitI32M32,
_I32,
_FailOnNegI32,
5,
{_PTR, _I32, _I32, _I64, _I32, _END}};
constexpr SymbolicAddressSignature SASigWaitI32M64 = {
SymbolicAddress::WaitI32M64,
_I32,
_FailOnNegI32,
5,
{_PTR, _I64, _I32, _I64, _I32, _END}};
constexpr SymbolicAddressSignature SASigWaitI64M32 = {
SymbolicAddress::WaitI64M32,
_I32,
_FailOnNegI32,
5,
{_PTR, _I32, _I64, _I64, _I32, _END}};
constexpr SymbolicAddressSignature SASigWaitI64M64 = {
SymbolicAddress::WaitI64M64,
_I32,
_FailOnNegI32,
5,
{_PTR, _I64, _I64, _I64, _I32, _END}};
constexpr SymbolicAddressSignature SASigWakeM32 = {
SymbolicAddress::WakeM32,
_I32,
_FailOnNegI32,
4,
{_PTR, _I32, _I32, _I32, _END}};
constexpr SymbolicAddressSignature SASigWakeM64 = {
SymbolicAddress::WakeM64,
_I32,
_FailOnNegI32,
4,
{_PTR, _I64, _I32, _I32, _END}};
constexpr SymbolicAddressSignature SASigMemCopyM32 = {
SymbolicAddress::MemCopyM32,
_VOID,
_FailOnNegI32,
5,
{_PTR, _I32, _I32, _I32, _PTR, _END}};
constexpr SymbolicAddressSignature SASigMemCopySharedM32 = {
SymbolicAddress::MemCopySharedM32,
_VOID,
_FailOnNegI32,
5,
{_PTR, _I32, _I32, _I32, _PTR, _END}};
constexpr SymbolicAddressSignature SASigMemCopyM64 = {
SymbolicAddress::MemCopyM64,
_VOID,
_FailOnNegI32,
5,
{_PTR, _I64, _I64, _I64, _PTR, _END}};
constexpr SymbolicAddressSignature SASigMemCopySharedM64 = {
SymbolicAddress::MemCopySharedM64,
_VOID,
_FailOnNegI32,
5,
{_PTR, _I64, _I64, _I64, _PTR, _END}};
constexpr SymbolicAddressSignature SASigMemCopyAny = {
SymbolicAddress::MemCopyAny,
_VOID,
_FailOnNegI32,
6,
{_PTR, _I64, _I64, _I64, _I32, _I32, _END}};
constexpr SymbolicAddressSignature SASigDataDrop = {
SymbolicAddress::DataDrop, _VOID, _FailOnNegI32, 2, {_PTR, _I32, _END}};
constexpr SymbolicAddressSignature SASigMemFillM32 = {
SymbolicAddress::MemFillM32,
_VOID,
_FailOnNegI32,
5,
{_PTR, _I32, _I32, _I32, _PTR, _END}};
constexpr SymbolicAddressSignature SASigMemFillSharedM32 = {
SymbolicAddress::MemFillSharedM32,
_VOID,
_FailOnNegI32,
5,
{_PTR, _I32, _I32, _I32, _PTR, _END}};
constexpr SymbolicAddressSignature SASigMemFillM64 = {
SymbolicAddress::MemFillM64,
_VOID,
_FailOnNegI32,
5,
{_PTR, _I64, _I32, _I64, _PTR, _END}};
constexpr SymbolicAddressSignature SASigMemFillSharedM64 = {
SymbolicAddress::MemFillSharedM64,
_VOID,
_FailOnNegI32,
5,
{_PTR, _I64, _I32, _I64, _PTR, _END}};
constexpr SymbolicAddressSignature SASigMemDiscardM32 = {
SymbolicAddress::MemDiscardM32,
_VOID,
_FailOnNegI32,
4,
{_PTR, _I32, _I32, _PTR, _END}};
constexpr SymbolicAddressSignature SASigMemDiscardSharedM32 = {
SymbolicAddress::MemDiscardSharedM32,
_VOID,
_FailOnNegI32,
4,
{_PTR, _I32, _I32, _PTR, _END}};
constexpr SymbolicAddressSignature SASigMemDiscardM64 = {
SymbolicAddress::MemDiscardM64,
_VOID,
_FailOnNegI32,
4,
{_PTR, _I64, _I64, _PTR, _END}};
constexpr SymbolicAddressSignature SASigMemDiscardSharedM64 = {
SymbolicAddress::MemDiscardSharedM64,
_VOID,
_FailOnNegI32,
4,
{_PTR, _I64, _I64, _PTR, _END}};
constexpr SymbolicAddressSignature SASigMemInitM32 = {
SymbolicAddress::MemInitM32,
_VOID,
_FailOnNegI32,
6,
{_PTR, _I32, _I32, _I32, _I32, _I32, _END}};
constexpr SymbolicAddressSignature SASigMemInitM64 = {
SymbolicAddress::MemInitM64,
_VOID,
_FailOnNegI32,
6,
{_PTR, _I64, _I32, _I32, _I32, _I32, _END}};
constexpr SymbolicAddressSignature SASigTableCopy = {
SymbolicAddress::TableCopy,
_VOID,
_FailOnNegI32,
6,
{_PTR, _I32, _I32, _I32, _I32, _I32, _END}};
constexpr SymbolicAddressSignature SASigElemDrop = {
SymbolicAddress::ElemDrop, _VOID, _FailOnNegI32, 2, {_PTR, _I32, _END}};
constexpr SymbolicAddressSignature SASigTableFill = {
SymbolicAddress::TableFill,
_VOID,
_FailOnNegI32,
5,
{_PTR, _I32, _RoN, _I32, _I32, _END}};
constexpr SymbolicAddressSignature SASigTableGet = {SymbolicAddress::TableGet,
_RoN,
_FailOnInvalidRef,
3,
{_PTR, _I32, _I32, _END}};
constexpr SymbolicAddressSignature SASigTableGrow = {
SymbolicAddress::TableGrow,
_I32,
_Infallible,
4,
{_PTR, _RoN, _I32, _I32, _END}};
constexpr SymbolicAddressSignature SASigTableInit = {
SymbolicAddress::TableInit,
_VOID,
_FailOnNegI32,
6,
{_PTR, _I32, _I32, _I32, _I32, _I32, _END}};
constexpr SymbolicAddressSignature SASigTableSet = {
SymbolicAddress::TableSet,
_VOID,
_FailOnNegI32,
4,
{_PTR, _I32, _RoN, _I32, _END}};
constexpr SymbolicAddressSignature SASigTableSize = {
SymbolicAddress::TableSize, _I32, _Infallible, 2, {_PTR, _I32, _END}};
constexpr SymbolicAddressSignature SASigRefFunc = {
SymbolicAddress::RefFunc, _RoN, _FailOnInvalidRef, 2, {_PTR, _I32, _END}};
constexpr SymbolicAddressSignature SASigPostBarrier = {
SymbolicAddress::PostBarrier, _VOID, _Infallible, 2, {_PTR, _PTR, _END}};
constexpr SymbolicAddressSignature SASigPostBarrierPrecise = {
SymbolicAddress::PostBarrierPrecise,
_VOID,
_Infallible,
3,
{_PTR, _PTR, _RoN, _END}};
constexpr SymbolicAddressSignature SASigPostBarrierPreciseWithOffset = {
SymbolicAddress::PostBarrierPreciseWithOffset,
_VOID,
_Infallible,
4,
{_PTR, _PTR, _I32, _RoN, _END}};
constexpr SymbolicAddressSignature SASigExceptionNew = {
SymbolicAddress::ExceptionNew, _RoN, _FailOnNullPtr, 2, {_PTR, _RoN, _END}};
constexpr SymbolicAddressSignature SASigThrowException = {
SymbolicAddress::ThrowException,
_VOID,
_FailOnNegI32,
2,
{_PTR, _RoN, _END}};
constexpr SymbolicAddressSignature SASigStructNewIL_true = {
SymbolicAddress::StructNewIL_true,
_RoN,
_FailOnNullPtr,
2,
{_PTR, _PTR, _END}};
constexpr SymbolicAddressSignature SASigStructNewIL_false = {
SymbolicAddress::StructNewIL_false,
_RoN,
_FailOnNullPtr,
2,
{_PTR, _PTR, _END}};
constexpr SymbolicAddressSignature SASigStructNewOOL_true = {
SymbolicAddress::StructNewOOL_true,
_RoN,
_FailOnNullPtr,
2,
{_PTR, _PTR, _END}};
constexpr SymbolicAddressSignature SASigStructNewOOL_false = {
SymbolicAddress::StructNewOOL_false,
_RoN,
_FailOnNullPtr,
2,
{_PTR, _PTR, _END}};
constexpr SymbolicAddressSignature SASigArrayNew_true = {
SymbolicAddress::ArrayNew_true,
_RoN,
_FailOnNullPtr,
3,
{_PTR, _I32, _PTR, _END}};
constexpr SymbolicAddressSignature SASigArrayNew_false = {
SymbolicAddress::ArrayNew_false,
_RoN,
_FailOnNullPtr,
3,
{_PTR, _I32, _PTR, _END}};
constexpr SymbolicAddressSignature SASigArrayNewData = {
SymbolicAddress::ArrayNewData,
_RoN,
_FailOnNullPtr,
5,
{_PTR, _I32, _I32, _PTR, _I32, _END}};
constexpr SymbolicAddressSignature SASigArrayNewElem = {
SymbolicAddress::ArrayNewElem,
_RoN,
_FailOnNullPtr,
5,
{_PTR, _I32, _I32, _PTR, _I32, _END}};
constexpr SymbolicAddressSignature SASigArrayInitData = {
SymbolicAddress::ArrayInitData,
_VOID,
_FailOnNegI32,
7,
{_PTR, _RoN, _I32, _I32, _I32, _PTR, _I32, _END}};
constexpr SymbolicAddressSignature SASigArrayInitElem = {
SymbolicAddress::ArrayInitElem,
_VOID,
_FailOnNegI32,
7,
{_PTR, _RoN, _I32, _I32, _I32, _PTR, _I32, _END}};
constexpr SymbolicAddressSignature SASigArrayCopy = {
SymbolicAddress::ArrayCopy,
_VOID,
_FailOnNegI32,
7,
{_PTR, _RoN, _I32, _RoN, _I32, _I32, _I32, _END}};
#define VISIT_BUILTIN_FUNC(op, export, sa_name, ...) \
constexpr SymbolicAddressSignature SASig##sa_name = { \
SymbolicAddress::sa_name, \
DECLARE_BUILTIN_MODULE_FUNC_RESULT_MIRTYPE_##op, \
DECLARE_BUILTIN_MODULE_FUNC_FAILMODE_##op, \
DECLARE_BUILTIN_MODULE_FUNC_PARAM_MIRTYPES_##op};
FOR_EACH_BUILTIN_MODULE_FUNC(VISIT_BUILTIN_FUNC)
#undef VISIT_BUILTIN_FUNC
#ifdef ENABLE_WASM_JSPI
constexpr SymbolicAddressSignature SASigUpdateSuspenderState = {
SymbolicAddress::UpdateSuspenderState,
_VOID,
_Infallible,
3,
{_PTR, _PTR, _I32, _END}};
#endif
} // namespace wasm
} // namespace js
#undef _F64
#undef _F32
#undef _I32
#undef _I64
#undef _PTR
#undef _RoN
#undef _VOID
#undef _END
#undef _Infallible
#undef _FailOnNegI32
#undef _FailOnNullPtr
#ifdef DEBUG
ABIType ToABIType(FailureMode mode) {
switch (mode) {
case FailureMode::FailOnNegI32:
return ABIType::Int32;
case FailureMode::FailOnNullPtr:
case FailureMode::FailOnInvalidRef:
return ABIType::General;
default:
MOZ_CRASH("unexpected failure mode");
}
}
ABIType ToABIType(MIRType type) {
switch (type) {
case MIRType::None:
case MIRType::Int32:
return ABIType::Int32;
case MIRType::Int64:
return ABIType::Int64;
case MIRType::Pointer:
case MIRType::WasmAnyRef:
return ABIType::General;
case MIRType::Float32:
return ABIType::Float32;
case MIRType::Double:
return ABIType::Float64;
default:
MOZ_CRASH("unexpected type");
}
}
ABIFunctionType ToABIType(const SymbolicAddressSignature& sig) {
MOZ_ASSERT_IF(sig.failureMode != FailureMode::Infallible,
ToABIType(sig.failureMode) == ToABIType(sig.retType));
int abiType = 0;
for (int i = 0; i < sig.numArgs; i++) {
abiType <<= ABITypeArgShift;
abiType |= uint32_t(ToABIType(sig.argTypes[i]));
}
abiType <<= ABITypeArgShift;
abiType |= uint32_t(ToABIType(sig.retType));
return ABIFunctionType(abiType);
}
#endif
// ============================================================================
// WebAssembly builtin C++ functions called from wasm code to implement internal
// wasm operations: implementations.
#if defined(JS_CODEGEN_ARM)
extern "C" {
extern MOZ_EXPORT int64_t __aeabi_idivmod(int, int);
extern MOZ_EXPORT int64_t __aeabi_uidivmod(int, int);
}
#endif
// This utility function can only be called for builtins that are called
// directly from wasm code.
static JitActivation* CallingActivation(JSContext* cx) {
Activation* act = cx->activation();
MOZ_ASSERT(act->asJit()->hasWasmExitFP());
return act->asJit();
}
template <typename Fn, typename... Ts>
static bool ForwardToMainStack(Fn fn, JSContext* cx, Ts... args) {
#ifdef ENABLE_WASM_JSPI
if (IsSuspendableStackActive(cx)) {
struct InvokeContext {
bool (*fn)(JSContext*, Ts...);
JSContext* cx;
std::tuple<Ts...> args;
static bool Run(InvokeContext* data) {
return data->fn(data->cx, std::get<Ts>(data->args)...);
}
} data = {fn, cx, std::make_tuple(args...)};
return CallOnMainStack(
cx, reinterpret_cast<CallOnMainStackFn>(InvokeContext::Run), &data);
}
#endif
return fn(cx, args...);
}
static bool WasmHandleDebugTrap() {
JSContext* cx = TlsContext.get(); // Cold code
JitActivation* activation = CallingActivation(cx);
Frame* fp = activation->wasmExitFP();
Instance* instance = GetNearestEffectiveInstance(fp);
const Code& code = instance->code();
MOZ_ASSERT(code.codeMeta().debugEnabled);
// The debug trap stub is the innermost frame. It's return address is the
// actual trap site.
const CallSite* site = code.lookupCallSite(fp->returnAddress());
MOZ_ASSERT(site);
// Advance to the actual trapping frame.
fp = fp->wasmCaller();
DebugFrame* debugFrame = DebugFrame::from(fp);
if (site->kind() == CallSite::EnterFrame) {
if (!instance->debug().enterFrameTrapsEnabled()) {
return true;
}
debugFrame->setIsDebuggee();
debugFrame->observe(cx);
if (!ForwardToMainStack(DebugAPI::onEnterFrame, cx,
js::AbstractFramePtr(debugFrame))) {
if (cx->isPropagatingForcedReturn()) {
cx->clearPropagatingForcedReturn();
// Ignoring forced return because changing code execution order is
// not yet implemented in the wasm baseline.
// TODO properly handle forced return and resume wasm execution.
JS_ReportErrorASCII(cx,
"Unexpected resumption value from onEnterFrame");
}
return false;
}
return true;
}
if (site->kind() == CallSite::LeaveFrame ||
site->kind() == CallSite::CollapseFrame) {
if (site->kind() == CallSite::LeaveFrame &&
!debugFrame->updateReturnJSValue(cx)) {
return false;
}
if (site->kind() == CallSite::CollapseFrame) {
debugFrame->discardReturnJSValue();
}
bool ok = ForwardToMainStack(DebugAPI::onLeaveFrame, cx,
js::AbstractFramePtr(debugFrame),
(const jsbytecode*)nullptr, true);
debugFrame->leave(cx);
return ok;
}
DebugState& debug = instance->debug();
MOZ_ASSERT(debug.hasBreakpointTrapAtOffset(site->lineOrBytecode()));
if (debug.stepModeEnabled(debugFrame->funcIndex())) {
if (!ForwardToMainStack(DebugAPI::onSingleStep, cx)) {
if (cx->isPropagatingForcedReturn()) {
cx->clearPropagatingForcedReturn();
// TODO properly handle forced return.
JS_ReportErrorASCII(cx,
"Unexpected resumption value from onSingleStep");
}
return false;
}
}
if (debug.hasBreakpointSite(site->lineOrBytecode())) {
if (!ForwardToMainStack(DebugAPI::onTrap, cx)) {
if (cx->isPropagatingForcedReturn()) {
cx->clearPropagatingForcedReturn();
// TODO properly handle forced return.
JS_ReportErrorASCII(
cx, "Unexpected resumption value from breakpoint handler");
}
return false;
}
}
return true;
}
// Check if the pending exception, if any, is catchable by wasm.
static WasmExceptionObject* GetOrWrapWasmException(JitActivation* activation,
JSContext* cx) {
if (!cx->isExceptionPending()) {
return nullptr;
}
// Traps are generally not catchable as wasm exceptions. The only case in
// which they are catchable is for Trap::ThrowReported, which the wasm
// compiler uses to throw exceptions and is the source of exceptions from C++.
if (activation->isWasmTrapping() &&
activation->wasmTrapData().trap != Trap::ThrowReported) {
return nullptr;
}
if (cx->isThrowingOverRecursed() || cx->isThrowingOutOfMemory()) {
return nullptr;
}
// Write the exception out here to exn to avoid having to get the pending
// exception and checking for OOM multiple times.
RootedValue exn(cx);
if (cx->getPendingException(&exn)) {
// Check if a JS exception originated from a wasm trap.
if (exn.isObject() && exn.toObject().is<ErrorObject>()) {
ErrorObject& err = exn.toObject().as<ErrorObject>();
if (err.fromWasmTrap()) {
return nullptr;
}
}
// Get or create a wasm exception to represent the pending exception
Rooted<WasmExceptionObject*> wasmExn(cx);
if (exn.isObject() && exn.toObject().is<WasmExceptionObject>()) {
// We're already throwing a wasm exception
wasmExn = &exn.toObject().as<WasmExceptionObject>();
// If wasm is rethrowing a wrapped JS value, then set the pending
// exception on cx to be the wrapped value. This will ensure that if we
// unwind out of wasm the wrapper exception will not escape.
//
// We also do this here, and not at the end of wasm::HandleThrow so that
// any DebugAPI calls see the wrapped JS value, not the wrapper
// exception.
if (wasmExn->isWrappedJSValue()) {
// Re-use exn to avoid needing a new root
exn = wasmExn->wrappedJSValue();
cx->setPendingException(exn, nullptr);
}
} else {
// Wrap all thrown JS values in a wasm exception. This is required so
// that all exceptions have tags, and the 'null' JS value becomes a
// non-null wasm exception.
wasmExn = WasmExceptionObject::wrapJSValue(cx, exn);
}
if (wasmExn) {
return wasmExn;
}
}
MOZ_ASSERT(cx->isThrowingOutOfMemory());
return nullptr;
}
static const wasm::TryNote* FindNonDelegateTryNote(
const wasm::Code& code, const uint8_t* pc, const CodeBlock** codeBlock) {
const wasm::TryNote* tryNote = code.lookupTryNote((void*)pc, codeBlock);
while (tryNote && tryNote->isDelegate()) {
pc = (*codeBlock)->segment->base() + tryNote->delegateOffset();
const wasm::TryNote* delegateTryNote =
code.lookupTryNote((void*)pc, codeBlock);
MOZ_RELEASE_ASSERT(delegateTryNote == nullptr ||
delegateTryNote->tryBodyBegin() <
tryNote->tryBodyBegin());
tryNote = delegateTryNote;
}
return tryNote;
}
// Request tier-2 compilation for the calling wasm function.
static void WasmHandleRequestTierUp(Instance* instance) {
JSContext* cx = instance->cx();
// Don't turn this into a release assert - TlsContext.get() can be expensive.
MOZ_ASSERT(cx == TlsContext.get());
// Neither this routine nor the stub that calls it make any attempt to
// communicate roots to the GC. This is OK because we will only be
// compiling code here, which shouldn't GC. Nevertheless ..
JS::AutoAssertNoGC nogc(cx);
JitActivation* activation = CallingActivation(cx);
Frame* fp = activation->wasmExitFP();
// Similarly, don't turn this into a release assert.
MOZ_ASSERT(instance == GetNearestEffectiveInstance(fp));
// Figure out the requesting funcIndex. We could add a field to the
// Instance and, in the slow path of BaseCompiler::addHotnessCheck, write it
// in there. That would avoid having to call LookupCodeBlock here, but (1)
// LookupCodeBlock is pretty cheap and (2) this would make hotness checks
// larger. It doesn't seem like a worthwhile tradeoff.
void* resumePC = fp->returnAddress();
const CodeRange* codeRange;
const CodeBlock* codeBlock = LookupCodeBlock(resumePC, &codeRange);
MOZ_RELEASE_ASSERT(codeBlock && codeRange);
uint32_t funcIndex = codeRange->funcIndex();
// See BaseCompiler::addHotnessCheck for rationale. If this fails, and
// `counter` is a very large negative number (close to -2^31), it may be that
// a hotness check didn't have its step patched in.
int32_t counter = instance->readHotnessCounter(funcIndex);
MOZ_RELEASE_ASSERT(counter >= -127 && counter <= -1);
// Function `funcIndex` is requesting tier-up. This can go one of three ways:
// - the request is a duplicate -- ignore
// - tier-up compilation succeeds -- we hope
// - tier-up compilation fails (eg, OOMs).
// We have no feasible way to recover.
//
// Regardless of the outcome, we want to defer duplicate requests as long as
// possible. So set the counter to "infinity" right now.
instance->resetHotnessCounter(funcIndex);
// Submit the collected profiling information for call_ref to be available
// for compilation.
instance->submitCallRefHints(funcIndex);
// Try to Ion-compile it. Note that `ok == true` signifies either
// "duplicate request" or "not a duplicate, and compilation succeeded".
bool ok = codeBlock->code->requestTierUp(funcIndex);
// If compilation failed, there's no feasible way to recover. We use the
// 'off thread' logging mechanism to avoid possibly triggering a GC.
if (!ok) {
wasm::LogOffThread("Failed to tier-up function=%d in instance=%p.",
funcIndex, instance);
}
}
// Unwind the activation in response to a thrown exception. This function is
// responsible for notifying the debugger of each unwound frame.
//
// This function will look for try-catch handlers and, if not trapping or
// throwing an uncatchable exception, will write the handler info in |*rfe|.
//
// If no try-catch handler is found, return to the caller to continue unwinding
// JS JIT frames.
void wasm::HandleExceptionWasm(JSContext* cx, JitFrameIter& iter,
jit::ResumeFromException* rfe) {
MOZ_ASSERT(iter.isWasm());
MOZ_ASSERT(CallingActivation(cx) == iter.activation());
MOZ_ASSERT(cx->activation()->asJit()->hasWasmExitFP());
MOZ_ASSERT(rfe->kind == ExceptionResumeKind::EntryFrame);
// WasmFrameIter iterates down wasm frames in the activation starting at
// JitActivation::wasmExitFP(). Calling WasmFrameIter::startUnwinding pops
// JitActivation::wasmExitFP() once each time WasmFrameIter is incremented,
// ultimately leaving no wasm exit FP when the WasmFrameIter is done(). This
// is necessary to prevent a wasm::DebugFrame from being observed again after
// we just called onLeaveFrame (which would lead to the frame being re-added
// to the map of live frames, right as it becomes trash).
#ifdef DEBUG