// Helpers for colors.
-// Depending on your terminal configuration, the colour names may not match the
-// observed colours.
-#define COLOUR(colour_code) "\033[" colour_code "m"
-#define BOLD(colour_code) "1;" colour_code
-#define NORMAL ""
-#define GREY "30"
-#define GREEN "32"
-#define ORANGE "33"
-#define BLUE "34"
-#define PURPLE "35"
-#define INDIGO "36"
-#define WHITE "37"
+#define COLOUR(colour_code) "\033[0;" colour_code "m"
+#define COLOUR_BOLD(colour_code) "\033[1;" colour_code "m"
+#define NORMAL ""
+#define GREY "30"
+#define RED "31"
+#define GREEN "32"
+#define YELLOW "33"
+#define BLUE "34"
+#define MAGENTA "35"
+#define CYAN "36"
+#define WHITE "37"
typedef char const * const TEXT_COLOUR;
TEXT_COLOUR clr_normal = FLAG_log_colour ? COLOUR(NORMAL) : "";
-TEXT_COLOUR clr_flag_name = FLAG_log_colour ? COLOUR(BOLD(GREY)) : "";
-TEXT_COLOUR clr_flag_value = FLAG_log_colour ? COLOUR(BOLD(WHITE)) : "";
-TEXT_COLOUR clr_reg_name = FLAG_log_colour ? COLOUR(BOLD(BLUE)) : "";
-TEXT_COLOUR clr_reg_value = FLAG_log_colour ? COLOUR(BOLD(INDIGO)) : "";
-TEXT_COLOUR clr_fpreg_name = FLAG_log_colour ? COLOUR(BOLD(ORANGE)) : "";
-TEXT_COLOUR clr_fpreg_value = FLAG_log_colour ? COLOUR(BOLD(PURPLE)) : "";
-TEXT_COLOUR clr_memory_value = FLAG_log_colour ? COLOUR(BOLD(GREEN)) : "";
-TEXT_COLOUR clr_memory_address = FLAG_log_colour ? COLOUR(GREEN) : "";
-TEXT_COLOUR clr_debug_number = FLAG_log_colour ? COLOUR(BOLD(ORANGE)) : "";
-TEXT_COLOUR clr_debug_message = FLAG_log_colour ? COLOUR(ORANGE) : "";
+TEXT_COLOUR clr_flag_name = FLAG_log_colour ? COLOUR_BOLD(WHITE) : "";
+TEXT_COLOUR clr_flag_value = FLAG_log_colour ? COLOUR(NORMAL) : "";
+TEXT_COLOUR clr_reg_name = FLAG_log_colour ? COLOUR_BOLD(CYAN) : "";
+TEXT_COLOUR clr_reg_value = FLAG_log_colour ? COLOUR(CYAN) : "";
+TEXT_COLOUR clr_fpreg_name = FLAG_log_colour ? COLOUR_BOLD(MAGENTA) : "";
+TEXT_COLOUR clr_fpreg_value = FLAG_log_colour ? COLOUR(MAGENTA) : "";
+TEXT_COLOUR clr_memory_address = FLAG_log_colour ? COLOUR_BOLD(BLUE) : "";
+TEXT_COLOUR clr_debug_number = FLAG_log_colour ? COLOUR_BOLD(YELLOW) : "";
+TEXT_COLOUR clr_debug_message = FLAG_log_colour ? COLOUR(YELLOW) : "";
TEXT_COLOUR clr_printf = FLAG_log_colour ? COLOUR(GREEN) : "";
uintptr_t Simulator::StackLimit() const {
// Leave a safety margin of 1024 bytes to prevent overrunning the stack when
// pushing values.
- return reinterpret_cast<uintptr_t>(stack_limit_) + 1024;
+ return stack_limit_ + 1024;
}
// Allocate and setup the simulator stack.
stack_size_ = (FLAG_sim_stack_size * KB) + (2 * stack_protection_size_);
- stack_ = new byte[stack_size_];
+ stack_ = reinterpret_cast<uintptr_t>(new byte[stack_size_]);
stack_limit_ = stack_ + stack_protection_size_;
- byte* tos = stack_ + stack_size_ - stack_protection_size_;
- // The stack pointer must be 16 bytes aligned.
- set_sp(reinterpret_cast<int64_t>(tos) & ~0xfUL);
+ uintptr_t tos = stack_ + stack_size_ - stack_protection_size_;
+ // The stack pointer must be 16-byte aligned.
+ set_sp(tos & ~0xfUL);
stream_ = stream;
print_disasm_ = new PrintDisassembler(stream_);
Simulator::~Simulator() {
- delete[] stack_;
+ delete[] reinterpret_cast<byte*>(stack_);
if (FLAG_log_instruction_stats) {
delete instrument_;
}
const char* Simulator::xreg_names[] = {
-"x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7",
-"x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15",
-"ip0", "ip1", "x18", "x19", "x20", "x21", "x22", "x23",
-"x24", "x25", "x26", "cp", "jssp", "fp", "lr", "xzr", "csp"};
+"x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7",
+"x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15",
+"ip0", "ip1", "x18", "x19", "x20", "x21", "x22", "x23",
+"x24", "x25", "x26", "cp", "jssp", "fp", "lr", "xzr", "csp"};
const char* Simulator::wreg_names[] = {
-"w0", "w1", "w2", "w3", "w4", "w5", "w6", "w7",
-"w8", "w9", "w10", "w11", "w12", "w13", "w14", "w15",
-"w16", "w17", "w18", "w19", "w20", "w21", "w22", "w23",
+"w0", "w1", "w2", "w3", "w4", "w5", "w6", "w7",
+"w8", "w9", "w10", "w11", "w12", "w13", "w14", "w15",
+"w16", "w17", "w18", "w19", "w20", "w21", "w22", "w23",
"w24", "w25", "w26", "wcp", "wjssp", "wfp", "wlr", "wzr", "wcsp"};
const char* Simulator::sreg_names[] = {
const char* Simulator::WRegNameForCode(unsigned code, Reg31Mode mode) {
+ STATIC_ASSERT(arraysize(Simulator::wreg_names) == (kNumberOfRegisters + 1));
DCHECK(code < kNumberOfRegisters);
+ // The modulo operator has no effect here, but it silences a broken GCC
+ // warning about out-of-bounds array accesses.
+ code %= kNumberOfRegisters;
+
// If the code represents the stack pointer, index the name after zr.
if ((code == kZeroRegCode) && (mode == Reg31IsStackPointer)) {
code = kZeroRegCode + 1;
const char* Simulator::XRegNameForCode(unsigned code, Reg31Mode mode) {
+ STATIC_ASSERT(arraysize(Simulator::xreg_names) == (kNumberOfRegisters + 1));
DCHECK(code < kNumberOfRegisters);
+ code %= kNumberOfRegisters;
+
// If the code represents the stack pointer, index the name after zr.
if ((code == kZeroRegCode) && (mode == Reg31IsStackPointer)) {
code = kZeroRegCode + 1;
const char* Simulator::SRegNameForCode(unsigned code) {
+ STATIC_ASSERT(arraysize(Simulator::sreg_names) == kNumberOfFPRegisters);
DCHECK(code < kNumberOfFPRegisters);
- return sreg_names[code];
+ return sreg_names[code % kNumberOfFPRegisters];
}
const char* Simulator::DRegNameForCode(unsigned code) {
+ STATIC_ASSERT(arraysize(Simulator::dreg_names) == kNumberOfFPRegisters);
DCHECK(code < kNumberOfFPRegisters);
- return dreg_names[code];
+ return dreg_names[code % kNumberOfFPRegisters];
}
const char* Simulator::VRegNameForCode(unsigned code) {
+ STATIC_ASSERT(arraysize(Simulator::vreg_names) == kNumberOfFPRegisters);
DCHECK(code < kNumberOfFPRegisters);
- return vreg_names[code];
+ return vreg_names[code % kNumberOfFPRegisters];
}
nzcv().SetZ(Z);
nzcv().SetC(C);
nzcv().SetV(V);
+ LogSystemRegister(NZCV);
}
return result;
}
} else {
UNREACHABLE();
}
+ LogSystemRegister(NZCV);
}
}
-void Simulator::PrintSystemRegisters(bool print_all) {
- static bool first_run = true;
+void Simulator::PrintSystemRegisters() {
+ PrintSystemRegister(NZCV);
+ PrintSystemRegister(FPCR);
+}
+
+
+void Simulator::PrintRegisters() {
+ for (unsigned i = 0; i < kNumberOfRegisters; i++) {
+ PrintRegister(i);
+ }
+}
- static SimSystemRegister last_nzcv;
- if (print_all || first_run || (last_nzcv.RawValue() != nzcv().RawValue())) {
- fprintf(stream_, "# %sFLAGS: %sN:%d Z:%d C:%d V:%d%s\n",
- clr_flag_name,
- clr_flag_value,
- nzcv().N(), nzcv().Z(), nzcv().C(), nzcv().V(),
- clr_normal);
+
+void Simulator::PrintFPRegisters() {
+ for (unsigned i = 0; i < kNumberOfFPRegisters; i++) {
+ PrintFPRegister(i);
}
- last_nzcv = nzcv();
+}
+
- static SimSystemRegister last_fpcr;
- if (print_all || first_run || (last_fpcr.RawValue() != fpcr().RawValue())) {
- static const char * rmode[] = {
- "0b00 (Round to Nearest)",
- "0b01 (Round towards Plus Infinity)",
- "0b10 (Round towards Minus Infinity)",
- "0b11 (Round towards Zero)"
- };
- DCHECK(fpcr().RMode() < arraysize(rmode));
- fprintf(stream_, "# %sFPCR: %sAHP:%d DN:%d FZ:%d RMode:%s%s\n",
- clr_flag_name,
- clr_flag_value,
- fpcr().AHP(), fpcr().DN(), fpcr().FZ(), rmode[fpcr().RMode()],
- clr_normal);
+void Simulator::PrintRegister(unsigned code, Reg31Mode r31mode) {
+ // Don't print writes into xzr.
+ if ((code == kZeroRegCode) && (r31mode == Reg31IsZeroRegister)) {
+ return;
}
- last_fpcr = fpcr();
- first_run = false;
+ // The template is "# x<code>:value".
+ fprintf(stream_, "# %s%5s: %s0x%016" PRIx64 "%s\n",
+ clr_reg_name, XRegNameForCode(code, r31mode),
+ clr_reg_value, reg<uint64_t>(code, r31mode), clr_normal);
}
-void Simulator::PrintRegisters(bool print_all_regs) {
- static bool first_run = true;
- static int64_t last_regs[kNumberOfRegisters];
+void Simulator::PrintFPRegister(unsigned code, PrintFPRegisterSizes sizes) {
+ // The template is "# v<code>:bits (d<code>:value, ...)".
- for (unsigned i = 0; i < kNumberOfRegisters; i++) {
- if (print_all_regs || first_run ||
- (last_regs[i] != xreg(i, Reg31IsStackPointer))) {
+ DCHECK(sizes != 0);
+ DCHECK((sizes & kPrintAllFPRegValues) == sizes);
+
+ // Print the raw bits.
+ fprintf(stream_, "# %s%5s: %s0x%016" PRIx64 "%s (",
+ clr_fpreg_name, VRegNameForCode(code),
+ clr_fpreg_value, fpreg<uint64_t>(code), clr_normal);
+
+ // Print all requested value interpretations.
+ bool need_separator = false;
+ if (sizes & kPrintDRegValue) {
+ fprintf(stream_, "%s%s%s: %s%g%s",
+ need_separator ? ", " : "",
+ clr_fpreg_name, DRegNameForCode(code),
+ clr_fpreg_value, fpreg<double>(code), clr_normal);
+ need_separator = true;
+ }
+
+ if (sizes & kPrintSRegValue) {
+ fprintf(stream_, "%s%s%s: %s%g%s",
+ need_separator ? ", " : "",
+ clr_fpreg_name, SRegNameForCode(code),
+ clr_fpreg_value, fpreg<float>(code), clr_normal);
+ need_separator = true;
+ }
+
+ // End the value list.
+ fprintf(stream_, ")\n");
+}
+
+
+void Simulator::PrintSystemRegister(SystemRegister id) {
+ switch (id) {
+ case NZCV:
+ fprintf(stream_, "# %sNZCV: %sN:%d Z:%d C:%d V:%d%s\n",
+ clr_flag_name, clr_flag_value,
+ nzcv().N(), nzcv().Z(), nzcv().C(), nzcv().V(),
+ clr_normal);
+ break;
+ case FPCR: {
+ static const char * rmode[] = {
+ "0b00 (Round to Nearest)",
+ "0b01 (Round towards Plus Infinity)",
+ "0b10 (Round towards Minus Infinity)",
+ "0b11 (Round towards Zero)"
+ };
+ DCHECK(fpcr().RMode() < arraysize(rmode));
fprintf(stream_,
- "# %s%4s:%s 0x%016" PRIx64 "%s\n",
- clr_reg_name,
- XRegNameForCode(i, Reg31IsStackPointer),
- clr_reg_value,
- xreg(i, Reg31IsStackPointer),
+ "# %sFPCR: %sAHP:%d DN:%d FZ:%d RMode:%s%s\n",
+ clr_flag_name, clr_flag_value,
+ fpcr().AHP(), fpcr().DN(), fpcr().FZ(), rmode[fpcr().RMode()],
clr_normal);
+ break;
}
- // Cache the new register value so the next run can detect any changes.
- last_regs[i] = xreg(i, Reg31IsStackPointer);
+ default:
+ UNREACHABLE();
}
- first_run = false;
}
-void Simulator::PrintFPRegisters(bool print_all_regs) {
- static bool first_run = true;
- static uint64_t last_regs[kNumberOfFPRegisters];
+void Simulator::PrintRead(uintptr_t address,
+ size_t size,
+ unsigned reg_code) {
+ USE(size); // Size is unused here.
- // Print as many rows of registers as necessary, keeping each individual
- // register in the same column each time (to make it easy to visually scan
- // for changes).
- for (unsigned i = 0; i < kNumberOfFPRegisters; i++) {
- if (print_all_regs || first_run || (last_regs[i] != dreg_bits(i))) {
- fprintf(stream_,
- "# %s %4s:%s 0x%016" PRIx64 "%s (%s%s:%s %g%s %s:%s %g%s)\n",
- clr_fpreg_name,
- VRegNameForCode(i),
- clr_fpreg_value,
- dreg_bits(i),
- clr_normal,
- clr_fpreg_name,
- DRegNameForCode(i),
- clr_fpreg_value,
- dreg(i),
- clr_fpreg_name,
- SRegNameForCode(i),
- clr_fpreg_value,
- sreg(i),
- clr_normal);
- }
- // Cache the new register value so the next run can detect any changes.
- last_regs[i] = dreg_bits(i);
+ // The template is "# x<code>:value <- address".
+ fprintf(stream_, "# %s%5s: %s0x%016" PRIx64 "%s",
+ clr_reg_name, XRegNameForCode(reg_code),
+ clr_reg_value, reg<uint64_t>(reg_code), clr_normal);
+
+ fprintf(stream_, " <- %s0x%016" PRIxPTR "%s\n",
+ clr_memory_address, address, clr_normal);
+}
+
+
+void Simulator::PrintReadFP(uintptr_t address,
+ size_t size,
+ unsigned reg_code) {
+ // The template is "# reg:bits (reg:value) <- address".
+ switch (size) {
+ case kSRegSize:
+ fprintf(stream_, "# %s%5s: %s0x%016" PRIx64 "%s (%s%s: %s%gf%s)",
+ clr_fpreg_name, VRegNameForCode(reg_code),
+ clr_fpreg_value, fpreg<uint64_t>(reg_code), clr_normal,
+ clr_fpreg_name, SRegNameForCode(reg_code),
+ clr_fpreg_value, fpreg<float>(reg_code), clr_normal);
+ break;
+ case kDRegSize:
+ fprintf(stream_, "# %s%5s: %s0x%016" PRIx64 "%s (%s%s: %s%g%s)",
+ clr_fpreg_name, VRegNameForCode(reg_code),
+ clr_fpreg_value, fpreg<uint64_t>(reg_code), clr_normal,
+ clr_fpreg_name, DRegNameForCode(reg_code),
+ clr_fpreg_value, fpreg<double>(reg_code), clr_normal);
+ break;
+ default:
+ UNREACHABLE();
}
- first_run = false;
+
+ fprintf(stream_, " <- %s0x%016" PRIxPTR "%s\n",
+ clr_memory_address, address, clr_normal);
}
-void Simulator::PrintProcessorState() {
- PrintSystemRegisters();
- PrintRegisters();
- PrintFPRegisters();
+void Simulator::PrintWrite(uintptr_t address,
+ size_t size,
+ unsigned reg_code) {
+ // The template is "# reg:value -> address". To keep the trace tidy and
+ // readable, the value is aligned with the values in the register trace.
+ switch (size) {
+ case kByteSizeInBytes:
+ fprintf(stream_, "# %s%5s<7:0>: %s0x%02" PRIx8 "%s",
+ clr_reg_name, WRegNameForCode(reg_code),
+ clr_reg_value, reg<uint8_t>(reg_code), clr_normal);
+ break;
+ case kHalfWordSizeInBytes:
+ fprintf(stream_, "# %s%5s<15:0>: %s0x%04" PRIx16 "%s",
+ clr_reg_name, WRegNameForCode(reg_code),
+ clr_reg_value, reg<uint16_t>(reg_code), clr_normal);
+ break;
+ case kWRegSize:
+ fprintf(stream_, "# %s%5s: %s0x%08" PRIx32 "%s",
+ clr_reg_name, WRegNameForCode(reg_code),
+ clr_reg_value, reg<uint32_t>(reg_code), clr_normal);
+ break;
+ case kXRegSize:
+ fprintf(stream_, "# %s%5s: %s0x%016" PRIx64 "%s",
+ clr_reg_name, XRegNameForCode(reg_code),
+ clr_reg_value, reg<uint64_t>(reg_code), clr_normal);
+ break;
+ default:
+ UNREACHABLE();
+ }
+
+ fprintf(stream_, " -> %s0x%016" PRIxPTR "%s\n",
+ clr_memory_address, address, clr_normal);
}
-void Simulator::PrintWrite(uintptr_t address, uint64_t value,
- unsigned num_bytes) {
- // The template is "# value -> address". The format string is not used
- // directly in the printf because some compilers struggle with the
- // parametrized width field (%0*).
- const char* format = "# %s0x%0*" PRIx64 "%s -> %s0x%016" PRIxPTR "%s\n";
- fprintf(stream_,
- format,
- clr_memory_value,
- num_bytes * 2, // The width in hexadecimal characters.
- value,
- clr_normal,
- clr_memory_address,
- address,
- clr_normal);
+void Simulator::PrintWriteFP(uintptr_t address,
+ size_t size,
+ unsigned reg_code) {
+ // The template is "# reg:bits (reg:value) -> address". To keep the trace tidy
+ // and readable, the value is aligned with the values in the register trace.
+ switch (size) {
+ case kSRegSize:
+ fprintf(stream_, "# %s%5s<31:0>: %s0x%08" PRIx32 "%s (%s%s: %s%gf%s)",
+ clr_fpreg_name, VRegNameForCode(reg_code),
+ clr_fpreg_value, fpreg<uint32_t>(reg_code), clr_normal,
+ clr_fpreg_name, SRegNameForCode(reg_code),
+ clr_fpreg_value, fpreg<float>(reg_code), clr_normal);
+ break;
+ case kDRegSize:
+ fprintf(stream_, "# %s%5s: %s0x%016" PRIx64 "%s (%s%s: %s%g%s)",
+ clr_fpreg_name, VRegNameForCode(reg_code),
+ clr_fpreg_value, fpreg<uint64_t>(reg_code), clr_normal,
+ clr_fpreg_name, DRegNameForCode(reg_code),
+ clr_fpreg_value, fpreg<double>(reg_code), clr_normal);
+ break;
+ default:
+ UNREACHABLE();
+ }
+
+ fprintf(stream_, " -> %s0x%016" PRIxPTR "%s\n",
+ clr_memory_address, address, clr_normal);
}
nzcv().SetZ(CalcZFlag(result));
nzcv().SetC(0);
nzcv().SetV(0);
+ LogSystemRegister(NZCV);
}
set_reg<T>(instr->Rd(), result, instr->RdMode());
} else {
// If the condition fails, set the status flags to the nzcv immediate.
nzcv().SetFlags(instr->Nzcv());
+ LogSystemRegister(NZCV);
}
}
AddrMode addrmode) {
unsigned srcdst = instr->Rt();
unsigned addr_reg = instr->Rn();
- uint8_t* address = LoadStoreAddress(addr_reg, offset, addrmode);
- uint8_t* stack = NULL;
+ uintptr_t address = LoadStoreAddress(addr_reg, offset, addrmode);
+ uintptr_t stack = 0;
// Handle the writeback for stores before the store. On a CPU the writeback
// and the store are atomic, but when running on the simulator it is possible
// For store the address post writeback is used to check access below the
// stack.
- stack = reinterpret_cast<uint8_t*>(sp());
+ stack = sp();
}
LoadStoreOp op = static_cast<LoadStoreOp>(instr->Mask(LoadStoreOpMask));
switch (op) {
- case LDRB_w: set_wreg(srcdst, MemoryRead<uint8_t>(address)); break;
- case LDRH_w: set_wreg(srcdst, MemoryRead<uint16_t>(address)); break;
- case LDR_w: set_wreg(srcdst, MemoryRead<uint32_t>(address)); break;
- case LDR_x: set_xreg(srcdst, MemoryRead<uint64_t>(address)); break;
- case LDRSB_w: set_wreg(srcdst, MemoryRead<int8_t>(address)); break;
- case LDRSH_w: set_wreg(srcdst, MemoryRead<int16_t>(address)); break;
- case LDRSB_x: set_xreg(srcdst, MemoryRead<int8_t>(address)); break;
- case LDRSH_x: set_xreg(srcdst, MemoryRead<int16_t>(address)); break;
- case LDRSW_x: set_xreg(srcdst, MemoryRead<int32_t>(address)); break;
- case LDR_s: set_sreg(srcdst, MemoryRead<float>(address)); break;
- case LDR_d: set_dreg(srcdst, MemoryRead<double>(address)); break;
+ // Use _no_log variants to suppress the register trace (LOG_REGS,
+ // LOG_FP_REGS). We will print a more detailed log.
+ case LDRB_w: set_wreg_no_log(srcdst, MemoryRead<uint8_t>(address)); break;
+ case LDRH_w: set_wreg_no_log(srcdst, MemoryRead<uint16_t>(address)); break;
+ case LDR_w: set_wreg_no_log(srcdst, MemoryRead<uint32_t>(address)); break;
+ case LDR_x: set_xreg_no_log(srcdst, MemoryRead<uint64_t>(address)); break;
+ case LDRSB_w: set_wreg_no_log(srcdst, MemoryRead<int8_t>(address)); break;
+ case LDRSH_w: set_wreg_no_log(srcdst, MemoryRead<int16_t>(address)); break;
+ case LDRSB_x: set_xreg_no_log(srcdst, MemoryRead<int8_t>(address)); break;
+ case LDRSH_x: set_xreg_no_log(srcdst, MemoryRead<int16_t>(address)); break;
+ case LDRSW_x: set_xreg_no_log(srcdst, MemoryRead<int32_t>(address)); break;
+ case LDR_s: set_sreg_no_log(srcdst, MemoryRead<float>(address)); break;
+ case LDR_d: set_dreg_no_log(srcdst, MemoryRead<double>(address)); break;
case STRB_w: MemoryWrite<uint8_t>(address, wreg(srcdst)); break;
case STRH_w: MemoryWrite<uint16_t>(address, wreg(srcdst)); break;
default: UNIMPLEMENTED();
}
+ // Print a detailed trace (including the memory address) instead of the basic
+ // register:value trace generated by set_*reg().
+ size_t access_size = 1 << instr->SizeLS();
+ if (instr->IsLoad()) {
+ if ((op == LDR_s) || (op == LDR_d)) {
+ LogReadFP(address, access_size, srcdst);
+ } else {
+ LogRead(address, access_size, srcdst);
+ }
+ } else {
+ if ((op == STR_s) || (op == STR_d)) {
+ LogWriteFP(address, access_size, srcdst);
+ } else {
+ LogWrite(address, access_size, srcdst);
+ }
+ }
+
// Handle the writeback for loads after the load to ensure safe pop
// operation even when interrupted in the middle of it. The stack pointer
// is only updated after the load so pop(fp) will never break the invariant
if (instr->IsLoad()) {
// For loads the address pre writeback is used to check access below the
// stack.
- stack = reinterpret_cast<uint8_t*>(sp());
+ stack = sp();
LoadStoreWriteBack(addr_reg, offset, addrmode);
}
unsigned rt = instr->Rt();
unsigned rt2 = instr->Rt2();
unsigned addr_reg = instr->Rn();
- int offset = instr->ImmLSPair() << instr->SizeLSPair();
- uint8_t* address = LoadStoreAddress(addr_reg, offset, addrmode);
- uint8_t* stack = NULL;
+ size_t access_size = 1 << instr->SizeLSPair();
+ int64_t offset = instr->ImmLSPair() * access_size;
+ uintptr_t address = LoadStoreAddress(addr_reg, offset, addrmode);
+ uintptr_t address2 = address + access_size;
+ uintptr_t stack = 0;
// Handle the writeback for stores before the store. On a CPU the writeback
// and the store are atomic, but when running on the simulator it is possible
// For store the address post writeback is used to check access below the
// stack.
- stack = reinterpret_cast<uint8_t*>(sp());
+ stack = sp();
}
LoadStorePairOp op =
DCHECK(((op & LoadStorePairLBit) == 0) || (rt != rt2));
switch (op) {
+ // Use _no_log variants to suppress the register trace (LOG_REGS,
+ // LOG_FP_REGS). We will print a more detailed log.
case LDP_w: {
- set_wreg(rt, MemoryRead<uint32_t>(address));
- set_wreg(rt2, MemoryRead<uint32_t>(address + kWRegSize));
+ DCHECK(access_size == kWRegSize);
+ set_wreg_no_log(rt, MemoryRead<uint32_t>(address));
+ set_wreg_no_log(rt2, MemoryRead<uint32_t>(address2));
break;
}
case LDP_s: {
- set_sreg(rt, MemoryRead<float>(address));
- set_sreg(rt2, MemoryRead<float>(address + kSRegSize));
+ DCHECK(access_size == kSRegSize);
+ set_sreg_no_log(rt, MemoryRead<float>(address));
+ set_sreg_no_log(rt2, MemoryRead<float>(address2));
break;
}
case LDP_x: {
- set_xreg(rt, MemoryRead<uint64_t>(address));
- set_xreg(rt2, MemoryRead<uint64_t>(address + kXRegSize));
+ DCHECK(access_size == kXRegSize);
+ set_xreg_no_log(rt, MemoryRead<uint64_t>(address));
+ set_xreg_no_log(rt2, MemoryRead<uint64_t>(address2));
break;
}
case LDP_d: {
- set_dreg(rt, MemoryRead<double>(address));
- set_dreg(rt2, MemoryRead<double>(address + kDRegSize));
+ DCHECK(access_size == kDRegSize);
+ set_dreg_no_log(rt, MemoryRead<double>(address));
+ set_dreg_no_log(rt2, MemoryRead<double>(address2));
break;
}
case LDPSW_x: {
- set_xreg(rt, MemoryRead<int32_t>(address));
- set_xreg(rt2, MemoryRead<int32_t>(address + kWRegSize));
+ DCHECK(access_size == kWRegSize);
+ set_xreg_no_log(rt, MemoryRead<int32_t>(address));
+ set_xreg_no_log(rt2, MemoryRead<int32_t>(address2));
break;
}
case STP_w: {
+ DCHECK(access_size == kWRegSize);
MemoryWrite<uint32_t>(address, wreg(rt));
- MemoryWrite<uint32_t>(address + kWRegSize, wreg(rt2));
+ MemoryWrite<uint32_t>(address2, wreg(rt2));
break;
}
case STP_s: {
+ DCHECK(access_size == kSRegSize);
MemoryWrite<float>(address, sreg(rt));
- MemoryWrite<float>(address + kSRegSize, sreg(rt2));
+ MemoryWrite<float>(address2, sreg(rt2));
break;
}
case STP_x: {
+ DCHECK(access_size == kXRegSize);
MemoryWrite<uint64_t>(address, xreg(rt));
- MemoryWrite<uint64_t>(address + kXRegSize, xreg(rt2));
+ MemoryWrite<uint64_t>(address2, xreg(rt2));
break;
}
case STP_d: {
+ DCHECK(access_size == kDRegSize);
MemoryWrite<double>(address, dreg(rt));
- MemoryWrite<double>(address + kDRegSize, dreg(rt2));
+ MemoryWrite<double>(address2, dreg(rt2));
break;
}
default: UNREACHABLE();
}
+ // Print a detailed trace (including the memory address) instead of the basic
+ // register:value trace generated by set_*reg().
+ if (instr->IsLoad()) {
+ if ((op == LDP_s) || (op == LDP_d)) {
+ LogReadFP(address, access_size, rt);
+ LogReadFP(address2, access_size, rt2);
+ } else {
+ LogRead(address, access_size, rt);
+ LogRead(address2, access_size, rt2);
+ }
+ } else {
+ if ((op == STP_s) || (op == STP_d)) {
+ LogWriteFP(address, access_size, rt);
+ LogWriteFP(address2, access_size, rt2);
+ } else {
+ LogWrite(address, access_size, rt);
+ LogWrite(address2, access_size, rt2);
+ }
+ }
+
// Handle the writeback for loads after the load to ensure safe pop
// operation even when interrupted in the middle of it. The stack pointer
// is only updated after the load so pop(fp) will never break the invariant
if (instr->IsLoad()) {
// For loads the address pre writeback is used to check access below the
// stack.
- stack = reinterpret_cast<uint8_t*>(sp());
+ stack = sp();
LoadStoreWriteBack(addr_reg, offset, addrmode);
}
void Simulator::VisitLoadLiteral(Instruction* instr) {
- uint8_t* address = instr->LiteralAddress();
+ uintptr_t address = instr->LiteralAddress();
unsigned rt = instr->Rt();
switch (instr->Mask(LoadLiteralMask)) {
- case LDR_w_lit: set_wreg(rt, MemoryRead<uint32_t>(address)); break;
- case LDR_x_lit: set_xreg(rt, MemoryRead<uint64_t>(address)); break;
- case LDR_s_lit: set_sreg(rt, MemoryRead<float>(address)); break;
- case LDR_d_lit: set_dreg(rt, MemoryRead<double>(address)); break;
+ // Use _no_log variants to suppress the register trace (LOG_REGS,
+ // LOG_FP_REGS), then print a more detailed log.
+ case LDR_w_lit:
+ set_wreg_no_log(rt, MemoryRead<uint32_t>(address));
+ LogRead(address, kWRegSize, rt);
+ break;
+ case LDR_x_lit:
+ set_xreg_no_log(rt, MemoryRead<uint64_t>(address));
+ LogRead(address, kXRegSize, rt);
+ break;
+ case LDR_s_lit:
+ set_sreg_no_log(rt, MemoryRead<float>(address));
+ LogReadFP(address, kSRegSize, rt);
+ break;
+ case LDR_d_lit:
+ set_dreg_no_log(rt, MemoryRead<double>(address));
+ LogReadFP(address, kDRegSize, rt);
+ break;
default: UNREACHABLE();
}
}
-uint8_t* Simulator::LoadStoreAddress(unsigned addr_reg,
- int64_t offset,
- AddrMode addrmode) {
+uintptr_t Simulator::LoadStoreAddress(unsigned addr_reg, int64_t offset,
+ AddrMode addrmode) {
const unsigned kSPRegCode = kSPRegInternalCode & kRegCodeMask;
- int64_t address = xreg(addr_reg, Reg31IsStackPointer);
+ uint64_t address = xreg(addr_reg, Reg31IsStackPointer);
if ((addr_reg == kSPRegCode) && ((address % 16) != 0)) {
// When the base register is SP the stack pointer is required to be
// quadword aligned prior to the address calculation and write-backs.
address += offset;
}
- return reinterpret_cast<uint8_t*>(address);
+ return address;
}
}
-void Simulator::CheckMemoryAccess(uint8_t* address, uint8_t* stack) {
+void Simulator::CheckMemoryAccess(uintptr_t address, uintptr_t stack) {
if ((address >= stack_limit_) && (address < stack)) {
fprintf(stream_, "ACCESS BELOW STACK POINTER:\n");
- fprintf(stream_, " sp is here: 0x%16p\n", stack);
- fprintf(stream_, " access was here: 0x%16p\n", address);
- fprintf(stream_, " stack limit is here: 0x%16p\n", stack_limit_);
+ fprintf(stream_, " sp is here: 0x%016" PRIx64 "\n", stack);
+ fprintf(stream_, " access was here: 0x%016" PRIx64 "\n", address);
+ fprintf(stream_, " stack limit is here: 0x%016" PRIx64 "\n", stack_limit_);
fprintf(stream_, "\n");
FATAL("ACCESS BELOW STACK POINTER");
}
} else {
// If the condition fails, set the status flags to the nzcv immediate.
nzcv().SetFlags(instr->Nzcv());
+ LogSystemRegister(NZCV);
}
break;
}
}
case MSR: {
switch (instr->ImmSystemRegister()) {
- case NZCV: nzcv().SetRawValue(xreg(instr->Rt())); break;
- case FPCR: fpcr().SetRawValue(xreg(instr->Rt())); break;
+ case NZCV:
+ nzcv().SetRawValue(xreg(instr->Rt()));
+ LogSystemRegister(NZCV);
+ break;
+ case FPCR:
+ fpcr().SetRawValue(xreg(instr->Rt()));
+ LogSystemRegister(FPCR);
+ break;
default: UNIMPLEMENTED();
}
break;
} else if ((strcmp(cmd, "print") == 0) || (strcmp(cmd, "p") == 0)) {
if (argc == 2) {
if (strcmp(arg1, "all") == 0) {
- PrintRegisters(true);
- PrintFPRegisters(true);
+ PrintRegisters();
+ PrintFPRegisters();
} else {
if (!PrintValue(arg1)) {
PrintF("%s unrecognized\n", arg1);
if (FLAG_trace_sim_messages || FLAG_trace_sim || (parameters & BREAK)) {
if (message != NULL) {
PrintF(stream_,
- "%sDebugger hit %d: %s%s%s\n",
+ "# %sDebugger hit %d: %s%s%s\n",
clr_debug_number,
code,
clr_debug_message,
clr_normal);
} else {
PrintF(stream_,
- "%sDebugger hit %d.%s\n",
+ "# %sDebugger hit %d.%s\n",
clr_debug_number,
code,
clr_normal);
// Don't print information that is already being traced.
parameters &= ~log_parameters();
// Print the requested information.
- if (parameters & LOG_SYS_REGS) PrintSystemRegisters(true);
- if (parameters & LOG_REGS) PrintRegisters(true);
- if (parameters & LOG_FP_REGS) PrintFPRegisters(true);
+ if (parameters & LOG_SYS_REGS) PrintSystemRegisters();
+ if (parameters & LOG_REGS) PrintRegisters();
+ if (parameters & LOG_FP_REGS) PrintFPRegisters();
}
// The stop parameters are inlined in the code. Skip them:
DCHECK(IsAligned(reinterpret_cast<uintptr_t>(pc_), kInstructionSize));
CheckBreakNext();
Decode(pc_);
- LogProcessorState();
increment_pc();
CheckBreakpoints();
}
return reg<int64_t>(code, r31mode);
}
- // Write 'size' bits of 'value' into an integer register. The value is
- // zero-extended. This behaviour matches AArch64 register writes.
-
- // Like set_reg(), but infer the access size from the template type.
+ // Write 'value' into an integer register. The value is zero-extended. This
+ // behaviour matches AArch64 register writes.
template<typename T>
void set_reg(unsigned code, T value,
Reg31Mode r31mode = Reg31IsZeroRegister) {
- DCHECK(code < kNumberOfRegisters);
- if (!IsZeroRegister(code, r31mode))
- registers_[code].Set(value);
+ set_reg_no_log(code, value, r31mode);
+ LogRegister(code, r31mode);
}
// Common specialized accessors for the set_reg() template.
set_reg(code, value, r31mode);
}
+ // As above, but don't automatically log the register update.
+ template <typename T>
+ void set_reg_no_log(unsigned code, T value,
+ Reg31Mode r31mode = Reg31IsZeroRegister) {
+ DCHECK(code < kNumberOfRegisters);
+ if (!IsZeroRegister(code, r31mode)) {
+ registers_[code].Set(value);
+ }
+ }
+
+ void set_wreg_no_log(unsigned code, int32_t value,
+ Reg31Mode r31mode = Reg31IsZeroRegister) {
+ set_reg_no_log(code, value, r31mode);
+ }
+
+ void set_xreg_no_log(unsigned code, int64_t value,
+ Reg31Mode r31mode = Reg31IsZeroRegister) {
+ set_reg_no_log(code, value, r31mode);
+ }
+
// Commonly-used special cases.
template<typename T>
void set_lr(T value) {
// This behaviour matches AArch64 register writes.
template<typename T>
void set_fpreg(unsigned code, T value) {
- DCHECK((sizeof(value) == kDRegSize) || (sizeof(value) == kSRegSize));
- DCHECK(code < kNumberOfFPRegisters);
- fpregisters_[code].Set(value);
+ set_fpreg_no_log(code, value);
+
+ if (sizeof(value) <= kSRegSize) {
+ LogFPRegister(code, kPrintSRegValue);
+ } else {
+ LogFPRegister(code, kPrintDRegValue);
+ }
}
// Common specialized accessors for the set_fpreg() template.
set_fpreg(code, value);
}
+ // As above, but don't automatically log the register update.
+ template <typename T>
+ void set_fpreg_no_log(unsigned code, T value) {
+ DCHECK((sizeof(value) == kDRegSize) || (sizeof(value) == kSRegSize));
+ DCHECK(code < kNumberOfFPRegisters);
+ fpregisters_[code].Set(value);
+ }
+
+ void set_sreg_no_log(unsigned code, float value) {
+ set_fpreg_no_log(code, value);
+ }
+
+ void set_dreg_no_log(unsigned code, double value) {
+ set_fpreg_no_log(code, value);
+ }
+
SimSystemRegister& nzcv() { return nzcv_; }
SimSystemRegister& fpcr() { return fpcr_; }
// Disassemble instruction at the given address.
void PrintInstructionsAt(Instruction* pc, uint64_t count);
- void PrintSystemRegisters(bool print_all = false);
- void PrintRegisters(bool print_all_regs = false);
- void PrintFPRegisters(bool print_all_regs = false);
- void PrintProcessorState();
- void PrintWrite(uintptr_t address, uint64_t value, unsigned num_bytes);
+ // Print all registers of the specified types.
+ void PrintRegisters();
+ void PrintFPRegisters();
+ void PrintSystemRegisters();
+
+ // Like Print* (above), but respect log_parameters().
void LogSystemRegisters() {
- if (log_parameters_ & LOG_SYS_REGS) PrintSystemRegisters();
+ if (log_parameters() & LOG_SYS_REGS) PrintSystemRegisters();
}
void LogRegisters() {
- if (log_parameters_ & LOG_REGS) PrintRegisters();
+ if (log_parameters() & LOG_REGS) PrintRegisters();
}
void LogFPRegisters() {
- if (log_parameters_ & LOG_FP_REGS) PrintFPRegisters();
+ if (log_parameters() & LOG_FP_REGS) PrintFPRegisters();
+ }
+
+ // Specify relevant register sizes, for PrintFPRegister.
+ //
+ // These values are bit masks; they can be combined in case multiple views of
+ // a machine register are interesting.
+ enum PrintFPRegisterSizes {
+ kPrintDRegValue = 1 << kDRegSize,
+ kPrintSRegValue = 1 << kSRegSize,
+ kPrintAllFPRegValues = kPrintDRegValue | kPrintSRegValue
+ };
+
+ // Print individual register values (after update).
+ void PrintRegister(unsigned code, Reg31Mode r31mode = Reg31IsStackPointer);
+ void PrintFPRegister(unsigned code,
+ PrintFPRegisterSizes sizes = kPrintAllFPRegValues);
+ void PrintSystemRegister(SystemRegister id);
+
+ // Like Print* (above), but respect log_parameters().
+ void LogRegister(unsigned code, Reg31Mode r31mode = Reg31IsStackPointer) {
+ if (log_parameters() & LOG_REGS) PrintRegister(code, r31mode);
}
- void LogProcessorState() {
- LogSystemRegisters();
- LogRegisters();
- LogFPRegisters();
+ void LogFPRegister(unsigned code,
+ PrintFPRegisterSizes sizes = kPrintAllFPRegValues) {
+ if (log_parameters() & LOG_FP_REGS) PrintFPRegister(code, sizes);
}
- template <typename T>
- void LogWrite(uintptr_t address, T value) {
- uint64_t raw_value = 0;
- DCHECK(sizeof(value) <= sizeof(raw_value));
- if (log_parameters_ & LOG_WRITE) {
- memcpy(&raw_value, &value, sizeof(value));
- PrintWrite(address, raw_value, sizeof(value));
- }
+ void LogSystemRegister(SystemRegister id) {
+ if (log_parameters() & LOG_SYS_REGS) PrintSystemRegister(id);
+ }
+
+ // Print memory accesses.
+ void PrintRead(uintptr_t address, size_t size, unsigned reg_code);
+ void PrintReadFP(uintptr_t address, size_t size, unsigned reg_code);
+ void PrintWrite(uintptr_t address, size_t size, unsigned reg_code);
+ void PrintWriteFP(uintptr_t address, size_t size, unsigned reg_code);
+
+ // Like Print* (above), but respect log_parameters().
+ void LogRead(uintptr_t address, size_t size, unsigned reg_code) {
+ if (log_parameters() & LOG_REGS) PrintRead(address, size, reg_code);
+ }
+ void LogReadFP(uintptr_t address, size_t size, unsigned reg_code) {
+ if (log_parameters() & LOG_FP_REGS) PrintReadFP(address, size, reg_code);
+ }
+ void LogWrite(uintptr_t address, size_t size, unsigned reg_code) {
+ if (log_parameters() & LOG_WRITE) PrintWrite(address, size, reg_code);
+ }
+ void LogWriteFP(uintptr_t address, size_t size, unsigned reg_code) {
+ if (log_parameters() & LOG_WRITE) PrintWriteFP(address, size, reg_code);
}
int log_parameters() { return log_parameters_; }
int64_t offset,
AddrMode addrmode);
void LoadStorePairHelper(Instruction* instr, AddrMode addrmode);
- uint8_t* LoadStoreAddress(unsigned addr_reg,
- int64_t offset,
- AddrMode addrmode);
+ uintptr_t LoadStoreAddress(unsigned addr_reg, int64_t offset,
+ AddrMode addrmode);
void LoadStoreWriteBack(unsigned addr_reg,
int64_t offset,
AddrMode addrmode);
- void CheckMemoryAccess(uint8_t* address, uint8_t* stack);
+ void CheckMemoryAccess(uintptr_t address, uintptr_t stack);
+ // Memory read helpers.
template <typename T, typename A>
T MemoryRead(A address) {
T value;
return value;
}
+ // Memory write helpers.
template <typename T, typename A>
void MemoryWrite(A address, T value) {
STATIC_ASSERT((sizeof(value) == 1) || (sizeof(value) == 2) ||
(sizeof(value) == 4) || (sizeof(value) == 8));
- LogWrite(reinterpret_cast<uintptr_t>(address), value);
memcpy(reinterpret_cast<void*>(address), &value, sizeof(value));
}
static const uint32_t kConditionFlagsMask = 0xf0000000;
// Stack
- byte* stack_;
- static const intptr_t stack_protection_size_ = KB;
- intptr_t stack_size_;
- byte* stack_limit_;
+ uintptr_t stack_;
+ static const size_t stack_protection_size_ = KB;
+ size_t stack_size_;
+ uintptr_t stack_limit_;
Decoder<DispatchingDecoderVisitor>* decoder_;
Decoder<DispatchingDecoderVisitor>* disassembler_decoder_;