| // Copyright 2013, ARM Limited |
| // All rights reserved. |
| // |
| // Redistribution and use in source and binary forms, with or without |
| // modification, are permitted provided that the following conditions are met: |
| // |
| // * Redistributions of source code must retain the above copyright notice, |
| // this list of conditions and the following disclaimer. |
| // * Redistributions in binary form must reproduce the above copyright notice, |
| // this list of conditions and the following disclaimer in the documentation |
| // and/or other materials provided with the distribution. |
| // * Neither the name of ARM Limited nor the names of its contributors may be |
| // used to endorse or promote products derived from this software without |
| // specific prior written permission. |
| // |
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND |
| // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
| // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
| // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE |
| // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
| // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR |
| // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER |
| // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, |
| // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| |
| #include "mozilla/DebugOnly.h" |
| |
| #include "jit/arm64/vixl/Debugger-vixl.h" |
| #include "jit/arm64/vixl/Simulator-vixl.h" |
| #include "jit/IonTypes.h" |
| #include "vm/Runtime.h" |
| |
| namespace vixl { |
| |
| |
| using mozilla::DebugOnly; |
| using js::jit::ABIFunctionType; |
| |
| Simulator::Simulator(Decoder* decoder, FILE* stream) |
| : stream_(nullptr) |
| , print_disasm_(nullptr) |
| , instrumentation_(nullptr) |
| , stack_(nullptr) |
| , stack_limit_(nullptr) |
| , decoder_(nullptr) |
| , oom_(false) |
| , lock_(nullptr) |
| { |
| this->init(decoder, stream); |
| } |
| |
| |
| Simulator::~Simulator() { |
| js_free(stack_); |
| stack_ = nullptr; |
| |
| // The decoder may outlive the simulator. |
| if (print_disasm_) { |
| decoder_->RemoveVisitor(print_disasm_); |
| js_delete(print_disasm_); |
| print_disasm_ = nullptr; |
| } |
| |
| if (instrumentation_) { |
| decoder_->RemoveVisitor(instrumentation_); |
| js_delete(instrumentation_); |
| instrumentation_ = nullptr; |
| } |
| } |
| |
| |
| void Simulator::ResetState() { |
| // Reset the system registers. |
| nzcv_ = SimSystemRegister::DefaultValueFor(NZCV); |
| fpcr_ = SimSystemRegister::DefaultValueFor(FPCR); |
| |
| // Reset registers to 0. |
| pc_ = nullptr; |
| pc_modified_ = false; |
| for (unsigned i = 0; i < kNumberOfRegisters; i++) { |
| set_xreg(i, 0xbadbeef); |
| } |
| // Set FP registers to a value that is a NaN in both 32-bit and 64-bit FP. |
| uint64_t nan_bits = UINT64_C(0x7ff0dead7f8beef1); |
| VIXL_ASSERT(IsSignallingNaN(rawbits_to_double(nan_bits & kDRegMask))); |
| VIXL_ASSERT(IsSignallingNaN(rawbits_to_float(nan_bits & kSRegMask))); |
| for (unsigned i = 0; i < kNumberOfFPRegisters; i++) { |
| set_dreg_bits(i, nan_bits); |
| } |
| // Returning to address 0 exits the Simulator. |
| set_lr(kEndOfSimAddress); |
| set_resume_pc(nullptr); |
| } |
| |
| |
| void Simulator::init(Decoder* decoder, FILE* stream) { |
| // Ensure that shift operations act as the simulator expects. |
| VIXL_ASSERT((static_cast<int32_t>(-1) >> 1) == -1); |
| VIXL_ASSERT((static_cast<uint32_t>(-1) >> 1) == 0x7FFFFFFF); |
| |
| instruction_stats_ = false; |
| |
| // Set up the decoder. |
| decoder_ = decoder; |
| decoder_->AppendVisitor(this); |
| |
| stream_ = stream; |
| print_disasm_ = js_new<PrintDisassembler>(stream_); |
| if (!print_disasm_) { |
| oom_ = true; |
| return; |
| } |
| set_coloured_trace(false); |
| trace_parameters_ = LOG_NONE; |
| |
| ResetState(); |
| |
| // Allocate and set up the simulator stack. |
| stack_ = (byte*)js_malloc(stack_size_); |
| if (!stack_) { |
| oom_ = true; |
| return; |
| } |
| stack_limit_ = stack_ + stack_protection_size_; |
| // Configure the starting stack pointer. |
| // - Find the top of the stack. |
| byte * tos = stack_ + stack_size_; |
| // - There's a protection region at both ends of the stack. |
| tos -= stack_protection_size_; |
| // - The stack pointer must be 16-byte aligned. |
| tos = AlignDown(tos, 16); |
| set_sp(tos); |
| |
| // Set the sample period to 10, as the VIXL examples and tests are short. |
| instrumentation_ = js_new<Instrument>("vixl_stats.csv", 10); |
| if (!instrumentation_) { |
| oom_ = true; |
| return; |
| } |
| |
| // Print a warning about exclusive-access instructions, but only the first |
| // time they are encountered. This warning can be silenced using |
| // SilenceExclusiveAccessWarning(). |
| print_exclusive_access_warning_ = true; |
| |
| lock_ = PR_NewLock(); |
| if (!lock_) { |
| oom_ = true; |
| return; |
| } |
| #ifdef DEBUG |
| lockOwner_ = nullptr; |
| #endif |
| redirection_ = nullptr; |
| } |
| |
| |
| Simulator* Simulator::Current() { |
| return js::TlsPerThreadData.get()->simulator(); |
| } |
| |
| |
| Simulator* Simulator::Create() { |
| Decoder *decoder = js_new<vixl::Decoder>(); |
| if (!decoder) |
| return nullptr; |
| |
| // FIXME: This just leaks the Decoder object for now, which is probably OK. |
| // FIXME: We should free it at some point. |
| // FIXME: Note that it can't be stored in the SimulatorRuntime due to lifetime conflicts. |
| Simulator *sim; |
| if (js_sb_getenv("USE_DEBUGGER") != nullptr) |
| sim = js_new<Debugger>(decoder, stdout); |
| else |
| sim = js_new<Simulator>(decoder, stdout); |
| |
| // Check if Simulator:init ran out of memory. |
| if (sim && sim->oom()) { |
| js_delete(sim); |
| return nullptr; |
| } |
| |
| return sim; |
| } |
| |
| |
| void Simulator::Destroy(Simulator* sim) { |
| js_delete(sim); |
| } |
| |
| |
| void Simulator::ExecuteInstruction() { |
| // The program counter should always be aligned. |
| VIXL_ASSERT(IsWordAligned(pc_)); |
| decoder_->Decode(pc_); |
| const Instruction* rpc = resume_pc_; |
| increment_pc(); |
| |
| if (MOZ_UNLIKELY(rpc)) { |
| JSRuntime::innermostAsmJSActivation()->setResumePC((void*)pc()); |
| set_pc(rpc); |
| // Just calling set_pc turns the pc_modified_ flag on, which means it doesn't |
| // auto-step after executing the next instruction. Force that to off so it |
| // will auto-step after executing the first instruction of the handler. |
| pc_modified_ = false; |
| resume_pc_ = nullptr; |
| } |
| } |
| |
| |
| uintptr_t Simulator::stackLimit() const { |
| return reinterpret_cast<uintptr_t>(stack_limit_); |
| } |
| |
| |
| uintptr_t* Simulator::addressOfStackLimit() { |
| return (uintptr_t*)&stack_limit_; |
| } |
| |
| |
| bool Simulator::overRecursed(uintptr_t newsp) const { |
| if (newsp) |
| newsp = xreg(31, Reg31IsStackPointer); |
| return newsp <= stackLimit(); |
| } |
| |
| |
| bool Simulator::overRecursedWithExtra(uint32_t extra) const { |
| uintptr_t newsp = xreg(31, Reg31IsStackPointer) - extra; |
| return newsp <= stackLimit(); |
| } |
| |
| |
| void Simulator::set_resume_pc(void* new_resume_pc) { |
| resume_pc_ = AddressUntag(reinterpret_cast<Instruction*>(new_resume_pc)); |
| } |
| |
| |
| 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 |
| // (with templates, like the callVM machinery), but since the |
| // number of called functions is miniscule, their types have been |
| // divined from the number of arguments. |
| if (argument_count == 8) { |
| // EnterJitData::jitcode. |
| set_xreg(0, va_arg(parameters, int64_t)); |
| // EnterJitData::maxArgc. |
| set_xreg(1, va_arg(parameters, unsigned)); |
| // EnterJitData::maxArgv. |
| set_xreg(2, va_arg(parameters, int64_t)); |
| // EnterJitData::osrFrame. |
| set_xreg(3, va_arg(parameters, int64_t)); |
| // EnterJitData::calleeToken. |
| set_xreg(4, va_arg(parameters, int64_t)); |
| // EnterJitData::scopeChain. |
| set_xreg(5, va_arg(parameters, int64_t)); |
| // EnterJitData::osrNumStackValues. |
| set_xreg(6, va_arg(parameters, unsigned)); |
| // Address of EnterJitData::result. |
| set_xreg(7, va_arg(parameters, int64_t)); |
| } else if (argument_count == 2) { |
| // EntryArg* args |
| set_xreg(0, va_arg(parameters, int64_t)); |
| // uint8_t* GlobalData |
| set_xreg(1, va_arg(parameters, int64_t)); |
| } else if (argument_count == 1) { // irregexp |
| // InputOutputData& data |
| 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)); |
| |
| // Execute the simulation. |
| DebugOnly<int64_t> entryStack = xreg(31, Reg31IsStackPointer); |
| RunFrom((Instruction*)entry); |
| DebugOnly<int64_t> exitStack = xreg(31, Reg31IsStackPointer); |
| VIXL_ASSERT(entryStack == exitStack); |
| |
| int64_t result = xreg(0); |
| if (js_sb_getenv("USE_DEBUGGER")) |
| printf("LEAVE\n"); |
| return result; |
| } |
| |
| |
| // Protects the icache and redirection properties of the simulator. |
| class AutoLockSimulatorCache |
| { |
| friend class Simulator; |
| |
| public: |
| explicit AutoLockSimulatorCache(Simulator* sim) : sim_(sim) { |
| PR_Lock(sim_->lock_); |
| VIXL_ASSERT(!sim_->lockOwner_); |
| #ifdef DEBUG |
| sim_->lockOwner_ = PR_GetCurrentThread(); |
| #endif |
| } |
| |
| ~AutoLockSimulatorCache() { |
| #ifdef DEBUG |
| VIXL_ASSERT(sim_->lockOwner_ == PR_GetCurrentThread()); |
| sim_->lockOwner_ = nullptr; |
| #endif |
| PR_Unlock(sim_->lock_); |
| } |
| |
| private: |
| Simulator* const sim_; |
| }; |
| |
| |
| // When the generated code calls a VM function (masm.callWithABI) we need to |
| // call that function instead of trying to execute it with the simulator |
| // (because it's x64 code instead of AArch64 code). We do that by redirecting the VM |
| // call to a svc (Supervisor Call) instruction that is handled by the |
| // simulator. We write the original destination of the jump just at a known |
| // offset from the svc instruction so the simulator knows what to call. |
| class Redirection |
| { |
| friend class Simulator; |
| |
| Redirection(void* nativeFunction, ABIFunctionType type, Simulator* sim) |
| : nativeFunction_(nativeFunction), |
| type_(type), |
| next_(nullptr) |
| { |
| next_ = sim->redirection(); |
| // TODO: Flush ICache? |
| sim->setRedirection(this); |
| |
| Instruction* instr = (Instruction*)(&svcInstruction_); |
| vixl::Assembler::svc(instr, kCallRtRedirected); |
| } |
| |
| public: |
| void* addressOfSvcInstruction() { return &svcInstruction_; } |
| void* nativeFunction() const { return nativeFunction_; } |
| ABIFunctionType type() const { return type_; } |
| |
| static Redirection* Get(void* nativeFunction, ABIFunctionType type) { |
| Simulator* sim = Simulator::Current(); |
| AutoLockSimulatorCache alsr(sim); |
| |
| // TODO: Store srt_ in the simulator for this assertion. |
| // VIXL_ASSERT_IF(pt->simulator(), pt->simulator()->srt_ == srt); |
| |
| Redirection* current = sim->redirection(); |
| for (; current != nullptr; current = current->next_) { |
| if (current->nativeFunction_ == nativeFunction) { |
| VIXL_ASSERT(current->type() == type); |
| return current; |
| } |
| } |
| |
| js::AutoEnterOOMUnsafeRegion oomUnsafe; |
| Redirection* redir = (Redirection*)js_malloc(sizeof(Redirection)); |
| if (!redir) |
| oomUnsafe.crash("Simulator redirection"); |
| new(redir) Redirection(nativeFunction, type, sim); |
| return redir; |
| } |
| |
| static const Redirection* FromSvcInstruction(const Instruction* svcInstruction) { |
| const uint8_t* addrOfSvc = reinterpret_cast<const uint8_t*>(svcInstruction); |
| const uint8_t* addrOfRedirection = addrOfSvc - offsetof(Redirection, svcInstruction_); |
| return reinterpret_cast<const Redirection*>(addrOfRedirection); |
| } |
| |
| private: |
| void* nativeFunction_; |
| uint32_t svcInstruction_; |
| ABIFunctionType type_; |
| Redirection* next_; |
| }; |
| |
| |
| void Simulator::setRedirection(Redirection* redirection) { |
| // VIXL_ASSERT(lockOwner_); TODO |
| redirection_ = redirection; |
| } |
| |
| |
| Redirection* Simulator::redirection() const { |
| return redirection_; |
| } |
| |
| |
| void* Simulator::RedirectNativeFunction(void* nativeFunction, ABIFunctionType type) { |
| Redirection* redirection = Redirection::Get(nativeFunction, type); |
| return redirection->addressOfSvcInstruction(); |
| } |
| |
| |
| 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); |
| return; |
| case kTraceOpcode: |
| DoTrace(instr); |
| return; |
| case kLogOpcode: |
| DoLog(instr); |
| return; |
| case kPrintfOpcode: |
| DoPrintf(instr); |
| return; |
| default: |
| HostBreakpoint(); |
| return; |
| } |
| case SVC: |
| // 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: |
| spStack_.append(xreg(31, Reg31IsStackPointer)); |
| return; |
| case kCheckStackPointer: { |
| int64_t current = xreg(31, Reg31IsStackPointer); |
| int64_t expected = spStack_.popCopy(); |
| VIXL_ASSERT(current == expected); |
| return; |
| } |
| default: |
| VIXL_UNIMPLEMENTED(); |
| } |
| break; |
| default: |
| VIXL_UNIMPLEMENTED(); |
| } |
| } |
| |
| |
| void Simulator::setGPR32Result(int32_t result) { |
| set_wreg(0, result); |
| } |
| |
| |
| void Simulator::setGPR64Result(int64_t result) { |
| set_xreg(0, result); |
| } |
| |
| |
| void Simulator::setFP32Result(float result) { |
| set_sreg(0, result); |
| } |
| |
| |
| void Simulator::setFP64Result(double result) { |
| set_dreg(0, result); |
| } |
| |
| |
| typedef int64_t (*Prototype_General0)(); |
| typedef int64_t (*Prototype_General1)(int64_t arg0); |
| typedef int64_t (*Prototype_General2)(int64_t arg0, int64_t arg1); |
| typedef int64_t (*Prototype_General3)(int64_t arg0, int64_t arg1, int64_t arg2); |
| typedef int64_t (*Prototype_General4)(int64_t arg0, int64_t arg1, int64_t arg2, int64_t arg3); |
| 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_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 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); |
| typedef double (*Prototype_Double_DoubleDoubleDoubleDouble)(double arg0, double arg1, |
| double arg2, double arg3); |
| |
| |
| // Simulator support for callWithABI(). |
| void |
| Simulator::VisitCallRedirection(const Instruction* instr) |
| { |
| VIXL_ASSERT(instr->Mask(ExceptionMask) == SVC); |
| VIXL_ASSERT(instr->ImmException() == kCallRtRedirected); |
| |
| const Redirection* redir = Redirection::FromSvcInstruction(instr); |
| uintptr_t nativeFn = reinterpret_cast<uintptr_t>(redir->nativeFunction()); |
| |
| // Stack must be aligned prior to the call. |
| // FIXME: It's actually our job to perform the alignment... |
| //VIXL_ASSERT((xreg(31, Reg31IsStackPointer) & (StackAlignment - 1)) == 0); |
| |
| // Used to assert that callee-saved registers are preserved. |
| DebugOnly<int64_t> x19 = xreg(19); |
| DebugOnly<int64_t> x20 = xreg(20); |
| DebugOnly<int64_t> x21 = xreg(21); |
| 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); |
| |
| // 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)); |
| |
| // Store argument register values in local variables for ease of use below. |
| int64_t x0 = xreg(0); |
| int64_t x1 = xreg(1); |
| int64_t x2 = xreg(2); |
| int64_t x3 = xreg(3); |
| int64_t x4 = xreg(4); |
| 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); |
| |
| // 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; |
| } |
| case js::jit::Args_General1: { |
| int64_t ret = reinterpret_cast<Prototype_General1>(nativeFn)(x0); |
| setGPR64Result(ret); |
| break; |
| } |
| case js::jit::Args_General2: { |
| int64_t ret = reinterpret_cast<Prototype_General2>(nativeFn)(x0, x1); |
| setGPR64Result(ret); |
| break; |
| } |
| case js::jit::Args_General3: { |
| int64_t ret = reinterpret_cast<Prototype_General3>(nativeFn)(x0, x1, x2); |
| setGPR64Result(ret); |
| break; |
| } |
| case js::jit::Args_General4: { |
| int64_t ret = reinterpret_cast<Prototype_General4>(nativeFn)(x0, x1, x2, x3); |
| setGPR64Result(ret); |
| break; |
| } |
| case js::jit::Args_General5: { |
| int64_t ret = reinterpret_cast<Prototype_General5>(nativeFn)(x0, x1, x2, x3, x4); |
| setGPR64Result(ret); |
| break; |
| } |
| case js::jit::Args_General6: { |
| int64_t ret = reinterpret_cast<Prototype_General6>(nativeFn)(x0, x1, x2, x3, x4, x5); |
| setGPR64Result(ret); |
| break; |
| } |
| case js::jit::Args_General7: { |
| int64_t ret = reinterpret_cast<Prototype_General7>(nativeFn)(x0, x1, x2, x3, x4, x5, x6); |
| 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; |
| } |
| |
| // 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: { |
| int64_t ret = reinterpret_cast<Prototype_Int_IntDouble>(nativeFn)(x0, d0); |
| setGPR64Result(ret); |
| break; |
| } |
| |
| case js::jit::Args_Int_IntDoubleIntInt: { |
| int64_t ret = reinterpret_cast<Prototype_Int_IntDoubleIntInt>(nativeFn)(x0, d0, x1, x2); |
| setGPR64Result(ret); |
| break; |
| } |
| |
| case js::jit::Args_Int_DoubleIntInt: { |
| int64_t ret = reinterpret_cast<Prototype_Int_DoubleIntInt>(nativeFn)(d0, x0, x1); |
| setGPR64Result(ret); |
| break; |
| } |
| |
| // Cases with float return type. |
| case js::jit::Args_Float32_Float32: { |
| float ret = reinterpret_cast<Prototype_Float32_Float32>(nativeFn)(s0); |
| 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: { |
| double ret = reinterpret_cast<Prototype_Double_Double>(nativeFn)(d0); |
| setFP64Result(ret); |
| break; |
| } |
| case js::jit::Args_Double_Int: { |
| double ret = reinterpret_cast<Prototype_Double_Int>(nativeFn)(x0); |
| setFP64Result(ret); |
| break; |
| } |
| case js::jit::Args_Double_DoubleInt: { |
| double ret = reinterpret_cast<Prototype_Double_DoubleInt>(nativeFn)(d0, x0); |
| setFP64Result(ret); |
| break; |
| } |
| case js::jit::Args_Double_DoubleDouble: { |
| double ret = reinterpret_cast<Prototype_Double_DoubleDouble>(nativeFn)(d0, d1); |
| setFP64Result(ret); |
| break; |
| } |
| case js::jit::Args_Double_DoubleDoubleDouble: { |
| double ret = reinterpret_cast<Prototype_Double_DoubleDoubleDouble>(nativeFn)(d0, d1, d2); |
| setFP64Result(ret); |
| break; |
| } |
| case js::jit::Args_Double_DoubleDoubleDoubleDouble: { |
| double ret = reinterpret_cast<Prototype_Double_DoubleDoubleDoubleDouble>(nativeFn)(d0, d1, d2, d3); |
| setFP64Result(ret); |
| break; |
| } |
| |
| case js::jit::Args_Double_IntDouble: { |
| double ret = reinterpret_cast<Prototype_Double_IntDouble>(nativeFn)(x0, d0); |
| setFP64Result(ret); |
| break; |
| } |
| |
| default: |
| MOZ_CRASH("Unknown function type."); |
| } |
| |
| // TODO: Nuke the volatile registers. |
| |
| // Assert that callee-saved registers are unchanged. |
| VIXL_ASSERT(xreg(19) == x19); |
| VIXL_ASSERT(xreg(20) == x20); |
| VIXL_ASSERT(xreg(21) == x21); |
| VIXL_ASSERT(xreg(22) == x22); |
| VIXL_ASSERT(xreg(23) == x23); |
| 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)); |
| |
| // Simulate a return. |
| set_lr(savedLR); |
| set_pc((Instruction*)savedLR); |
| if (js_sb_getenv("USE_DEBUGGER")) |
| printf("SVCRET\n"); |
| } |
| |
| |
| } // namespace vixl |
| |
| |
| vixl::Simulator* js::PerThreadData::simulator() const { |
| return runtime_->simulator(); |
| } |
| |
| |
| vixl::Simulator* JSRuntime::simulator() const { |
| return simulator_; |
| } |
| |
| |
| uintptr_t* JSRuntime::addressOfSimulatorStackLimit() { |
| return simulator_->addressOfStackLimit(); |
| } |
| |