// 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.
// If this environment variable is present, trace the executed instructions. // (Very helpful for debugging code generation crashes.) if (getenv("VIXL_TRACE")) {
set_trace_parameters(LOG_DISASM);
}
}
// 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(RawbitsToDouble(nan_bits & kDRegMask)));
VIXL_ASSERT(IsSignallingNaN(RawbitsToFloat(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);
}
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);
// Allocate and set up the simulator stack.
stack_ = js_pod_malloc<byte>(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. if (getenv("VIXL_STATS")) {
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;
}
// 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.
js::UniquePtr<Simulator> sim; if (getenv("USE_DEBUGGER") != nullptr) {
sim.reset(js_new<Debugger>(decoder, stdout));
} else {
sim.reset(js_new<Simulator>(decoder, stdout));
}
// Check if Simulator:init ran out of memory. if (sim && sim->oom()) { return nullptr;
}
#ifdef JS_CACHE_SIMULATOR_ARM64 // Register the simulator in the Simulator process to handle cache flushes // across threads.
js::jit::AutoLockSimulatorCache alsc; if (!SimulatorProcess::registerSimulator(sim.get())) { return nullptr;
} #endif
void Simulator::ExecuteInstruction() { // The program counter should always be aligned.
VIXL_ASSERT(IsWordAligned(pc_)); #ifdef JS_CACHE_SIMULATOR_ARM64 if (pendingCacheRequests) { // We're here emulating the behavior of the membarrier carried over on // real hardware does; see syscalls to membarrier in MozCpu-vixl.cpp. // There's a slight difference that the simulator is not being // interrupted: instead, we effectively run the icache flush request // before executing the next instruction, which is close enough and // sufficient for our use case.
js::jit::AutoLockSimulatorCache alsc;
FlushICache();
} #endif
decoder_->Decode(pc_);
increment_pc();
}
// 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));
} elseif (argument_count == 2) { // EntryArg* args
set_xreg(0, va_arg(parameters, int64_t)); // uint8_t* GlobalData
set_xreg(1, va_arg(parameters, int64_t));
} elseif (argument_count == 1) { // irregexp // InputOutputData& data
set_xreg(0, va_arg(parameters, int64_t));
} elseif (argument_count == 0) { // testsJit.cpp // accept.
} else {
MOZ_CRASH("Unknown number of arguments");
}
va_end(parameters);
// Call must transition back to native code on exit.
VIXL_ASSERT(get_lr() == int64_t(kEndOfSimAddress));
int64_t result = xreg(0); if (getenv("USE_DEBUGGER")) {
printf("LEAVE\n");
} return result;
}
// 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
{ friendclass Simulator;
// TODO: Store srt_ in the simulator for this assertion. // VIXL_ASSERT_IF(pt->simulator(), pt->simulator()->srt_ == srt);
Redirection* current = SimulatorProcess::redirection(); for (; current != nullptr; current = current->next_) { if (current->nativeFunction_ == nativeFunction) {
VIXL_ASSERT(current->type() == type); return current;
}
}
// Note: we can't use js_new here because the constructor is private.
js::AutoEnterOOMUnsafeRegion oomUnsafe;
Redirection* redir = js_pod_malloc<Redirection>(1); if (!redir)
oomUnsafe.crash("Simulator redirection"); new(redir) Redirection(nativeFunction, type); return redir;
}
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 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: {
js::AutoEnterOOMUnsafeRegion oomUnsafe; if (!spStack_.append(get_sp()))
oomUnsafe.crash("tracking stack for ARM64 simulator"); return;
} case kCheckStackPointer: {
DebugOnly<int64_t> current = get_sp();
DebugOnly<int64_t> expected = spStack_.popCopy();
VIXL_ASSERT(current == expected); return;
} default:
VIXL_UNIMPLEMENTED();
} break; default:
VIXL_UNIMPLEMENTED();
}
}
// 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);
#ifdef DEBUG
qreg_t qregs[kNumberOfCalleeSavedFPRegisters] = {}; for (unsigned i = 0; i < kNumberOfCalleeSavedFPRegisters; i++) {
qregs[i] = qreg(kFirstCalleeSavedFPRegisterIndex + i);
} #endif
// Get the SP for reading stack arguments
int64_t* sp = reinterpret_cast<int64_t*>(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));
if (single_stepping_) {
single_step_callback_(single_step_callback_arg_, this, nullptr);
}
// Dispatch the call and set the return value. switch (redir->type()) {
ABI_FUNCTION_TYPE_ARM64_SIM_DISPATCH
default:
MOZ_CRASH("Unknown function type.");
}
if (single_stepping_) {
single_step_callback_(single_step_callback_arg_, this, nullptr);
}
// Nuke the volatile registers. x0-x7 are used as result registers, but except // for x0, none are used in the above signatures. for (int i = 1; i <= 18; i++) { // Code feed 1 bad data
set_xreg(i, int64_t(0xc0defeed1badda7a));
}
// v0-v7 are used as argument and result registers. We're currently only using // v0 as an output register, so clobber the remaining registers. for (unsigned i = 1; i < kFirstCalleeSavedFPRegisterIndex; i++) {
set_qreg(i, code_feed_1bad_data);
}
// Bottom 64 bits of v8-v15 are callee preserved. for (unsigned i = 0; i < kNumberOfCalleeSavedFPRegisters; i++) {
qreg_t r = qreg(kFirstCalleeSavedFPRegisterIndex + i);
// Clobber high 64 bits.
std::memcpy(&r.val[sizeof(int64_t)], &code_feed_1bad_data.val, sizeof(int64_t));
set_qreg(kFirstCalleeSavedFPRegisterIndex + i, r);
}
// v16-v31 are temporary registers and caller preserved.
constexpr unsigned kFirstTempFPRegisterIndex =
kFirstCalleeSavedFPRegisterIndex + kNumberOfCalleeSavedFPRegisters; for (unsigned i = kFirstTempFPRegisterIndex; i < kNumberOfVRegisters; i++) {
set_qreg(i, code_feed_1bad_data);
}
// Simulate a return.
set_lr(savedLR);
set_pc((Instruction*)savedLR); if (getenv("USE_DEBUGGER"))
printf("SVCRET\n");
}
#ifdef JS_CACHE_SIMULATOR_ARM64 void
Simulator::FlushICache()
{ // Flush the caches recorded by the current thread as well as what got // recorded from other threads before this call. auto& vec = SimulatorProcess::getICacheFlushes(this); for (auto& flush : vec) {
decoder_->FlushICache(flush.start, flush.length);
}
vec.clear();
pendingCacheRequests = false;
}
void CachingDecoder::Decode(const Instruction* instr) {
InstDecodedKind state; if (lastPage_ && lastPage_->contains(instr)) {
state = lastPage_->decode(instr);
} else {
uintptr_t key = SinglePageDecodeCache::PageStart(instr);
ICacheMap::AddPtr p = iCache_.lookupForAdd(key); if (p) {
lastPage_ = p->value();
state = lastPage_->decode(instr);
} else {
js::AutoEnterOOMUnsafeRegion oomUnsafe;
SinglePageDecodeCache* newPage = js_new<SinglePageDecodeCache>(instr); if (!newPage || !iCache_.add(p, key, newPage)) {
oomUnsafe.crash("Simulator SinglePageDecodeCache");
}
lastPage_ = newPage;
state = InstDecodedKind::NotDecodedYet;
}
}
void CachingDecoder::FlushICache(void* start, size_t size) {
MOZ_ASSERT(uintptr_t(start) % vixl::kInstructionSize == 0);
MOZ_ASSERT(size % vixl::kInstructionSize == 0); const uint8_t* it = reinterpret_cast<const uint8_t*>(start); const uint8_t* end = it + size;
SinglePageDecodeCache* last = nullptr; for (; it < end; it += vixl::kInstructionSize) { auto instr = reinterpret_cast<const Instruction*>(it); if (last && last->contains(instr)) {
last->clearDecode(instr);
} else {
uintptr_t key = SinglePageDecodeCache::PageStart(instr);
ICacheMap::Ptr p = iCache_.lookup(key); if (p) {
last = p->value();
last->clearDecode(instr);
}
}
}
} #endif
} // namespace vixl
namespace js { namespace jit {
#ifdef JS_CACHE_SIMULATOR_ARM64 void SimulatorProcess::recordICacheFlush(void* start, size_t length) {
singleton_->lock_.assertOwnedByCurrentThread();
AutoEnterOOMUnsafeRegion oomUnsafe;
ICacheFlush range{start, length}; for (auto& s : singleton_->pendingFlushes_) { if (!s.records.append(range)) {
oomUnsafe.crash("Simulator recordFlushICache");
}
}
}
void SimulatorProcess::membarrier() {
singleton_->lock_.assertOwnedByCurrentThread(); for (auto& s : singleton_->pendingFlushes_) {
s.thread->pendingCacheRequests = true;
}
}
SimulatorProcess::ICacheFlushes& SimulatorProcess::getICacheFlushes(Simulator* sim) {
singleton_->lock_.assertOwnedByCurrentThread(); for (auto& s : singleton_->pendingFlushes_) { if (s.thread == sim) { return s.records;
}
}
MOZ_CRASH("Simulator is not registered in the SimulatorProcess");
}
void SimulatorProcess::unregisterSimulator(Simulator* sim) {
singleton_->lock_.assertOwnedByCurrentThread(); for (auto& s : singleton_->pendingFlushes_) { if (s.thread == sim) {
singleton_->pendingFlushes_.erase(&s); return;
}
}
MOZ_CRASH("Simulator is not registered in the SimulatorProcess");
} #endif// !JS_CACHE_SIMULATOR_ARM64
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.