#include "wasm/WasmBuiltins.h"
#include "mozilla/Atomics.h"
#include "fdlibm.h"
#include "jslibmath.h"
#include "jit/AtomicOperations.h"
#include "jit/InlinableNatives.h"
#include "jit/MacroAssembler.h"
#include "threading/Mutex.h"
#include "wasm/WasmInstance.h"
#include "wasm/WasmStubs.h"
#include "vm/Debugger-inl.h"
#include "vm/Stack-inl.h"
using namespace js;
using namespace jit;
using namespace wasm;
using mozilla::HashGeneric;
using mozilla::IsNaN;
using mozilla::MakeEnumeratedRange;
static const unsigned BUILTIN_THUNK_LIFO_SIZE = 64 * 1024;
#define _F64 MIRType::Double
#define _F32 MIRType::Float32
#define _I32 MIRType::Int32
#define _I64 MIRType::Int64
#define _PTR MIRType::Pointer
#define _RoN MIRType::RefOrNull
#define _VOID MIRType::None
#define _END MIRType::None
namespace js {
namespace wasm {
const SymbolicAddressSignature SASigSinD = {
SymbolicAddress::SinD, _F64, 1, {_F64, _END}};
const SymbolicAddressSignature SASigCosD = {
SymbolicAddress::CosD, _F64, 1, {_F64, _END}};
const SymbolicAddressSignature SASigTanD = {
SymbolicAddress::TanD, _F64, 1, {_F64, _END}};
const SymbolicAddressSignature SASigASinD = {
SymbolicAddress::ASinD, _F64, 1, {_F64, _END}};
const SymbolicAddressSignature SASigACosD = {
SymbolicAddress::ACosD, _F64, 1, {_F64, _END}};
const SymbolicAddressSignature SASigATanD = {
SymbolicAddress::ATanD, _F64, 1, {_F64, _END}};
const SymbolicAddressSignature SASigCeilD = {
SymbolicAddress::CeilD, _F64, 1, {_F64, _END}};
const SymbolicAddressSignature SASigCeilF = {
SymbolicAddress::CeilF, _F32, 1, {_F32, _END}};
const SymbolicAddressSignature SASigFloorD = {
SymbolicAddress::FloorD, _F64, 1, {_F64, _END}};
const SymbolicAddressSignature SASigFloorF = {
SymbolicAddress::FloorF, _F32, 1, {_F32, _END}};
const SymbolicAddressSignature SASigTruncD = {
SymbolicAddress::TruncD, _F64, 1, {_F64, _END}};
const SymbolicAddressSignature SASigTruncF = {
SymbolicAddress::TruncF, _F32, 1, {_F32, _END}};
const SymbolicAddressSignature SASigNearbyIntD = {
SymbolicAddress::NearbyIntD, _F64, 1, {_F64, _END}};
const SymbolicAddressSignature SASigNearbyIntF = {
SymbolicAddress::NearbyIntF, _F32, 1, {_F32, _END}};
const SymbolicAddressSignature SASigExpD = {
SymbolicAddress::ExpD, _F64, 1, {_F64, _END}};
const SymbolicAddressSignature SASigLogD = {
SymbolicAddress::LogD, _F64, 1, {_F64, _END}};
const SymbolicAddressSignature SASigPowD = {
SymbolicAddress::PowD, _F64, 2, {_F64, _F64, _END}};
const SymbolicAddressSignature SASigATan2D = {
SymbolicAddress::ATan2D, _F64, 2, {_F64, _F64, _END}};
const SymbolicAddressSignature SASigMemoryGrow = {
SymbolicAddress::MemoryGrow, _I32, 2, {_PTR, _I32, _END}};
const SymbolicAddressSignature SASigMemorySize = {
SymbolicAddress::MemorySize, _I32, 1, {_PTR, _END}};
const SymbolicAddressSignature SASigWaitI32 = {
SymbolicAddress::WaitI32, _I32, 4, {_PTR, _I32, _I32, _I64, _END}};
const SymbolicAddressSignature SASigWaitI64 = {
SymbolicAddress::WaitI64, _I32, 4, {_PTR, _I32, _I64, _I64, _END}};
const SymbolicAddressSignature SASigWake = {
SymbolicAddress::Wake, _I32, 3, {_PTR, _I32, _I32, _END}};
const SymbolicAddressSignature SASigMemCopy = {
SymbolicAddress::MemCopy, _I32, 4, {_PTR, _I32, _I32, _I32, _END}};
const SymbolicAddressSignature SASigDataDrop = {
SymbolicAddress::DataDrop, _I32, 2, {_PTR, _I32, _END}};
const SymbolicAddressSignature SASigMemFill = {
SymbolicAddress::MemFill, _I32, 4, {_PTR, _I32, _I32, _I32, _END}};
const SymbolicAddressSignature SASigMemInit = {
SymbolicAddress::MemInit, _I32, 5, {_PTR, _I32, _I32, _I32, _I32, _END}};
const SymbolicAddressSignature SASigTableCopy = {
SymbolicAddress::TableCopy,
_I32,
6,
{_PTR, _I32, _I32, _I32, _I32, _I32, _END}};
const SymbolicAddressSignature SASigElemDrop = {
SymbolicAddress::ElemDrop, _I32, 2, {_PTR, _I32, _END}};
const SymbolicAddressSignature SASigTableGet = {
SymbolicAddress::TableGet, _PTR, 3, {_PTR, _I32, _I32, _END}};
const SymbolicAddressSignature SASigTableGrow = {
SymbolicAddress::TableGrow, _I32, 4, {_PTR, _I32, _RoN, _I32, _END}};
const SymbolicAddressSignature SASigTableInit = {
SymbolicAddress::TableInit,
_I32,
6,
{_PTR, _I32, _I32, _I32, _I32, _I32, _END}};
const SymbolicAddressSignature SASigTableSet = {
SymbolicAddress::TableSet, _I32, 4, {_PTR, _I32, _RoN, _I32, _END}};
const SymbolicAddressSignature SASigTableSize = {
SymbolicAddress::TableSize, _I32, 2, {_PTR, _I32, _END}};
const SymbolicAddressSignature SASigPostBarrier = {
SymbolicAddress::PostBarrier, _VOID, 2, {_PTR, _PTR, _END}};
const SymbolicAddressSignature SASigPostBarrierFiltering = {
SymbolicAddress::PostBarrierFiltering, _VOID, 2, {_PTR, _PTR, _END}};
const SymbolicAddressSignature SASigStructNew = {
SymbolicAddress::StructNew, _RoN, 2, {_PTR, _I32, _END}};
const SymbolicAddressSignature SASigStructNarrow = {
SymbolicAddress::StructNarrow, _RoN, 4, {_PTR, _I32, _I32, _RoN, _END}};
} }
#undef _F64
#undef _F32
#undef _I32
#undef _I64
#undef _PTR
#undef _RoN
#undef _VOID
#undef _END
#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
static JitActivation* CallingActivation() {
Activation* act = TlsContext.get()->activation();
MOZ_ASSERT(act->asJit()->hasWasmExitFP());
return act->asJit();
}
static bool WasmHandleDebugTrap() {
JitActivation* activation = CallingActivation();
JSContext* cx = activation->cx();
Frame* fp = activation->wasmExitFP();
Instance* instance = fp->tls->instance;
const Code& code = instance->code();
MOZ_ASSERT(code.metadata().debugEnabled);
const CallSite* site = code.lookupCallSite(fp->returnAddress);
MOZ_ASSERT(site);
fp = fp->callerFP;
DebugFrame* debugFrame = DebugFrame::from(fp);
if (site->kind() == CallSite::EnterFrame) {
if (!instance->debug().enterFrameTrapsEnabled()) {
return true;
}
debugFrame->setIsDebuggee();
debugFrame->observe(cx);
ResumeMode mode = Debugger::onEnterFrame(cx, debugFrame);
if (mode == ResumeMode::Return) {
JS_ReportErrorASCII(cx, "Unexpected resumption value from onEnterFrame");
return false;
}
return mode == ResumeMode::Continue;
}
if (site->kind() == CallSite::LeaveFrame) {
debugFrame->updateReturnJSValue();
bool ok = Debugger::onLeaveFrame(cx, debugFrame, nullptr, true);
debugFrame->leave(cx);
return ok;
}
DebugState& debug = instance->debug();
MOZ_ASSERT(debug.hasBreakpointTrapAtOffset(site->lineOrBytecode()));
if (debug.stepModeEnabled(debugFrame->funcIndex())) {
RootedValue result(cx, UndefinedValue());
ResumeMode mode = Debugger::onSingleStep(cx, &result);
if (mode == ResumeMode::Return) {
JS_ReportErrorASCII(cx, "Unexpected resumption value from onSingleStep");
return false;
}
if (mode != ResumeMode::Continue) {
return false;
}
}
if (debug.hasBreakpointSite(site->lineOrBytecode())) {
RootedValue result(cx, UndefinedValue());
ResumeMode mode = Debugger::onTrap(cx, &result);
if (mode == ResumeMode::Return) {
JS_ReportErrorASCII(
cx, "Unexpected resumption value from breakpoint handler");
return false;
}
if (mode != ResumeMode::Continue) {
return false;
}
}
return true;
}
void* wasm::HandleThrow(JSContext* cx, WasmFrameIter& iter) {
MOZ_ASSERT(CallingActivation() == iter.activation());
MOZ_ASSERT(!iter.done());
iter.setUnwind(WasmFrameIter::Unwind::True);
RootedWasmInstanceObject keepAlive(cx, iter.instance()->object());
for (; !iter.done(); ++iter) {
cx->setRealmForJitExceptionHandler(iter.instance()->realm());
if (!iter.debugEnabled()) {
continue;
}
DebugFrame* frame = iter.debugFrame();
frame->clearReturnJSValue();
if (cx->isExceptionPending()) {
ResumeMode mode = Debugger::onExceptionUnwind(cx, frame);
if (mode == ResumeMode::Return) {
JS_ReportErrorASCII(
cx, "Unexpected resumption value from onExceptionUnwind");
}
}
bool ok = Debugger::onLeaveFrame(cx, frame, nullptr, false);
if (ok) {
JS_ReportErrorASCII(cx, "Unexpected success from onLeaveFrame");
}
frame->leave(cx);
}
MOZ_ASSERT(!cx->activation()->asJit()->isWasmTrapping(),
"unwinding clears the trapping state");
return iter.unwoundAddressOfReturnAddress();
}
static void* WasmHandleThrow() {
JitActivation* activation = CallingActivation();
JSContext* cx = activation->cx();
WasmFrameIter iter(activation);
return HandleThrow(cx, iter);
}
static void* ReportError(JSContext* cx, unsigned errorNumber) {
JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, errorNumber);
return nullptr;
};
static void* CheckInterrupt(JSContext* cx, JitActivation* activation) {
ResetInterruptState(cx);
if (!CheckForInterrupt(cx)) {
return nullptr;
}
void* resumePC = activation->wasmTrapData().resumePC;
activation->finishWasmTrap();
return resumePC;
}
static void* WasmHandleTrap() {
JitActivation* activation = CallingActivation();
JSContext* cx = activation->cx();
switch (activation->wasmTrapData().trap) {
case Trap::Unreachable:
return ReportError(cx, JSMSG_WASM_UNREACHABLE);
case Trap::IntegerOverflow:
return ReportError(cx, JSMSG_WASM_INTEGER_OVERFLOW);
case Trap::InvalidConversionToInteger:
return ReportError(cx, JSMSG_WASM_INVALID_CONVERSION);
case Trap::IntegerDivideByZero:
return ReportError(cx, JSMSG_WASM_INT_DIVIDE_BY_ZERO);
case Trap::IndirectCallToNull:
return ReportError(cx, JSMSG_WASM_IND_CALL_TO_NULL);
case Trap::IndirectCallBadSig:
return ReportError(cx, JSMSG_WASM_IND_CALL_BAD_SIG);
case Trap::NullPointerDereference:
return ReportError(cx, JSMSG_WASM_DEREF_NULL);
case Trap::OutOfBounds:
return ReportError(cx, JSMSG_WASM_OUT_OF_BOUNDS);
case Trap::UnalignedAccess:
return ReportError(cx, JSMSG_WASM_UNALIGNED_ACCESS);
case Trap::CheckInterrupt:
return CheckInterrupt(cx, activation);
case Trap::StackOverflow:
if (!CheckRecursionLimit(cx)) {
return nullptr;
}
if (activation->wasmExitFP()->tls->isInterrupted()) {
return CheckInterrupt(cx, activation);
}
return ReportError(cx, JSMSG_OVER_RECURSED);
case Trap::ThrowReported:
return nullptr;
case Trap::Limit:
break;
}
MOZ_CRASH("unexpected trap");
}
static void WasmReportInt64JSCall() {
JSContext* cx = TlsContext.get();
JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
JSMSG_WASM_BAD_I64_TYPE);
}
static int32_t CoerceInPlace_ToInt32(Value* rawVal) {
JSContext* cx = TlsContext.get();
int32_t i32;
RootedValue val(cx, *rawVal);
if (!ToInt32(cx, val, &i32)) {
*rawVal = PoisonedObjectValue(0x42);
return false;
}
*rawVal = Int32Value(i32);
return true;
}
static int32_t CoerceInPlace_ToNumber(Value* rawVal) {
JSContext* cx = TlsContext.get();
double dbl;
RootedValue val(cx, *rawVal);
if (!ToNumber(cx, val, &dbl)) {
*rawVal = PoisonedObjectValue(0x42);
return false;
}
*rawVal = DoubleValue(dbl);
return true;
}
static int32_t CoerceInPlace_JitEntry(int funcExportIndex, TlsData* tlsData,
Value* argv) {
JSContext* cx = CallingActivation()->cx();
const Code& code = tlsData->instance->code();
const FuncExport& fe =
code.metadata(code.stableTier()).funcExports[funcExportIndex];
for (size_t i = 0; i < fe.funcType().args().length(); i++) {
HandleValue arg = HandleValue::fromMarkedLocation(&argv[i]);
switch (fe.funcType().args()[i].code()) {
case ValType::I32: {
int32_t i32;
if (!ToInt32(cx, arg, &i32)) {
return false;
}
argv[i] = Int32Value(i32);
break;
}
case ValType::F32:
case ValType::F64: {
double dbl;
if (!ToNumber(cx, arg, &dbl)) {
return false;
}
argv[i] = DoubleValue(dbl);
break;
}
default: {
MOZ_CRASH("unexpected input argument in CoerceInPlace_JitEntry");
}
}
}
return true;
}
static int64_t DivI64(uint32_t x_hi, uint32_t x_lo, uint32_t y_hi,
uint32_t y_lo) {
int64_t x = ((uint64_t)x_hi << 32) + x_lo;
int64_t y = ((uint64_t)y_hi << 32) + y_lo;
MOZ_ASSERT(x != INT64_MIN || y != -1);
MOZ_ASSERT(y != 0);
return x / y;
}
static int64_t UDivI64(uint32_t x_hi, uint32_t x_lo, uint32_t y_hi,
uint32_t y_lo) {
uint64_t x = ((uint64_t)x_hi << 32) + x_lo;
uint64_t y = ((uint64_t)y_hi << 32) + y_lo;
MOZ_ASSERT(y != 0);
return x / y;
}
static int64_t ModI64(uint32_t x_hi, uint32_t x_lo, uint32_t y_hi,
uint32_t y_lo) {
int64_t x = ((uint64_t)x_hi << 32) + x_lo;
int64_t y = ((uint64_t)y_hi << 32) + y_lo;
MOZ_ASSERT(x != INT64_MIN || y != -1);
MOZ_ASSERT(y != 0);
return x % y;
}
static int64_t UModI64(uint32_t x_hi, uint32_t x_lo, uint32_t y_hi,
uint32_t y_lo) {
uint64_t x = ((uint64_t)x_hi << 32) + x_lo;
uint64_t y = ((uint64_t)y_hi << 32) + y_lo;
MOZ_ASSERT(y != 0);
return x % y;
}
static int64_t TruncateDoubleToInt64(double input) {
if (input >= double(INT64_MAX) || input < double(INT64_MIN) || IsNaN(input)) {
return 0x8000000000000000;
}
return int64_t(input);
}
static uint64_t TruncateDoubleToUint64(double input) {
if (input >= double(UINT64_MAX) || input <= -1.0 || IsNaN(input)) {
return 0x8000000000000000;
}
return uint64_t(input);
}
static int64_t SaturatingTruncateDoubleToInt64(double input) {
if (fabs(input) < -double(INT64_MIN)) {
return int64_t(input);
}
if (IsNaN(input)) {
return 0;
}
if (input > 0) {
return INT64_MAX;
}
return INT64_MIN;
}
static uint64_t SaturatingTruncateDoubleToUint64(double input) {
if (input >= -double(INT64_MIN) * 2.0) {
return UINT64_MAX;
}
if (input > -1.0) {
return uint64_t(input);
}
return 0;
}
static double Int64ToDouble(int32_t x_hi, uint32_t x_lo) {
int64_t x = int64_t((uint64_t(x_hi) << 32)) + int64_t(x_lo);
return double(x);
}
static float Int64ToFloat32(int32_t x_hi, uint32_t x_lo) {
int64_t x = int64_t((uint64_t(x_hi) << 32)) + int64_t(x_lo);
return float(x);
}
static double Uint64ToDouble(int32_t x_hi, uint32_t x_lo) {
uint64_t x = (uint64_t(x_hi) << 32) + uint64_t(x_lo);
return double(x);
}
static float Uint64ToFloat32(int32_t x_hi, uint32_t x_lo) {
uint64_t x = (uint64_t(x_hi) << 32) + uint64_t(x_lo);
return float(x);
}
template <class F>
static inline void* FuncCast(F* funcPtr, ABIFunctionType abiType) {
void* pf = JS_FUNC_TO_DATA_PTR(void*, funcPtr);
#ifdef JS_SIMULATOR
pf = Simulator::RedirectNativeFunction(pf, abiType);
#endif
return pf;
}
#ifdef WASM_CODEGEN_DEBUG
void wasm::PrintI32(int32_t val) { fprintf(stderr, "i32(%d) ", val); }
void wasm::PrintPtr(uint8_t* val) { fprintf(stderr, "ptr(%p) ", val); }
void wasm::PrintF32(float val) { fprintf(stderr, "f32(%f) ", val); }
void wasm::PrintF64(double val) { fprintf(stderr, "f64(%lf) ", val); }
void wasm::PrintText(const char* out) { fprintf(stderr, "%s", out); }
#endif
void* wasm::AddressOf(SymbolicAddress imm, ABIFunctionType* abiType) {
switch (imm) {
case SymbolicAddress::HandleDebugTrap:
*abiType = Args_General0;
return FuncCast(WasmHandleDebugTrap, *abiType);
case SymbolicAddress::HandleThrow:
*abiType = Args_General0;
return FuncCast(WasmHandleThrow, *abiType);
case SymbolicAddress::HandleTrap:
*abiType = Args_General0;
return FuncCast(WasmHandleTrap, *abiType);
case SymbolicAddress::ReportInt64JSCall:
*abiType = Args_General0;
return FuncCast(WasmReportInt64JSCall, *abiType);
case SymbolicAddress::CallImport_Void:
*abiType = Args_General4;
return FuncCast(Instance::callImport_void, *abiType);
case SymbolicAddress::CallImport_I32:
*abiType = Args_General4;
return FuncCast(Instance::callImport_i32, *abiType);
case SymbolicAddress::CallImport_I64:
*abiType = Args_General4;
return FuncCast(Instance::callImport_i64, *abiType);
case SymbolicAddress::CallImport_F64:
*abiType = Args_General4;
return FuncCast(Instance::callImport_f64, *abiType);
case SymbolicAddress::CallImport_AnyRef:
*abiType = Args_General4;
return FuncCast(Instance::callImport_anyref, *abiType);
case SymbolicAddress::CoerceInPlace_ToInt32:
*abiType = Args_General1;
return FuncCast(CoerceInPlace_ToInt32, *abiType);
case SymbolicAddress::CoerceInPlace_ToNumber:
*abiType = Args_General1;
return FuncCast(CoerceInPlace_ToNumber, *abiType);
case SymbolicAddress::CoerceInPlace_JitEntry:
*abiType = Args_General3;
return FuncCast(CoerceInPlace_JitEntry, *abiType);
case SymbolicAddress::ToInt32:
*abiType = Args_Int_Double;
return FuncCast<int32_t(double)>(JS::ToInt32, *abiType);
case SymbolicAddress::DivI64:
*abiType = Args_General4;
return FuncCast(DivI64, *abiType);
case SymbolicAddress::UDivI64:
*abiType = Args_General4;
return FuncCast(UDivI64, *abiType);
case SymbolicAddress::ModI64:
*abiType = Args_General4;
return FuncCast(ModI64, *abiType);
case SymbolicAddress::UModI64:
*abiType = Args_General4;
return FuncCast(UModI64, *abiType);
case SymbolicAddress::TruncateDoubleToUint64:
*abiType = Args_Int64_Double;
return FuncCast(TruncateDoubleToUint64, *abiType);
case SymbolicAddress::TruncateDoubleToInt64:
*abiType = Args_Int64_Double;
return FuncCast(TruncateDoubleToInt64, *abiType);
case SymbolicAddress::SaturatingTruncateDoubleToUint64:
*abiType = Args_Int64_Double;
return FuncCast(SaturatingTruncateDoubleToUint64, *abiType);
case SymbolicAddress::SaturatingTruncateDoubleToInt64:
*abiType = Args_Int64_Double;
return FuncCast(SaturatingTruncateDoubleToInt64, *abiType);
case SymbolicAddress::Uint64ToDouble:
*abiType = Args_Double_IntInt;
return FuncCast(Uint64ToDouble, *abiType);
case SymbolicAddress::Uint64ToFloat32:
*abiType = Args_Float32_IntInt;
return FuncCast(Uint64ToFloat32, *abiType);
case SymbolicAddress::Int64ToDouble:
*abiType = Args_Double_IntInt;
return FuncCast(Int64ToDouble, *abiType);
case SymbolicAddress::Int64ToFloat32:
*abiType = Args_Float32_IntInt;
return FuncCast(Int64ToFloat32, *abiType);
#if defined(JS_CODEGEN_ARM)
case SymbolicAddress::aeabi_idivmod:
*abiType = Args_General2;
return FuncCast(__aeabi_idivmod, *abiType);
case SymbolicAddress::aeabi_uidivmod:
*abiType = Args_General2;
return FuncCast(__aeabi_uidivmod, *abiType);
#endif
case SymbolicAddress::ModD:
*abiType = Args_Double_DoubleDouble;
return FuncCast(NumberMod, *abiType);
case SymbolicAddress::SinD:
*abiType = Args_Double_Double;
return FuncCast<double(double)>(sin, *abiType);
case SymbolicAddress::CosD:
*abiType = Args_Double_Double;
return FuncCast<double(double)>(cos, *abiType);
case SymbolicAddress::TanD:
*abiType = Args_Double_Double;
return FuncCast<double(double)>(tan, *abiType);
case SymbolicAddress::ASinD:
*abiType = Args_Double_Double;
return FuncCast<double(double)>(fdlibm::asin, *abiType);
case SymbolicAddress::ACosD:
*abiType = Args_Double_Double;
return FuncCast<double(double)>(fdlibm::acos, *abiType);
case SymbolicAddress::ATanD:
*abiType = Args_Double_Double;
return FuncCast<double(double)>(fdlibm::atan, *abiType);
case SymbolicAddress::CeilD:
*abiType = Args_Double_Double;
return FuncCast<double(double)>(fdlibm::ceil, *abiType);
case SymbolicAddress::CeilF:
*abiType = Args_Float32_Float32;
return FuncCast<float(float)>(fdlibm::ceilf, *abiType);
case SymbolicAddress::FloorD:
*abiType = Args_Double_Double;
return FuncCast<double(double)>(fdlibm::floor, *abiType);
case SymbolicAddress::FloorF:
*abiType = Args_Float32_Float32;
return FuncCast<float(float)>(fdlibm::floorf, *abiType);
case SymbolicAddress::TruncD:
*abiType = Args_Double_Double;
return FuncCast<double(double)>(fdlibm::trunc, *abiType);
case SymbolicAddress::TruncF:
*abiType = Args_Float32_Float32;
return FuncCast<float(float)>(fdlibm::truncf, *abiType);
case SymbolicAddress::NearbyIntD:
*abiType = Args_Double_Double;
return FuncCast<double(double)>(fdlibm::nearbyint, *abiType);
case SymbolicAddress::NearbyIntF:
*abiType = Args_Float32_Float32;
return FuncCast<float(float)>(fdlibm::nearbyintf, *abiType);
case SymbolicAddress::ExpD:
*abiType = Args_Double_Double;
return FuncCast<double(double)>(fdlibm::exp, *abiType);
case SymbolicAddress::LogD:
*abiType = Args_Double_Double;
return FuncCast<double(double)>(fdlibm::log, *abiType);
case SymbolicAddress::PowD:
*abiType = Args_Double_DoubleDouble;
return FuncCast(ecmaPow, *abiType);
case SymbolicAddress::ATan2D:
*abiType = Args_Double_DoubleDouble;
return FuncCast(ecmaAtan2, *abiType);
case SymbolicAddress::MemoryGrow:
*abiType = Args_General2;
return FuncCast(Instance::memoryGrow_i32, *abiType);
case SymbolicAddress::MemorySize:
*abiType = Args_General1;
return FuncCast(Instance::memorySize_i32, *abiType);
case SymbolicAddress::WaitI32:
*abiType = Args_Int_GeneralGeneralGeneralInt64;
return FuncCast(Instance::wait_i32, *abiType);
case SymbolicAddress::WaitI64:
*abiType = Args_Int_GeneralGeneralInt64Int64;
return FuncCast(Instance::wait_i64, *abiType);
case SymbolicAddress::Wake:
*abiType = Args_General3;
return FuncCast(Instance::wake, *abiType);
case SymbolicAddress::MemCopy:
*abiType = Args_General4;
return FuncCast(Instance::memCopy, *abiType);
case SymbolicAddress::DataDrop:
*abiType = Args_General2;
return FuncCast(Instance::dataDrop, *abiType);
case SymbolicAddress::MemFill:
*abiType = Args_General4;
return FuncCast(Instance::memFill, *abiType);
case SymbolicAddress::MemInit:
*abiType = Args_General5;
return FuncCast(Instance::memInit, *abiType);
case SymbolicAddress::TableCopy:
*abiType = Args_General6;
return FuncCast(Instance::tableCopy, *abiType);
case SymbolicAddress::ElemDrop:
*abiType = Args_General2;
return FuncCast(Instance::elemDrop, *abiType);
case SymbolicAddress::TableInit:
*abiType = Args_General6;
return FuncCast(Instance::tableInit, *abiType);
case SymbolicAddress::TableGet:
*abiType = Args_General3;
return FuncCast(Instance::tableGet, *abiType);
case SymbolicAddress::TableGrow:
*abiType = Args_General4;
return FuncCast(Instance::tableGrow, *abiType);
case SymbolicAddress::TableSet:
*abiType = Args_General4;
return FuncCast(Instance::tableSet, *abiType);
case SymbolicAddress::TableSize:
*abiType = Args_General2;
return FuncCast(Instance::tableSize, *abiType);
case SymbolicAddress::PostBarrier:
*abiType = Args_General2;
return FuncCast(Instance::postBarrier, *abiType);
case SymbolicAddress::PostBarrierFiltering:
*abiType = Args_General2;
return FuncCast(Instance::postBarrierFiltering, *abiType);
case SymbolicAddress::StructNew:
*abiType = Args_General2;
return FuncCast(Instance::structNew, *abiType);
case SymbolicAddress::StructNarrow:
*abiType = Args_General4;
return FuncCast(Instance::structNarrow, *abiType);
#if defined(JS_CODEGEN_MIPS32)
case SymbolicAddress::js_jit_gAtomic64Lock:
return &js::jit::gAtomic64Lock;
#endif
#ifdef WASM_CODEGEN_DEBUG
case SymbolicAddress::PrintI32:
*abiType = Args_General1;
return FuncCast(PrintI32, *abiType);
case SymbolicAddress::PrintPtr:
*abiType = Args_General1;
return FuncCast(PrintPtr, *abiType);
case SymbolicAddress::PrintF32:
*abiType = Args_Int_Float32;
return FuncCast(PrintF32, *abiType);
case SymbolicAddress::PrintF64:
*abiType = Args_Int_Double;
return FuncCast(PrintF64, *abiType);
case SymbolicAddress::PrintText:
*abiType = Args_General1;
return FuncCast(PrintText, *abiType);
#endif
case SymbolicAddress::Limit:
break;
}
MOZ_CRASH("Bad SymbolicAddress");
}
bool wasm::NeedsBuiltinThunk(SymbolicAddress sym) {
switch (sym) {
case SymbolicAddress::HandleDebugTrap: case SymbolicAddress::HandleThrow: case SymbolicAddress::HandleTrap: case SymbolicAddress::CallImport_Void: case SymbolicAddress::CallImport_I32:
case SymbolicAddress::CallImport_I64:
case SymbolicAddress::CallImport_F64:
case SymbolicAddress::CallImport_AnyRef:
case SymbolicAddress::CoerceInPlace_ToInt32: case SymbolicAddress::CoerceInPlace_ToNumber:
#if defined(JS_CODEGEN_MIPS32)
case SymbolicAddress::js_jit_gAtomic64Lock:
#endif
#ifdef WASM_CODEGEN_DEBUG
case SymbolicAddress::PrintI32:
case SymbolicAddress::PrintPtr:
case SymbolicAddress::PrintF32:
case SymbolicAddress::PrintF64:
case SymbolicAddress::PrintText: #endif
return false;
case SymbolicAddress::ToInt32:
case SymbolicAddress::DivI64:
case SymbolicAddress::UDivI64:
case SymbolicAddress::ModI64:
case SymbolicAddress::UModI64:
case SymbolicAddress::TruncateDoubleToUint64:
case SymbolicAddress::TruncateDoubleToInt64:
case SymbolicAddress::SaturatingTruncateDoubleToUint64:
case SymbolicAddress::SaturatingTruncateDoubleToInt64:
case SymbolicAddress::Uint64ToDouble:
case SymbolicAddress::Uint64ToFloat32:
case SymbolicAddress::Int64ToDouble:
case SymbolicAddress::Int64ToFloat32:
#if defined(JS_CODEGEN_ARM)
case SymbolicAddress::aeabi_idivmod:
case SymbolicAddress::aeabi_uidivmod:
#endif
case SymbolicAddress::ModD:
case SymbolicAddress::SinD:
case SymbolicAddress::CosD:
case SymbolicAddress::TanD:
case SymbolicAddress::ASinD:
case SymbolicAddress::ACosD:
case SymbolicAddress::ATanD:
case SymbolicAddress::CeilD:
case SymbolicAddress::CeilF:
case SymbolicAddress::FloorD:
case SymbolicAddress::FloorF:
case SymbolicAddress::TruncD:
case SymbolicAddress::TruncF:
case SymbolicAddress::NearbyIntD:
case SymbolicAddress::NearbyIntF:
case SymbolicAddress::ExpD:
case SymbolicAddress::LogD:
case SymbolicAddress::PowD:
case SymbolicAddress::ATan2D:
case SymbolicAddress::MemoryGrow:
case SymbolicAddress::MemorySize:
case SymbolicAddress::WaitI32:
case SymbolicAddress::WaitI64:
case SymbolicAddress::Wake:
case SymbolicAddress::CoerceInPlace_JitEntry:
case SymbolicAddress::ReportInt64JSCall:
case SymbolicAddress::MemCopy:
case SymbolicAddress::DataDrop:
case SymbolicAddress::MemFill:
case SymbolicAddress::MemInit:
case SymbolicAddress::TableCopy:
case SymbolicAddress::ElemDrop:
case SymbolicAddress::TableGet:
case SymbolicAddress::TableGrow:
case SymbolicAddress::TableInit:
case SymbolicAddress::TableSet:
case SymbolicAddress::TableSize:
case SymbolicAddress::PostBarrier:
case SymbolicAddress::PostBarrierFiltering:
case SymbolicAddress::StructNew:
case SymbolicAddress::StructNarrow:
return true;
case SymbolicAddress::Limit:
break;
}
MOZ_CRASH("unexpected symbolic address");
}
#define FOR_EACH_UNARY_NATIVE(_) \
_(math_sin, MathSin) \
_(math_tan, MathTan) \
_(math_cos, MathCos) \
_(math_exp, MathExp) \
_(math_log, MathLog) \
_(math_asin, MathASin) \
_(math_atan, MathATan) \
_(math_acos, MathACos) \
_(math_log10, MathLog10) \
_(math_log2, MathLog2) \
_(math_log1p, MathLog1P) \
_(math_expm1, MathExpM1) \
_(math_sinh, MathSinH) \
_(math_tanh, MathTanH) \
_(math_cosh, MathCosH) \
_(math_asinh, MathASinH) \
_(math_atanh, MathATanH) \
_(math_acosh, MathACosH) \
_(math_sign, MathSign) \
_(math_trunc, MathTrunc) \
_(math_cbrt, MathCbrt)
#define FOR_EACH_BINARY_NATIVE(_) \
_(ecmaAtan2, MathATan2) \
_(ecmaHypot, MathHypot) \
_(ecmaPow, MathPow)
#define DEFINE_UNARY_FLOAT_WRAPPER(func, _) \
static float func##_impl_f32(float x) { \
return float(func##_impl(double(x))); \
}
#define DEFINE_BINARY_FLOAT_WRAPPER(func, _) \
static float func##_f32(float x, float y) { \
return float(func(double(x), double(y))); \
}
FOR_EACH_UNARY_NATIVE(DEFINE_UNARY_FLOAT_WRAPPER)
FOR_EACH_BINARY_NATIVE(DEFINE_BINARY_FLOAT_WRAPPER)
#undef DEFINE_UNARY_FLOAT_WRAPPER
#undef DEFINE_BINARY_FLOAT_WRAPPER
struct TypedNative {
InlinableNative native;
ABIFunctionType abiType;
TypedNative(InlinableNative native, ABIFunctionType abiType)
: native(native), abiType(abiType) {}
typedef TypedNative Lookup;
static HashNumber hash(const Lookup& l) {
return HashGeneric(uint32_t(l.native), uint32_t(l.abiType));
}
static bool match(const TypedNative& lhs, const Lookup& rhs) {
return lhs.native == rhs.native && lhs.abiType == rhs.abiType;
}
};
using TypedNativeToFuncPtrMap =
HashMap<TypedNative, void*, TypedNative, SystemAllocPolicy>;
static bool PopulateTypedNatives(TypedNativeToFuncPtrMap* typedNatives) {
#define ADD_OVERLOAD(funcName, native, abiType) \
if (!typedNatives->putNew(TypedNative(InlinableNative::native, abiType), \
FuncCast(funcName, abiType))) \
return false;
#define ADD_UNARY_OVERLOADS(funcName, native) \
ADD_OVERLOAD(funcName##_impl, native, Args_Double_Double) \
ADD_OVERLOAD(funcName##_impl_f32, native, Args_Float32_Float32)
#define ADD_BINARY_OVERLOADS(funcName, native) \
ADD_OVERLOAD(funcName, native, Args_Double_DoubleDouble) \
ADD_OVERLOAD(funcName##_f32, native, Args_Float32_Float32Float32)
FOR_EACH_UNARY_NATIVE(ADD_UNARY_OVERLOADS)
FOR_EACH_BINARY_NATIVE(ADD_BINARY_OVERLOADS)
#undef ADD_UNARY_OVERLOADS
#undef ADD_BINARY_OVERLOADS
return true;
}
#undef FOR_EACH_UNARY_NATIVE
#undef FOR_EACH_BINARY_NATIVE
using TypedNativeToCodeRangeMap =
HashMap<TypedNative, uint32_t, TypedNative, SystemAllocPolicy>;
using SymbolicAddressToCodeRangeArray =
EnumeratedArray<SymbolicAddress, SymbolicAddress::Limit, uint32_t>;
struct BuiltinThunks {
uint8_t* codeBase;
size_t codeSize;
CodeRangeVector codeRanges;
TypedNativeToCodeRangeMap typedNativeToCodeRange;
SymbolicAddressToCodeRangeArray symbolicAddressToCodeRange;
BuiltinThunks() : codeBase(nullptr), codeSize(0) {}
~BuiltinThunks() {
if (codeBase) {
DeallocateExecutableMemory(codeBase, codeSize);
}
}
};
Mutex initBuiltinThunks(mutexid::WasmInitBuiltinThunks);
Atomic<const BuiltinThunks*> builtinThunks;
bool wasm::EnsureBuiltinThunksInitialized() {
LockGuard<Mutex> guard(initBuiltinThunks);
if (builtinThunks) {
return true;
}
auto thunks = MakeUnique<BuiltinThunks>();
if (!thunks) {
return false;
}
LifoAlloc lifo(BUILTIN_THUNK_LIFO_SIZE);
TempAllocator tempAlloc(&lifo);
WasmMacroAssembler masm(tempAlloc);
for (auto sym : MakeEnumeratedRange(SymbolicAddress::Limit)) {
if (!NeedsBuiltinThunk(sym)) {
thunks->symbolicAddressToCodeRange[sym] = UINT32_MAX;
continue;
}
uint32_t codeRangeIndex = thunks->codeRanges.length();
thunks->symbolicAddressToCodeRange[sym] = codeRangeIndex;
ABIFunctionType abiType;
void* funcPtr = AddressOf(sym, &abiType);
ExitReason exitReason(sym);
CallableOffsets offsets;
if (!GenerateBuiltinThunk(masm, abiType, exitReason, funcPtr, &offsets)) {
return false;
}
if (!thunks->codeRanges.emplaceBack(CodeRange::BuiltinThunk, offsets)) {
return false;
}
}
TypedNativeToFuncPtrMap typedNatives;
if (!PopulateTypedNatives(&typedNatives)) {
return false;
}
for (TypedNativeToFuncPtrMap::Range r = typedNatives.all(); !r.empty();
r.popFront()) {
TypedNative typedNative = r.front().key();
uint32_t codeRangeIndex = thunks->codeRanges.length();
if (!thunks->typedNativeToCodeRange.putNew(typedNative, codeRangeIndex)) {
return false;
}
ABIFunctionType abiType = typedNative.abiType;
void* funcPtr = r.front().value();
ExitReason exitReason = ExitReason::Fixed::BuiltinNative;
CallableOffsets offsets;
if (!GenerateBuiltinThunk(masm, abiType, exitReason, funcPtr, &offsets)) {
return false;
}
if (!thunks->codeRanges.emplaceBack(CodeRange::BuiltinThunk, offsets)) {
return false;
}
}
masm.finish();
if (masm.oom()) {
return false;
}
size_t allocSize = AlignBytes(masm.bytesNeeded(), ExecutableCodePageSize);
thunks->codeSize = allocSize;
thunks->codeBase = (uint8_t*)AllocateExecutableMemory(
allocSize, ProtectionSetting::Writable, MemCheckKind::MakeUndefined);
if (!thunks->codeBase) {
return false;
}
masm.executableCopy(thunks->codeBase, false);
memset(thunks->codeBase + masm.bytesNeeded(), 0,
allocSize - masm.bytesNeeded());
masm.processCodeLabels(thunks->codeBase);
PatchDebugSymbolicAccesses(thunks->codeBase, masm);
MOZ_ASSERT(masm.callSites().empty());
MOZ_ASSERT(masm.callSiteTargets().empty());
MOZ_ASSERT(masm.callFarJumps().empty());
MOZ_ASSERT(masm.trapSites().empty());
MOZ_ASSERT(masm.callFarJumps().empty());
ExecutableAllocator::cacheFlush(thunks->codeBase, thunks->codeSize);
if (!ExecutableAllocator::makeExecutable(thunks->codeBase,
thunks->codeSize)) {
return false;
}
builtinThunks = thunks.release();
return true;
}
void wasm::ReleaseBuiltinThunks() {
if (builtinThunks) {
const BuiltinThunks* ptr = builtinThunks;
js_delete(const_cast<BuiltinThunks*>(ptr));
builtinThunks = nullptr;
}
}
void* wasm::SymbolicAddressTarget(SymbolicAddress sym) {
MOZ_ASSERT(builtinThunks);
ABIFunctionType abiType;
void* funcPtr = AddressOf(sym, &abiType);
if (!NeedsBuiltinThunk(sym)) {
return funcPtr;
}
const BuiltinThunks& thunks = *builtinThunks;
uint32_t codeRangeIndex = thunks.symbolicAddressToCodeRange[sym];
return thunks.codeBase + thunks.codeRanges[codeRangeIndex].begin();
}
static Maybe<ABIFunctionType> ToBuiltinABIFunctionType(
const FuncType& funcType) {
const ValTypeVector& args = funcType.args();
ExprType ret = funcType.ret();
uint32_t abiType;
switch (ret.code()) {
case ExprType::F32:
abiType = ArgType_Float32 << RetType_Shift;
break;
case ExprType::F64:
abiType = ArgType_Double << RetType_Shift;
break;
default:
return Nothing();
}
if ((args.length() + 1) > (sizeof(uint32_t) * 8 / ArgType_Shift)) {
return Nothing();
}
for (size_t i = 0; i < args.length(); i++) {
switch (args[i].code()) {
case ValType::F32:
abiType |= (ArgType_Float32 << (ArgType_Shift * (i + 1)));
break;
case ValType::F64:
abiType |= (ArgType_Double << (ArgType_Shift * (i + 1)));
break;
default:
return Nothing();
}
}
return Some(ABIFunctionType(abiType));
}
void* wasm::MaybeGetBuiltinThunk(HandleFunction f, const FuncType& funcType) {
MOZ_ASSERT(builtinThunks);
if (!f->isNative() || !f->hasJitInfo() ||
f->jitInfo()->type() != JSJitInfo::InlinableNative) {
return nullptr;
}
Maybe<ABIFunctionType> abiType = ToBuiltinABIFunctionType(funcType);
if (!abiType) {
return nullptr;
}
TypedNative typedNative(f->jitInfo()->inlinableNative, *abiType);
const BuiltinThunks& thunks = *builtinThunks;
auto p = thunks.typedNativeToCodeRange.readonlyThreadsafeLookup(typedNative);
if (!p) {
return nullptr;
}
return thunks.codeBase + thunks.codeRanges[p->value()].begin();
}
bool wasm::LookupBuiltinThunk(void* pc, const CodeRange** codeRange,
uint8_t** codeBase) {
if (!builtinThunks) {
return false;
}
const BuiltinThunks& thunks = *builtinThunks;
if (pc < thunks.codeBase || pc >= thunks.codeBase + thunks.codeSize) {
return false;
}
*codeBase = thunks.codeBase;
CodeRange::OffsetInCode target((uint8_t*)pc - thunks.codeBase);
*codeRange = LookupInSorted(thunks.codeRanges, target);
return !!*codeRange;
}