Bug 1436955 - ARM64 Simulator, bugfixes and wasm support. r=bbouvier
authorLars T Hansen <lhansen@mozilla.com>
Tue, 30 Jan 2018 01:38:26 +0100
changeset 405281 b24d7a6d720a9035690114de4b89badfb1bdf700
parent 405280 885e8ce07968ad2c961d6b19542eb509055ba321
child 405282 6e46ff4ff446e5548326e9d3468485c15934e088
push id100196
push userlhansen@mozilla.com
push dateMon, 26 Feb 2018 15:20:22 +0000
treeherdermozilla-inbound@6e46ff4ff446 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbbouvier
bugs1436955
milestone60.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1436955 - ARM64 Simulator, bugfixes and wasm support. r=bbouvier - fix a bug where the wrong register was picked up - support wasm out-of-bounds traps - support wasm illegal instruction traps - support (float,float)->float callout signature - support wait() and wake() callout signatures
js/src/jit/arm64/vixl/MozSimulator-vixl.cpp
js/src/jit/arm64/vixl/Simulator-vixl.cpp
js/src/jit/arm64/vixl/Simulator-vixl.h
--- a/js/src/jit/arm64/vixl/MozSimulator-vixl.cpp
+++ b/js/src/jit/arm64/vixl/MozSimulator-vixl.cpp
@@ -28,25 +28,26 @@
 
 #include "jit/arm64/vixl/Debugger-vixl.h"
 #include "jit/arm64/vixl/Simulator-vixl.h"
 #include "jit/IonTypes.h"
 #include "js/Utility.h"
 #include "threading/LockGuard.h"
 #include "vm/Runtime.h"
 #include "wasm/WasmInstance.h"
+#include "wasm/WasmProcess.h"
 #include "wasm/WasmSignalHandlers.h"
 
 js::jit::SimulatorProcess* js::jit::SimulatorProcess::singleton_ = nullptr;
 
 namespace vixl {
 
-
 using mozilla::DebugOnly;
 using js::jit::ABIFunctionType;
+using js::jit::JitActivation;
 using js::jit::SimulatorProcess;
 
 Simulator::Simulator(JSContext* cx, Decoder* decoder, FILE* stream)
   : cx_(cx)
   , stream_(nullptr)
   , print_disasm_(nullptr)
   , instrumentation_(nullptr)
   , stack_(nullptr)
@@ -213,58 +214,118 @@ uintptr_t Simulator::stackLimit() const 
 
 uintptr_t* Simulator::addressOfStackLimit() {
   return (uintptr_t*)&stack_limit_;
 }
 
 
 bool Simulator::overRecursed(uintptr_t newsp) const {
   if (newsp)
-    newsp = xreg(31, Reg31IsStackPointer);
+    newsp = get_sp();
   return newsp <= stackLimit();
 }
 
 
 bool Simulator::overRecursedWithExtra(uint32_t extra) const {
-  uintptr_t newsp = xreg(31, Reg31IsStackPointer) - extra;
+  uintptr_t newsp = get_sp() - extra;
   return newsp <= stackLimit();
 }
 
 
 void Simulator::trigger_wasm_interrupt() {
   MOZ_ASSERT(!wasm_interrupt_);
   wasm_interrupt_ = true;
 }
 
 
+static inline JitActivation*
+GetJitActivation(JSContext* cx)
+{
+    if (!js::wasm::CodeExists)
+        return nullptr;
+    if (!cx->activation() || !cx->activation()->isJit())
+        return nullptr;
+    return cx->activation()->asJit();
+}
+
+JS::ProfilingFrameIterator::RegisterState
+Simulator::registerState()
+{
+  JS::ProfilingFrameIterator::RegisterState state;
+  state.pc = (uint8_t*) get_pc();
+  state.fp = (uint8_t*) get_fp();
+  state.lr = (uint8_t*) get_lr();
+  state.sp = (uint8_t*) get_sp();
+  return state;
+}
+
 // The signal handler only redirects the PC to the interrupt stub when the PC is
 // in function code. However, this guard is racy for the ARM simulator since the
 // signal handler samples PC in the middle of simulating an instruction and thus
 // the current PC may have advanced once since the signal handler's guard. So we
 // re-check here.
-void Simulator::handle_wasm_interrupt() {
+void Simulator::handle_wasm_interrupt()
+{
+  if (!js::wasm::CodeExists)
+    return;
+
   uint8_t* pc = (uint8_t*)get_pc();
-  uint8_t* fp = (uint8_t*)xreg(30);
 
   const js::wasm::ModuleSegment* ms = nullptr;
   if (!js::wasm::InInterruptibleCode(cx_, pc, &ms))
       return;
 
-  JS::ProfilingFrameIterator::RegisterState state;
-  state.pc = pc;
-  state.fp = fp;
-  state.lr = (uint8_t*) xreg(30);
-  state.sp = (uint8_t*) xreg(31);
+  JitActivation* act = GetJitActivation(cx_);
+  if (!act)
+      return;
 
-  if (!cx_->activation_->asJit()->startWasmInterrupt(state))
+  if (!act->startWasmInterrupt(registerState()))
       return;
 
   set_pc((Instruction*)ms->interruptCode());
 }
 
+bool
+Simulator::handle_wasm_seg_fault(uintptr_t addr, unsigned numBytes)
+{
+    JitActivation* act = GetJitActivation(cx_);
+    if (!act)
+        return false;
+
+    uint8_t* pc = (uint8_t*)get_pc();
+    uint8_t* fp = (uint8_t*)get_fp();
+
+    const js::wasm::CodeSegment* segment = js::wasm::LookupCodeSegment(pc);
+    if (!segment)
+        return false;
+    const js::wasm::ModuleSegment* moduleSegment = segment->asModule();
+
+    js::wasm::Instance* instance = js::wasm::LookupFaultingInstance(*moduleSegment, pc, fp);
+    if (!instance)
+	return false;
+
+    MOZ_RELEASE_ASSERT(&instance->code() == &moduleSegment->code());
+
+    if (!instance->memoryAccessInGuardRegion((uint8_t*)addr, numBytes))
+        return false;
+
+    const js::wasm::MemoryAccess* memoryAccess = instance->code().lookupMemoryAccess(pc);
+    if (!memoryAccess) {
+        if (!act->startWasmInterrupt(registerState()))
+	    MOZ_CRASH("Cannot start interrupt");
+        if (!instance->code().containsCodePC(pc))
+            MOZ_CRASH("Cannot map PC to trap handler");
+        set_pc((Instruction*)moduleSegment->outOfBoundsCode());
+        return true;
+    }
+
+    MOZ_ASSERT(memoryAccess->hasTrapOutOfLineCode());
+    set_pc((Instruction*)memoryAccess->trapOutOfLineCode(moduleSegment->base()));
+    return true;
+}
 
 int64_t Simulator::call(uint8_t* entry, int argument_count, ...) {
   va_list parameters;
   va_start(parameters, argument_count);
 
   // First eight arguments passed in registers.
   VIXL_ASSERT(argument_count <= 8);
   // This code should use the type of the called function
@@ -298,22 +359,22 @@ int64_t Simulator::call(uint8_t* entry, 
       set_xreg(0, va_arg(parameters, int64_t));
   } else {
       MOZ_CRASH("Unknown number of arguments");
   }
 
   va_end(parameters);
 
   // Call must transition back to native code on exit.
-  VIXL_ASSERT(xreg(30) == int64_t(kEndOfSimAddress));
+  VIXL_ASSERT(get_lr() == int64_t(kEndOfSimAddress));
 
   // Execute the simulation.
-  DebugOnly<int64_t> entryStack = xreg(31, Reg31IsStackPointer);
+  DebugOnly<int64_t> entryStack = get_sp();
   RunFrom((Instruction*)entry);
-  DebugOnly<int64_t> exitStack = xreg(31, Reg31IsStackPointer);
+  DebugOnly<int64_t> exitStack = get_sp();
   VIXL_ASSERT(entryStack == exitStack);
 
   int64_t result = xreg(0);
   if (getenv("USE_DEBUGGER"))
       printf("LEAVE\n");
   return result;
 }
 
@@ -398,29 +459,53 @@ class Redirection
 
 
 
 void* Simulator::RedirectNativeFunction(void* nativeFunction, ABIFunctionType type) {
   Redirection* redirection = Redirection::Get(nativeFunction, type);
   return redirection->addressOfSvcInstruction();
 }
 
+bool
+Simulator::handle_wasm_ill_fault()
+{
+    JitActivation* act = GetJitActivation(cx_);
+    if (!act)
+        return false;
+
+    uint8_t* pc = (uint8_t*)get_pc();
+
+    const js::wasm::CodeSegment* segment = js::wasm::LookupCodeSegment(pc);
+    if (!segment || !segment->isModule())
+        return false;
+    const js::wasm::ModuleSegment* moduleSegment = segment->asModule();
+
+    js::wasm::Trap trap;
+    js::wasm::BytecodeOffset bytecode;
+    if (!moduleSegment->code().lookupTrap(pc, &trap, &bytecode))
+        return false;
+
+    act->startWasmTrap(trap, bytecode.offset, registerState());
+    set_pc((Instruction*)moduleSegment->trapCode());
+    return true;
+}
 
 void Simulator::VisitException(const Instruction* instr) {
   switch (instr->Mask(ExceptionMask)) {
     case BRK: {
       int lowbit  = ImmException_offset;
       int highbit = ImmException_offset + ImmException_width - 1;
       HostBreakpoint(instr->Bits(highbit, lowbit));
       break;
     }
     case HLT:
       switch (instr->ImmException()) {
         case kUnreachableOpcode:
-          DoUnreachable(instr);
+          if (!handle_wasm_ill_fault())
+              DoUnreachable(instr);
           return;
         case kTraceOpcode:
           DoTrace(instr);
           return;
         case kLogOpcode:
           DoLog(instr);
           return;
         case kPrintfOpcode:
@@ -434,22 +519,22 @@ void Simulator::VisitException(const Ins
       // The SVC instruction is hijacked by the JIT as a pseudo-instruction
       // causing the Simulator to execute host-native code for callWithABI.
       switch (instr->ImmException()) {
         case kCallRtRedirected:
           VisitCallRedirection(instr);
           return;
         case kMarkStackPointer: {
           js::AutoEnterOOMUnsafeRegion oomUnsafe;
-          if (!spStack_.append(xreg(31, Reg31IsStackPointer)))
+          if (!spStack_.append(get_sp()))
             oomUnsafe.crash("tracking stack for ARM64 simulator");
           return;
         }
         case kCheckStackPointer: {
-          int64_t current = xreg(31, Reg31IsStackPointer);
+          int64_t current = get_sp();
           int64_t expected = spStack_.popCopy();
           VIXL_ASSERT(current == expected);
           return;
         }
         default:
           VIXL_UNIMPLEMENTED();
       }
       break;
@@ -487,24 +572,29 @@ typedef int64_t (*Prototype_General4)(in
 typedef int64_t (*Prototype_General5)(int64_t arg0, int64_t arg1, int64_t arg2, int64_t arg3,
                                       int64_t arg4);
 typedef int64_t (*Prototype_General6)(int64_t arg0, int64_t arg1, int64_t arg2, int64_t arg3,
                                       int64_t arg4, int64_t arg5);
 typedef int64_t (*Prototype_General7)(int64_t arg0, int64_t arg1, int64_t arg2, int64_t arg3,
                                       int64_t arg4, int64_t arg5, int64_t arg6);
 typedef int64_t (*Prototype_General8)(int64_t arg0, int64_t arg1, int64_t arg2, int64_t arg3,
                                       int64_t arg4, int64_t arg5, int64_t arg6, int64_t arg7);
+typedef int64_t (*Prototype_GeneralGeneralGeneralInt64)(int64_t arg0, int32_t arg1, int32_t arg2,
+                                                        int64_t arg3);
+typedef int64_t (*Prototype_GeneralGeneralInt64Int64)(int64_t arg0, int32_t arg1, int64_t arg2,
+                                                      int64_t arg3);
 
 typedef int64_t (*Prototype_Int_Double)(double arg0);
 typedef int64_t (*Prototype_Int_IntDouble)(int32_t arg0, double arg1);
 typedef int64_t (*Prototype_Int_DoubleIntInt)(double arg0, uint64_t arg1, uint64_t arg2);
 typedef int64_t (*Prototype_Int_IntDoubleIntInt)(uint64_t arg0, double arg1,
                                                  uint64_t arg2, uint64_t arg3);
 
 typedef float (*Prototype_Float32_Float32)(float arg0);
+typedef float (*Prototype_Float32_Float32Float32)(float arg0, float arg1);
 
 typedef double (*Prototype_Double_None)();
 typedef double (*Prototype_Double_Double)(double arg0);
 typedef double (*Prototype_Double_Int)(int32_t arg0);
 typedef double (*Prototype_Double_DoubleInt)(double arg0, int64_t arg1);
 typedef double (*Prototype_Double_IntDouble)(int64_t arg0, double arg1);
 typedef double (*Prototype_Double_DoubleDouble)(double arg0, double arg1);
 typedef double (*Prototype_Double_DoubleDoubleDouble)(double arg0, double arg1, double arg2);
@@ -533,17 +623,17 @@ Simulator::VisitCallRedirection(const In
   DebugOnly<int64_t> x22 = xreg(22);
   DebugOnly<int64_t> x23 = xreg(23);
   DebugOnly<int64_t> x24 = xreg(24);
   DebugOnly<int64_t> x25 = xreg(25);
   DebugOnly<int64_t> x26 = xreg(26);
   DebugOnly<int64_t> x27 = xreg(27);
   DebugOnly<int64_t> x28 = xreg(28);
   DebugOnly<int64_t> x29 = xreg(29);
-  DebugOnly<int64_t> savedSP = xreg(31, Reg31IsStackPointer);
+  DebugOnly<int64_t> savedSP = get_sp();
 
   // Remember LR for returning from the "call".
   int64_t savedLR = xreg(30);
 
   // Allow recursive Simulator calls: returning from the call must stop
   // the simulation and transition back to native Simulator code.
   set_xreg(30, int64_t(kEndOfSimAddress));
 
@@ -556,16 +646,17 @@ Simulator::VisitCallRedirection(const In
   int64_t x5 = xreg(5);
   int64_t x6 = xreg(6);
   int64_t x7 = xreg(7);
   double d0 = dreg(0);
   double d1 = dreg(1);
   double d2 = dreg(2);
   double d3 = dreg(3);
   float s0 = sreg(0);
+  float s1 = sreg(1);
 
   // Dispatch the call and set the return value.
   switch (redir->type()) {
     // Cases with int64_t return type.
     case js::jit::Args_General0: {
       int64_t ret = reinterpret_cast<Prototype_General0>(nativeFn)();
       setGPR64Result(ret);
       break;
@@ -605,16 +696,26 @@ Simulator::VisitCallRedirection(const In
       setGPR64Result(ret);
       break;
     }
     case js::jit::Args_General8: {
       int64_t ret = reinterpret_cast<Prototype_General8>(nativeFn)(x0, x1, x2, x3, x4, x5, x6, x7);
       setGPR64Result(ret);
       break;
     }
+    case js::jit::Args_Int_GeneralGeneralGeneralInt64: {
+      int64_t ret = reinterpret_cast<Prototype_GeneralGeneralGeneralInt64>(nativeFn)(x0, x1, x2, x3);
+      setGPR64Result(ret);
+      break;
+    }
+    case js::jit::Args_Int_GeneralGeneralInt64Int64: {
+      int64_t ret = reinterpret_cast<Prototype_GeneralGeneralInt64Int64>(nativeFn)(x0, x1, x2, x3);
+      setGPR64Result(ret);
+      break;
+    }
 
     // Cases with GPR return type. This can be int32 or int64, but int64 is a safer assumption.
     case js::jit::Args_Int_Double: {
       int64_t ret = reinterpret_cast<Prototype_Int_Double>(nativeFn)(d0);
       setGPR64Result(ret);
       break;
     }
     case js::jit::Args_Int_IntDouble: {
@@ -636,16 +737,21 @@ Simulator::VisitCallRedirection(const In
     }
 
     // Cases with float return type.
     case js::jit::Args_Float32_Float32: {
       float ret = reinterpret_cast<Prototype_Float32_Float32>(nativeFn)(s0);
       setFP32Result(ret);
       break;
     }
+    case js::jit::Args_Float32_Float32Float32: {
+      float ret = reinterpret_cast<Prototype_Float32_Float32Float32>(nativeFn)(s0, s1);
+      setFP32Result(ret);
+      break;
+    }
 
     // Cases with double return type.
     case js::jit::Args_Double_None: {
       double ret = reinterpret_cast<Prototype_Double_None>(nativeFn)();
       setFP64Result(ret);
       break;
     }
     case js::jit::Args_Double_Double: {
@@ -700,17 +806,17 @@ Simulator::VisitCallRedirection(const In
   VIXL_ASSERT(xreg(24) == x24);
   VIXL_ASSERT(xreg(25) == x25);
   VIXL_ASSERT(xreg(26) == x26);
   VIXL_ASSERT(xreg(27) == x27);
   VIXL_ASSERT(xreg(28) == x28);
   VIXL_ASSERT(xreg(29) == x29);
 
   // Assert that the stack is unchanged.
-  VIXL_ASSERT(savedSP == xreg(31, Reg31IsStackPointer));
+  VIXL_ASSERT(savedSP == get_sp());
 
   // Simulate a return.
   set_lr(savedLR);
   set_pc((Instruction*)savedLR);
   if (getenv("USE_DEBUGGER"))
     printf("SVCRET\n");
 }
 
--- a/js/src/jit/arm64/vixl/Simulator-vixl.cpp
+++ b/js/src/jit/arm64/vixl/Simulator-vixl.cpp
@@ -1007,64 +1007,95 @@ void Simulator::VisitLoadStoreRegisterOf
   VIXL_ASSERT((ext == UXTW) || (ext == UXTX) || (ext == SXTW) || (ext == SXTX));
   unsigned shift_amount = instr->ImmShiftLS() * instr->SizeLS();
 
   int64_t offset = ExtendValue(kXRegSize, xreg(instr->Rm()), ext,
                                shift_amount);
   LoadStoreHelper(instr, offset, Offset);
 }
 
-
+template<typename T>
+static T Faulted() {
+    return ~0;
+}
+
+template<>
+Simulator::qreg_t Faulted() {
+    static_assert(kQRegSizeInBytes == 16, "Known constraint");
+    static Simulator::qreg_t dummy = { {
+	255, 255, 255, 255, 255, 255, 255, 255,
+	255, 255, 255, 255, 255, 255, 255, 255
+    } };
+    return dummy;
+}
+
+template<typename T> T
+Simulator::Read(uintptr_t address)
+{
+    address = Memory::AddressUntag(address);
+    if (handle_wasm_seg_fault(address, sizeof(T)))
+	return Faulted<T>();
+    return Memory::Read<T>(address);
+}
+
+template <typename T> void
+Simulator::Write(uintptr_t address, T value)
+{
+    address = Memory::AddressUntag(address);
+    if (handle_wasm_seg_fault(address, sizeof(T)))
+	return;
+    Memory::Write<T>(address, value);
+}
 
 void Simulator::LoadStoreHelper(const Instruction* instr,
                                 int64_t offset,
                                 AddrMode addrmode) {
   unsigned srcdst = instr->Rt();
   uintptr_t address = AddressModeHelper(instr->Rn(), offset, addrmode);
 
   LoadStoreOp op = static_cast<LoadStoreOp>(instr->Mask(LoadStoreMask));
   switch (op) {
     case LDRB_w:
-      set_wreg(srcdst, Memory::Read<uint8_t>(address), NoRegLog); break;
+      set_wreg(srcdst, Read<uint8_t>(address), NoRegLog); break;
     case LDRH_w:
-      set_wreg(srcdst, Memory::Read<uint16_t>(address), NoRegLog); break;
+      set_wreg(srcdst, Read<uint16_t>(address), NoRegLog); break;
     case LDR_w:
-      set_wreg(srcdst, Memory::Read<uint32_t>(address), NoRegLog); break;
+      set_wreg(srcdst, Read<uint32_t>(address), NoRegLog); break;
     case LDR_x:
-      set_xreg(srcdst, Memory::Read<uint64_t>(address), NoRegLog); break;
+      set_xreg(srcdst, Read<uint64_t>(address), NoRegLog); break;
     case LDRSB_w:
-      set_wreg(srcdst, Memory::Read<int8_t>(address), NoRegLog); break;
+      set_wreg(srcdst, Read<int8_t>(address), NoRegLog); break;
     case LDRSH_w:
-      set_wreg(srcdst, Memory::Read<int16_t>(address), NoRegLog); break;
+      set_wreg(srcdst, Read<int16_t>(address), NoRegLog); break;
     case LDRSB_x:
-      set_xreg(srcdst, Memory::Read<int8_t>(address), NoRegLog); break;
+      set_xreg(srcdst, Read<int8_t>(address), NoRegLog); break;
     case LDRSH_x:
-      set_xreg(srcdst, Memory::Read<int16_t>(address), NoRegLog); break;
+      set_xreg(srcdst, Read<int16_t>(address), NoRegLog); break;
     case LDRSW_x:
-      set_xreg(srcdst, Memory::Read<int32_t>(address), NoRegLog); break;
+      set_xreg(srcdst, Read<int32_t>(address), NoRegLog); break;
     case LDR_b:
-      set_breg(srcdst, Memory::Read<uint8_t>(address), NoRegLog); break;
+      set_breg(srcdst, Read<uint8_t>(address), NoRegLog); break;
     case LDR_h:
-      set_hreg(srcdst, Memory::Read<uint16_t>(address), NoRegLog); break;
+      set_hreg(srcdst, Read<uint16_t>(address), NoRegLog); break;
     case LDR_s:
-      set_sreg(srcdst, Memory::Read<float>(address), NoRegLog); break;
+      set_sreg(srcdst, Read<float>(address), NoRegLog); break;
     case LDR_d:
-      set_dreg(srcdst, Memory::Read<double>(address), NoRegLog); break;
+      set_dreg(srcdst, Read<double>(address), NoRegLog); break;
     case LDR_q:
-      set_qreg(srcdst, Memory::Read<qreg_t>(address), NoRegLog); break;
-
-    case STRB_w:  Memory::Write<uint8_t>(address, wreg(srcdst)); break;
-    case STRH_w:  Memory::Write<uint16_t>(address, wreg(srcdst)); break;
-    case STR_w:   Memory::Write<uint32_t>(address, wreg(srcdst)); break;
-    case STR_x:   Memory::Write<uint64_t>(address, xreg(srcdst)); break;
-    case STR_b:   Memory::Write<uint8_t>(address, breg(srcdst)); break;
-    case STR_h:   Memory::Write<uint16_t>(address, hreg(srcdst)); break;
-    case STR_s:   Memory::Write<float>(address, sreg(srcdst)); break;
-    case STR_d:   Memory::Write<double>(address, dreg(srcdst)); break;
-    case STR_q:   Memory::Write<qreg_t>(address, qreg(srcdst)); break;
+      set_qreg(srcdst, Read<qreg_t>(address), NoRegLog); break;
+
+    case STRB_w:  Write<uint8_t>(address, wreg(srcdst)); break;
+    case STRH_w:  Write<uint16_t>(address, wreg(srcdst)); break;
+    case STR_w:   Write<uint32_t>(address, wreg(srcdst)); break;
+    case STR_x:   Write<uint64_t>(address, xreg(srcdst)); break;
+    case STR_b:   Write<uint8_t>(address, breg(srcdst)); break;
+    case STR_h:   Write<uint16_t>(address, hreg(srcdst)); break;
+    case STR_s:   Write<float>(address, sreg(srcdst)); break;
+    case STR_d:   Write<double>(address, dreg(srcdst)); break;
+    case STR_q:   Write<qreg_t>(address, qreg(srcdst)); break;
 
     // Ignore prfm hint instructions.
     case PRFM: break;
 
     default: VIXL_UNIMPLEMENTED();
   }
 
   unsigned access_size = 1 << instr->SizeLS();
@@ -1124,68 +1155,68 @@ void Simulator::LoadStorePairHelper(cons
 
   // 'rt' and 'rt2' can only be aliased for stores.
   VIXL_ASSERT(((op & LoadStorePairLBit) == 0) || (rt != rt2));
 
   switch (op) {
     // Use NoRegLog to suppress the register trace (LOG_REGS, LOG_FP_REGS). We
     // will print a more detailed log.
     case LDP_w: {
-      set_wreg(rt, Memory::Read<uint32_t>(address), NoRegLog);
-      set_wreg(rt2, Memory::Read<uint32_t>(address2), NoRegLog);
+      set_wreg(rt, Read<uint32_t>(address), NoRegLog);
+      set_wreg(rt2, Read<uint32_t>(address2), NoRegLog);
       break;
     }
     case LDP_s: {
-      set_sreg(rt, Memory::Read<float>(address), NoRegLog);
-      set_sreg(rt2, Memory::Read<float>(address2), NoRegLog);
+      set_sreg(rt, Read<float>(address), NoRegLog);
+      set_sreg(rt2, Read<float>(address2), NoRegLog);
       break;
     }
     case LDP_x: {
-      set_xreg(rt, Memory::Read<uint64_t>(address), NoRegLog);
-      set_xreg(rt2, Memory::Read<uint64_t>(address2), NoRegLog);
+      set_xreg(rt, Read<uint64_t>(address), NoRegLog);
+      set_xreg(rt2, Read<uint64_t>(address2), NoRegLog);
       break;
     }
     case LDP_d: {
-      set_dreg(rt, Memory::Read<double>(address), NoRegLog);
-      set_dreg(rt2, Memory::Read<double>(address2), NoRegLog);
+      set_dreg(rt, Read<double>(address), NoRegLog);
+      set_dreg(rt2, Read<double>(address2), NoRegLog);
       break;
     }
     case LDP_q: {
-      set_qreg(rt, Memory::Read<qreg_t>(address), NoRegLog);
-      set_qreg(rt2, Memory::Read<qreg_t>(address2), NoRegLog);
+      set_qreg(rt, Read<qreg_t>(address), NoRegLog);
+      set_qreg(rt2, Read<qreg_t>(address2), NoRegLog);
       break;
     }
     case LDPSW_x: {
-      set_xreg(rt, Memory::Read<int32_t>(address), NoRegLog);
-      set_xreg(rt2, Memory::Read<int32_t>(address2), NoRegLog);
+      set_xreg(rt, Read<int32_t>(address), NoRegLog);
+      set_xreg(rt2, Read<int32_t>(address2), NoRegLog);
       break;
     }
     case STP_w: {
-      Memory::Write<uint32_t>(address, wreg(rt));
-      Memory::Write<uint32_t>(address2, wreg(rt2));
+      Write<uint32_t>(address, wreg(rt));
+      Write<uint32_t>(address2, wreg(rt2));
       break;
     }
     case STP_s: {
-      Memory::Write<float>(address, sreg(rt));
-      Memory::Write<float>(address2, sreg(rt2));
+      Write<float>(address, sreg(rt));
+      Write<float>(address2, sreg(rt2));
       break;
     }
     case STP_x: {
-      Memory::Write<uint64_t>(address, xreg(rt));
-      Memory::Write<uint64_t>(address2, xreg(rt2));
+      Write<uint64_t>(address, xreg(rt));
+      Write<uint64_t>(address2, xreg(rt2));
       break;
     }
     case STP_d: {
-      Memory::Write<double>(address, dreg(rt));
-      Memory::Write<double>(address2, dreg(rt2));
+      Write<double>(address, dreg(rt));
+      Write<double>(address2, dreg(rt2));
       break;
     }
     case STP_q: {
-      Memory::Write<qreg_t>(address, qreg(rt));
-      Memory::Write<qreg_t>(address2, qreg(rt2));
+      Write<qreg_t>(address, qreg(rt));
+      Write<qreg_t>(address2, qreg(rt2));
       break;
     }
     default: VIXL_UNREACHABLE();
   }
 
   // Print a detailed trace (including the memory address) instead of the basic
   // register:value trace generated by set_*reg().
   if (instr->IsLoad()) {
@@ -1271,42 +1302,42 @@ void Simulator::VisitLoadStoreExclusive(
     }
 
     // Use NoRegLog to suppress the register trace (LOG_REGS, LOG_FP_REGS). We
     // will print a more detailed log.
     switch (op) {
       case LDXRB_w:
       case LDAXRB_w:
       case LDARB_w:
-        set_wreg(rt, Memory::Read<uint8_t>(address), NoRegLog);
+        set_wreg(rt, Read<uint8_t>(address), NoRegLog);
         break;
       case LDXRH_w:
       case LDAXRH_w:
       case LDARH_w:
-        set_wreg(rt, Memory::Read<uint16_t>(address), NoRegLog);
+        set_wreg(rt, Read<uint16_t>(address), NoRegLog);
         break;
       case LDXR_w:
       case LDAXR_w:
       case LDAR_w:
-        set_wreg(rt, Memory::Read<uint32_t>(address), NoRegLog);
+        set_wreg(rt, Read<uint32_t>(address), NoRegLog);
         break;
       case LDXR_x:
       case LDAXR_x:
       case LDAR_x:
-        set_xreg(rt, Memory::Read<uint64_t>(address), NoRegLog);
+        set_xreg(rt, Read<uint64_t>(address), NoRegLog);
         break;
       case LDXP_w:
       case LDAXP_w:
-        set_wreg(rt, Memory::Read<uint32_t>(address), NoRegLog);
-        set_wreg(rt2, Memory::Read<uint32_t>(address + element_size), NoRegLog);
+        set_wreg(rt, Read<uint32_t>(address), NoRegLog);
+        set_wreg(rt2, Read<uint32_t>(address + element_size), NoRegLog);
         break;
       case LDXP_x:
       case LDAXP_x:
-        set_xreg(rt, Memory::Read<uint64_t>(address), NoRegLog);
-        set_xreg(rt2, Memory::Read<uint64_t>(address + element_size), NoRegLog);
+        set_xreg(rt, Read<uint64_t>(address), NoRegLog);
+        set_xreg(rt2, Read<uint64_t>(address + element_size), NoRegLog);
         break;
       default:
         VIXL_UNREACHABLE();
     }
 
     if (is_acquire_release) {
       // Approximate load-acquire by issuing a full barrier after the load.
       __sync_synchronize();
@@ -1336,42 +1367,42 @@ void Simulator::VisitLoadStoreExclusive(
       local_monitor_.MaybeClear();
     }
 
     if (do_store) {
       switch (op) {
         case STXRB_w:
         case STLXRB_w:
         case STLRB_w:
-          Memory::Write<uint8_t>(address, wreg(rt));
+          Write<uint8_t>(address, wreg(rt));
           break;
         case STXRH_w:
         case STLXRH_w:
         case STLRH_w:
-          Memory::Write<uint16_t>(address, wreg(rt));
+          Write<uint16_t>(address, wreg(rt));
           break;
         case STXR_w:
         case STLXR_w:
         case STLR_w:
-          Memory::Write<uint32_t>(address, wreg(rt));
+          Write<uint32_t>(address, wreg(rt));
           break;
         case STXR_x:
         case STLXR_x:
         case STLR_x:
-          Memory::Write<uint64_t>(address, xreg(rt));
+          Write<uint64_t>(address, xreg(rt));
           break;
         case STXP_w:
         case STLXP_w:
-          Memory::Write<uint32_t>(address, wreg(rt));
-          Memory::Write<uint32_t>(address + element_size, wreg(rt2));
+          Write<uint32_t>(address, wreg(rt));
+          Write<uint32_t>(address + element_size, wreg(rt2));
           break;
         case STXP_x:
         case STLXP_x:
-          Memory::Write<uint64_t>(address, xreg(rt));
-          Memory::Write<uint64_t>(address + element_size, xreg(rt2));
+          Write<uint64_t>(address, xreg(rt));
+          Write<uint64_t>(address + element_size, xreg(rt2));
           break;
         default:
           VIXL_UNREACHABLE();
       }
 
       LogWrite(address, rt, GetPrintRegisterFormatForSize(element_size));
       if (is_pair) {
         LogWrite(address + element_size, rt2,
@@ -1388,37 +1419,37 @@ void Simulator::VisitLoadLiteral(const I
 
   // Verify that the calculated address is available to the host.
   VIXL_ASSERT(address == static_cast<uintptr_t>(address));
 
   switch (instr->Mask(LoadLiteralMask)) {
     // Use NoRegLog to suppress the register trace (LOG_REGS, LOG_VREGS), then
     // print a more detailed log.
     case LDR_w_lit:
-      set_wreg(rt, Memory::Read<uint32_t>(address), NoRegLog);
+      set_wreg(rt, Read<uint32_t>(address), NoRegLog);
       LogRead(address, rt, kPrintWReg);
       break;
     case LDR_x_lit:
-      set_xreg(rt, Memory::Read<uint64_t>(address), NoRegLog);
+      set_xreg(rt, Read<uint64_t>(address), NoRegLog);
       LogRead(address, rt, kPrintXReg);
       break;
     case LDR_s_lit:
-      set_sreg(rt, Memory::Read<float>(address), NoRegLog);
+      set_sreg(rt, Read<float>(address), NoRegLog);
       LogVRead(address, rt, kPrintSReg);
       break;
     case LDR_d_lit:
-      set_dreg(rt, Memory::Read<double>(address), NoRegLog);
+      set_dreg(rt, Read<double>(address), NoRegLog);
       LogVRead(address, rt, kPrintDReg);
       break;
     case LDR_q_lit:
-      set_qreg(rt, Memory::Read<qreg_t>(address), NoRegLog);
+      set_qreg(rt, Read<qreg_t>(address), NoRegLog);
       LogVRead(address, rt, kPrintReg1Q);
       break;
     case LDRSW_x_lit:
-      set_xreg(rt, Memory::Read<int32_t>(address), NoRegLog);
+      set_xreg(rt, Read<int32_t>(address), NoRegLog);
       LogRead(address, rt, kPrintWReg);
       break;
 
     // Ignore prfm hint instructions.
     case PRFM_lit: break;
 
     default: VIXL_UNREACHABLE();
   }
@@ -2237,17 +2268,17 @@ bool Simulator::FPProcessNaNs(const Inst
 void Simulator::SysOp_W(int op, int64_t val) {
   switch (op) {
     case IVAU:
     case CVAC:
     case CVAU:
     case CIVAC: {
       // Perform a dummy memory access to ensure that we have read access
       // to the specified address.
-      volatile uint8_t y = Memory::Read<uint8_t>(val);
+      volatile uint8_t y = Read<uint8_t>(val);
       USE(y);
       // TODO: Implement "case ZVA:".
       break;
     }
     default:
       VIXL_UNIMPLEMENTED();
   }
 }
--- a/js/src/jit/arm64/vixl/Simulator-vixl.h
+++ b/js/src/jit/arm64/vixl/Simulator-vixl.h
@@ -716,37 +716,45 @@ class Simulator : public DecoderVisitor 
   void setFP64Result(double result);
   void VisitCallRedirection(const Instruction* instr);
   static uintptr_t StackLimit() {
     return Simulator::Current()->stackLimit();
   }
   static bool supportsAtomics() {
     return true;
   }
+  template<typename T> T Read(uintptr_t address);
+  template <typename T> void Write(uintptr_t address_, T value);
+  JS::ProfilingFrameIterator::RegisterState registerState();
 
   void ResetState();
 
   // Run the simulator.
   virtual void Run();
   void RunFrom(const Instruction* first);
 
   // Simulation helpers.
   const Instruction* pc() const { return pc_; }
   const Instruction* get_pc() const { return pc_; }
+  int64_t get_sp() const { return xreg(31, Reg31IsStackPointer); }
+  int64_t get_lr() const { return xreg(30); }
+  int64_t get_fp() const { return xreg(29); }
 
   template <typename T>
   T get_pc_as() const { return reinterpret_cast<T>(const_cast<Instruction*>(pc())); }
 
   void set_pc(const Instruction* new_pc) {
     pc_ = Memory::AddressUntag(new_pc);
     pc_modified_ = true;
   }
 
   void trigger_wasm_interrupt();
   void handle_wasm_interrupt();
+  bool handle_wasm_ill_fault();
+  bool handle_wasm_seg_fault(uintptr_t addr, unsigned numBytes);
 
   void increment_pc() {
     if (!pc_modified_) {
       pc_ = pc_->NextInstruction();
     }
 
     pc_modified_ = false;
   }