summaryrefslogtreecommitdiffstats
path: root/js/src/wasm/WasmStubs.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/wasm/WasmStubs.cpp')
-rw-r--r--js/src/wasm/WasmStubs.cpp1151
1 files changed, 1151 insertions, 0 deletions
diff --git a/js/src/wasm/WasmStubs.cpp b/js/src/wasm/WasmStubs.cpp
new file mode 100644
index 000000000..4f56430b9
--- /dev/null
+++ b/js/src/wasm/WasmStubs.cpp
@@ -0,0 +1,1151 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
+ *
+ * Copyright 2015 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
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * 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/WasmStubs.h"
+
+#include "mozilla/ArrayUtils.h"
+
+#include "wasm/WasmCode.h"
+#include "wasm/WasmIonCompile.h"
+
+#include "jit/MacroAssembler-inl.h"
+
+using namespace js;
+using namespace js::jit;
+using namespace js::wasm;
+
+using mozilla::ArrayLength;
+
+static void
+AssertStackAlignment(MacroAssembler& masm, uint32_t alignment, uint32_t addBeforeAssert = 0)
+{
+ MOZ_ASSERT((sizeof(Frame) + masm.framePushed() + addBeforeAssert) % alignment == 0);
+ masm.assertStackAlignment(alignment, addBeforeAssert);
+}
+
+static unsigned
+StackDecrementForCall(MacroAssembler& masm, uint32_t alignment, unsigned bytesToPush)
+{
+ return StackDecrementForCall(alignment, sizeof(Frame) + masm.framePushed(), bytesToPush);
+}
+
+template <class VectorT>
+static unsigned
+StackArgBytes(const VectorT& args)
+{
+ ABIArgIter<VectorT> iter(args);
+ while (!iter.done())
+ iter++;
+ return iter.stackBytesConsumedSoFar();
+}
+
+template <class VectorT>
+static unsigned
+StackDecrementForCall(MacroAssembler& masm, uint32_t alignment, const VectorT& args,
+ unsigned extraBytes = 0)
+{
+ return StackDecrementForCall(masm, alignment, StackArgBytes(args) + extraBytes);
+}
+
+#if defined(JS_CODEGEN_ARM)
+// The ARM system ABI also includes d15 & s31 in the non volatile float registers.
+// Also exclude lr (a.k.a. r14) as we preserve it manually)
+static const LiveRegisterSet NonVolatileRegs =
+ LiveRegisterSet(GeneralRegisterSet(Registers::NonVolatileMask&
+ ~(uint32_t(1) << Registers::lr)),
+ FloatRegisterSet(FloatRegisters::NonVolatileMask
+ | (1ULL << FloatRegisters::d15)
+ | (1ULL << FloatRegisters::s31)));
+#else
+static const LiveRegisterSet NonVolatileRegs =
+ LiveRegisterSet(GeneralRegisterSet(Registers::NonVolatileMask),
+ FloatRegisterSet(FloatRegisters::NonVolatileMask));
+#endif
+
+#if defined(JS_CODEGEN_MIPS32)
+// Mips is using one more double slot due to stack alignment for double values.
+// Look at MacroAssembler::PushRegsInMask(RegisterSet set)
+static const unsigned FramePushedAfterSave = NonVolatileRegs.gprs().size() * sizeof(intptr_t) +
+ NonVolatileRegs.fpus().getPushSizeInBytes() +
+ sizeof(double);
+#elif defined(JS_CODEGEN_NONE)
+static const unsigned FramePushedAfterSave = 0;
+#else
+static const unsigned FramePushedAfterSave = NonVolatileRegs.gprs().size() * sizeof(intptr_t)
+ + NonVolatileRegs.fpus().getPushSizeInBytes();
+#endif
+static const unsigned FramePushedForEntrySP = FramePushedAfterSave + sizeof(void*);
+
+// Generate a stub that enters wasm from a C++ caller via the native ABI. The
+// signature of the entry point is Module::ExportFuncPtr. The exported wasm
+// function has an ABI derived from its specific signature, so this function
+// must map from the ABI of ExportFuncPtr to the export's signature's ABI.
+Offsets
+wasm::GenerateEntry(MacroAssembler& masm, const FuncExport& fe)
+{
+ masm.haltingAlign(CodeAlignment);
+
+ Offsets offsets;
+ offsets.begin = masm.currentOffset();
+
+ // Save the return address if it wasn't already saved by the call insn.
+#if defined(JS_CODEGEN_ARM)
+ masm.push(lr);
+#elif defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)
+ masm.push(ra);
+#endif
+
+ // Save all caller non-volatile registers before we clobber them here and in
+ // the asm.js callee (which does not preserve non-volatile registers).
+ masm.setFramePushed(0);
+ masm.PushRegsInMask(NonVolatileRegs);
+ MOZ_ASSERT(masm.framePushed() == FramePushedAfterSave);
+
+ // Put the 'argv' argument into a non-argument/return/TLS register so that
+ // we can use 'argv' while we fill in the arguments for the asm.js callee.
+ Register argv = ABINonArgReturnReg0;
+ Register scratch = ABINonArgReturnReg1;
+
+ // Read the arguments of wasm::ExportFuncPtr according to the native ABI.
+ // The entry stub's frame is only 1 word, not the usual 2 for wasm::Frame.
+ const unsigned argBase = sizeof(void*) + masm.framePushed();
+ ABIArgGenerator abi;
+ ABIArg arg;
+
+ // arg 1: ExportArg*
+ arg = abi.next(MIRType::Pointer);
+ if (arg.kind() == ABIArg::GPR)
+ masm.movePtr(arg.gpr(), argv);
+ else
+ masm.loadPtr(Address(masm.getStackPointer(), argBase + arg.offsetFromArgBase()), argv);
+
+ // Arg 2: TlsData*
+ arg = abi.next(MIRType::Pointer);
+ if (arg.kind() == ABIArg::GPR)
+ masm.movePtr(arg.gpr(), WasmTlsReg);
+ else
+ masm.loadPtr(Address(masm.getStackPointer(), argBase + arg.offsetFromArgBase()), WasmTlsReg);
+
+ // Setup pinned registers that are assumed throughout wasm code.
+ masm.loadWasmPinnedRegsFromTls();
+
+ // Save 'argv' on the stack so that we can recover it after the call. Use
+ // a second non-argument/return register as temporary scratch.
+ masm.Push(argv);
+
+ // Save the stack pointer in the WasmActivation right before dynamically
+ // aligning the stack so that it may be recovered on return or throw.
+ MOZ_ASSERT(masm.framePushed() == FramePushedForEntrySP);
+ masm.loadWasmActivationFromTls(scratch);
+ masm.storeStackPtr(Address(scratch, WasmActivation::offsetOfEntrySP()));
+
+ // Dynamically align the stack since ABIStackAlignment is not necessarily
+ // WasmStackAlignment. We'll use entrySP to recover the original stack
+ // pointer on return.
+ masm.andToStackPtr(Imm32(~(WasmStackAlignment - 1)));
+
+ // Bump the stack for the call.
+ masm.reserveStack(AlignBytes(StackArgBytes(fe.sig().args()), WasmStackAlignment));
+
+ // Copy parameters out of argv and into the registers/stack-slots specified by
+ // the system ABI.
+ for (ABIArgValTypeIter iter(fe.sig().args()); !iter.done(); iter++) {
+ unsigned argOffset = iter.index() * sizeof(ExportArg);
+ Address src(argv, argOffset);
+ MIRType type = iter.mirType();
+ switch (iter->kind()) {
+ case ABIArg::GPR:
+ if (type == MIRType::Int32)
+ masm.load32(src, iter->gpr());
+ else if (type == MIRType::Int64)
+ masm.load64(src, iter->gpr64());
+ break;
+#ifdef JS_CODEGEN_REGISTER_PAIR
+ case ABIArg::GPR_PAIR:
+ if (type == MIRType::Int64)
+ masm.load64(src, iter->gpr64());
+ else
+ MOZ_CRASH("wasm uses hardfp for function calls.");
+ break;
+#endif
+ case ABIArg::FPU: {
+ static_assert(sizeof(ExportArg) >= jit::Simd128DataSize,
+ "ExportArg must be big enough to store SIMD values");
+ switch (type) {
+ case MIRType::Int8x16:
+ case MIRType::Int16x8:
+ case MIRType::Int32x4:
+ case MIRType::Bool8x16:
+ case MIRType::Bool16x8:
+ case MIRType::Bool32x4:
+ masm.loadUnalignedSimd128Int(src, iter->fpu());
+ break;
+ case MIRType::Float32x4:
+ masm.loadUnalignedSimd128Float(src, iter->fpu());
+ break;
+ case MIRType::Double:
+ masm.loadDouble(src, iter->fpu());
+ break;
+ case MIRType::Float32:
+ masm.loadFloat32(src, iter->fpu());
+ break;
+ default:
+ MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("unexpected FPU type");
+ break;
+ }
+ break;
+ }
+ case ABIArg::Stack:
+ switch (type) {
+ case MIRType::Int32:
+ masm.load32(src, scratch);
+ masm.storePtr(scratch, Address(masm.getStackPointer(), iter->offsetFromArgBase()));
+ break;
+ case MIRType::Int64: {
+ Register sp = masm.getStackPointer();
+#if JS_BITS_PER_WORD == 32
+ masm.load32(Address(src.base, src.offset + INT64LOW_OFFSET), scratch);
+ masm.store32(scratch, Address(sp, iter->offsetFromArgBase() + INT64LOW_OFFSET));
+ masm.load32(Address(src.base, src.offset + INT64HIGH_OFFSET), scratch);
+ masm.store32(scratch, Address(sp, iter->offsetFromArgBase() + INT64HIGH_OFFSET));
+#else
+ Register64 scratch64(scratch);
+ masm.load64(src, scratch64);
+ masm.store64(scratch64, Address(sp, iter->offsetFromArgBase()));
+#endif
+ break;
+ }
+ case MIRType::Double:
+ masm.loadDouble(src, ScratchDoubleReg);
+ masm.storeDouble(ScratchDoubleReg,
+ Address(masm.getStackPointer(), iter->offsetFromArgBase()));
+ break;
+ case MIRType::Float32:
+ masm.loadFloat32(src, ScratchFloat32Reg);
+ masm.storeFloat32(ScratchFloat32Reg,
+ Address(masm.getStackPointer(), iter->offsetFromArgBase()));
+ break;
+ case MIRType::Int8x16:
+ case MIRType::Int16x8:
+ case MIRType::Int32x4:
+ case MIRType::Bool8x16:
+ case MIRType::Bool16x8:
+ case MIRType::Bool32x4:
+ masm.loadUnalignedSimd128Int(src, ScratchSimd128Reg);
+ masm.storeAlignedSimd128Int(
+ ScratchSimd128Reg, Address(masm.getStackPointer(), iter->offsetFromArgBase()));
+ break;
+ case MIRType::Float32x4:
+ masm.loadUnalignedSimd128Float(src, ScratchSimd128Reg);
+ masm.storeAlignedSimd128Float(
+ ScratchSimd128Reg, Address(masm.getStackPointer(), iter->offsetFromArgBase()));
+ break;
+ default:
+ MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("unexpected stack arg type");
+ }
+ break;
+ }
+ }
+
+ // Call into the real function.
+ masm.assertStackAlignment(WasmStackAlignment);
+ masm.call(CallSiteDesc(CallSiteDesc::Func), fe.funcIndex());
+
+ // Recover the stack pointer value before dynamic alignment.
+ masm.loadWasmActivationFromTls(scratch);
+ masm.loadStackPtr(Address(scratch, WasmActivation::offsetOfEntrySP()));
+ masm.setFramePushed(FramePushedForEntrySP);
+
+ // Recover the 'argv' pointer which was saved before aligning the stack.
+ masm.Pop(argv);
+
+ // Store the return value in argv[0]
+ switch (fe.sig().ret()) {
+ case ExprType::Void:
+ break;
+ case ExprType::I32:
+ masm.store32(ReturnReg, Address(argv, 0));
+ break;
+ case ExprType::I64:
+ masm.store64(ReturnReg64, Address(argv, 0));
+ break;
+ case ExprType::F32:
+ if (!JitOptions.wasmTestMode)
+ masm.canonicalizeFloat(ReturnFloat32Reg);
+ masm.storeFloat32(ReturnFloat32Reg, Address(argv, 0));
+ break;
+ case ExprType::F64:
+ if (!JitOptions.wasmTestMode)
+ masm.canonicalizeDouble(ReturnDoubleReg);
+ masm.storeDouble(ReturnDoubleReg, Address(argv, 0));
+ break;
+ case ExprType::I8x16:
+ case ExprType::I16x8:
+ case ExprType::I32x4:
+ case ExprType::B8x16:
+ case ExprType::B16x8:
+ case ExprType::B32x4:
+ // We don't have control on argv alignment, do an unaligned access.
+ masm.storeUnalignedSimd128Int(ReturnSimd128Reg, Address(argv, 0));
+ break;
+ case ExprType::F32x4:
+ // We don't have control on argv alignment, do an unaligned access.
+ masm.storeUnalignedSimd128Float(ReturnSimd128Reg, Address(argv, 0));
+ break;
+ case ExprType::Limit:
+ MOZ_CRASH("Limit");
+ }
+
+ // Restore clobbered non-volatile registers of the caller.
+ masm.PopRegsInMask(NonVolatileRegs);
+ MOZ_ASSERT(masm.framePushed() == 0);
+
+ masm.move32(Imm32(true), ReturnReg);
+ masm.ret();
+
+ offsets.end = masm.currentOffset();
+ return offsets;
+}
+
+static void
+StackCopy(MacroAssembler& masm, MIRType type, Register scratch, Address src, Address dst)
+{
+ if (type == MIRType::Int32) {
+ masm.load32(src, scratch);
+ masm.store32(scratch, dst);
+ } else if (type == MIRType::Int64) {
+#if JS_BITS_PER_WORD == 32
+ masm.load32(Address(src.base, src.offset + INT64LOW_OFFSET), scratch);
+ masm.store32(scratch, Address(dst.base, dst.offset + INT64LOW_OFFSET));
+ masm.load32(Address(src.base, src.offset + INT64HIGH_OFFSET), scratch);
+ masm.store32(scratch, Address(dst.base, dst.offset + INT64HIGH_OFFSET));
+#else
+ Register64 scratch64(scratch);
+ masm.load64(src, scratch64);
+ masm.store64(scratch64, dst);
+#endif
+ } else if (type == MIRType::Float32) {
+ masm.loadFloat32(src, ScratchFloat32Reg);
+ masm.storeFloat32(ScratchFloat32Reg, dst);
+ } else {
+ MOZ_ASSERT(type == MIRType::Double);
+ masm.loadDouble(src, ScratchDoubleReg);
+ masm.storeDouble(ScratchDoubleReg, dst);
+ }
+}
+
+typedef bool ToValue;
+
+static void
+FillArgumentArray(MacroAssembler& masm, const ValTypeVector& args, unsigned argOffset,
+ unsigned offsetToCallerStackArgs, Register scratch, ToValue toValue)
+{
+ for (ABIArgValTypeIter i(args); !i.done(); i++) {
+ Address dst(masm.getStackPointer(), argOffset + i.index() * sizeof(Value));
+
+ MIRType type = i.mirType();
+ switch (i->kind()) {
+ case ABIArg::GPR:
+ if (type == MIRType::Int32) {
+ if (toValue)
+ masm.storeValue(JSVAL_TYPE_INT32, i->gpr(), dst);
+ else
+ masm.store32(i->gpr(), dst);
+ } else if (type == MIRType::Int64) {
+ // We can't box int64 into Values (yet).
+ if (toValue)
+ masm.breakpoint();
+ else
+ masm.store64(i->gpr64(), dst);
+ } else {
+ MOZ_CRASH("unexpected input type?");
+ }
+ break;
+#ifdef JS_CODEGEN_REGISTER_PAIR
+ case ABIArg::GPR_PAIR:
+ if (type == MIRType::Int64)
+ masm.store64(i->gpr64(), dst);
+ else
+ MOZ_CRASH("wasm uses hardfp for function calls.");
+ break;
+#endif
+ case ABIArg::FPU: {
+ MOZ_ASSERT(IsFloatingPointType(type));
+ FloatRegister srcReg = i->fpu();
+ if (type == MIRType::Double) {
+ if (toValue) {
+ // Preserve the NaN pattern in the input.
+ masm.moveDouble(srcReg, ScratchDoubleReg);
+ srcReg = ScratchDoubleReg;
+ masm.canonicalizeDouble(srcReg);
+ }
+ masm.storeDouble(srcReg, dst);
+ } else {
+ MOZ_ASSERT(type == MIRType::Float32);
+ if (toValue) {
+ // JS::Values can't store Float32, so convert to a Double.
+ masm.convertFloat32ToDouble(srcReg, ScratchDoubleReg);
+ masm.canonicalizeDouble(ScratchDoubleReg);
+ masm.storeDouble(ScratchDoubleReg, dst);
+ } else {
+ // Preserve the NaN pattern in the input.
+ masm.moveFloat32(srcReg, ScratchFloat32Reg);
+ masm.canonicalizeFloat(ScratchFloat32Reg);
+ masm.storeFloat32(ScratchFloat32Reg, dst);
+ }
+ }
+ break;
+ }
+ case ABIArg::Stack: {
+ Address src(masm.getStackPointer(), offsetToCallerStackArgs + i->offsetFromArgBase());
+ if (toValue) {
+ if (type == MIRType::Int32) {
+ masm.load32(src, scratch);
+ masm.storeValue(JSVAL_TYPE_INT32, scratch, dst);
+ } else if (type == MIRType::Int64) {
+ // We can't box int64 into Values (yet).
+ masm.breakpoint();
+ } else {
+ MOZ_ASSERT(IsFloatingPointType(type));
+ if (type == MIRType::Float32) {
+ masm.loadFloat32(src, ScratchFloat32Reg);
+ masm.convertFloat32ToDouble(ScratchFloat32Reg, ScratchDoubleReg);
+ } else {
+ masm.loadDouble(src, ScratchDoubleReg);
+ }
+ masm.canonicalizeDouble(ScratchDoubleReg);
+ masm.storeDouble(ScratchDoubleReg, dst);
+ }
+ } else {
+ StackCopy(masm, type, scratch, src, dst);
+ }
+ break;
+ }
+ }
+ }
+}
+
+// Generate a wrapper function with the standard intra-wasm call ABI which simply
+// calls an import. This wrapper function allows any import to be treated like a
+// normal wasm function for the purposes of exports and table calls. In
+// particular, the wrapper function provides:
+// - a table entry, so JS imports can be put into tables
+// - normal (non-)profiling entries, so that, if the import is re-exported,
+// an entry stub can be generated and called without any special cases
+FuncOffsets
+wasm::GenerateImportFunction(jit::MacroAssembler& masm, const FuncImport& fi, SigIdDesc sigId)
+{
+ masm.setFramePushed(0);
+
+ unsigned tlsBytes = sizeof(void*);
+ unsigned framePushed = StackDecrementForCall(masm, WasmStackAlignment, fi.sig().args(), tlsBytes);
+
+ FuncOffsets offsets;
+ GenerateFunctionPrologue(masm, framePushed, sigId, &offsets);
+
+ // The argument register state is already setup by our caller. We just need
+ // to be sure not to clobber it before the call.
+ Register scratch = ABINonArgReg0;
+
+ // Copy our frame's stack arguments to the callee frame's stack argument.
+ unsigned offsetToCallerStackArgs = sizeof(Frame) + masm.framePushed();
+ ABIArgValTypeIter i(fi.sig().args());
+ for (; !i.done(); i++) {
+ if (i->kind() != ABIArg::Stack)
+ continue;
+
+ Address src(masm.getStackPointer(), offsetToCallerStackArgs + i->offsetFromArgBase());
+ Address dst(masm.getStackPointer(), i->offsetFromArgBase());
+ StackCopy(masm, i.mirType(), scratch, src, dst);
+ }
+
+ // Save the TLS register so it can be restored later.
+ uint32_t tlsStackOffset = i.stackBytesConsumedSoFar();
+ masm.storePtr(WasmTlsReg, Address(masm.getStackPointer(), tlsStackOffset));
+
+ // Call the import exit stub.
+ CallSiteDesc desc(CallSiteDesc::Dynamic);
+ masm.wasmCallImport(desc, CalleeDesc::import(fi.tlsDataOffset()));
+
+ // Restore the TLS register and pinned regs, per wasm function ABI.
+ masm.loadPtr(Address(masm.getStackPointer(), tlsStackOffset), WasmTlsReg);
+ masm.loadWasmPinnedRegsFromTls();
+
+ GenerateFunctionEpilogue(masm, framePushed, &offsets);
+
+ masm.wasmEmitTrapOutOfLineCode();
+
+ offsets.end = masm.currentOffset();
+ return offsets;
+}
+
+// Generate a stub that is called via the internal ABI derived from the
+// signature of the import and calls into an appropriate callImport C++
+// function, having boxed all the ABI arguments into a homogeneous Value array.
+ProfilingOffsets
+wasm::GenerateImportInterpExit(MacroAssembler& masm, const FuncImport& fi, uint32_t funcImportIndex,
+ Label* throwLabel)
+{
+ masm.setFramePushed(0);
+
+ // Argument types for Module::callImport_*:
+ static const MIRType typeArray[] = { MIRType::Pointer, // Instance*
+ MIRType::Pointer, // funcImportIndex
+ MIRType::Int32, // argc
+ MIRType::Pointer }; // argv
+ MIRTypeVector invokeArgTypes;
+ MOZ_ALWAYS_TRUE(invokeArgTypes.append(typeArray, ArrayLength(typeArray)));
+
+ // At the point of the call, the stack layout shall be (sp grows to the left):
+ // | stack args | padding | Value argv[] | padding | retaddr | caller stack args |
+ // The padding between stack args and argv ensures that argv is aligned. The
+ // padding between argv and retaddr ensures that sp is aligned.
+ unsigned argOffset = AlignBytes(StackArgBytes(invokeArgTypes), sizeof(double));
+ unsigned argBytes = Max<size_t>(1, fi.sig().args().length()) * sizeof(Value);
+ unsigned framePushed = StackDecrementForCall(masm, ABIStackAlignment, argOffset + argBytes);
+
+ ProfilingOffsets offsets;
+ GenerateExitPrologue(masm, framePushed, ExitReason::ImportInterp, &offsets);
+
+ // Fill the argument array.
+ unsigned offsetToCallerStackArgs = sizeof(Frame) + masm.framePushed();
+ Register scratch = ABINonArgReturnReg0;
+ FillArgumentArray(masm, fi.sig().args(), argOffset, offsetToCallerStackArgs, scratch, ToValue(false));
+
+ // Prepare the arguments for the call to Module::callImport_*.
+ ABIArgMIRTypeIter i(invokeArgTypes);
+
+ // argument 0: Instance*
+ Address instancePtr(WasmTlsReg, offsetof(TlsData, instance));
+ if (i->kind() == ABIArg::GPR) {
+ masm.loadPtr(instancePtr, i->gpr());
+ } else {
+ masm.loadPtr(instancePtr, scratch);
+ masm.storePtr(scratch, Address(masm.getStackPointer(), i->offsetFromArgBase()));
+ }
+ i++;
+
+ // argument 1: funcImportIndex
+ if (i->kind() == ABIArg::GPR)
+ masm.mov(ImmWord(funcImportIndex), i->gpr());
+ else
+ masm.store32(Imm32(funcImportIndex), Address(masm.getStackPointer(), i->offsetFromArgBase()));
+ i++;
+
+ // argument 2: argc
+ unsigned argc = fi.sig().args().length();
+ if (i->kind() == ABIArg::GPR)
+ masm.mov(ImmWord(argc), i->gpr());
+ else
+ masm.store32(Imm32(argc), Address(masm.getStackPointer(), i->offsetFromArgBase()));
+ i++;
+
+ // argument 3: argv
+ Address argv(masm.getStackPointer(), argOffset);
+ if (i->kind() == ABIArg::GPR) {
+ masm.computeEffectiveAddress(argv, i->gpr());
+ } else {
+ masm.computeEffectiveAddress(argv, scratch);
+ masm.storePtr(scratch, Address(masm.getStackPointer(), i->offsetFromArgBase()));
+ }
+ i++;
+ MOZ_ASSERT(i.done());
+
+ // Make the call, test whether it succeeded, and extract the return value.
+ AssertStackAlignment(masm, ABIStackAlignment);
+ switch (fi.sig().ret()) {
+ case ExprType::Void:
+ masm.call(SymbolicAddress::CallImport_Void);
+ masm.branchTest32(Assembler::Zero, ReturnReg, ReturnReg, throwLabel);
+ break;
+ case ExprType::I32:
+ masm.call(SymbolicAddress::CallImport_I32);
+ masm.branchTest32(Assembler::Zero, ReturnReg, ReturnReg, throwLabel);
+ masm.load32(argv, ReturnReg);
+ break;
+ case ExprType::I64:
+ masm.call(SymbolicAddress::CallImport_I64);
+ masm.branchTest32(Assembler::Zero, ReturnReg, ReturnReg, throwLabel);
+ masm.load64(argv, ReturnReg64);
+ break;
+ case ExprType::F32:
+ masm.call(SymbolicAddress::CallImport_F64);
+ masm.branchTest32(Assembler::Zero, ReturnReg, ReturnReg, throwLabel);
+ masm.loadDouble(argv, ReturnDoubleReg);
+ masm.convertDoubleToFloat32(ReturnDoubleReg, ReturnFloat32Reg);
+ break;
+ case ExprType::F64:
+ masm.call(SymbolicAddress::CallImport_F64);
+ masm.branchTest32(Assembler::Zero, ReturnReg, ReturnReg, throwLabel);
+ masm.loadDouble(argv, ReturnDoubleReg);
+ break;
+ case ExprType::I8x16:
+ case ExprType::I16x8:
+ case ExprType::I32x4:
+ case ExprType::F32x4:
+ case ExprType::B8x16:
+ case ExprType::B16x8:
+ case ExprType::B32x4:
+ MOZ_CRASH("SIMD types shouldn't be returned from a FFI");
+ case ExprType::Limit:
+ MOZ_CRASH("Limit");
+ }
+
+ // The native ABI preserves the TLS, heap and global registers since they
+ // are non-volatile.
+ MOZ_ASSERT(NonVolatileRegs.has(WasmTlsReg));
+#if defined(JS_CODEGEN_X64) || \
+ defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_ARM64) || \
+ defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)
+ MOZ_ASSERT(NonVolatileRegs.has(HeapReg));
+#endif
+#if defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_ARM64) || \
+ defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)
+ MOZ_ASSERT(NonVolatileRegs.has(GlobalReg));
+#endif
+
+ GenerateExitEpilogue(masm, framePushed, ExitReason::ImportInterp, &offsets);
+
+ offsets.end = masm.currentOffset();
+ return offsets;
+}
+
+static const unsigned SavedTlsReg = sizeof(void*);
+
+// Generate a stub that is called via the internal ABI derived from the
+// signature of the import and calls into a compatible JIT function,
+// having boxed all the ABI arguments into the JIT stack frame layout.
+ProfilingOffsets
+wasm::GenerateImportJitExit(MacroAssembler& masm, const FuncImport& fi, Label* throwLabel)
+{
+ masm.setFramePushed(0);
+
+ // JIT calls use the following stack layout (sp grows to the left):
+ // | retaddr | descriptor | callee | argc | this | arg1..N |
+ // After the JIT frame, the global register (if present) is saved since the
+ // JIT's ABI does not preserve non-volatile regs. Also, unlike most ABIs,
+ // the JIT ABI requires that sp be JitStackAlignment-aligned *after* pushing
+ // the return address.
+ static_assert(WasmStackAlignment >= JitStackAlignment, "subsumes");
+ unsigned sizeOfRetAddr = sizeof(void*);
+ unsigned jitFrameBytes = 3 * sizeof(void*) + (1 + fi.sig().args().length()) * sizeof(Value);
+ unsigned totalJitFrameBytes = sizeOfRetAddr + jitFrameBytes + SavedTlsReg;
+ unsigned jitFramePushed = StackDecrementForCall(masm, JitStackAlignment, totalJitFrameBytes) -
+ sizeOfRetAddr;
+
+ ProfilingOffsets offsets;
+ GenerateExitPrologue(masm, jitFramePushed, ExitReason::ImportJit, &offsets);
+
+ // 1. Descriptor
+ size_t argOffset = 0;
+ uint32_t descriptor = MakeFrameDescriptor(jitFramePushed, JitFrame_Entry,
+ JitFrameLayout::Size());
+ masm.storePtr(ImmWord(uintptr_t(descriptor)), Address(masm.getStackPointer(), argOffset));
+ argOffset += sizeof(size_t);
+
+ // 2. Callee
+ Register callee = ABINonArgReturnReg0; // live until call
+ Register scratch = ABINonArgReturnReg1; // repeatedly clobbered
+
+ // 2.1. Get callee
+ masm.loadWasmGlobalPtr(fi.tlsDataOffset() + offsetof(FuncImportTls, obj), callee);
+
+ // 2.2. Save callee
+ masm.storePtr(callee, Address(masm.getStackPointer(), argOffset));
+ argOffset += sizeof(size_t);
+
+ // 2.3. Load callee executable entry point
+ masm.loadPtr(Address(callee, JSFunction::offsetOfNativeOrScript()), callee);
+ masm.loadBaselineOrIonNoArgCheck(callee, callee, nullptr);
+
+ // 3. Argc
+ unsigned argc = fi.sig().args().length();
+ masm.storePtr(ImmWord(uintptr_t(argc)), Address(masm.getStackPointer(), argOffset));
+ argOffset += sizeof(size_t);
+
+ // 4. |this| value
+ masm.storeValue(UndefinedValue(), Address(masm.getStackPointer(), argOffset));
+ argOffset += sizeof(Value);
+
+ // 5. Fill the arguments
+ unsigned offsetToCallerStackArgs = jitFramePushed + sizeof(Frame);
+ FillArgumentArray(masm, fi.sig().args(), argOffset, offsetToCallerStackArgs, scratch, ToValue(true));
+ argOffset += fi.sig().args().length() * sizeof(Value);
+ MOZ_ASSERT(argOffset == jitFrameBytes);
+
+ // 6. Jit code will clobber all registers, even non-volatiles. WasmTlsReg
+ // must be kept live for the benefit of the epilogue, so push it on the
+ // stack so that it can be restored before the epilogue.
+ static_assert(SavedTlsReg == sizeof(void*), "stack frame accounting");
+ masm.storePtr(WasmTlsReg, Address(masm.getStackPointer(), jitFrameBytes));
+
+ {
+ // Enable Activation.
+ //
+ // This sequence requires two registers, and needs to preserve the
+ // 'callee' register, so there are three live registers.
+ MOZ_ASSERT(callee == WasmIonExitRegCallee);
+ Register cx = WasmIonExitRegE0;
+ Register act = WasmIonExitRegE1;
+
+ // JitActivation* act = cx->activation();
+ masm.movePtr(SymbolicAddress::Context, cx);
+ masm.loadPtr(Address(cx, JSContext::offsetOfActivation()), act);
+
+ // act.active_ = true;
+ masm.store8(Imm32(1), Address(act, JitActivation::offsetOfActiveUint8()));
+
+ // cx->jitActivation = act;
+ masm.storePtr(act, Address(cx, offsetof(JSContext, jitActivation)));
+
+ // cx->profilingActivation_ = act;
+ masm.storePtr(act, Address(cx, JSContext::offsetOfProfilingActivation()));
+ }
+
+ AssertStackAlignment(masm, JitStackAlignment, sizeOfRetAddr);
+ masm.callJitNoProfiler(callee);
+ AssertStackAlignment(masm, JitStackAlignment, sizeOfRetAddr);
+
+ {
+ // Disable Activation.
+ //
+ // This sequence needs three registers, and must preserve the JSReturnReg_Data and
+ // JSReturnReg_Type, so there are five live registers.
+ MOZ_ASSERT(JSReturnReg_Data == WasmIonExitRegReturnData);
+ MOZ_ASSERT(JSReturnReg_Type == WasmIonExitRegReturnType);
+ Register cx = WasmIonExitRegD0;
+ Register act = WasmIonExitRegD1;
+ Register tmp = WasmIonExitRegD2;
+
+ // JitActivation* act = cx->activation();
+ masm.movePtr(SymbolicAddress::Context, cx);
+ masm.loadPtr(Address(cx, JSContext::offsetOfActivation()), act);
+
+ // cx->jitTop = act->prevJitTop_;
+ masm.loadPtr(Address(act, JitActivation::offsetOfPrevJitTop()), tmp);
+ masm.storePtr(tmp, Address(cx, offsetof(JSContext, jitTop)));
+
+ // cx->jitActivation = act->prevJitActivation_;
+ masm.loadPtr(Address(act, JitActivation::offsetOfPrevJitActivation()), tmp);
+ masm.storePtr(tmp, Address(cx, offsetof(JSContext, jitActivation)));
+
+ // cx->profilingActivation = act->prevProfilingActivation_;
+ masm.loadPtr(Address(act, Activation::offsetOfPrevProfiling()), tmp);
+ masm.storePtr(tmp, Address(cx, JSContext::offsetOfProfilingActivation()));
+
+ // act->active_ = false;
+ masm.store8(Imm32(0), Address(act, JitActivation::offsetOfActiveUint8()));
+ }
+
+ // As explained above, the frame was aligned for the JIT ABI such that
+ // (sp + sizeof(void*)) % JitStackAlignment == 0
+ // But now we possibly want to call one of several different C++ functions,
+ // so subtract the sizeof(void*) so that sp is aligned for an ABI call.
+ static_assert(ABIStackAlignment <= JitStackAlignment, "subsumes");
+ masm.reserveStack(sizeOfRetAddr);
+ unsigned nativeFramePushed = masm.framePushed();
+ AssertStackAlignment(masm, ABIStackAlignment);
+
+ masm.branchTestMagic(Assembler::Equal, JSReturnOperand, throwLabel);
+
+ Label oolConvert;
+ switch (fi.sig().ret()) {
+ case ExprType::Void:
+ break;
+ case ExprType::I32:
+ masm.convertValueToInt32(JSReturnOperand, ReturnDoubleReg, ReturnReg, &oolConvert,
+ /* -0 check */ false);
+ break;
+ case ExprType::I64:
+ // We don't expect int64 to be returned from Ion yet, because of a
+ // guard in callImport.
+ masm.breakpoint();
+ break;
+ case ExprType::F32:
+ masm.convertValueToFloat(JSReturnOperand, ReturnFloat32Reg, &oolConvert);
+ break;
+ case ExprType::F64:
+ masm.convertValueToDouble(JSReturnOperand, ReturnDoubleReg, &oolConvert);
+ break;
+ case ExprType::I8x16:
+ case ExprType::I16x8:
+ case ExprType::I32x4:
+ case ExprType::F32x4:
+ case ExprType::B8x16:
+ case ExprType::B16x8:
+ case ExprType::B32x4:
+ MOZ_CRASH("SIMD types shouldn't be returned from an import");
+ case ExprType::Limit:
+ MOZ_CRASH("Limit");
+ }
+
+ Label done;
+ masm.bind(&done);
+
+ // Ion code does not respect the system ABI's callee-saved register
+ // conventions so reload any assumed-non-volatile registers. Note that the
+ // reserveStack(sizeOfRetAddr) above means that the stack pointer is at a
+ // different offset than when WasmTlsReg was stored.
+ masm.loadPtr(Address(masm.getStackPointer(), jitFrameBytes + sizeOfRetAddr), WasmTlsReg);
+
+ GenerateExitEpilogue(masm, masm.framePushed(), ExitReason::ImportJit, &offsets);
+
+ if (oolConvert.used()) {
+ masm.bind(&oolConvert);
+ masm.setFramePushed(nativeFramePushed);
+
+ // Coercion calls use the following stack layout (sp grows to the left):
+ // | args | padding | Value argv[1] | padding | exit Frame |
+ MIRTypeVector coerceArgTypes;
+ JS_ALWAYS_TRUE(coerceArgTypes.append(MIRType::Pointer));
+ unsigned offsetToCoerceArgv = AlignBytes(StackArgBytes(coerceArgTypes), sizeof(Value));
+ MOZ_ASSERT(nativeFramePushed >= offsetToCoerceArgv + sizeof(Value));
+ AssertStackAlignment(masm, ABIStackAlignment);
+
+ // Store return value into argv[0]
+ masm.storeValue(JSReturnOperand, Address(masm.getStackPointer(), offsetToCoerceArgv));
+
+ // argument 0: argv
+ ABIArgMIRTypeIter i(coerceArgTypes);
+ Address argv(masm.getStackPointer(), offsetToCoerceArgv);
+ if (i->kind() == ABIArg::GPR) {
+ masm.computeEffectiveAddress(argv, i->gpr());
+ } else {
+ masm.computeEffectiveAddress(argv, scratch);
+ masm.storePtr(scratch, Address(masm.getStackPointer(), i->offsetFromArgBase()));
+ }
+ i++;
+ MOZ_ASSERT(i.done());
+
+ // Call coercion function
+ AssertStackAlignment(masm, ABIStackAlignment);
+ switch (fi.sig().ret()) {
+ case ExprType::I32:
+ masm.call(SymbolicAddress::CoerceInPlace_ToInt32);
+ masm.branchTest32(Assembler::Zero, ReturnReg, ReturnReg, throwLabel);
+ masm.unboxInt32(Address(masm.getStackPointer(), offsetToCoerceArgv), ReturnReg);
+ break;
+ case ExprType::F64:
+ masm.call(SymbolicAddress::CoerceInPlace_ToNumber);
+ masm.branchTest32(Assembler::Zero, ReturnReg, ReturnReg, throwLabel);
+ masm.loadDouble(Address(masm.getStackPointer(), offsetToCoerceArgv), ReturnDoubleReg);
+ break;
+ case ExprType::F32:
+ masm.call(SymbolicAddress::CoerceInPlace_ToNumber);
+ masm.branchTest32(Assembler::Zero, ReturnReg, ReturnReg, throwLabel);
+ masm.loadDouble(Address(masm.getStackPointer(), offsetToCoerceArgv), ReturnDoubleReg);
+ masm.convertDoubleToFloat32(ReturnDoubleReg, ReturnFloat32Reg);
+ break;
+ default:
+ MOZ_CRASH("Unsupported convert type");
+ }
+
+ masm.jump(&done);
+ masm.setFramePushed(0);
+ }
+
+ MOZ_ASSERT(masm.framePushed() == 0);
+
+ offsets.end = masm.currentOffset();
+ return offsets;
+}
+
+// Generate a stub that calls into ReportTrap with the right trap reason.
+// This stub is called with ABIStackAlignment by a trap out-of-line path. A
+// profiling prologue/epilogue is used so that stack unwinding picks up the
+// current WasmActivation. Unwinding will begin at the caller of this trap exit.
+ProfilingOffsets
+wasm::GenerateTrapExit(MacroAssembler& masm, Trap trap, Label* throwLabel)
+{
+ masm.haltingAlign(CodeAlignment);
+
+ masm.setFramePushed(0);
+
+ MIRTypeVector args;
+ MOZ_ALWAYS_TRUE(args.append(MIRType::Int32));
+
+ uint32_t framePushed = StackDecrementForCall(masm, ABIStackAlignment, args);
+
+ ProfilingOffsets offsets;
+ GenerateExitPrologue(masm, framePushed, ExitReason::Trap, &offsets);
+
+ ABIArgMIRTypeIter i(args);
+ if (i->kind() == ABIArg::GPR)
+ masm.move32(Imm32(int32_t(trap)), i->gpr());
+ else
+ masm.store32(Imm32(int32_t(trap)), Address(masm.getStackPointer(), i->offsetFromArgBase()));
+ i++;
+ MOZ_ASSERT(i.done());
+
+ masm.assertStackAlignment(ABIStackAlignment);
+ masm.call(SymbolicAddress::ReportTrap);
+
+ masm.jump(throwLabel);
+
+ GenerateExitEpilogue(masm, framePushed, ExitReason::Trap, &offsets);
+
+ offsets.end = masm.currentOffset();
+ return offsets;
+}
+
+// Generate a stub which is only used by the signal handlers to handle out of
+// bounds access by experimental SIMD.js and Atomics and unaligned accesses on
+// ARM. This stub is executed by direct PC transfer from the faulting memory
+// access and thus the stack depth is unknown. Since WasmActivation::fp is not
+// set before calling the error reporter, the current wasm activation will be
+// lost. This stub should be removed when SIMD.js and Atomics are moved to wasm
+// and given proper traps and when we use a non-faulting strategy for unaligned
+// ARM access.
+static Offsets
+GenerateGenericMemoryAccessTrap(MacroAssembler& masm, SymbolicAddress reporter, Label* throwLabel)
+{
+ masm.haltingAlign(CodeAlignment);
+
+ Offsets offsets;
+ offsets.begin = masm.currentOffset();
+
+ // sp can be anything at this point, so ensure it is aligned when calling
+ // into C++. We unconditionally jump to throw so don't worry about
+ // restoring sp.
+ masm.andToStackPtr(Imm32(~(ABIStackAlignment - 1)));
+ if (ShadowStackSpace)
+ masm.subFromStackPtr(Imm32(ShadowStackSpace));
+
+ masm.call(reporter);
+ masm.jump(throwLabel);
+
+ offsets.end = masm.currentOffset();
+ return offsets;
+}
+
+Offsets
+wasm::GenerateOutOfBoundsExit(MacroAssembler& masm, Label* throwLabel)
+{
+ return GenerateGenericMemoryAccessTrap(masm, SymbolicAddress::ReportOutOfBounds, throwLabel);
+}
+
+Offsets
+wasm::GenerateUnalignedExit(MacroAssembler& masm, Label* throwLabel)
+{
+ return GenerateGenericMemoryAccessTrap(masm, SymbolicAddress::ReportUnalignedAccess, throwLabel);
+}
+
+static const LiveRegisterSet AllRegsExceptSP(
+ GeneralRegisterSet(Registers::AllMask & ~(uint32_t(1) << Registers::StackPointer)),
+ FloatRegisterSet(FloatRegisters::AllMask));
+
+// The async interrupt-callback exit is called from arbitrarily-interrupted wasm
+// code. That means we must first save *all* registers and restore *all*
+// registers (except the stack pointer) when we resume. The address to resume to
+// (assuming that js::HandleExecutionInterrupt doesn't indicate that the
+// execution should be aborted) is stored in WasmActivation::resumePC_.
+// Unfortunately, loading this requires a scratch register which we don't have
+// after restoring all registers. To hack around this, push the resumePC on the
+// stack so that it can be popped directly into PC.
+Offsets
+wasm::GenerateInterruptExit(MacroAssembler& masm, Label* throwLabel)
+{
+ masm.haltingAlign(CodeAlignment);
+
+ Offsets offsets;
+ offsets.begin = masm.currentOffset();
+
+#if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64)
+ // Be very careful here not to perturb the machine state before saving it
+ // to the stack. In particular, add/sub instructions may set conditions in
+ // the flags register.
+ masm.push(Imm32(0)); // space for resumePC
+ masm.pushFlags(); // after this we are safe to use sub
+ masm.setFramePushed(0); // set to zero so we can use masm.framePushed() below
+ masm.PushRegsInMask(AllRegsExceptSP); // save all GP/FP registers (except SP)
+
+ Register scratch = ABINonArgReturnReg0;
+
+ // Store resumePC into the reserved space.
+ masm.loadWasmActivationFromSymbolicAddress(scratch);
+ masm.loadPtr(Address(scratch, WasmActivation::offsetOfResumePC()), scratch);
+ masm.storePtr(scratch, Address(masm.getStackPointer(), masm.framePushed() + sizeof(void*)));
+
+ // We know that StackPointer is word-aligned, but not necessarily
+ // stack-aligned, so we need to align it dynamically.
+ masm.moveStackPtrTo(ABINonVolatileReg);
+ masm.andToStackPtr(Imm32(~(ABIStackAlignment - 1)));
+ if (ShadowStackSpace)
+ masm.subFromStackPtr(Imm32(ShadowStackSpace));
+
+ masm.assertStackAlignment(ABIStackAlignment);
+ masm.call(SymbolicAddress::HandleExecutionInterrupt);
+
+ masm.branchIfFalseBool(ReturnReg, throwLabel);
+
+ // Restore the StackPointer to its position before the call.
+ masm.moveToStackPtr(ABINonVolatileReg);
+
+ // Restore the machine state to before the interrupt.
+ masm.PopRegsInMask(AllRegsExceptSP); // restore all GP/FP registers (except SP)
+ masm.popFlags(); // after this, nothing that sets conditions
+ masm.ret(); // pop resumePC into PC
+#elif defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)
+ // Reserve space to store resumePC and HeapReg.
+ masm.subFromStackPtr(Imm32(2 * sizeof(intptr_t)));
+ // set to zero so we can use masm.framePushed() below.
+ masm.setFramePushed(0);
+ static_assert(!SupportsSimd, "high lanes of SIMD registers need to be saved too.");
+ // save all registers,except sp. After this stack is alligned.
+ masm.PushRegsInMask(AllRegsExceptSP);
+
+ // Save the stack pointer in a non-volatile register.
+ masm.moveStackPtrTo(s0);
+ // Align the stack.
+ masm.ma_and(StackPointer, StackPointer, Imm32(~(ABIStackAlignment - 1)));
+
+ // Store resumePC into the reserved space.
+ masm.loadWasmActivationFromSymbolicAddress(IntArgReg0);
+ masm.loadPtr(Address(IntArgReg0, WasmActivation::offsetOfResumePC()), IntArgReg1);
+ masm.storePtr(IntArgReg1, Address(s0, masm.framePushed()));
+ // Store HeapReg into the reserved space.
+ masm.storePtr(HeapReg, Address(s0, masm.framePushed() + sizeof(intptr_t)));
+
+# ifdef USES_O32_ABI
+ // MIPS ABI requires rewserving stack for registes $a0 to $a3.
+ masm.subFromStackPtr(Imm32(4 * sizeof(intptr_t)));
+# endif
+
+ masm.assertStackAlignment(ABIStackAlignment);
+ masm.call(SymbolicAddress::HandleExecutionInterrupt);
+
+# ifdef USES_O32_ABI
+ masm.addToStackPtr(Imm32(4 * sizeof(intptr_t)));
+# endif
+
+ masm.branchIfFalseBool(ReturnReg, throwLabel);
+
+ // This will restore stack to the address before the call.
+ masm.moveToStackPtr(s0);
+ masm.PopRegsInMask(AllRegsExceptSP);
+
+ // Pop resumePC into PC. Clobber HeapReg to make the jump and restore it
+ // during jump delay slot.
+ masm.loadPtr(Address(StackPointer, 0), HeapReg);
+ // Reclaim the reserve space.
+ masm.addToStackPtr(Imm32(2 * sizeof(intptr_t)));
+ masm.as_jr(HeapReg);
+ masm.loadPtr(Address(StackPointer, -sizeof(intptr_t)), HeapReg);
+#elif defined(JS_CODEGEN_ARM)
+ masm.setFramePushed(0); // set to zero so we can use masm.framePushed() below
+
+ // Save all GPR, except the stack pointer.
+ masm.PushRegsInMask(LiveRegisterSet(
+ GeneralRegisterSet(Registers::AllMask & ~(1<<Registers::sp)),
+ FloatRegisterSet(uint32_t(0))));
+
+ // Save both the APSR and FPSCR in non-volatile registers.
+ masm.as_mrs(r4);
+ masm.as_vmrs(r5);
+ // Save the stack pointer in a non-volatile register.
+ masm.mov(sp,r6);
+ // Align the stack.
+ masm.as_bic(sp, sp, Imm8(7));
+
+ // Store resumePC into the return PC stack slot.
+ masm.loadWasmActivationFromSymbolicAddress(IntArgReg0);
+ masm.loadPtr(Address(IntArgReg0, WasmActivation::offsetOfResumePC()), IntArgReg1);
+ masm.storePtr(IntArgReg1, Address(r6, 14 * sizeof(uint32_t*)));
+
+ // Save all FP registers
+ static_assert(!SupportsSimd, "high lanes of SIMD registers need to be saved too.");
+ masm.PushRegsInMask(LiveRegisterSet(GeneralRegisterSet(0),
+ FloatRegisterSet(FloatRegisters::AllDoubleMask)));
+
+ masm.assertStackAlignment(ABIStackAlignment);
+ masm.call(SymbolicAddress::HandleExecutionInterrupt);
+
+ masm.branchIfFalseBool(ReturnReg, throwLabel);
+
+ // Restore the machine state to before the interrupt. this will set the pc!
+
+ // Restore all FP registers
+ masm.PopRegsInMask(LiveRegisterSet(GeneralRegisterSet(0),
+ FloatRegisterSet(FloatRegisters::AllDoubleMask)));
+ masm.mov(r6,sp);
+ masm.as_vmsr(r5);
+ masm.as_msr(r4);
+ // Restore all GP registers
+ masm.startDataTransferM(IsLoad, sp, IA, WriteBack);
+ masm.transferReg(r0);
+ masm.transferReg(r1);
+ masm.transferReg(r2);
+ masm.transferReg(r3);
+ masm.transferReg(r4);
+ masm.transferReg(r5);
+ masm.transferReg(r6);
+ masm.transferReg(r7);
+ masm.transferReg(r8);
+ masm.transferReg(r9);
+ masm.transferReg(r10);
+ masm.transferReg(r11);
+ masm.transferReg(r12);
+ masm.transferReg(lr);
+ masm.finishDataTransfer();
+ masm.ret();
+#elif defined(JS_CODEGEN_ARM64)
+ MOZ_CRASH();
+#elif defined (JS_CODEGEN_NONE)
+ MOZ_CRASH();
+#else
+# error "Unknown architecture!"
+#endif
+
+ offsets.end = masm.currentOffset();
+ return offsets;
+}
+
+// Generate a stub that restores the stack pointer to what it was on entry to
+// the wasm activation, sets the return register to 'false' and then executes a
+// return which will return from this wasm activation to the caller. This stub
+// should only be called after the caller has reported an error (or, in the case
+// of the interrupt stub, intends to interrupt execution).
+Offsets
+wasm::GenerateThrowStub(MacroAssembler& masm, Label* throwLabel)
+{
+ masm.haltingAlign(CodeAlignment);
+
+ masm.bind(throwLabel);
+
+ Offsets offsets;
+ offsets.begin = masm.currentOffset();
+
+ // We are about to pop all frames in this WasmActivation. Set fp to null to
+ // maintain the invariant that fp is either null or pointing to a valid
+ // frame.
+ Register scratch = ABINonArgReturnReg0;
+ masm.loadWasmActivationFromSymbolicAddress(scratch);
+ masm.storePtr(ImmWord(0), Address(scratch, WasmActivation::offsetOfFP()));
+
+ masm.setFramePushed(FramePushedForEntrySP);
+ masm.loadStackPtr(Address(scratch, WasmActivation::offsetOfEntrySP()));
+ masm.Pop(scratch);
+ masm.PopRegsInMask(NonVolatileRegs);
+ MOZ_ASSERT(masm.framePushed() == 0);
+
+ masm.mov(ImmWord(0), ReturnReg);
+ masm.ret();
+
+ offsets.end = masm.currentOffset();
+ return offsets;
+}