diff --git a/js/src/jit/arm64/vixl/MozSimulator-vixl.cpp b/js/src/jit/arm64/vixl/MozSimulator-vixl.cpp index cb93d7111530..219f1cb9b049 100644 --- a/js/src/jit/arm64/vixl/MozSimulator-vixl.cpp +++ b/js/src/jit/arm64/vixl/MozSimulator-vixl.cpp @@ -33,15 +33,16 @@ #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) @@ -218,13 +219,13 @@ uintptr_t* Simulator::addressOfStackLimit() { 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(); } @@ -235,31 +236,91 @@ void Simulator::trigger_wasm_interrupt() { } +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; @@ -303,12 +364,12 @@ int64_t Simulator::call(uint8_t* entry, int argument_count, ...) { 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 entryStack = xreg(31, Reg31IsStackPointer); + DebugOnly entryStack = get_sp(); RunFrom((Instruction*)entry); - DebugOnly exitStack = xreg(31, Reg31IsStackPointer); + DebugOnly exitStack = get_sp(); VIXL_ASSERT(entryStack == exitStack); int64_t result = xreg(0); @@ -403,6 +464,29 @@ void* Simulator::RedirectNativeFunction(void* nativeFunction, ABIFunctionType ty 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)) { @@ -415,7 +499,8 @@ void Simulator::VisitException(const Instruction* instr) { case HLT: switch (instr->ImmException()) { case kUnreachableOpcode: - DoUnreachable(instr); + if (!handle_wasm_ill_fault()) + DoUnreachable(instr); return; case kTraceOpcode: DoTrace(instr); @@ -439,12 +524,12 @@ void Simulator::VisitException(const Instruction* 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; @@ -492,6 +577,10 @@ typedef int64_t (*Prototype_General7)(int64_t arg0, int64_t arg1, int64_t arg2, 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); @@ -500,6 +589,7 @@ 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); @@ -538,7 +628,7 @@ Simulator::VisitCallRedirection(const Instruction* instr) DebugOnly x27 = xreg(27); DebugOnly x28 = xreg(28); DebugOnly x29 = xreg(29); - DebugOnly savedSP = xreg(31, Reg31IsStackPointer); + DebugOnly savedSP = get_sp(); // Remember LR for returning from the "call". int64_t savedLR = xreg(30); @@ -561,6 +651,7 @@ Simulator::VisitCallRedirection(const Instruction* instr) 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()) { @@ -610,6 +701,16 @@ Simulator::VisitCallRedirection(const Instruction* instr) setGPR64Result(ret); break; } + case js::jit::Args_Int_GeneralGeneralGeneralInt64: { + int64_t ret = reinterpret_cast(nativeFn)(x0, x1, x2, x3); + setGPR64Result(ret); + break; + } + case js::jit::Args_Int_GeneralGeneralInt64Int64: { + int64_t ret = reinterpret_cast(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: { @@ -641,6 +742,11 @@ Simulator::VisitCallRedirection(const Instruction* instr) setFP32Result(ret); break; } + case js::jit::Args_Float32_Float32Float32: { + float ret = reinterpret_cast(nativeFn)(s0, s1); + setFP32Result(ret); + break; + } // Cases with double return type. case js::jit::Args_Double_None: { @@ -705,7 +811,7 @@ Simulator::VisitCallRedirection(const Instruction* instr) 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); diff --git a/js/src/jit/arm64/vixl/Simulator-vixl.cpp b/js/src/jit/arm64/vixl/Simulator-vixl.cpp index 9d95a2bb4b81..ea2031e3bc32 100644 --- a/js/src/jit/arm64/vixl/Simulator-vixl.cpp +++ b/js/src/jit/arm64/vixl/Simulator-vixl.cpp @@ -1012,7 +1012,38 @@ void Simulator::VisitLoadStoreRegisterOffset(const Instruction* instr) { LoadStoreHelper(instr, offset, Offset); } +template +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 T +Simulator::Read(uintptr_t address) +{ + address = Memory::AddressUntag(address); + if (handle_wasm_seg_fault(address, sizeof(T))) + return Faulted(); + return Memory::Read(address); +} + +template void +Simulator::Write(uintptr_t address, T value) +{ + address = Memory::AddressUntag(address); + if (handle_wasm_seg_fault(address, sizeof(T))) + return; + Memory::Write(address, value); +} void Simulator::LoadStoreHelper(const Instruction* instr, int64_t offset, @@ -1023,43 +1054,43 @@ void Simulator::LoadStoreHelper(const Instruction* instr, LoadStoreOp op = static_cast(instr->Mask(LoadStoreMask)); switch (op) { case LDRB_w: - set_wreg(srcdst, Memory::Read(address), NoRegLog); break; + set_wreg(srcdst, Read(address), NoRegLog); break; case LDRH_w: - set_wreg(srcdst, Memory::Read(address), NoRegLog); break; + set_wreg(srcdst, Read(address), NoRegLog); break; case LDR_w: - set_wreg(srcdst, Memory::Read(address), NoRegLog); break; + set_wreg(srcdst, Read(address), NoRegLog); break; case LDR_x: - set_xreg(srcdst, Memory::Read(address), NoRegLog); break; + set_xreg(srcdst, Read(address), NoRegLog); break; case LDRSB_w: - set_wreg(srcdst, Memory::Read(address), NoRegLog); break; + set_wreg(srcdst, Read(address), NoRegLog); break; case LDRSH_w: - set_wreg(srcdst, Memory::Read(address), NoRegLog); break; + set_wreg(srcdst, Read(address), NoRegLog); break; case LDRSB_x: - set_xreg(srcdst, Memory::Read(address), NoRegLog); break; + set_xreg(srcdst, Read(address), NoRegLog); break; case LDRSH_x: - set_xreg(srcdst, Memory::Read(address), NoRegLog); break; + set_xreg(srcdst, Read(address), NoRegLog); break; case LDRSW_x: - set_xreg(srcdst, Memory::Read(address), NoRegLog); break; + set_xreg(srcdst, Read(address), NoRegLog); break; case LDR_b: - set_breg(srcdst, Memory::Read(address), NoRegLog); break; + set_breg(srcdst, Read(address), NoRegLog); break; case LDR_h: - set_hreg(srcdst, Memory::Read(address), NoRegLog); break; + set_hreg(srcdst, Read(address), NoRegLog); break; case LDR_s: - set_sreg(srcdst, Memory::Read(address), NoRegLog); break; + set_sreg(srcdst, Read(address), NoRegLog); break; case LDR_d: - set_dreg(srcdst, Memory::Read(address), NoRegLog); break; + set_dreg(srcdst, Read(address), NoRegLog); break; case LDR_q: - set_qreg(srcdst, Memory::Read(address), NoRegLog); break; + set_qreg(srcdst, Read(address), NoRegLog); break; - case STRB_w: Memory::Write(address, wreg(srcdst)); break; - case STRH_w: Memory::Write(address, wreg(srcdst)); break; - case STR_w: Memory::Write(address, wreg(srcdst)); break; - case STR_x: Memory::Write(address, xreg(srcdst)); break; - case STR_b: Memory::Write(address, breg(srcdst)); break; - case STR_h: Memory::Write(address, hreg(srcdst)); break; - case STR_s: Memory::Write(address, sreg(srcdst)); break; - case STR_d: Memory::Write(address, dreg(srcdst)); break; - case STR_q: Memory::Write(address, qreg(srcdst)); break; + case STRB_w: Write(address, wreg(srcdst)); break; + case STRH_w: Write(address, wreg(srcdst)); break; + case STR_w: Write(address, wreg(srcdst)); break; + case STR_x: Write(address, xreg(srcdst)); break; + case STR_b: Write(address, breg(srcdst)); break; + case STR_h: Write(address, hreg(srcdst)); break; + case STR_s: Write(address, sreg(srcdst)); break; + case STR_d: Write(address, dreg(srcdst)); break; + case STR_q: Write(address, qreg(srcdst)); break; // Ignore prfm hint instructions. case PRFM: break; @@ -1129,58 +1160,58 @@ void Simulator::LoadStorePairHelper(const Instruction* instr, // 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(address), NoRegLog); - set_wreg(rt2, Memory::Read(address2), NoRegLog); + set_wreg(rt, Read(address), NoRegLog); + set_wreg(rt2, Read(address2), NoRegLog); break; } case LDP_s: { - set_sreg(rt, Memory::Read(address), NoRegLog); - set_sreg(rt2, Memory::Read(address2), NoRegLog); + set_sreg(rt, Read(address), NoRegLog); + set_sreg(rt2, Read(address2), NoRegLog); break; } case LDP_x: { - set_xreg(rt, Memory::Read(address), NoRegLog); - set_xreg(rt2, Memory::Read(address2), NoRegLog); + set_xreg(rt, Read(address), NoRegLog); + set_xreg(rt2, Read(address2), NoRegLog); break; } case LDP_d: { - set_dreg(rt, Memory::Read(address), NoRegLog); - set_dreg(rt2, Memory::Read(address2), NoRegLog); + set_dreg(rt, Read(address), NoRegLog); + set_dreg(rt2, Read(address2), NoRegLog); break; } case LDP_q: { - set_qreg(rt, Memory::Read(address), NoRegLog); - set_qreg(rt2, Memory::Read(address2), NoRegLog); + set_qreg(rt, Read(address), NoRegLog); + set_qreg(rt2, Read(address2), NoRegLog); break; } case LDPSW_x: { - set_xreg(rt, Memory::Read(address), NoRegLog); - set_xreg(rt2, Memory::Read(address2), NoRegLog); + set_xreg(rt, Read(address), NoRegLog); + set_xreg(rt2, Read(address2), NoRegLog); break; } case STP_w: { - Memory::Write(address, wreg(rt)); - Memory::Write(address2, wreg(rt2)); + Write(address, wreg(rt)); + Write(address2, wreg(rt2)); break; } case STP_s: { - Memory::Write(address, sreg(rt)); - Memory::Write(address2, sreg(rt2)); + Write(address, sreg(rt)); + Write(address2, sreg(rt2)); break; } case STP_x: { - Memory::Write(address, xreg(rt)); - Memory::Write(address2, xreg(rt2)); + Write(address, xreg(rt)); + Write(address2, xreg(rt2)); break; } case STP_d: { - Memory::Write(address, dreg(rt)); - Memory::Write(address2, dreg(rt2)); + Write(address, dreg(rt)); + Write(address2, dreg(rt2)); break; } case STP_q: { - Memory::Write(address, qreg(rt)); - Memory::Write(address2, qreg(rt2)); + Write(address, qreg(rt)); + Write(address2, qreg(rt2)); break; } default: VIXL_UNREACHABLE(); @@ -1276,32 +1307,32 @@ void Simulator::VisitLoadStoreExclusive(const Instruction* instr) { case LDXRB_w: case LDAXRB_w: case LDARB_w: - set_wreg(rt, Memory::Read(address), NoRegLog); + set_wreg(rt, Read(address), NoRegLog); break; case LDXRH_w: case LDAXRH_w: case LDARH_w: - set_wreg(rt, Memory::Read(address), NoRegLog); + set_wreg(rt, Read(address), NoRegLog); break; case LDXR_w: case LDAXR_w: case LDAR_w: - set_wreg(rt, Memory::Read(address), NoRegLog); + set_wreg(rt, Read(address), NoRegLog); break; case LDXR_x: case LDAXR_x: case LDAR_x: - set_xreg(rt, Memory::Read(address), NoRegLog); + set_xreg(rt, Read(address), NoRegLog); break; case LDXP_w: case LDAXP_w: - set_wreg(rt, Memory::Read(address), NoRegLog); - set_wreg(rt2, Memory::Read(address + element_size), NoRegLog); + set_wreg(rt, Read(address), NoRegLog); + set_wreg(rt2, Read(address + element_size), NoRegLog); break; case LDXP_x: case LDAXP_x: - set_xreg(rt, Memory::Read(address), NoRegLog); - set_xreg(rt2, Memory::Read(address + element_size), NoRegLog); + set_xreg(rt, Read(address), NoRegLog); + set_xreg(rt2, Read(address + element_size), NoRegLog); break; default: VIXL_UNREACHABLE(); @@ -1341,32 +1372,32 @@ void Simulator::VisitLoadStoreExclusive(const Instruction* instr) { case STXRB_w: case STLXRB_w: case STLRB_w: - Memory::Write(address, wreg(rt)); + Write(address, wreg(rt)); break; case STXRH_w: case STLXRH_w: case STLRH_w: - Memory::Write(address, wreg(rt)); + Write(address, wreg(rt)); break; case STXR_w: case STLXR_w: case STLR_w: - Memory::Write(address, wreg(rt)); + Write(address, wreg(rt)); break; case STXR_x: case STLXR_x: case STLR_x: - Memory::Write(address, xreg(rt)); + Write(address, xreg(rt)); break; case STXP_w: case STLXP_w: - Memory::Write(address, wreg(rt)); - Memory::Write(address + element_size, wreg(rt2)); + Write(address, wreg(rt)); + Write(address + element_size, wreg(rt2)); break; case STXP_x: case STLXP_x: - Memory::Write(address, xreg(rt)); - Memory::Write(address + element_size, xreg(rt2)); + Write(address, xreg(rt)); + Write(address + element_size, xreg(rt2)); break; default: VIXL_UNREACHABLE(); @@ -1393,27 +1424,27 @@ void Simulator::VisitLoadLiteral(const Instruction* instr) { // 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(address), NoRegLog); + set_wreg(rt, Read(address), NoRegLog); LogRead(address, rt, kPrintWReg); break; case LDR_x_lit: - set_xreg(rt, Memory::Read(address), NoRegLog); + set_xreg(rt, Read(address), NoRegLog); LogRead(address, rt, kPrintXReg); break; case LDR_s_lit: - set_sreg(rt, Memory::Read(address), NoRegLog); + set_sreg(rt, Read(address), NoRegLog); LogVRead(address, rt, kPrintSReg); break; case LDR_d_lit: - set_dreg(rt, Memory::Read(address), NoRegLog); + set_dreg(rt, Read(address), NoRegLog); LogVRead(address, rt, kPrintDReg); break; case LDR_q_lit: - set_qreg(rt, Memory::Read(address), NoRegLog); + set_qreg(rt, Read(address), NoRegLog); LogVRead(address, rt, kPrintReg1Q); break; case LDRSW_x_lit: - set_xreg(rt, Memory::Read(address), NoRegLog); + set_xreg(rt, Read(address), NoRegLog); LogRead(address, rt, kPrintWReg); break; @@ -2242,7 +2273,7 @@ void Simulator::SysOp_W(int op, int64_t val) { case CIVAC: { // Perform a dummy memory access to ensure that we have read access // to the specified address. - volatile uint8_t y = Memory::Read(val); + volatile uint8_t y = Read(val); USE(y); // TODO: Implement "case ZVA:". break; diff --git a/js/src/jit/arm64/vixl/Simulator-vixl.h b/js/src/jit/arm64/vixl/Simulator-vixl.h index 07956733ea04..4a58419ed93c 100644 --- a/js/src/jit/arm64/vixl/Simulator-vixl.h +++ b/js/src/jit/arm64/vixl/Simulator-vixl.h @@ -721,6 +721,9 @@ class Simulator : public DecoderVisitor { static bool supportsAtomics() { return true; } + template T Read(uintptr_t address); + template void Write(uintptr_t address_, T value); + JS::ProfilingFrameIterator::RegisterState registerState(); void ResetState(); @@ -731,6 +734,9 @@ class Simulator : public DecoderVisitor { // 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 T get_pc_as() const { return reinterpret_cast(const_cast(pc())); } @@ -742,6 +748,8 @@ class Simulator : public DecoderVisitor { 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_) {