8227127: Era designator not displayed correctly using the COMPAT provider
Reviewed-by: rriggs
/*
* Copyright (c) 2014, Red Hat Inc. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*
*/
#ifndef _CPU_STATE_H
#define _CPU_STATE_H
#include <sys/types.h>
/*
* symbolic names used to identify general registers which also match
* the registers indices in machine code
*
* We have 32 general registers which can be read/written as 32 bit or
* 64 bit sources/sinks and are appropriately referred to as Wn or Xn
* in the assembly code. Some instructions mix these access modes
* (e.g. ADD X0, X1, W2) so the implementation of the instruction
* needs to *know* which type of read or write access is required.
*/
enum GReg {
R0,
R1,
R2,
R3,
R4,
R5,
R6,
R7,
R8,
R9,
R10,
R11,
R12,
R13,
R14,
R15,
R16,
R17,
R18,
R19,
R20,
R21,
R22,
R23,
R24,
R25,
R26,
R27,
R28,
R29,
R30,
R31,
// and now the aliases
RSCRATCH1=R8,
RSCRATCH2=R9,
RMETHOD=R12,
RESP=R20,
RDISPATCH=R21,
RBCP=R22,
RLOCALS=R24,
RMONITORS=R25,
RCPOOL=R26,
RHEAPBASE=R27,
RTHREAD=R28,
FP = R29,
LR = R30,
SP = R31,
ZR = R31
};
/*
* symbolic names used to refer to floating point registers which also
* match the registers indices in machine code
*
* We have 32 FP registers which can be read/written as 8, 16, 32, 64
* and 128 bit sources/sinks and are appropriately referred to as Bn,
* Hn, Sn, Dn and Qn in the assembly code. Some instructions mix these
* access modes (e.g. FCVT S0, D0) so the implementation of the
* instruction needs to *know* which type of read or write access is
* required.
*/
enum VReg {
V0,
V1,
V2,
V3,
V4,
V5,
V6,
V7,
V8,
V9,
V10,
V11,
V12,
V13,
V14,
V15,
V16,
V17,
V18,
V19,
V20,
V21,
V22,
V23,
V24,
V25,
V26,
V27,
V28,
V29,
V30,
V31,
};
/**
* all the different integer bit patterns for the components of a
* general register are overlaid here using a union so as to allow all
* reading and writing of the desired bits.
*
* n.b. the ARM spec says that when you write a 32 bit register you
* are supposed to write the low 32 bits and zero the high 32
* bits. But we don't actually have to care about this because Java
* will only ever consume the 32 bits value as a 64 bit quantity after
* an explicit extend.
*/
union GRegisterValue
{
int8_t s8;
int16_t s16;
int32_t s32;
int64_t s64;
u_int8_t u8;
u_int16_t u16;
u_int32_t u32;
u_int64_t u64;
};
class GRegister
{
public:
GRegisterValue value;
};
/*
* float registers provide for storage of a single, double or quad
* word format float in the same register. single floats are not
* paired within each double register as per 32 bit arm. instead each
* 128 bit register Vn embeds the bits for Sn, and Dn in the lower
* quarter and half, respectively, of the bits for Qn.
*
* The upper bits can also be accessed as single or double floats by
* the float vector operations using indexing e.g. V1.D[1], V1.S[3]
* etc and, for SIMD operations using a horrible index range notation.
*
* The spec also talks about accessing float registers as half words
* and bytes with Hn and Bn providing access to the low 16 and 8 bits
* of Vn but it is not really clear what these bits represent. We can
* probably ignore this for Java anyway. However, we do need to access
* the raw bits at 32 and 64 bit resolution to load to/from integer
* registers.
*/
union FRegisterValue
{
float s;
double d;
long double q;
// eventually we will need to be able to access the data as a vector
// the integral array elements allow us to access the bits in s, d,
// q, vs and vd at an appropriate level of granularity
u_int8_t vb[16];
u_int16_t vh[8];
u_int32_t vw[4];
u_int64_t vx[2];
float vs[4];
double vd[2];
};
class FRegister
{
public:
FRegisterValue value;
};
/*
* CPSR register -- this does not exist as a directly accessible
* register but we need to store the flags so we can implement
* flag-seting and flag testing operations
*
* we can possibly use injected x86 asm to report the outcome of flag
* setting operations. if so we will need to grab the flags
* immediately after the operation in order to ensure we don't lose
* them because of the actions of the simulator. so we still need
* somewhere to store the condition codes.
*/
class CPSRRegister
{
public:
u_int32_t value;
/*
* condition register bit select values
*
* the order of bits here is important because some of
* the flag setting conditional instructions employ a
* bit field to populate the flags when a false condition
* bypasses execution of the operation and we want to
* be able to assign the flags register using the
* supplied value.
*/
enum CPSRIdx {
V_IDX,
C_IDX,
Z_IDX,
N_IDX
};
enum CPSRMask {
V = 1 << V_IDX,
C = 1 << C_IDX,
Z = 1 << Z_IDX,
N = 1 << N_IDX
};
static const int CPSR_ALL_FLAGS = (V | C | Z | N);
};
// auxiliary function to assemble the relevant bits from
// the x86 EFLAGS register into an ARM CPSR value
#define X86_V_IDX 11
#define X86_C_IDX 0
#define X86_Z_IDX 6
#define X86_N_IDX 7
#define X86_V (1 << X86_V_IDX)
#define X86_C (1 << X86_C_IDX)
#define X86_Z (1 << X86_Z_IDX)
#define X86_N (1 << X86_N_IDX)
inline u_int32_t convertX86Flags(u_int32_t x86flags)
{
u_int32_t flags;
// set N flag
flags = ((x86flags & X86_N) >> X86_N_IDX);
// shift then or in Z flag
flags <<= 1;
flags |= ((x86flags & X86_Z) >> X86_Z_IDX);
// shift then or in C flag
flags <<= 1;
flags |= ((x86flags & X86_C) >> X86_C_IDX);
// shift then or in V flag
flags <<= 1;
flags |= ((x86flags & X86_V) >> X86_V_IDX);
return flags;
}
inline u_int32_t convertX86FlagsFP(u_int32_t x86flags)
{
// x86 flags set by fcomi(x,y) are ZF:PF:CF
// (yes, that's PF for parity, WTF?)
// where
// 0) 0:0:0 means x > y
// 1) 0:0:1 means x < y
// 2) 1:0:0 means x = y
// 3) 1:1:1 means x and y are unordered
// note that we don't have to check PF so
// we really have a simple 2-bit case switch
// the corresponding ARM64 flags settings
// in hi->lo bit order are
// 0) --C-
// 1) N---
// 2) -ZC-
// 3) --CV
static u_int32_t armFlags[] = {
0b0010,
0b1000,
0b0110,
0b0011
};
// pick out the ZF and CF bits
u_int32_t zc = ((x86flags & X86_Z) >> X86_Z_IDX);
zc <<= 1;
zc |= ((x86flags & X86_C) >> X86_C_IDX);
return armFlags[zc];
}
/*
* FPSR register -- floating point status register
* this register includes IDC, IXC, UFC, OFC, DZC, IOC and QC bits,
* and the floating point N, Z, C, V bits but the latter are unused in
* aarch64 mode. the sim ignores QC for now.
*
* bit positions are as per the ARMv7 FPSCR register
*
* IDC : 7 ==> Input Denormal (cumulative exception bit)
* IXC : 4 ==> Inexact
* UFC : 3 ==> Underflow
* OFC : 2 ==> Overflow
* DZC : 1 ==> Division by Zero
* IOC : 0 ==> Invalid Operation
*/
class FPSRRegister
{
public:
u_int32_t value;
// indices for bits in the FPSR register value
enum FPSRIdx {
IO_IDX = 0,
DZ_IDX = 1,
OF_IDX = 2,
UF_IDX = 3,
IX_IDX = 4,
ID_IDX = 7
};
// corresponding bits as numeric values
enum FPSRMask {
IO = (1 << IO_IDX),
DZ = (1 << DZ_IDX),
OF = (1 << OF_IDX),
UF = (1 << UF_IDX),
IX = (1 << IX_IDX),
ID = (1 << ID_IDX)
};
static const int FPSR_ALL_FPSRS = (IO | DZ | OF | UF | IX | ID);
};
// debugger support
enum PrintFormat
{
FMT_DECIMAL,
FMT_HEX,
FMT_SINGLE,
FMT_DOUBLE,
FMT_QUAD,
FMT_MULTI
};
/*
* model of the registers and other state associated with the cpu
*/
class CPUState
{
friend class AArch64Simulator;
private:
// this is the PC of the instruction being executed
u_int64_t pc;
// this is the PC of the instruction to be executed next
// it is defaulted to pc + 4 at instruction decode but
// execute may reset it
u_int64_t nextpc;
GRegister gr[33]; // extra register at index 32 is used
// to hold zero value
FRegister fr[32];
CPSRRegister cpsr;
FPSRRegister fpsr;
public:
CPUState() {
gr[20].value.u64 = 0; // establish initial condition for
// checkAssertions()
trace_counter = 0;
}
// General Register access macros
// only xreg or xregs can be used as an lvalue in order to update a
// register. this ensures that the top part of a register is always
// assigned when it is written by the sim.
inline u_int64_t &xreg(GReg reg, int r31_is_sp) {
if (reg == R31 && !r31_is_sp) {
return gr[32].value.u64;
} else {
return gr[reg].value.u64;
}
}
inline int64_t &xregs(GReg reg, int r31_is_sp) {
if (reg == R31 && !r31_is_sp) {
return gr[32].value.s64;
} else {
return gr[reg].value.s64;
}
}
inline u_int32_t wreg(GReg reg, int r31_is_sp) {
if (reg == R31 && !r31_is_sp) {
return gr[32].value.u32;
} else {
return gr[reg].value.u32;
}
}
inline int32_t wregs(GReg reg, int r31_is_sp) {
if (reg == R31 && !r31_is_sp) {
return gr[32].value.s32;
} else {
return gr[reg].value.s32;
}
}
inline u_int32_t hreg(GReg reg, int r31_is_sp) {
if (reg == R31 && !r31_is_sp) {
return gr[32].value.u16;
} else {
return gr[reg].value.u16;
}
}
inline int32_t hregs(GReg reg, int r31_is_sp) {
if (reg == R31 && !r31_is_sp) {
return gr[32].value.s16;
} else {
return gr[reg].value.s16;
}
}
inline u_int32_t breg(GReg reg, int r31_is_sp) {
if (reg == R31 && !r31_is_sp) {
return gr[32].value.u8;
} else {
return gr[reg].value.u8;
}
}
inline int32_t bregs(GReg reg, int r31_is_sp) {
if (reg == R31 && !r31_is_sp) {
return gr[32].value.s8;
} else {
return gr[reg].value.s8;
}
}
// FP Register access macros
// all non-vector accessors return a reference so we can both read
// and assign
inline float &sreg(VReg reg) {
return fr[reg].value.s;
}
inline double &dreg(VReg reg) {
return fr[reg].value.d;
}
inline long double &qreg(VReg reg) {
return fr[reg].value.q;
}
// all vector register accessors return a pointer
inline float *vsreg(VReg reg) {
return &fr[reg].value.vs[0];
}
inline double *vdreg(VReg reg) {
return &fr[reg].value.vd[0];
}
inline u_int8_t *vbreg(VReg reg) {
return &fr[reg].value.vb[0];
}
inline u_int16_t *vhreg(VReg reg) {
return &fr[reg].value.vh[0];
}
inline u_int32_t *vwreg(VReg reg) {
return &fr[reg].value.vw[0];
}
inline u_int64_t *vxreg(VReg reg) {
return &fr[reg].value.vx[0];
}
union GRegisterValue prev_sp, prev_fp;
static const int trace_size = 256;
u_int64_t trace_buffer[trace_size];
int trace_counter;
bool checkAssertions()
{
// Make sure that SP is 16-aligned
// Also make sure that ESP is above SP.
// We don't care about checking ESP if it is null, i.e. it hasn't
// been used yet.
if (gr[31].value.u64 & 0x0f) {
asm volatile("nop");
return false;
}
return true;
}
// pc register accessors
// this instruction can be used to fetch the current PC
u_int64_t getPC();
// instead of setting the current PC directly you can
// first set the next PC (either absolute or PC-relative)
// and later copy the next PC into the current PC
// this supports a default increment by 4 at instruction
// fetch with an optional reset by control instructions
u_int64_t getNextPC();
void setNextPC(u_int64_t next);
void offsetNextPC(int64_t offset);
// install nextpc as current pc
void updatePC();
// this instruction can be used to save the next PC to LR
// just before installing a branch PC
inline void saveLR() { gr[LR].value.u64 = nextpc; }
// cpsr register accessors
u_int32_t getCPSRRegister();
void setCPSRRegister(u_int32_t flags);
// read a specific subset of the flags as a bit pattern
// mask should be composed using elements of enum FlagMask
u_int32_t getCPSRBits(u_int32_t mask);
// assign a specific subset of the flags as a bit pattern
// mask and value should be composed using elements of enum FlagMask
void setCPSRBits(u_int32_t mask, u_int32_t value);
// test the value of a single flag returned as 1 or 0
u_int32_t testCPSR(CPSRRegister::CPSRIdx idx);
// set a single flag
void setCPSR(CPSRRegister::CPSRIdx idx);
// clear a single flag
void clearCPSR(CPSRRegister::CPSRIdx idx);
// utility method to set ARM CSPR flags from an x86 bit mask generated by integer arithmetic
void setCPSRRegisterFromX86(u_int64_t x86Flags);
// utility method to set ARM CSPR flags from an x86 bit mask generated by floating compare
void setCPSRRegisterFromX86FP(u_int64_t x86Flags);
// fpsr register accessors
u_int32_t getFPSRRegister();
void setFPSRRegister(u_int32_t flags);
// read a specific subset of the fprs bits as a bit pattern
// mask should be composed using elements of enum FPSRRegister::FlagMask
u_int32_t getFPSRBits(u_int32_t mask);
// assign a specific subset of the flags as a bit pattern
// mask and value should be composed using elements of enum FPSRRegister::FlagMask
void setFPSRBits(u_int32_t mask, u_int32_t value);
// test the value of a single flag returned as 1 or 0
u_int32_t testFPSR(FPSRRegister::FPSRIdx idx);
// set a single flag
void setFPSR(FPSRRegister::FPSRIdx idx);
// clear a single flag
void clearFPSR(FPSRRegister::FPSRIdx idx);
// debugger support
void printPC(int pending, const char *trailing = "\n");
void printInstr(u_int32_t instr, void (*dasm)(u_int64_t), const char *trailing = "\n");
void printGReg(GReg reg, PrintFormat format = FMT_HEX, const char *trailing = "\n");
void printVReg(VReg reg, PrintFormat format = FMT_HEX, const char *trailing = "\n");
void printCPSR(const char *trailing = "\n");
void printFPSR(const char *trailing = "\n");
void dumpState();
};
#endif // ifndef _CPU_STATE_H