Index: runtime/vm/simulator_arm64.cc |
=================================================================== |
--- runtime/vm/simulator_arm64.cc (revision 0) |
+++ runtime/vm/simulator_arm64.cc (revision 0) |
@@ -0,0 +1,714 @@ |
+// Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file |
+// for details. All rights reserved. Use of this source code is governed by a |
+// BSD-style license that can be found in the LICENSE file. |
+ |
+#include <math.h> // for isnan. |
+#include <setjmp.h> |
+#include <stdlib.h> |
+ |
+#include "vm/globals.h" |
+#if defined(TARGET_ARCH_ARM64) |
+ |
+// Only build the simulator if not compiling for real ARM hardware. |
+#if !defined(HOST_ARCH_ARM64) |
+ |
+#include "vm/simulator.h" |
+ |
+#include "vm/assembler.h" |
+#include "vm/constants_arm64.h" |
+#include "vm/cpu.h" |
+#include "vm/disassembler.h" |
+#include "vm/native_arguments.h" |
+#include "vm/stack_frame.h" |
+#include "vm/thread.h" |
+ |
+namespace dart { |
+ |
+DEFINE_FLAG(bool, trace_sim, false, "Trace simulator execution."); |
+DEFINE_FLAG(int, stop_sim_at, 0, "Address to stop simulator at."); |
+ |
+ |
+// This macro provides a platform independent use of sscanf. The reason for |
+// SScanF not being implemented in a platform independent way through |
+// OS in the same way as SNPrint is that the Windows C Run-Time |
+// Library does not provide vsscanf. |
+#define SScanF sscanf // NOLINT |
+ |
+ |
+Simulator::Simulator() { |
+ // Setup simulator support first. Some of this information is needed to |
+ // setup the architecture state. |
+ // We allocate the stack here, the size is computed as the sum of |
+ // the size specified by the user and the buffer space needed for |
+ // handling stack overflow exceptions. To be safe in potential |
+ // stack underflows we also add some underflow buffer space. |
+ stack_ = new char[(Isolate::GetSpecifiedStackSize() + |
+ Isolate::kStackSizeBuffer + |
+ kSimulatorStackUnderflowSize)]; |
+ pc_modified_ = false; |
+ icount_ = 0; |
+ break_pc_ = NULL; |
+ break_instr_ = 0; |
+ top_exit_frame_info_ = 0; |
+ |
+ // Setup architecture state. |
+ // All registers are initialized to zero to start with. |
+ for (int i = 0; i < kNumberOfCpuRegisters; i++) { |
+ registers_[i] = 0; |
+ } |
+ n_flag_ = false; |
+ z_flag_ = false; |
+ c_flag_ = false; |
+ v_flag_ = false; |
+ |
+ // The sp is initialized to point to the bottom (high address) of the |
+ // allocated stack area. |
+ registers_[SP] = StackTop(); |
+ // The lr and pc are initialized to a known bad value that will cause an |
+ // access violation if the simulator ever tries to execute it. |
+ registers_[LR] = kBadLR; |
+ pc_ = kBadLR; |
+} |
+ |
+ |
+Simulator::~Simulator() { |
+ delete[] stack_; |
+ Isolate* isolate = Isolate::Current(); |
+ if (isolate != NULL) { |
+ isolate->set_simulator(NULL); |
+ } |
+} |
+ |
+ |
+// Get the active Simulator for the current isolate. |
+Simulator* Simulator::Current() { |
+ Simulator* simulator = Isolate::Current()->simulator(); |
+ if (simulator == NULL) { |
+ simulator = new Simulator(); |
+ Isolate::Current()->set_simulator(simulator); |
+ } |
+ return simulator; |
+} |
+ |
+ |
+// Sets the register in the architecture state. |
+void Simulator::set_register(Register reg, int64_t value, R31Type r31t) { |
+ // register is in range, and if it is R31, a mode is specified. |
+ ASSERT((reg >= 0) && (reg < kNumberOfCpuRegisters)); |
+ ASSERT((reg != R31) || (r31t != R31IsUndef)); |
+ if ((reg != R31) || (r31t != R31IsZR)) { |
+ registers_[reg] = value; |
+ } |
+} |
+ |
+ |
+// Get the register from the architecture state. |
+int64_t Simulator::get_register(Register reg, R31Type r31t) const { |
+ ASSERT((reg >= 0) && (reg < kNumberOfCpuRegisters)); |
+ ASSERT((reg != R31) || (r31t != R31IsUndef)); |
+ if ((reg == R31) && (r31t == R31IsZR)) { |
+ return 0; |
+ } else { |
+ return registers_[reg]; |
+ } |
+} |
+ |
+ |
+void Simulator::set_wregister(Register reg, int32_t value, R31Type r31t) { |
+ ASSERT((reg >= 0) && (reg < kNumberOfCpuRegisters)); |
+ ASSERT((reg != R31) || (r31t != R31IsUndef)); |
+ // When setting in W mode, clear the high bits. |
+ if ((reg != R31) || (r31t != R31IsZR)) { |
+ registers_[reg] = Utils::LowHighTo64Bits(static_cast<uint32_t>(value), 0); |
+ } |
+} |
+ |
+ |
+// Get the register from the architecture state. |
+int32_t Simulator::get_wregister(Register reg, R31Type r31t) const { |
+ ASSERT((reg >= 0) && (reg < kNumberOfCpuRegisters)); |
+ ASSERT((reg != R31) || (r31t != R31IsUndef)); |
+ if ((reg == R31) && (r31t == R31IsZR)) { |
+ return 0; |
+ } else { |
+ return registers_[reg]; |
+ } |
+} |
+ |
+ |
+// Raw access to the PC register. |
+void Simulator::set_pc(int64_t value) { |
+ pc_modified_ = true; |
+ pc_ = value; |
+} |
+ |
+ |
+// Raw access to the PC register without the special adjustment when reading. |
+int64_t Simulator::get_pc() const { |
+ return pc_; |
+} |
+ |
+ |
+void Simulator::HandleIllegalAccess(uword addr, Instr* instr) { |
+ uword fault_pc = get_pc(); |
+ // TODO(zra): drop into debugger. |
+ char buffer[128]; |
+ snprintf(buffer, sizeof(buffer), |
+ "illegal memory access at 0x%" Px ", pc=0x%" Px "\n", |
+ addr, fault_pc); |
+ // The debugger will return control in non-interactive mode. |
+ FATAL("Cannot continue execution after illegal memory access."); |
+} |
+ |
+ |
+void Simulator::UnimplementedInstruction(Instr* instr) { |
+ char buffer[64]; |
+ snprintf(buffer, sizeof(buffer), "Unimplemented instruction: pc=%p\n", instr); |
+ // TODO(zra): drop into debugger. |
+ FATAL("Cannot continue execution after unimplemented instruction."); |
+} |
+ |
+ |
+// Returns the top of the stack area to enable checking for stack pointer |
+// validity. |
+uword Simulator::StackTop() const { |
+ // To be safe in potential stack underflows we leave some buffer above and |
+ // set the stack top. |
+ return reinterpret_cast<uword>(stack_) + |
+ (Isolate::GetSpecifiedStackSize() + Isolate::kStackSizeBuffer); |
+} |
+ |
+ |
+// Unsupported instructions use Format to print an error and stop execution. |
+void Simulator::Format(Instr* instr, const char* format) { |
+ OS::Print("Simulator found unsupported instruction:\n 0x%p: %s\n", |
+ instr, |
+ format); |
+ UNIMPLEMENTED(); |
+} |
+ |
+ |
+// Calculate and set the Negative and Zero flags. |
+void Simulator::SetNZFlagsW(int32_t val) { |
+ n_flag_ = (val < 0); |
+ z_flag_ = (val == 0); |
+} |
+ |
+ |
+// Calculate C flag value for additions. |
+bool Simulator::CarryFromW(int32_t left, int32_t right) { |
+ uint32_t uleft = static_cast<uint32_t>(left); |
+ uint32_t uright = static_cast<uint32_t>(right); |
+ uint32_t urest = 0xffffffffU - uleft; |
+ |
+ return (uright > urest); |
+} |
+ |
+ |
+// Calculate C flag value for subtractions. |
+bool Simulator::BorrowFromW(int32_t left, int32_t right) { |
+ uint32_t uleft = static_cast<uint32_t>(left); |
+ uint32_t uright = static_cast<uint32_t>(right); |
+ |
+ return (uright > uleft); |
+} |
+ |
+ |
+// Calculate V flag value for additions and subtractions. |
+bool Simulator::OverflowFromW(int32_t alu_out, |
+ int32_t left, int32_t right, bool addition) { |
+ bool overflow; |
+ if (addition) { |
+ // operands have the same sign |
+ overflow = ((left >= 0 && right >= 0) || (left < 0 && right < 0)) |
+ // and operands and result have different sign |
+ && ((left < 0 && alu_out >= 0) || (left >= 0 && alu_out < 0)); |
+ } else { |
+ // operands have different signs |
+ overflow = ((left < 0 && right >= 0) || (left >= 0 && right < 0)) |
+ // and first operand and result have different signs |
+ && ((left < 0 && alu_out >= 0) || (left >= 0 && alu_out < 0)); |
+ } |
+ return overflow; |
+} |
+ |
+ |
+// Calculate and set the Negative and Zero flags. |
+void Simulator::SetNZFlagsX(int64_t val) { |
+ n_flag_ = (val < 0); |
+ z_flag_ = (val == 0); |
+} |
+ |
+ |
+// Calculate C flag value for additions. |
+bool Simulator::CarryFromX(int64_t left, int64_t right) { |
+ uint64_t uleft = static_cast<uint64_t>(left); |
+ uint64_t uright = static_cast<uint64_t>(right); |
+ uint64_t urest = 0xffffffffffffffffULL - uleft; |
+ |
+ return (uright > urest); |
+} |
+ |
+ |
+// Calculate C flag value for subtractions. |
+bool Simulator::BorrowFromX(int64_t left, int64_t right) { |
+ uint64_t uleft = static_cast<uint64_t>(left); |
+ uint64_t uright = static_cast<uint64_t>(right); |
+ |
+ return (uright > uleft); |
+} |
+ |
+ |
+// Calculate V flag value for additions and subtractions. |
+bool Simulator::OverflowFromX(int64_t alu_out, |
+ int64_t left, int64_t right, bool addition) { |
+ bool overflow; |
+ if (addition) { |
+ // operands have the same sign |
+ overflow = ((left >= 0 && right >= 0) || (left < 0 && right < 0)) |
+ // and operands and result have different sign |
+ && ((left < 0 && alu_out >= 0) || (left >= 0 && alu_out < 0)); |
+ } else { |
+ // operands have different signs |
+ overflow = ((left < 0 && right >= 0) || (left >= 0 && right < 0)) |
+ // and first operand and result have different signs |
+ && ((left < 0 && alu_out >= 0) || (left >= 0 && alu_out < 0)); |
+ } |
+ return overflow; |
+} |
+ |
+ |
+// Set the Carry flag. |
+void Simulator::SetCFlag(bool val) { |
+ c_flag_ = val; |
+} |
+ |
+ |
+// Set the oVerflow flag. |
+void Simulator::SetVFlag(bool val) { |
+ v_flag_ = val; |
+} |
+ |
+ |
+void Simulator::DecodeMoveWide(Instr* instr) { |
+ UnimplementedInstruction(instr); |
+} |
+ |
+ |
+void Simulator::DecodeAddSubImm(Instr* instr) { |
+ switch (instr->Bit(30)) { |
+ case 0: { |
+ // Format(instr, "addi'sf's 'rd, 'rn, 'imm12s"); |
+ const Register rd = instr->RdField(); |
+ const Register rn = instr->RnField(); |
+ const uint32_t imm = (instr->Bit(22) == 1) ? (instr->Imm12Field() << 12) |
+ : (instr->Imm12Field()); |
+ if (instr->SFField()) { |
+ // 64-bit add. |
+ const int64_t rn_val = get_register(rn, instr->RnMode()); |
+ const int64_t alu_out = rn_val + imm; |
+ set_register(rd, alu_out, instr->RdMode()); |
+ if (instr->HasS()) { |
+ SetNZFlagsX(alu_out); |
+ SetCFlag(CarryFromX(rn_val, imm)); |
+ SetVFlag(OverflowFromX(alu_out, rn_val, imm, true)); |
+ } |
+ } else { |
+ // 32-bit add. |
+ const int32_t rn_val = get_wregister(rn, instr->RnMode()); |
+ const int32_t alu_out = rn_val + imm; |
+ set_wregister(rd, alu_out, instr->RdMode()); |
+ if (instr->HasS()) { |
+ SetNZFlagsW(alu_out); |
+ SetCFlag(CarryFromW(rn_val, imm)); |
+ SetVFlag(OverflowFromW(alu_out, rn_val, imm, true)); |
+ } |
+ } |
+ break; |
+ } |
+ default: |
+ UnimplementedInstruction(instr); |
+ break; |
+ } |
+} |
+ |
+void Simulator::DecodeDPImmediate(Instr* instr) { |
+ if (instr->IsMoveWideOp()) { |
+ DecodeMoveWide(instr); |
+ } else if (instr->IsAddSubImmOp()) { |
+ DecodeAddSubImm(instr); |
+ } else { |
+ UnimplementedInstruction(instr); |
+ } |
+} |
+ |
+ |
+void Simulator::DecodeExceptionGen(Instr* instr) { |
+ UnimplementedInstruction(instr); |
+} |
+ |
+ |
+void Simulator::DecodeSystem(Instr* instr) { |
+ if ((instr->Bits(0, 8) == 0x5f) && (instr->Bits(12, 4) == 2) && |
+ (instr->Bits(16, 3) == 3) && (instr->Bits(19, 2) == 0) && |
+ (instr->Bit(21) == 0)) { |
+ if (instr->Bits(8, 4) == 0) { |
+ // Format(instr, "nop"); |
+ } else { |
+ UnimplementedInstruction(instr); |
+ } |
+ } else { |
+ UnimplementedInstruction(instr); |
+ } |
+} |
+ |
+ |
+void Simulator::DecodeUnconditionalBranchReg(Instr* instr) { |
+ if ((instr->Bits(0, 5) == 0) && (instr->Bits(10, 6) == 0) && |
+ (instr->Bits(16, 5) == 0x1f)) { |
+ switch (instr->Bits(21, 4)) { |
+ case 2: { |
+ // Format(instr, "ret 'rn"); |
+ const Register rn = instr->RnField(); |
+ const int64_t rn_val = get_register(rn, instr->RnMode()); |
+ set_pc(rn_val); |
+ break; |
+ } |
+ default: |
+ UnimplementedInstruction(instr); |
+ break; |
+ } |
+ } else { |
+ UnimplementedInstruction(instr); |
+ } |
+} |
+ |
+ |
+void Simulator::DecodeCompareBranch(Instr* instr) { |
+ if (instr->IsExceptionGenOp()) { |
+ DecodeExceptionGen(instr); |
+ } else if (instr->IsSystemOp()) { |
+ DecodeSystem(instr); |
+ } else if (instr->IsUnconditionalBranchRegOp()) { |
+ DecodeUnconditionalBranchReg(instr); |
+ } else { |
+ UnimplementedInstruction(instr); |
+ } |
+} |
+ |
+ |
+void Simulator::DecodeLoadStore(Instr* instr) { |
+ UnimplementedInstruction(instr); |
+} |
+ |
+ |
+int64_t Simulator::ShiftOperand(uint8_t reg_size, |
+ int64_t value, |
+ Shift shift_type, |
+ uint8_t amount) { |
+ if (amount == 0) { |
+ return value; |
+ } |
+ int64_t mask = reg_size == kXRegSizeInBits ? kXRegMask : kWRegMask; |
+ switch (shift_type) { |
+ case LSL: |
+ return (value << amount) & mask; |
+ case LSR: |
+ return static_cast<uint64_t>(value) >> amount; |
+ case ASR: { |
+ // Shift used to restore the sign. |
+ uint8_t s_shift = kXRegSizeInBits - reg_size; |
+ // Value with its sign restored. |
+ int64_t s_value = (value << s_shift) >> s_shift; |
+ return (s_value >> amount) & mask; |
+ } |
+ case ROR: { |
+ if (reg_size == kWRegSizeInBits) { |
+ value &= kWRegMask; |
+ } |
+ return (static_cast<uint64_t>(value) >> amount) | |
+ ((value & ((1L << amount) - 1L)) << (reg_size - amount)); |
+ } |
+ default: |
+ UNIMPLEMENTED(); |
+ return 0; |
+ } |
+} |
+ |
+ |
+int64_t Simulator::ExtendOperand(uint8_t reg_size, |
+ int64_t value, |
+ Extend extend_type, |
+ uint8_t amount) { |
+ switch (extend_type) { |
+ case UXTB: |
+ value &= 0xff; |
+ break; |
+ case UXTH: |
+ value &= 0xffff; |
+ break; |
+ case UXTW: |
+ value &= 0xffffffff; |
+ break; |
+ case SXTB: |
+ value = (value << 56) >> 56; |
+ break; |
+ case SXTH: |
+ value = (value << 48) >> 48; |
+ break; |
+ case SXTW: |
+ value = (value << 32) >> 32; |
+ break; |
+ case UXTX: |
+ case SXTX: |
+ break; |
+ default: |
+ UNREACHABLE(); |
+ } |
+ int64_t mask = (reg_size == kXRegSizeInBits) ? kXRegMask : kWRegMask; |
+ return (value << amount) & mask; |
+} |
+ |
+ |
+int64_t Simulator::DecodeShiftExtendOperand(Instr* instr) { |
+ const Register rm = instr->RmField(); |
+ const int64_t rm_val = get_register(rm, R31IsZR); |
+ const uint8_t size = instr->SFField() ? kXRegSizeInBits : kWRegSizeInBits; |
+ if (instr->IsShift()) { |
+ const Shift shift_type = instr->ShiftTypeField(); |
+ const uint8_t shift_amount = instr->Imm6Field(); |
+ return ShiftOperand(size, rm_val, shift_type, shift_amount); |
+ } else { |
+ ASSERT(instr->IsExtend()); |
+ const Extend extend_type = instr->ExtendTypeField(); |
+ const uint8_t shift_amount = instr->Imm3Field(); |
+ return ExtendOperand(size, rm_val, extend_type, shift_amount); |
+ } |
+ UNREACHABLE(); |
+ return -1; |
+} |
+ |
+ |
+void Simulator::DecodeAddSubShiftExt(Instr* instr) { |
+ switch (instr->Bit(30)) { |
+ case 0: { |
+ // Format(instr, "add'sf's 'rd, 'rn, 'shift_op"); |
+ const Register rd = instr->RdField(); |
+ const Register rn = instr->RnField(); |
+ const int64_t rm_val = DecodeShiftExtendOperand(instr); |
+ if (instr->SFField()) { |
+ // 64-bit add. |
+ const int64_t rn_val = get_register(rn, instr->RnMode()); |
+ const int64_t alu_out = rn_val + rm_val; |
+ set_register(rd, alu_out, instr->RdMode()); |
+ if (instr->HasS()) { |
+ SetNZFlagsX(alu_out); |
+ SetCFlag(CarryFromX(rn_val, rm_val)); |
+ SetVFlag(OverflowFromX(alu_out, rn_val, rm_val, true)); |
+ } |
+ } else { |
+ // 32-bit add. |
+ const int32_t rn_val = get_wregister(rn, instr->RnMode()); |
+ const int32_t rm_val32 = static_cast<int32_t>(rm_val & kWRegMask); |
+ const int32_t alu_out = rn_val + rm_val32; |
+ set_wregister(rd, alu_out, instr->RdMode()); |
+ if (instr->HasS()) { |
+ SetNZFlagsW(alu_out); |
+ SetCFlag(CarryFromW(rn_val, rm_val32)); |
+ SetVFlag(OverflowFromW(alu_out, rn_val, rm_val32, true)); |
+ } |
+ } |
+ break; |
+ } |
+ default: |
+ UnimplementedInstruction(instr); |
+ break; |
+ } |
+} |
+ |
+ |
+void Simulator::DecodeDPRegister(Instr* instr) { |
+ if (instr->IsAddSubShiftExtOp()) { |
+ DecodeAddSubShiftExt(instr); |
+ } else { |
+ UnimplementedInstruction(instr); |
+ } |
+} |
+ |
+ |
+void Simulator::DecodeDPSimd1(Instr* instr) { |
+ UnimplementedInstruction(instr); |
+} |
+ |
+ |
+void Simulator::DecodeDPSimd2(Instr* instr) { |
+ UnimplementedInstruction(instr); |
+} |
+ |
+ |
+// Executes the current instruction. |
+void Simulator::InstructionDecode(Instr* instr) { |
+ pc_modified_ = false; |
+ if (FLAG_trace_sim) { |
+ const uword start = reinterpret_cast<uword>(instr); |
+ const uword end = start + Instr::kInstrSize; |
+ Disassembler::Disassemble(start, end); |
+ } |
+ |
+ if (instr->IsDPImmediateOp()) { |
+ DecodeDPImmediate(instr); |
+ } else if (instr->IsCompareBranchOp()) { |
+ DecodeCompareBranch(instr); |
+ } else if (instr->IsLoadStoreOp()) { |
+ DecodeLoadStore(instr); |
+ } else if (instr->IsDPRegisterOp()) { |
+ DecodeDPRegister(instr); |
+ } else if (instr->IsDPSimd1Op()) { |
+ DecodeDPSimd1(instr); |
+ } else { |
+ ASSERT(instr->IsDPSimd2Op()); |
+ DecodeDPSimd2(instr); |
+ } |
+ |
+ if (!pc_modified_) { |
+ set_pc(reinterpret_cast<int64_t>(instr) + Instr::kInstrSize); |
+ } |
+} |
+ |
+ |
+void Simulator::Execute() { |
+ // Get the PC to simulate. Cannot use the accessor here as we need the |
+ // raw PC value and not the one used as input to arithmetic instructions. |
+ uword program_counter = get_pc(); |
+ |
+ if (FLAG_stop_sim_at == 0) { |
+ // Fast version of the dispatch loop without checking whether the simulator |
+ // should be stopping at a particular executed instruction. |
+ while (program_counter != kEndSimulatingPC) { |
+ Instr* instr = reinterpret_cast<Instr*>(program_counter); |
+ icount_++; |
+ if (IsIllegalAddress(program_counter)) { |
+ HandleIllegalAccess(program_counter, instr); |
+ } else { |
+ InstructionDecode(instr); |
+ } |
+ program_counter = get_pc(); |
+ } |
+ } else { |
+ // FLAG_stop_sim_at is at the non-default value. Stop in the debugger when |
+ // we reach the particular instruction count. |
+ while (program_counter != kEndSimulatingPC) { |
+ Instr* instr = reinterpret_cast<Instr*>(program_counter); |
+ icount_++; |
+ if (icount_ == FLAG_stop_sim_at) { |
+ // TODO(zra): Add a debugger. |
+ UNIMPLEMENTED(); |
+ } else if (IsIllegalAddress(program_counter)) { |
+ HandleIllegalAccess(program_counter, instr); |
+ } else { |
+ InstructionDecode(instr); |
+ } |
+ program_counter = get_pc(); |
+ } |
+ } |
+} |
+ |
+ |
+int64_t Simulator::Call(int64_t entry, |
+ int64_t parameter0, |
+ int64_t parameter1, |
+ int64_t parameter2, |
+ int64_t parameter3) { |
+ // Save the SP register before the call so we can restore it. |
+ int32_t sp_before_call = get_register(SP, R31IsSP); |
+ |
+ // Setup parameters. |
+ set_register(R0, parameter0); |
+ set_register(R1, parameter1); |
+ set_register(R2, parameter2); |
+ set_register(R3, parameter3); |
+ |
+ // Make sure the activation frames are properly aligned. |
+ int32_t stack_pointer = sp_before_call; |
+ if (OS::ActivationFrameAlignment() > 1) { |
+ stack_pointer = |
+ Utils::RoundDown(stack_pointer, OS::ActivationFrameAlignment()); |
+ } |
+ set_register(SP, stack_pointer, R31IsSP); |
+ |
+ // Prepare to execute the code at entry. |
+ set_pc(entry); |
+ // Put down marker for end of simulation. The simulator will stop simulation |
+ // when the PC reaches this value. By saving the "end simulation" value into |
+ // the LR the simulation stops when returning to this call point. |
+ set_register(LR, kEndSimulatingPC); |
+ |
+ // Remember the values of callee-saved registers. |
+ int64_t r19_val = get_register(R19); |
+ int64_t r20_val = get_register(R20); |
+ int64_t r21_val = get_register(R21); |
+ int64_t r22_val = get_register(R22); |
+ int64_t r23_val = get_register(R23); |
+ int64_t r24_val = get_register(R24); |
+ int64_t r25_val = get_register(R25); |
+ int64_t r26_val = get_register(R26); |
+ int64_t r27_val = get_register(R27); |
+ int64_t r28_val = get_register(R28); |
+ int64_t r29_val = get_register(R29); |
+ |
+ // Setup the callee-saved registers with a known value. To be able to check |
+ // that they are preserved properly across dart execution. |
+ int64_t callee_saved_value = icount_; |
+ set_register(R19, callee_saved_value); |
+ set_register(R20, callee_saved_value); |
+ set_register(R21, callee_saved_value); |
+ set_register(R22, callee_saved_value); |
+ set_register(R23, callee_saved_value); |
+ set_register(R24, callee_saved_value); |
+ set_register(R25, callee_saved_value); |
+ set_register(R26, callee_saved_value); |
+ set_register(R27, callee_saved_value); |
+ set_register(R28, callee_saved_value); |
+ set_register(R29, callee_saved_value); |
+ |
+ // Start the simulation |
+ Execute(); |
+ |
+ // Check that the callee-saved registers have been preserved. |
+ ASSERT(callee_saved_value == get_register(R19)); |
+ ASSERT(callee_saved_value == get_register(R20)); |
+ ASSERT(callee_saved_value == get_register(R21)); |
+ ASSERT(callee_saved_value == get_register(R22)); |
+ ASSERT(callee_saved_value == get_register(R23)); |
+ ASSERT(callee_saved_value == get_register(R24)); |
+ ASSERT(callee_saved_value == get_register(R25)); |
+ ASSERT(callee_saved_value == get_register(R26)); |
+ ASSERT(callee_saved_value == get_register(R27)); |
+ ASSERT(callee_saved_value == get_register(R28)); |
+ ASSERT(callee_saved_value == get_register(R29)); |
+ |
+ // Restore callee-saved registers with the original value. |
+ set_register(R19, r19_val); |
+ set_register(R20, r20_val); |
+ set_register(R21, r21_val); |
+ set_register(R22, r22_val); |
+ set_register(R23, r23_val); |
+ set_register(R24, r24_val); |
+ set_register(R25, r25_val); |
+ set_register(R26, r26_val); |
+ set_register(R27, r27_val); |
+ set_register(R28, r28_val); |
+ set_register(R29, r29_val); |
+ |
+ // Restore the SP register and return R1:R0. |
+ set_register(SP, sp_before_call, R31IsSP); |
+ int64_t return_value; |
+ return_value = get_register(R0); |
+ return return_value; |
+} |
+ |
+} // namespace dart |
+ |
+#endif // !defined(HOST_ARCH_ARM64) |
+ |
+#endif // defined TARGET_ARCH_ARM64 |