VIXL Release 1.1

Refer to the README.md and LICENCE files for details.
This commit is contained in:
armvixl 2013-08-15 17:21:42 +01:00
parent ad96eda894
commit 578645f14e
28 changed files with 3121 additions and 883 deletions

View File

@ -1,5 +1,5 @@
VIXL: Runtime Code Generation Library Version 1.0
=================================================
VIXL: AArch64 Runtime Code Generation Library Version 1.1
=========================================================
Contents:
@ -15,7 +15,7 @@ Requirements
To build VIXL the following software is required:
1. Python 2.7
2. Scons 2.0
2. SCons 2.0
3. GCC 4.4
A 64-bit host machine is required, implementing an LP64 data model. VIXL has
@ -28,6 +28,7 @@ To run the linter stage of the tests, the following software is also required:
Refer to the 'Usage' section for details.
Overview
========
@ -41,6 +42,9 @@ VIXL is made of three components.
The simulator allows generated code to be run on another architecture
without the need for a full ISA model.
The VIXL git repository can be found [on GitHub][vixl]. Changes from previous
versions of VIXL can be found in the [Changelog](doc/changelog.md).
Known Limitations
=================
@ -55,7 +59,8 @@ were deemed unnecessary:
* A few miscellaneous integer and floating point instructions are missing.
The VIXL simulator supports only those instructions that the VIXL assembler can
generate.
generate. The `doc` directory contains a
[list of supported instructions](doc/supported-instructions.md).
Usage
@ -110,7 +115,7 @@ figure; they should be timed using the `time` command.
Getting Started
---------------
A short introduction to using VIXL can be found at `doc/getting-started.md`.
A short introduction to using VIXL can be found [here](doc/getting-started.md).
Example source code is provided in the `examples` directory. Build this using
`scons target=examples` from the root directory.
@ -118,3 +123,6 @@ Example source code is provided in the `examples` directory. Build this using
[cpplint]: https://google-styleguide.googlecode.com/svn-history/r104/trunk/cpplint/cpplint.py
"Google's cpplint.py script."
[vixl]: https://github.com/armvixl/vixl
"The VIXL repository on GitHub."

View File

@ -40,6 +40,7 @@ src/a64/debugger-a64.cc
src/a64/disasm-a64.cc
src/a64/cpu-a64.cc
src/a64/simulator-a64.cc
src/a64/instrument-a64.cc
'''.split()
PROJ_EXAMPLES_DIR = 'examples'
PROJ_EXAMPLES_SRC_FILES = '''
@ -69,6 +70,7 @@ TARGET_SRC_FILES = {
test/test-utils-a64.cc
test/test-assembler-a64.cc
test/test-disasm-a64.cc
test/test-fuzz-a64.cc
test/examples/test-examples.cc
'''.split() + PROJ_EXAMPLES_SRC_FILES,
'bench_dataop': '''

12
doc/changelog.md Normal file
View File

@ -0,0 +1,12 @@
VIXL Change Log
===============
* 1.1
+ Improved robustness of instruction decoder and disassembler.
+ Added support for double-to-float conversions using `fcvt`.
+ Added support for more fixed-point to floating-point conversions (`ucvtf`
and `scvtf`).
+ Added instruction statistics collection class `instrument-a64.cc`.
* 1.0
+ Initial release.

View File

@ -436,13 +436,18 @@ void Assembler::ret(const Register& xn) {
}
void Assembler::b(int imm26, Condition cond) {
if (cond == al) {
Emit(B | ImmUncondBranch(imm26));
} else {
// The immediate field is only 19bit wide here.
Emit(B_cond | ImmCondBranch(imm26) | cond);
}
void Assembler::b(int imm26) {
Emit(B | ImmUncondBranch(imm26));
}
void Assembler::b(int imm19, Condition cond) {
Emit(B_cond | ImmCondBranch(imm19) | cond);
}
void Assembler::b(Label* label) {
b(UpdateAndGetInstructionOffsetTo(label));
}
@ -748,28 +753,33 @@ void Assembler::csneg(const Register& rd,
void Assembler::cset(const Register &rd, Condition cond) {
ASSERT((cond != al) && (cond != nv));
Register zr = AppropriateZeroRegFor(rd);
csinc(rd, zr, zr, InvertCondition(cond));
}
void Assembler::csetm(const Register &rd, Condition cond) {
ASSERT((cond != al) && (cond != nv));
Register zr = AppropriateZeroRegFor(rd);
csinv(rd, zr, zr, InvertCondition(cond));
}
void Assembler::cinc(const Register &rd, const Register &rn, Condition cond) {
ASSERT((cond != al) && (cond != nv));
csinc(rd, rn, rn, InvertCondition(cond));
}
void Assembler::cinv(const Register &rd, const Register &rn, Condition cond) {
ASSERT((cond != al) && (cond != nv));
csinv(rd, rn, rn, InvertCondition(cond));
}
void Assembler::cneg(const Register &rd, const Register &rn, Condition cond) {
ASSERT((cond != al) && (cond != nv));
csneg(rd, rn, rn, InvertCondition(cond));
}
@ -781,7 +791,6 @@ void Assembler::ConditionalSelect(const Register& rd,
ConditionalSelectOp op) {
ASSERT(rd.size() == rn.size());
ASSERT(rd.size() == rm.size());
ASSERT(cond != al);
Emit(SF(rd) | op | Rm(rm) | Cond(cond) | Rn(rn) | Rd(rd));
}
@ -1257,14 +1266,6 @@ void Assembler::frintz(const FPRegister& fd,
}
void Assembler::fcvt(const FPRegister& fd,
const FPRegister& fn) {
// Only float to double conversion is supported.
ASSERT(fd.Is64Bits() && fn.Is32Bits());
FPDataProcessing1Source(fd, fn, FCVT_ds);
}
void Assembler::fcmp(const FPRegister& fn,
const FPRegister& fm) {
ASSERT(fn.size() == fm.size());
@ -1298,7 +1299,6 @@ void Assembler::fcsel(const FPRegister& fd,
Condition cond) {
ASSERT(fd.size() == fn.size());
ASSERT(fd.size() == fm.size());
ASSERT(cond != al);
Emit(FPType(fd) | FCSEL | Rm(fm) | Cond(cond) | Rn(fn) | Rd(fd));
}
@ -1310,6 +1310,20 @@ void Assembler::FPConvertToInt(const Register& rd,
}
void Assembler::fcvt(const FPRegister& fd,
const FPRegister& fn) {
if (fd.Is64Bits()) {
// Convert float to double.
ASSERT(fn.Is32Bits());
FPDataProcessing1Source(fd, fn, FCVT_ds);
} else {
// Convert double to float.
ASSERT(fn.Is64Bits());
FPDataProcessing1Source(fd, fn, FCVT_sd);
}
}
void Assembler::fcvtmu(const Register& rd, const FPRegister& fn) {
FPConvertToInt(rd, fn, FCVTMU);
}
@ -1347,13 +1361,9 @@ void Assembler::fcvtzs(const Register& rd,
void Assembler::scvtf(const FPRegister& fd,
const Register& rn,
unsigned fbits) {
// We support double register destinations only.
ASSERT(fd.Is64Bits());
if (fbits == 0) {
Emit(SF(rn) | FPType(fd) | SCVTF | Rn(rn) | Rd(fd));
} else {
// For fixed point numbers, we support X register sources only.
ASSERT(rn.Is64Bits());
Emit(SF(rn) | FPType(fd) | SCVTF_fixed | FPScale(64 - fbits) | Rn(rn) |
Rd(fd));
}
@ -1363,13 +1373,9 @@ void Assembler::scvtf(const FPRegister& fd,
void Assembler::ucvtf(const FPRegister& fd,
const Register& rn,
unsigned fbits) {
// We support double register destinations only.
ASSERT(fd.Is64Bits());
if (fbits == 0) {
Emit(SF(rn) | FPType(fd) | UCVTF | Rn(rn) | Rd(fd));
} else {
// For fixed point numbers, we support X register sources only.
ASSERT(rn.Is64Bits());
Emit(SF(rn) | FPType(fd) | UCVTF_fixed | FPScale(64 - fbits) | Rn(rn) |
Rd(fd));
}

View File

@ -660,11 +660,17 @@ class Assembler {
// Branch to register with return hint.
void ret(const Register& xn = lr);
// Branch to label.
void b(Label* label, Condition cond = al);
// Unconditional branch to label.
void b(Label* label);
// Branch to PC offset.
void b(int imm26, Condition cond = al);
// Conditional branch to label.
void b(Label* label, Condition cond);
// Unconditional branch to PC offset.
void b(int imm26);
// Conditional branch to PC offset.
void b(int imm19, Condition cond);
// Branch with link to label.
void bl(Label* label);
@ -1212,9 +1218,6 @@ class Assembler {
// FP round to integer (towards zero).
void frintz(const FPRegister& fd, const FPRegister& fn);
// FP convert single to double precision.
void fcvt(const FPRegister& fd, const FPRegister& fn);
// FP compare registers.
void fcmp(const FPRegister& fn, const FPRegister& fm);
@ -1238,6 +1241,9 @@ class Assembler {
const FPRegister& fn,
FPIntegerConvertOp op);
// FP convert between single and double precision.
void fcvt(const FPRegister& fd, const FPRegister& fn);
// Convert FP to unsigned integer (round towards -infinity).
void fcvtmu(const Register& rd, const FPRegister& fn);

View File

@ -44,94 +44,118 @@ R(8) R(9) R(10) R(11) R(12) R(13) R(14) R(15) \
R(16) R(17) R(18) R(19) R(20) R(21) R(22) R(23) \
R(24) R(25) R(26) R(27) R(28) R(29) R(30) R(31)
#define FIELDS_LIST(V) \
#define INSTRUCTION_FIELDS_LIST(V_) \
/* Register fields */ \
V(Rd, 4, 0, Bits) /* Destination register. */ \
V(Rn, 9, 5, Bits) /* First source register. */ \
V(Rm, 20, 16, Bits) /* Second source register. */ \
V(Ra, 14, 10, Bits) /* Third source register. */ \
V(Rt, 4, 0, Bits) /* Load dest / store source. */ \
V(Rt2, 14, 10, Bits) /* Load second dest / */ \
V_(Rd, 4, 0, Bits) /* Destination register. */ \
V_(Rn, 9, 5, Bits) /* First source register. */ \
V_(Rm, 20, 16, Bits) /* Second source register. */ \
V_(Ra, 14, 10, Bits) /* Third source register. */ \
V_(Rt, 4, 0, Bits) /* Load dest / store source. */ \
V_(Rt2, 14, 10, Bits) /* Load second dest / */ \
/* store second source. */ \
V(PrefetchMode, 4, 0, Bits) \
V_(PrefetchMode, 4, 0, Bits) \
\
/* Common bits */ \
V(SixtyFourBits, 31, 31, Bits) \
V(FlagsUpdate, 29, 29, Bits) \
V_(SixtyFourBits, 31, 31, Bits) \
V_(FlagsUpdate, 29, 29, Bits) \
\
/* PC relative addressing */ \
V(ImmPCRelHi, 23, 5, SignedBits) \
V(ImmPCRelLo, 30, 29, Bits) \
V_(ImmPCRelHi, 23, 5, SignedBits) \
V_(ImmPCRelLo, 30, 29, Bits) \
\
/* Add/subtract/logical shift register */ \
V(ShiftDP, 23, 22, Bits) \
V(ImmDPShift, 15, 10, Bits) \
V_(ShiftDP, 23, 22, Bits) \
V_(ImmDPShift, 15, 10, Bits) \
\
/* Add/subtract immediate */ \
V(ImmAddSub, 21, 10, Bits) \
V(ShiftAddSub, 23, 22, Bits) \
V_(ImmAddSub, 21, 10, Bits) \
V_(ShiftAddSub, 23, 22, Bits) \
\
/* Add/substract extend */ \
V(ImmExtendShift, 12, 10, Bits) \
V(ExtendMode, 15, 13, Bits) \
V_(ImmExtendShift, 12, 10, Bits) \
V_(ExtendMode, 15, 13, Bits) \
\
/* Move wide */ \
V(ImmMoveWide, 20, 5, Bits) \
V(ShiftMoveWide, 22, 21, Bits) \
V_(ImmMoveWide, 20, 5, Bits) \
V_(ShiftMoveWide, 22, 21, Bits) \
\
/* Logical immediate, bitfield and extract */ \
V(BitN, 22, 22, Bits) \
V(ImmRotate, 21, 16, Bits) \
V(ImmSetBits, 15, 10, Bits) \
V(ImmR, 21, 16, Bits) \
V(ImmS, 15, 10, Bits) \
V_(BitN, 22, 22, Bits) \
V_(ImmRotate, 21, 16, Bits) \
V_(ImmSetBits, 15, 10, Bits) \
V_(ImmR, 21, 16, Bits) \
V_(ImmS, 15, 10, Bits) \
\
/* Test and branch immediate */ \
V(ImmTestBranch, 18, 5, SignedBits) \
V(ImmTestBranchBit40, 23, 19, Bits) \
V(ImmTestBranchBit5, 31, 31, Bits) \
V_(ImmTestBranch, 18, 5, SignedBits) \
V_(ImmTestBranchBit40, 23, 19, Bits) \
V_(ImmTestBranchBit5, 31, 31, Bits) \
\
/* Conditionals */ \
V(Condition, 15, 12, Bits) \
V(ConditionBranch, 3, 0, Bits) \
V(Nzcv, 3, 0, Bits) \
V(ImmCondCmp, 20, 16, Bits) \
V(ImmCondBranch, 23, 5, SignedBits) \
V_(Condition, 15, 12, Bits) \
V_(ConditionBranch, 3, 0, Bits) \
V_(Nzcv, 3, 0, Bits) \
V_(ImmCondCmp, 20, 16, Bits) \
V_(ImmCondBranch, 23, 5, SignedBits) \
\
/* Floating point */ \
V(FPType, 23, 22, Bits) \
V(ImmFP, 20, 13, Bits) \
V(FPScale, 15, 10, Bits) \
V_(FPType, 23, 22, Bits) \
V_(ImmFP, 20, 13, Bits) \
V_(FPScale, 15, 10, Bits) \
\
/* Load Store */ \
V(ImmLS, 20, 12, SignedBits) \
V(ImmLSUnsigned, 21, 10, Bits) \
V(ImmLSPair, 21, 15, SignedBits) \
V(SizeLS, 31, 30, Bits) \
V(ImmShiftLS, 12, 12, Bits) \
V_(ImmLS, 20, 12, SignedBits) \
V_(ImmLSUnsigned, 21, 10, Bits) \
V_(ImmLSPair, 21, 15, SignedBits) \
V_(SizeLS, 31, 30, Bits) \
V_(ImmShiftLS, 12, 12, Bits) \
\
/* Other immediates */ \
V(ImmUncondBranch, 25, 0, SignedBits) \
V(ImmCmpBranch, 23, 5, SignedBits) \
V(ImmLLiteral, 23, 5, SignedBits) \
V(ImmException, 20, 5, Bits) \
V(ImmHint, 11, 5, Bits) \
V(ImmSystemRegister, 19, 5, Bits) \
V_(ImmUncondBranch, 25, 0, SignedBits) \
V_(ImmCmpBranch, 23, 5, SignedBits) \
V_(ImmLLiteral, 23, 5, SignedBits) \
V_(ImmException, 20, 5, Bits) \
V_(ImmHint, 11, 5, Bits) \
\
/* System */ \
V(Cn, 15, 12, Bits) \
V(Cm, 11, 8, Bits)
/* System (MRS, MSR) */ \
V_(ImmSystemRegister, 19, 5, Bits) \
V_(SysO0, 19, 19, Bits) \
V_(SysOp1, 18, 16, Bits) \
V_(SysOp2, 7, 5, Bits) \
V_(CRn, 15, 12, Bits) \
V_(CRm, 11, 8, Bits) \
#define SYSTEM_REGISTER_FIELDS_LIST(V_, M_) \
/* NZCV */ \
V_(Flags, 31, 28, Bits) \
V_(N, 31, 31, Bits) \
V_(Z, 30, 30, Bits) \
V_(C, 29, 29, Bits) \
V_(V, 28, 28, Bits) \
M_(NZCV, Flags_mask) \
\
/* FPCR */ \
V_(AHP, 26, 26, Bits) \
V_(DN, 25, 25, Bits) \
V_(FZ, 24, 24, Bits) \
V_(RMode, 23, 22, Bits) \
M_(FPCR, AHP_mask | DN_mask | FZ_mask | RMode_mask)
// Fields offsets.
#define DECLARE_FIELDS_OFFSETS(Name, HighBit, LowBit, X) \
const int Name##_offset = LowBit; \
const int Name##_width = HighBit - LowBit + 1; \
const int Name##_mask = ((1 << Name##_width) - 1) << LowBit;
FIELDS_LIST(DECLARE_FIELDS_OFFSETS)
const uint32_t Name##_mask = ((1 << Name##_width) - 1) << LowBit;
#define NOTHING(A, B)
INSTRUCTION_FIELDS_LIST(DECLARE_FIELDS_OFFSETS)
SYSTEM_REGISTER_FIELDS_LIST(DECLARE_FIELDS_OFFSETS, NOTHING)
#undef NOTHING
#undef DECLARE_FIELDS_BITS
// ImmPCRel is a compound field (not present in FIELDS_LIST), formed from
// ImmPCRelLo and ImmPCRelHi.
// ImmPCRel is a compound field (not present in INSTRUCTION_FIELDS_LIST), formed
// from ImmPCRelLo and ImmPCRelHi.
const int ImmPCRel_mask = ImmPCRelLo_mask | ImmPCRelHi_mask;
// Condition codes.
@ -150,11 +174,14 @@ enum Condition {
lt = 11,
gt = 12,
le = 13,
al = 14
al = 14,
nv = 15 // Behaves as always/al.
};
inline Condition InvertCondition(Condition cond) {
ASSERT(cond != al);
// Conditions al and nv behave identically, as "always true". They can't be
// inverted, because there is no "always false" condition.
ASSERT((cond != al) && (cond != nv));
return static_cast<Condition>(cond ^ 1);
}
@ -163,31 +190,32 @@ enum FlagsUpdate {
LeaveFlags = 0
};
const int N_offset = 31;
const int Z_offset = 30;
const int C_offset = 29;
const int V_offset = 28;
const int Flags_offset = V_offset;
enum StatusFlags {
NoFlag = 0,
VFlag = 0x10000000u,
CFlag = 0x20000000u,
CVFlag = 0x30000000u,
ZFlag = 0x40000000u,
ZVFlag = 0x50000000u,
ZCFlag = 0x60000000u,
ZCVFlag = 0x70000000u,
NFlag = 0x80000000u,
NVFlag = 0x90000000u,
NCFlag = 0xa0000000u,
NCVFlag = 0xb0000000u,
NZFlag = 0xc0000000u,
NZVFlag = 0xd0000000u,
NZCFlag = 0xe0000000u,
NZCVFlag = 0xf0000000u
// Derive the flag combinations from the system register bit descriptions.
NFlag = N_mask,
ZFlag = Z_mask,
CFlag = C_mask,
VFlag = V_mask,
NZFlag = NFlag | ZFlag,
NCFlag = NFlag | CFlag,
NVFlag = NFlag | VFlag,
ZCFlag = ZFlag | CFlag,
ZVFlag = ZFlag | VFlag,
CVFlag = CFlag | VFlag,
NZCFlag = NFlag | ZFlag | CFlag,
NZVFlag = NFlag | ZFlag | VFlag,
NCVFlag = NFlag | CFlag | VFlag,
ZCVFlag = ZFlag | CFlag | VFlag,
NZCVFlag = NFlag | ZFlag | CFlag | VFlag,
// Floating-point comparison results.
FPEqualFlag = ZCFlag,
FPLessThanFlag = NFlag,
FPGreaterThanFlag = CFlag,
FPUnorderedFlag = CVFlag
};
const unsigned int Flags_mask = NZCVFlag;
enum Shift {
NO_SHIFT = -1,
@ -220,10 +248,18 @@ enum SystemHint {
// System/special register names.
// This information is not encoded as one field but as the concatenation of
// multiple fields (lsb of Op0, Op1, Crn, Crm, Op2).
// multiple fields (Op0<0>, Op1, Crn, Crm, Op2).
enum SystemRegister {
NZCV = ((0xb << 16) | (0x4 << Cn_offset) | (0x2 << Cm_offset)) >>
ImmSystemRegister_offset
NZCV = ((0x1 << SysO0_offset) |
(0x3 << SysOp1_offset) |
(0x4 << CRn_offset) |
(0x2 << CRm_offset) |
(0x0 << SysOp2_offset)) >> ImmSystemRegister_offset,
FPCR = ((0x1 << SysO0_offset) |
(0x3 << SysOp1_offset) |
(0x4 << CRn_offset) |
(0x4 << CRm_offset) |
(0x0 << SysOp2_offset)) >> ImmSystemRegister_offset
};
// Instruction enumerations.
@ -516,7 +552,12 @@ enum ExceptionOp {
ExceptionMask = 0xFFE0001F,
HLT = ExceptionFixed | 0x00400000,
BRK = ExceptionFixed | 0x00200000,
SVC = ExceptionFixed | 0x00000001
SVC = ExceptionFixed | 0x00000001,
HVC = ExceptionFixed | 0x00000002,
SMC = ExceptionFixed | 0x00000003,
DCPS1 = ExceptionFixed | 0x00A00001,
DCPS2 = ExceptionFixed | 0x00A00002,
DCPS3 = ExceptionFixed | 0x00A00003
};
// Any load or store.
@ -766,24 +807,32 @@ enum DataProcessing2SourceOp {
DataProcessing2SourceFixed = 0x1AC00000,
DataProcessing2SourceFMask = 0x5FE00000,
DataProcessing2SourceMask = 0xFFE0FC00,
UDIV_w = DataProcessing2SourceFixed | 0x00000800,
UDIV_x = DataProcessing2SourceFixed | 0x80000800,
UDIV = UDIV_w,
SDIV_w = DataProcessing2SourceFixed | 0x00000C00,
SDIV_x = DataProcessing2SourceFixed | 0x80000C00,
SDIV = SDIV_w,
LSLV_w = DataProcessing2SourceFixed | 0x00002000,
LSLV_x = DataProcessing2SourceFixed | 0x80002000,
LSLV = LSLV_w,
LSRV_w = DataProcessing2SourceFixed | 0x00002400,
LSRV_x = DataProcessing2SourceFixed | 0x80002400,
LSRV = LSRV_w,
ASRV_w = DataProcessing2SourceFixed | 0x00002800,
ASRV_x = DataProcessing2SourceFixed | 0x80002800,
ASRV = ASRV_w,
RORV_w = DataProcessing2SourceFixed | 0x00002C00,
RORV_x = DataProcessing2SourceFixed | 0x80002C00,
RORV = RORV_w
UDIV_w = DataProcessing2SourceFixed | 0x00000800,
UDIV_x = DataProcessing2SourceFixed | 0x80000800,
UDIV = UDIV_w,
SDIV_w = DataProcessing2SourceFixed | 0x00000C00,
SDIV_x = DataProcessing2SourceFixed | 0x80000C00,
SDIV = SDIV_w,
LSLV_w = DataProcessing2SourceFixed | 0x00002000,
LSLV_x = DataProcessing2SourceFixed | 0x80002000,
LSLV = LSLV_w,
LSRV_w = DataProcessing2SourceFixed | 0x00002400,
LSRV_x = DataProcessing2SourceFixed | 0x80002400,
LSRV = LSRV_w,
ASRV_w = DataProcessing2SourceFixed | 0x00002800,
ASRV_x = DataProcessing2SourceFixed | 0x80002800,
ASRV = ASRV_w,
RORV_w = DataProcessing2SourceFixed | 0x00002C00,
RORV_x = DataProcessing2SourceFixed | 0x80002C00,
RORV = RORV_w,
CRC32B = DataProcessing2SourceFixed | 0x00004000,
CRC32H = DataProcessing2SourceFixed | 0x00004400,
CRC32W = DataProcessing2SourceFixed | 0x00004800,
CRC32X = DataProcessing2SourceFixed | SixtyFourBits | 0x00004C00,
CRC32CB = DataProcessing2SourceFixed | 0x00005000,
CRC32CH = DataProcessing2SourceFixed | 0x00005400,
CRC32CW = DataProcessing2SourceFixed | 0x00005800,
CRC32CX = DataProcessing2SourceFixed | SixtyFourBits | 0x00005C00
};
// Data processing 3 source.
@ -1038,11 +1087,18 @@ enum FPFixedPointConvertOp {
UCVTF_dx_fixed = UCVTF_fixed | SixtyFourBits | FP64
};
// Unknown instruction. These are defined to make fixed bit assertion easier.
enum UnknownOp {
UnknownFixed = 0x00000000,
UnknownFMask = 0x00000000
// Unimplemented and unallocated instructions. These are defined to make fixed
// bit assertion easier.
enum UnimplementedOp {
UnimplementedFixed = 0x00000000,
UnimplementedFMask = 0x00000000
};
enum UnallocatedOp {
UnallocatedFixed = 0x00000000,
UnallocatedFMask = 0x00000000
};
} // namespace vixl
#endif // VIXL_A64_CONSTANTS_A64_H_

View File

@ -440,7 +440,7 @@ const char* PrintCommand::kArguments = "<entity>";
const char* PrintCommand::kHelp =
" print the given entity\n"
" entity can be 'regs' for W and X registers, 'fpregs' for S and D\n"
" registers, 'flags' for CPU flags and 'pc'."
" registers, 'sysregs' for system registers (including NZCV) or 'pc'."
;
const char* MemCommand::kAliases[] = { "mem", "m", NULL };
@ -621,8 +621,8 @@ void Debugger::VisitException(Instruction* instr) {
}
void Debugger::LogFlags() {
if (log_parameters_ & LOG_FLAGS) PrintFlags();
void Debugger::LogSystemRegisters() {
if (log_parameters_ & LOG_SYS_REGS) PrintSystemRegisters();
}
@ -637,7 +637,7 @@ void Debugger::LogFPRegisters() {
void Debugger::LogProcessorState() {
LogFlags();
LogSystemRegisters();
LogRegisters();
LogFPRegisters();
}
@ -775,7 +775,7 @@ void Debugger::DoLog(Instruction* instr) {
// We don't support a one-shot LOG_DISASM.
ASSERT((parameters & LOG_DISASM) == 0);
// Print the requested information.
if (parameters & LOG_FLAGS) PrintFlags(true);
if (parameters & LOG_SYS_REGS) PrintSystemRegisters(true);
if (parameters & LOG_REGS) PrintRegisters(true);
if (parameters & LOG_FP_REGS) PrintFPRegisters(true);
@ -1322,8 +1322,8 @@ bool PrintCommand::Run(Debugger* debugger) {
debugger->PrintRegisters(true);
} else if (strcmp(identifier, "fpregs") == 0) {
debugger->PrintFPRegisters(true);
} else if (strcmp(identifier, "flags") == 0) {
debugger->PrintFlags(true);
} else if (strcmp(identifier, "sysregs") == 0) {
debugger->PrintSystemRegisters(true);
} else if (strcmp(identifier, "pc") == 0) {
printf("pc = %16p\n", reinterpret_cast<void*>(debugger->pc()));
} else {

View File

@ -87,10 +87,10 @@ enum TraceParameters {
LOG_DISASM = 1 << 0, // Log disassembly.
LOG_REGS = 1 << 1, // Log general purpose registers.
LOG_FP_REGS = 1 << 2, // Log floating-point registers.
LOG_FLAGS = 1 << 3, // Log the status flags.
LOG_SYS_REGS = 1 << 3, // Log the flags and system registers.
LOG_STATE = LOG_REGS | LOG_FP_REGS | LOG_FLAGS,
LOG_ALL = LOG_DISASM | LOG_REGS | LOG_FP_REGS | LOG_FLAGS
LOG_STATE = LOG_REGS | LOG_FP_REGS | LOG_SYS_REGS,
LOG_ALL = LOG_DISASM | LOG_REGS | LOG_FP_REGS | LOG_SYS_REGS
};
// Debugger parameters
@ -147,7 +147,7 @@ class Debugger : public Simulator {
inline bool pending_request() { return pending_request_; }
inline void update_pending_request() {
const int kLoggingMask = LOG_FLAGS | LOG_REGS | LOG_FP_REGS;
const int kLoggingMask = LOG_STATE;
const bool logging = (log_parameters_ & kLoggingMask) != 0;
const bool debugging = IsDebuggerRunning();
@ -160,7 +160,7 @@ class Debugger : public Simulator {
const FormatToken* format);
private:
void LogFlags();
void LogSystemRegisters();
void LogRegisters();
void LogFPRegisters();
void LogProcessorState();

View File

@ -31,71 +31,80 @@
namespace vixl {
// Top-level instruction decode function.
void Decoder::Decode(Instruction *instr) {
switch (instr->Bits(27, 24)) {
// 1: Add/sub immediate.
// A: Logical shifted register.
// Add/sub with carry.
// Conditional compare register.
// Conditional compare immediate.
// Conditional select.
// Data processing 1 source.
// Data processing 2 source.
// B: Add/sub shifted register.
// Add/sub extended register.
// Data processing 3 source.
case 0x1:
case 0xA:
case 0xB: DecodeDataProcessing(instr); break;
if (instr->Bits(28, 27) == 0) {
VisitUnallocated(instr);
} else {
switch (instr->Bits(27, 24)) {
// 0: PC relative addressing.
case 0x0: DecodePCRelAddressing(instr); break;
// 2: Logical immediate.
// Move wide immediate.
case 0x2: DecodeLogical(instr); break;
// 1: Add/sub immediate.
case 0x1: DecodeAddSubImmediate(instr); break;
// 3: Bitfield.
// Extract.
case 0x3: DecodeBitfieldExtract(instr); break;
// A: Logical shifted register.
// Add/sub with carry.
// Conditional compare register.
// Conditional compare immediate.
// Conditional select.
// Data processing 1 source.
// Data processing 2 source.
// B: Add/sub shifted register.
// Add/sub extended register.
// Data processing 3 source.
case 0xA:
case 0xB: DecodeDataProcessing(instr); break;
// 0: PC relative addressing.
// 4: Unconditional branch immediate.
// Exception generation.
// Compare and branch immediate.
// 5: Compare and branch immediate.
// Conditional branch.
// System.
// 6,7: Unconditional branch.
// Test and branch immediate.
case 0x0:
case 0x4:
case 0x5:
case 0x6:
case 0x7: DecodeBranchSystemException(instr); break;
// 2: Logical immediate.
// Move wide immediate.
case 0x2: DecodeLogical(instr); break;
// 8,9: Load/store register pair post-index.
// Load register literal.
// Load/store register unscaled immediate.
// Load/store register immediate post-index.
// Load/store register immediate pre-index.
// Load/store register offset.
// Load/store exclusive.
// C,D: Load/store register pair offset.
// Load/store register pair pre-index.
// Load/store register unsigned immediate.
case 0x8:
case 0x9:
case 0xC:
case 0xD: DecodeLoadStore(instr); break;
// 3: Bitfield.
// Extract.
case 0x3: DecodeBitfieldExtract(instr); break;
// E: FP fixed point conversion.
// FP integer conversion.
// FP data processing 1 source.
// FP compare.
// FP immediate.
// FP data processing 2 source.
// FP conditional compare.
// FP conditional select.
// F: FP data processing 3 source.
case 0xE:
case 0xF: DecodeFP(instr); break;
// 4: Unconditional branch immediate.
// Exception generation.
// Compare and branch immediate.
// 5: Compare and branch immediate.
// Conditional branch.
// System.
// 6,7: Unconditional branch.
// Test and branch immediate.
case 0x4:
case 0x5:
case 0x6:
case 0x7: DecodeBranchSystemException(instr); break;
// 8,9: Load/store register pair post-index.
// Load register literal.
// Load/store register unscaled immediate.
// Load/store register immediate post-index.
// Load/store register immediate pre-index.
// Load/store register offset.
// Load/store exclusive.
// C,D: Load/store register pair offset.
// Load/store register pair pre-index.
// Load/store register unsigned immediate.
// Advanced SIMD.
case 0x8:
case 0x9:
case 0xC:
case 0xD: DecodeLoadStore(instr); break;
// E: FP fixed point conversion.
// FP integer conversion.
// FP data processing 1 source.
// FP compare.
// FP immediate.
// FP data processing 2 source.
// FP conditional compare.
// FP conditional select.
// Advanced SIMD.
// F: FP data processing 3 source.
// Advanced SIMD.
case 0xE:
case 0xF: DecodeFP(instr); break;
}
}
}
@ -151,88 +160,117 @@ void Decoder::RemoveVisitor(DecoderVisitor* visitor) {
}
void Decoder::DecodeBranchSystemException(Instruction *instr) {
ASSERT((instr->Bits(27, 24) == 0x0) ||
(instr->Bits(27, 24) == 0x4) ||
void Decoder::DecodePCRelAddressing(Instruction* instr) {
ASSERT(instr->Bits(27, 24) == 0x0);
// We know bit 28 is set, as <b28:b27> = 0 is filtered out at the top level
// decode.
ASSERT(instr->Bit(28) == 0x1);
VisitPCRelAddressing(instr);
}
void Decoder::DecodeBranchSystemException(Instruction* instr) {
ASSERT((instr->Bits(27, 24) == 0x4) ||
(instr->Bits(27, 24) == 0x5) ||
(instr->Bits(27, 24) == 0x6) ||
(instr->Bits(27, 24) == 0x7) );
if (instr->Bit(26) == 0) {
VisitPCRelAddressing(instr);
} else {
switch (instr->Bits(31, 29)) {
case 0:
case 4: {
VisitUnconditionalBranch(instr);
break;
switch (instr->Bits(31, 29)) {
case 0:
case 4: {
VisitUnconditionalBranch(instr);
break;
}
case 1:
case 5: {
if (instr->Bit(25) == 0) {
VisitCompareBranch(instr);
} else {
VisitTestBranch(instr);
}
case 1:
case 5: {
if (instr->Bit(25) == 0) {
VisitCompareBranch(instr);
break;
}
case 2: {
if (instr->Bit(25) == 0) {
if ((instr->Bit(24) == 0x1) ||
(instr->Mask(0x01000010) == 0x00000010)) {
VisitUnallocated(instr);
} else {
VisitTestBranch(instr);
VisitConditionalBranch(instr);
}
break;
} else {
VisitUnallocated(instr);
}
case 2: {
UNALLOC(instr, SpacedBits(2, 24, 4) == 0x1);
UNALLOC(instr, Bit(24) == 0x1);
VisitConditionalBranch(instr);
break;
}
case 6: {
if (instr->Bit(25) == 0) {
if (instr->Bit(24) == 0) {
UNALLOC(instr, Bits(4, 2) != 0);
UNALLOC(instr, Mask(0x00E0001D) == 0x00200001);
UNALLOC(instr, Mask(0x00E0001E) == 0x00200002);
UNALLOC(instr, Mask(0x00E0001D) == 0x00400001);
UNALLOC(instr, Mask(0x00E0001E) == 0x00400002);
UNALLOC(instr, Mask(0x00E0001C) == 0x00600000);
UNALLOC(instr, Mask(0x00E0001C) == 0x00800000);
UNALLOC(instr, Mask(0x00E0001F) == 0x00A00000);
UNALLOC(instr, Mask(0x00C0001C) == 0x00C00000);
VisitException(instr);
break;
}
case 6: {
if (instr->Bit(25) == 0) {
if (instr->Bit(24) == 0) {
if ((instr->Bits(4, 2) != 0) ||
(instr->Mask(0x00E0001D) == 0x00200001) ||
(instr->Mask(0x00E0001D) == 0x00400001) ||
(instr->Mask(0x00E0001E) == 0x00200002) ||
(instr->Mask(0x00E0001E) == 0x00400002) ||
(instr->Mask(0x00E0001C) == 0x00600000) ||
(instr->Mask(0x00E0001C) == 0x00800000) ||
(instr->Mask(0x00E0001F) == 0x00A00000) ||
(instr->Mask(0x00C0001C) == 0x00C00000)) {
VisitUnallocated(instr);
} else {
UNALLOC(instr, Mask(0x0038E000) == 0x00000000);
UNALLOC(instr, Mask(0x0039E000) == 0x00002000);
UNALLOC(instr, Mask(0x003AE000) == 0x00002000);
UNALLOC(instr, Mask(0x003CE000) == 0x00042000);
UNALLOC(instr, Mask(0x003FFFC0) == 0x000320C0);
UNALLOC(instr, Mask(0x003FF100) == 0x00032100);
UNALLOC(instr, Mask(0x003FF200) == 0x00032200);
UNALLOC(instr, Mask(0x003FF400) == 0x00032400);
UNALLOC(instr, Mask(0x003FF800) == 0x00032800);
UNALLOC(instr, Mask(0x003FF0E0) == 0x00033000);
UNALLOC(instr, Mask(0x003FF0E0) == 0x003FF020);
UNALLOC(instr, Mask(0x003FF0E0) == 0x003FF060);
UNALLOC(instr, Mask(0x003FF0E0) == 0x003FF0E0);
UNALLOC(instr, Mask(0x0038F000) == 0x00005000);
UNALLOC(instr, Mask(0x0038E000) == 0x00006000);
UNALLOC(instr, SpacedBits(4, 21, 20, 19, 15) == 0x1);
UNALLOC(instr, Bits(21, 19) == 0x4);
VisitSystem(instr);
VisitException(instr);
}
} else {
UNALLOC(instr, Bits(20, 16) != 0x1F);
UNALLOC(instr, Bits(15, 10) != 0);
UNALLOC(instr, Bits(4, 0) != 0);
UNALLOC(instr, Bits(24, 21) == 0x3);
UNALLOC(instr, Bits(24, 22) == 0x3);
UNALLOC(instr, Bit(24) == 0x1);
if (instr->Bits(23, 22) == 0) {
const Instr masked_003FF0E0 = instr->Mask(0x003FF0E0);
if ((instr->Bits(21, 19) == 0x4) ||
(masked_003FF0E0 == 0x00033000) ||
(masked_003FF0E0 == 0x003FF020) ||
(masked_003FF0E0 == 0x003FF060) ||
(masked_003FF0E0 == 0x003FF0E0) ||
(instr->Mask(0x00388000) == 0x00008000) ||
(instr->Mask(0x0038E000) == 0x00000000) ||
(instr->Mask(0x0039E000) == 0x00002000) ||
(instr->Mask(0x003AE000) == 0x00002000) ||
(instr->Mask(0x003CE000) == 0x00042000) ||
(instr->Mask(0x003FFFC0) == 0x000320C0) ||
(instr->Mask(0x003FF100) == 0x00032100) ||
(instr->Mask(0x003FF200) == 0x00032200) ||
(instr->Mask(0x003FF400) == 0x00032400) ||
(instr->Mask(0x003FF800) == 0x00032800) ||
(instr->Mask(0x0038F000) == 0x00005000) ||
(instr->Mask(0x0038E000) == 0x00006000)) {
VisitUnallocated(instr);
} else {
VisitSystem(instr);
}
} else {
VisitUnallocated(instr);
}
}
} else {
if ((instr->Bit(24) == 0x1) ||
(instr->Bits(20, 16) != 0x1F) ||
(instr->Bits(15, 10) != 0) ||
(instr->Bits(4, 0) != 0) ||
(instr->Bits(24, 21) == 0x3) ||
(instr->Bits(24, 22) == 0x3)) {
VisitUnallocated(instr);
} else {
VisitUnconditionalBranchToRegister(instr);
}
break;
}
default: VisitUnknown(instr);
break;
}
case 3:
case 7: {
VisitUnallocated(instr);
break;
}
}
}
void Decoder::DecodeLoadStore(Instruction *instr) {
void Decoder::DecodeLoadStore(Instruction* instr) {
ASSERT((instr->Bits(27, 24) == 0x8) ||
(instr->Bits(27, 24) == 0x9) ||
(instr->Bits(27, 24) == 0xC) ||
@ -243,274 +281,424 @@ void Decoder::DecodeLoadStore(Instruction *instr) {
if (instr->Bit(29) == 0) {
if (instr->Bit(26) == 0) {
// TODO: VisitLoadStoreExclusive.
UNIMPLEMENTED();
VisitUnimplemented(instr);
} else {
// TODO: VisitLoadStoreAdvSIMD.
UNIMPLEMENTED();
DecodeAdvSIMDLoadStore(instr);
}
} else {
UNALLOC(instr, Bits(31, 30) == 0x3);
UNALLOC(instr, SpacedBits(4, 26, 31, 30, 22) == 0x2);
if (instr->Bit(23) == 0) {
UNALLOC(instr, SpacedBits(4, 26, 31, 30, 22) == 0x3);
VisitLoadStorePairNonTemporal(instr);
if ((instr->Bits(31, 30) == 0x3) ||
(instr->Mask(0xC4400000) == 0x40000000)) {
VisitUnallocated(instr);
} else {
VisitLoadStorePairPostIndex(instr);
if (instr->Bit(23) == 0) {
if (instr->Mask(0xC4400000) == 0xC0400000) {
VisitUnallocated(instr);
} else {
VisitLoadStorePairNonTemporal(instr);
}
} else {
VisitLoadStorePairPostIndex(instr);
}
}
}
} else {
if (instr->Bit(29) == 0) {
UNALLOC(instr, SpacedBits(3, 26, 31, 30) == 0x7);
VisitLoadLiteral(instr);
} else {
UNALLOC(instr, SpacedBits(4, 26, 23, 22, 31) == 0x7);
UNALLOC(instr, SpacedBits(3, 26, 23, 30) == 0x7);
UNALLOC(instr, SpacedBits(3, 26, 23, 31) == 0x7);
if (instr->Bit(21) == 0) {
switch (instr->Bits(11, 10)) {
case 0: {
VisitLoadStoreUnscaledOffset(instr);
break;
}
case 1: {
UNALLOC(instr, SpacedBits(5, 26, 23, 22, 31, 30) == 0xB);
VisitLoadStorePostIndex(instr);
break;
}
case 3: {
UNALLOC(instr, SpacedBits(5, 26, 23, 22, 31, 30) == 0xB);
VisitLoadStorePreIndex(instr);
break;
}
default: VisitUnknown(instr);
}
if (instr->Mask(0xC4000000) == 0xC4000000) {
VisitUnallocated(instr);
} else {
UNALLOC(instr, Bit(14) == 0);
VisitLoadStoreRegisterOffset(instr);
VisitLoadLiteral(instr);
}
} else {
if ((instr->Mask(0x84C00000) == 0x80C00000) ||
(instr->Mask(0x44800000) == 0x44800000) ||
(instr->Mask(0x84800000) == 0x84800000)) {
VisitUnallocated(instr);
} else {
if (instr->Bit(21) == 0) {
switch (instr->Bits(11, 10)) {
case 0: {
VisitLoadStoreUnscaledOffset(instr);
break;
}
case 1: {
if (instr->Mask(0xC4C00000) == 0xC0800000) {
VisitUnallocated(instr);
} else {
VisitLoadStorePostIndex(instr);
}
break;
}
case 2: {
// TODO: VisitLoadStoreRegisterOffsetUnpriv.
VisitUnimplemented(instr);
break;
}
case 3: {
if (instr->Mask(0xC4C00000) == 0xC0800000) {
VisitUnallocated(instr);
} else {
VisitLoadStorePreIndex(instr);
}
break;
}
}
} else {
if (instr->Bits(11, 10) == 0x2) {
if (instr->Bit(14) == 0) {
VisitUnallocated(instr);
} else {
VisitLoadStoreRegisterOffset(instr);
}
} else {
VisitUnallocated(instr);
}
}
}
}
}
} else {
if (instr->Bit(28) == 0) {
UNALLOC(instr, SpacedBits(4, 26, 31, 30, 22) == 0x2);
UNALLOC(instr, Bits(31, 30) == 0x3);
if (instr->Bit(23) == 0) {
VisitLoadStorePairOffset(instr);
if (instr->Bit(29) == 0) {
VisitUnallocated(instr);
} else {
VisitLoadStorePairPreIndex(instr);
if ((instr->Bits(31, 30) == 0x3) ||
(instr->Mask(0xC4400000) == 0x40000000)) {
VisitUnallocated(instr);
} else {
if (instr->Bit(23) == 0) {
VisitLoadStorePairOffset(instr);
} else {
VisitLoadStorePairPreIndex(instr);
}
}
}
} else {
UNALLOC(instr, SpacedBits(4, 26, 23, 22, 31) == 0x7);
UNALLOC(instr, SpacedBits(3, 26, 23, 30) == 0x7);
UNALLOC(instr, SpacedBits(3, 26, 23, 31) == 0x7);
VisitLoadStoreUnsignedOffset(instr);
if (instr->Bit(29) == 0) {
VisitUnallocated(instr);
} else {
if ((instr->Mask(0x84C00000) == 0x80C00000) ||
(instr->Mask(0x44800000) == 0x44800000) ||
(instr->Mask(0x84800000) == 0x84800000)) {
VisitUnallocated(instr);
} else {
VisitLoadStoreUnsignedOffset(instr);
}
}
}
}
}
void Decoder::DecodeLogical(Instruction *instr) {
void Decoder::DecodeLogical(Instruction* instr) {
ASSERT(instr->Bits(27, 24) == 0x2);
UNALLOC(instr, SpacedBits(2, 31, 22) == 0x1);
if (instr->Bit(23) == 0) {
VisitLogicalImmediate(instr);
if (instr->Mask(0x80400000) == 0x00400000) {
VisitUnallocated(instr);
} else {
UNALLOC(instr, Bits(30, 29) == 0x1);
VisitMoveWideImmediate(instr);
if (instr->Bit(23) == 0) {
VisitLogicalImmediate(instr);
} else {
if (instr->Bits(30, 29) == 0x1) {
VisitUnallocated(instr);
} else {
VisitMoveWideImmediate(instr);
}
}
}
}
void Decoder::DecodeBitfieldExtract(Instruction *instr) {
void Decoder::DecodeBitfieldExtract(Instruction* instr) {
ASSERT(instr->Bits(27, 24) == 0x3);
UNALLOC(instr, SpacedBits(2, 31, 22) == 0x2);
UNALLOC(instr, SpacedBits(2, 31, 22) == 0x1);
UNALLOC(instr, SpacedBits(2, 31, 15) == 0x1);
if (instr->Bit(23) == 0) {
UNALLOC(instr, SpacedBits(2, 31, 21) == 0x1);
UNALLOC(instr, Bits(30, 29) == 0x3);
VisitBitfield(instr);
if ((instr->Mask(0x80400000) == 0x80000000) ||
(instr->Mask(0x80400000) == 0x00400000) ||
(instr->Mask(0x80008000) == 0x00008000)) {
VisitUnallocated(instr);
} else if (instr->Bit(23) == 0) {
if ((instr->Mask(0x80200000) == 0x00200000) ||
(instr->Mask(0x60000000) == 0x60000000)) {
VisitUnallocated(instr);
} else {
VisitBitfield(instr);
}
} else {
UNALLOC(instr, SpacedBits(3, 30, 29, 21) == 0x1);
UNALLOC(instr, Bits(30, 29) != 0);
VisitExtract(instr);
if ((instr->Mask(0x60200000) == 0x00200000) ||
(instr->Mask(0x60000000) != 0x00000000)) {
VisitUnallocated(instr);
} else {
VisitExtract(instr);
}
}
}
void Decoder::DecodeDataProcessing(Instruction *instr) {
ASSERT((instr->Bits(27, 24) == 0x1) ||
(instr->Bits(27, 24) == 0xA) ||
void Decoder::DecodeAddSubImmediate(Instruction* instr) {
ASSERT(instr->Bits(27, 24) == 0x1);
if (instr->Bit(23) == 1) {
VisitUnallocated(instr);
} else {
VisitAddSubImmediate(instr);
}
}
void Decoder::DecodeDataProcessing(Instruction* instr) {
ASSERT((instr->Bits(27, 24) == 0xA) ||
(instr->Bits(27, 24) == 0xB) );
if (instr->Bit(27) == 0) {
UNALLOC(instr, Bit(23) == 0x1);
VisitAddSubImmediate(instr);
} else if (instr->Bit(24) == 0) {
if (instr->Bit(24) == 0) {
if (instr->Bit(28) == 0) {
UNALLOC(instr, SpacedBits(2, 31, 15) == 0x1);
VisitLogicalShifted(instr);
if (instr->Mask(0x80008000) == 0x00008000) {
VisitUnallocated(instr);
} else {
VisitLogicalShifted(instr);
}
} else {
switch (instr->Bits(23, 21)) {
case 0: {
UNALLOC(instr, Bits(15, 10) != 0);
VisitAddSubWithCarry(instr);
if (instr->Mask(0x0000FC00) != 0) {
VisitUnallocated(instr);
} else {
VisitAddSubWithCarry(instr);
}
break;
}
case 2: {
UNALLOC(instr, SpacedBits(2, 10, 4) != 0);
UNALLOC(instr, Bit(29) == 0x0);
if (instr->Bit(11) == 0) {
VisitConditionalCompareRegister(instr);
if ((instr->Bit(29) == 0) ||
(instr->Mask(0x00000410) != 0)) {
VisitUnallocated(instr);
} else {
VisitConditionalCompareImmediate(instr);
if (instr->Bit(11) == 0) {
VisitConditionalCompareRegister(instr);
} else {
VisitConditionalCompareImmediate(instr);
}
}
break;
}
case 4: {
UNALLOC(instr, SpacedBits(2, 11, 29) != 0);
VisitConditionalSelect(instr);
break;
}
case 6: {
UNALLOC(instr, Bit(29) == 1);
UNALLOC(instr, Bits(15, 14) != 0);
if (instr->Bit(30) == 0) {
UNALLOC(instr, Bits(15, 11) == 0);
UNALLOC(instr, Bits(15, 12) == 0x1);
UNALLOC(instr, Bits(15, 12) == 0x3);
VisitDataProcessing2Source(instr);
if (instr->Mask(0x20000800) != 0x00000000) {
VisitUnallocated(instr);
} else {
UNALLOC(instr, Bit(13) == 1);
UNALLOC(instr, Bits(20, 16) != 0);
UNALLOC(instr, Mask(0xA01FFC00) == 0x00000C00);
UNALLOC(instr, Mask(0x201FF800) == 0x00001800);
VisitDataProcessing1Source(instr);
VisitConditionalSelect(instr);
}
break;
}
default: VisitUnknown(instr);
case 6: {
if (instr->Bit(29) == 0x1) {
VisitUnallocated(instr);
} else {
if (instr->Bit(30) == 0) {
if ((instr->Bit(15) == 0x1) ||
(instr->Bits(15, 11) == 0) ||
(instr->Bits(15, 12) == 0x1) ||
(instr->Bits(15, 12) == 0x3) ||
(instr->Bits(15, 13) == 0x3) ||
(instr->Mask(0x8000EC00) == 0x00004C00) ||
(instr->Mask(0x8000E800) == 0x80004000) ||
(instr->Mask(0x8000E400) == 0x80004000)) {
VisitUnallocated(instr);
} else {
VisitDataProcessing2Source(instr);
}
} else {
if ((instr->Bit(13) == 1) ||
(instr->Bits(20, 16) != 0) ||
(instr->Bits(15, 14) != 0) ||
(instr->Mask(0xA01FFC00) == 0x00000C00) ||
(instr->Mask(0x201FF800) == 0x00001800)) {
VisitUnallocated(instr);
} else {
VisitDataProcessing1Source(instr);
}
}
break;
}
}
case 1:
case 3:
case 5:
case 7: VisitUnallocated(instr); break;
}
}
} else {
if (instr->Bit(28) == 0) {
if (instr->Bit(21) == 0) {
UNALLOC(instr, Bits(23, 22) == 0x3);
UNALLOC(instr, SpacedBits(2, 31, 15) == 0x1);
VisitAddSubShifted(instr);
if (instr->Bit(21) == 0) {
if ((instr->Bits(23, 22) == 0x3) ||
(instr->Mask(0x80008000) == 0x00008000)) {
VisitUnallocated(instr);
} else {
VisitAddSubShifted(instr);
}
} else {
UNALLOC(instr, SpacedBits(2, 23, 22) != 0);
UNALLOC(instr, SpacedBits(2, 12, 10) == 0x3);
UNALLOC(instr, Bits(12, 11) == 0x3);
VisitAddSubExtended(instr);
if ((instr->Mask(0x00C00000) != 0x00000000) ||
(instr->Mask(0x00001400) == 0x00001400) ||
(instr->Mask(0x00001800) == 0x00001800)) {
VisitUnallocated(instr);
} else {
VisitAddSubExtended(instr);
}
}
} else {
UNALLOC(instr, Mask(0xE0E08000) == 0x00200000);
UNALLOC(instr, Mask(0xE0E08000) == 0x00208000);
UNALLOC(instr, Mask(0xE0E08000) == 0x00400000);
UNALLOC(instr, Mask(0x60E08000) == 0x00408000);
UNALLOC(instr, SpacedBits(5, 30, 29, 23, 22, 21) == 0x3);
UNALLOC(instr, SpacedBits(5, 30, 29, 23, 22, 21) == 0x4);
UNALLOC(instr, Mask(0xE0E08000) == 0x00A00000);
UNALLOC(instr, Mask(0xE0E08000) == 0x00A08000);
UNALLOC(instr, Mask(0xE0E08000) == 0x00C00000);
UNALLOC(instr, Mask(0x60E08000) == 0x00C08000);
UNALLOC(instr, SpacedBits(5, 30, 29, 23, 22, 21) == 0x7);
UNALLOC(instr, Bits(30, 29) == 0x1);
UNALLOC(instr, Bit(30) == 0x1);
VisitDataProcessing3Source(instr);
if ((instr->Bit(30) == 0x1) ||
(instr->Bits(30, 29) == 0x1) ||
(instr->Mask(0xE0600000) == 0x00200000) ||
(instr->Mask(0xE0608000) == 0x00400000) ||
(instr->Mask(0x60608000) == 0x00408000) ||
(instr->Mask(0x60E00000) == 0x00E00000) ||
(instr->Mask(0x60E00000) == 0x00800000) ||
(instr->Mask(0x60E00000) == 0x00600000)) {
VisitUnallocated(instr);
} else {
VisitDataProcessing3Source(instr);
}
}
}
}
void Decoder::DecodeFP(Instruction *instr) {
void Decoder::DecodeFP(Instruction* instr) {
ASSERT((instr->Bits(27, 24) == 0xE) ||
(instr->Bits(27, 24) == 0xF) );
UNALLOC(instr, Bit(29) == 0x1);
if (instr->Bit(24) == 0) {
if (instr->Bit(21) == 0) {
UNALLOC(instr, Bit(23) == 1);
UNALLOC(instr, SpacedBits(2, 31, 15) == 0);
UNALLOC(instr, SpacedBits(3, 18, 17, 19) == 0);
UNALLOC(instr, SpacedBits(3, 18, 17, 20) == 0);
UNALLOC(instr, SpacedBits(3, 18, 17, 19) == 0x3);
UNALLOC(instr, SpacedBits(3, 18, 17, 20) == 0x3);
UNALLOC(instr, Bit(18) == 1);
VisitFPFixedPointConvert(instr);
if (instr->Bit(28) == 0) {
DecodeAdvSIMDDataProcessing(instr);
} else {
if (instr->Bit(29) == 1) {
VisitUnallocated(instr);
} else {
if (instr->Bits(15, 10) == 0) {
UNALLOC(instr, SpacedBits(3, 18, 17, 19) == 0x3);
UNALLOC(instr, SpacedBits(3, 18, 17, 20) == 0x3);
UNALLOC(instr, SpacedBits(3, 18, 17, 19) == 0x5);
UNALLOC(instr, SpacedBits(3, 18, 17, 20) == 0x5);
UNALLOC(instr, Mask(0xA0C60000) == 0x80060000);
UNALLOC(instr, Mask(0xA0CE0000) == 0x000E0000);
UNALLOC(instr, Mask(0xA0D60000) == 0x00160000);
UNALLOC(instr, Mask(0xA0C60000) == 0x00460000);
UNALLOC(instr, Mask(0xA0CE0000) == 0x804E0000);
UNALLOC(instr, Mask(0xA0D60000) == 0x80560000);
UNALLOC(instr, SpacedBits(4, 23, 22, 18, 29) == 0x8);
UNALLOC(instr, SpacedBits(5, 23, 22, 18, 17, 29) == 0x14);
UNALLOC(instr, Mask(0xA0C60000) == 0x00860000);
UNALLOC(instr, Mask(0xA0CE0000) == 0x80860000);
UNALLOC(instr, Mask(0xA0D60000) == 0x80960000);
UNALLOC(instr, Bits(23, 22) == 0x3);
VisitFPIntegerConvert(instr);
} else if (instr->Bits(14, 10) == 16) {
UNALLOC(instr, SpacedBits(3, 31, 19, 20) != 0);
UNALLOC(instr, Mask(0xA0DF8000) == 0x00020000);
UNALLOC(instr, Mask(0xA0DF8000) == 0x00030000);
UNALLOC(instr, Mask(0xA0DF8000) == 0x00068000);
UNALLOC(instr, Mask(0xA0DF8000) == 0x00428000);
UNALLOC(instr, Mask(0xA0DF8000) == 0x00430000);
UNALLOC(instr, Mask(0xA0DF8000) == 0x00468000);
UNALLOC(instr, Mask(0xA0D80000) == 0x00800000);
UNALLOC(instr, Mask(0xA0DE0000) == 0x00C00000);
UNALLOC(instr, Mask(0xA0DF0000) == 0x00C30000);
UNALLOC(instr, Mask(0xA0DC0000) == 0x00C40000);
VisitFPDataProcessing1Source(instr);
} else if (instr->Bits(13, 10) == 8) {
UNALLOC(instr, SpacedBits(2, 31, 23) != 0);
UNALLOC(instr, Bits(2, 0) != 0);
UNALLOC(instr, Bits(15, 14) != 0);
VisitFPCompare(instr);
} else if (instr->Bits(12, 10) == 4) {
UNALLOC(instr, Bits(9, 5) != 0);
UNALLOC(instr, SpacedBits(2, 31, 23) != 0);
VisitFPImmediate(instr);
if (instr->Bits(31, 30) == 0x3) {
VisitUnallocated(instr);
} else if (instr->Bits(31, 30) == 0x1) {
DecodeAdvSIMDDataProcessing(instr);
} else {
UNALLOC(instr, SpacedBits(2, 31, 23) != 0);
switch (instr->Bits(11, 10)) {
case 1: {
VisitFPConditionalCompare(instr);
break;
if (instr->Bit(24) == 0) {
if (instr->Bit(21) == 0) {
if ((instr->Bit(23) == 1) ||
(instr->Bit(18) == 1) ||
(instr->Mask(0x80008000) == 0x00000000) ||
(instr->Mask(0x000E0000) == 0x00000000) ||
(instr->Mask(0x000E0000) == 0x000A0000) ||
(instr->Mask(0x00160000) == 0x00000000) ||
(instr->Mask(0x00160000) == 0x00120000)) {
VisitUnallocated(instr);
} else {
VisitFPFixedPointConvert(instr);
}
} else {
if (instr->Bits(15, 10) == 32) {
VisitUnallocated(instr);
} else if (instr->Bits(15, 10) == 0) {
if ((instr->Bits(23, 22) == 0x3) ||
(instr->Mask(0x000E0000) == 0x000A0000) ||
(instr->Mask(0x000E0000) == 0x000C0000) ||
(instr->Mask(0x00160000) == 0x00120000) ||
(instr->Mask(0x00160000) == 0x00140000) ||
(instr->Mask(0x20C40000) == 0x00800000) ||
(instr->Mask(0x20C60000) == 0x00840000) ||
(instr->Mask(0xA0C60000) == 0x80060000) ||
(instr->Mask(0xA0C60000) == 0x00860000) ||
(instr->Mask(0xA0C60000) == 0x00460000) ||
(instr->Mask(0xA0CE0000) == 0x80860000) ||
(instr->Mask(0xA0CE0000) == 0x804E0000) ||
(instr->Mask(0xA0CE0000) == 0x000E0000) ||
(instr->Mask(0xA0D60000) == 0x00160000) ||
(instr->Mask(0xA0D60000) == 0x80560000) ||
(instr->Mask(0xA0D60000) == 0x80960000)) {
VisitUnallocated(instr);
} else {
VisitFPIntegerConvert(instr);
}
} else if (instr->Bits(14, 10) == 16) {
const Instr masked_A0DF8000 = instr->Mask(0xA0DF8000);
if ((instr->Mask(0x80180000) != 0) ||
(masked_A0DF8000 == 0x00020000) ||
(masked_A0DF8000 == 0x00030000) ||
(masked_A0DF8000 == 0x00068000) ||
(masked_A0DF8000 == 0x00428000) ||
(masked_A0DF8000 == 0x00430000) ||
(masked_A0DF8000 == 0x00468000) ||
(instr->Mask(0xA0D80000) == 0x00800000) ||
(instr->Mask(0xA0DE0000) == 0x00C00000) ||
(instr->Mask(0xA0DF0000) == 0x00C30000) ||
(instr->Mask(0xA0DC0000) == 0x00C40000)) {
VisitUnallocated(instr);
} else {
VisitFPDataProcessing1Source(instr);
}
} else if (instr->Bits(13, 10) == 8) {
if ((instr->Bits(15, 14) != 0) ||
(instr->Bits(2, 0) != 0) ||
(instr->Mask(0x80800000) != 0x00000000)) {
VisitUnallocated(instr);
} else {
VisitFPCompare(instr);
}
} else if (instr->Bits(12, 10) == 4) {
if ((instr->Bits(9, 5) != 0) ||
(instr->Mask(0x80800000) != 0x00000000)) {
VisitUnallocated(instr);
} else {
VisitFPImmediate(instr);
}
} else {
if (instr->Mask(0x80800000) != 0x00000000) {
VisitUnallocated(instr);
} else {
switch (instr->Bits(11, 10)) {
case 1: {
VisitFPConditionalCompare(instr);
break;
}
case 2: {
if ((instr->Bits(15, 14) == 0x3) ||
(instr->Mask(0x00009000) == 0x00009000) ||
(instr->Mask(0x0000A000) == 0x0000A000)) {
VisitUnallocated(instr);
} else {
VisitFPDataProcessing2Source(instr);
}
break;
}
case 3: {
VisitFPConditionalSelect(instr);
break;
}
default: UNREACHABLE();
}
}
}
}
case 2: {
UNALLOC(instr, SpacedBits(2, 15, 12) == 0x3);
UNALLOC(instr, SpacedBits(2, 15, 13) == 0x3);
UNALLOC(instr, Bits(15, 14) == 0x3);
VisitFPDataProcessing2Source(instr);
break;
} else {
// Bit 30 == 1 has been handled earlier.
ASSERT(instr->Bit(30) == 0);
if (instr->Mask(0xA0800000) != 0) {
VisitUnallocated(instr);
} else {
VisitFPDataProcessing3Source(instr);
}
case 3: {
VisitFPConditionalSelect(instr);
break;
}
default: VisitUnknown(instr);
}
}
}
} else {
UNALLOC(instr, Bit(31) == 0x1);
UNALLOC(instr, Bit(23) == 0x1);
VisitFPDataProcessing3Source(instr);
}
}
void Decoder::DecodeAdvSIMDLoadStore(Instruction* instr) {
// TODO: Implement Advanced SIMD load/store instruction decode.
ASSERT(instr->Bits(29, 25) == 0x6);
VisitUnimplemented(instr);
}
void Decoder::DecodeAdvSIMDDataProcessing(Instruction* instr) {
// TODO: Implement Advanced SIMD data processing instruction decode.
ASSERT(instr->Bits(27, 25) == 0x7);
VisitUnimplemented(instr);
}
#define DEFINE_VISITOR_CALLERS(A) \
void Decoder::Visit##A(Instruction *instr) { \
ASSERT(instr->Mask(A##FMask) == A##Fixed); \

View File

@ -33,17 +33,6 @@
#include "a64/instructions-a64.h"
#ifdef DEBUG
#define UNALLOC(I, P) \
if (I->P) { \
printf("Instruction 0x%08" PRIx32 " uses an unallocated encoding.\n", \
I->InstructionBits()); \
} \
ASSERT(!(I->P));
#else
#define UNALLOC(I, P) ((void) 0)
#endif
// List macro containing all visitors needed by the decoder class.
#define VISITOR_LIST(V) \
@ -89,7 +78,8 @@
V(FPDataProcessing3Source) \
V(FPIntegerConvert) \
V(FPFixedPointConvert) \
V(Unknown)
V(Unallocated) \
V(Unimplemented)
namespace vixl {
@ -146,42 +136,62 @@ class Decoder: public DecoderVisitor {
// Remove a previously registered visitor class from the list of visitors
// stored by the decoder.
void RemoveVisitor(DecoderVisitor *visitor);
void RemoveVisitor(DecoderVisitor* visitor);
#define DECLARE(A) void Visit##A(Instruction* instr);
VISITOR_LIST(DECLARE)
#undef DECLARE
private:
// Decode the PC relative addressing instruction, and call the corresponding
// visitors.
// On entry, instruction bits 27:24 = 0x0.
void DecodePCRelAddressing(Instruction* instr);
// Decode the add/subtract immediate instruction, and call the correspoding
// visitors.
// On entry, instruction bits 27:24 = 0x1.
void DecodeAddSubImmediate(Instruction* instr);
// Decode the branch, system command, and exception generation parts of
// the instruction tree, and call the corresponding visitors.
// On entry, instruction bits 27:24 = {0x0, 0x4, 0x5, 0x6, 0x7}.
void DecodeBranchSystemException(Instruction *instr);
// On entry, instruction bits 27:24 = {0x4, 0x5, 0x6, 0x7}.
void DecodeBranchSystemException(Instruction* instr);
// Decode the load and store parts of the instruction tree, and call
// the corresponding visitors.
// On entry, instruction bits 27:24 = {0x8, 0x9, 0xC, 0xD}.
void DecodeLoadStore(Instruction *instr);
void DecodeLoadStore(Instruction* instr);
// Decode the logical immediate and move wide immediate parts of the
// instruction tree, and call the corresponding visitors.
// On entry, instruction bits 27:24 = 0x2.
void DecodeLogical(Instruction *instr);
void DecodeLogical(Instruction* instr);
// Decode the bitfield and extraction parts of the instruction tree,
// and call the corresponding visitors.
// On entry, instruction bits 27:24 = 0x3.
void DecodeBitfieldExtract(Instruction *instr);
void DecodeBitfieldExtract(Instruction* instr);
// Decode the data processing parts of the instruction tree, and call the
// corresponding visitors.
// On entry, instruction bits 27:24 = {0x1, 0xA, 0xB}.
void DecodeDataProcessing(Instruction *instr);
void DecodeDataProcessing(Instruction* instr);
// Decode the floating point parts of the instruction tree, and call the
// corresponding visitors.
// On entry, instruction bits 27:24 = {0xE, 0xF}.
void DecodeFP(Instruction *instr);
void DecodeFP(Instruction* instr);
// Decode the Advanced SIMD (NEON) load/store part of the instruction tree,
// and call the corresponding visitors.
// On entry, instruction bits 29:25 = 0x6.
void DecodeAdvSIMDLoadStore(Instruction* instr);
// Decode the Advanced SIMD (NEON) data processing part of the instruction
// tree, and call the corresponding visitors.
// On entry, instruction bits 27:25 = 0x7.
void DecodeAdvSIMDDataProcessing(Instruction* instr);
};
} // namespace vixl

View File

@ -60,7 +60,7 @@ void Disassembler::VisitAddSubImmediate(Instruction* instr) {
bool rd_is_zr = RdIsZROrSP(instr);
bool stack_op = (rd_is_zr || RnIsZROrSP(instr)) &&
(instr->ImmAddSub() == 0) ? true : false;
const char *mnemonic = "unknown";
const char *mnemonic = "";
const char *form = "'Rds, 'Rns, 'IAddSub";
const char *form_cmp = "'Rns, 'IAddSub";
const char *form_mov = "'Rds, 'Rns";
@ -95,7 +95,7 @@ void Disassembler::VisitAddSubImmediate(Instruction* instr) {
}
break;
}
default: form = "(AddSubImmediate)";
default: UNREACHABLE();
}
Format(instr, mnemonic, form);
}
@ -104,7 +104,7 @@ void Disassembler::VisitAddSubImmediate(Instruction* instr) {
void Disassembler::VisitAddSubShifted(Instruction* instr) {
bool rd_is_zr = RdIsZROrSP(instr);
bool rn_is_zr = RnIsZROrSP(instr);
const char *mnemonic = "unknown";
const char *mnemonic = "";
const char *form = "'Rd, 'Rn, 'Rm'HDP";
const char *form_cmp = "'Rn, 'Rm'HDP";
const char *form_neg = "'Rd, 'Rm'HDP";
@ -142,7 +142,7 @@ void Disassembler::VisitAddSubShifted(Instruction* instr) {
}
break;
}
default: form = "(AddSubShifted)";
default: UNREACHABLE();
}
Format(instr, mnemonic, form);
}
@ -150,7 +150,7 @@ void Disassembler::VisitAddSubShifted(Instruction* instr) {
void Disassembler::VisitAddSubExtended(Instruction* instr) {
bool rd_is_zr = RdIsZROrSP(instr);
const char *mnemonic = "unknown";
const char *mnemonic = "";
Extend mode = static_cast<Extend>(instr->ExtendMode());
const char *form = ((mode == UXTX) || (mode == SXTX)) ?
"'Rds, 'Rns, 'Xm'Ext" : "'Rds, 'Rns, 'Wm'Ext";
@ -180,7 +180,7 @@ void Disassembler::VisitAddSubExtended(Instruction* instr) {
}
break;
}
default: form = "(AddSubExtended)";
default: UNREACHABLE();
}
Format(instr, mnemonic, form);
}
@ -188,7 +188,7 @@ void Disassembler::VisitAddSubExtended(Instruction* instr) {
void Disassembler::VisitAddSubWithCarry(Instruction* instr) {
bool rn_is_zr = RnIsZROrSP(instr);
const char *mnemonic = "unknown";
const char *mnemonic = "";
const char *form = "'Rd, 'Rn, 'Rm";
const char *form_neg = "'Rd, 'Rm";
@ -215,7 +215,7 @@ void Disassembler::VisitAddSubWithCarry(Instruction* instr) {
}
break;
}
default: form = "(AddSubWithCarry)";
default: UNREACHABLE();
}
Format(instr, mnemonic, form);
}
@ -224,9 +224,15 @@ void Disassembler::VisitAddSubWithCarry(Instruction* instr) {
void Disassembler::VisitLogicalImmediate(Instruction* instr) {
bool rd_is_zr = RdIsZROrSP(instr);
bool rn_is_zr = RnIsZROrSP(instr);
const char *mnemonic = "unknown";
const char *mnemonic = "";
const char *form = "'Rds, 'Rn, 'ITri";
if (instr->ImmLogical() == 0) {
// The immediate encoded in the instruction is not in the expected format.
Format(instr, "unallocated", "(LogicalImmediate)");
return;
}
switch (instr->Mask(LogicalImmediateMask)) {
case AND_w_imm:
case AND_x_imm: mnemonic = "and"; break;
@ -252,7 +258,7 @@ void Disassembler::VisitLogicalImmediate(Instruction* instr) {
}
break;
}
default: form = "(LogicalImmediate)";
default: UNREACHABLE();
}
Format(instr, mnemonic, form);
}
@ -290,7 +296,7 @@ bool Disassembler::IsMovzMovnImm(unsigned reg_size, uint64_t value) {
void Disassembler::VisitLogicalShifted(Instruction* instr) {
bool rd_is_zr = RdIsZROrSP(instr);
bool rn_is_zr = RnIsZROrSP(instr);
const char *mnemonic = "unknown";
const char *mnemonic = "";
const char *form = "'Rd, 'Rn, 'Rm'HLo";
switch (instr->Mask(LogicalShiftedMask)) {
@ -331,7 +337,7 @@ void Disassembler::VisitLogicalShifted(Instruction* instr) {
}
break;
}
default: form = "(LogicalShifted)";
default: UNREACHABLE();
}
Format(instr, mnemonic, form);
@ -339,7 +345,7 @@ void Disassembler::VisitLogicalShifted(Instruction* instr) {
void Disassembler::VisitConditionalCompareRegister(Instruction* instr) {
const char *mnemonic = "unknown";
const char *mnemonic = "";
const char *form = "'Rn, 'Rm, 'INzcv, 'Cond";
switch (instr->Mask(ConditionalCompareRegisterMask)) {
@ -347,14 +353,14 @@ void Disassembler::VisitConditionalCompareRegister(Instruction* instr) {
case CCMN_x: mnemonic = "ccmn"; break;
case CCMP_w:
case CCMP_x: mnemonic = "ccmp"; break;
default: form = "(ConditionalCompareRegister)";
default: UNREACHABLE();
}
Format(instr, mnemonic, form);
}
void Disassembler::VisitConditionalCompareImmediate(Instruction* instr) {
const char *mnemonic = "unknown";
const char *mnemonic = "";
const char *form = "'Rn, 'IP, 'INzcv, 'Cond";
switch (instr->Mask(ConditionalCompareImmediateMask)) {
@ -362,7 +368,7 @@ void Disassembler::VisitConditionalCompareImmediate(Instruction* instr) {
case CCMN_x_imm: mnemonic = "ccmn"; break;
case CCMP_w_imm:
case CCMP_x_imm: mnemonic = "ccmp"; break;
default: form = "(ConditionalCompareImmediate)";
default: UNREACHABLE();
}
Format(instr, mnemonic, form);
}
@ -371,21 +377,24 @@ void Disassembler::VisitConditionalCompareImmediate(Instruction* instr) {
void Disassembler::VisitConditionalSelect(Instruction* instr) {
bool rnm_is_zr = (RnIsZROrSP(instr) && RmIsZROrSP(instr));
bool rn_is_rm = (instr->Rn() == instr->Rm());
const char *mnemonic = "unknown";
const char *mnemonic = "";
const char *form = "'Rd, 'Rn, 'Rm, 'Cond";
const char *form_test = "'Rd, 'CInv";
const char *form_update = "'Rd, 'Rn, 'CInv";
Condition cond = static_cast<Condition>(instr->Condition());
bool invertible_cond = (cond != al) && (cond != nv);
switch (instr->Mask(ConditionalSelectMask)) {
case CSEL_w:
case CSEL_x: mnemonic = "csel"; break;
case CSINC_w:
case CSINC_x: {
mnemonic = "csinc";
if (rnm_is_zr) {
if (rnm_is_zr && invertible_cond) {
mnemonic = "cset";
form = form_test;
} else if (rn_is_rm) {
} else if (rn_is_rm && invertible_cond) {
mnemonic = "cinc";
form = form_update;
}
@ -394,10 +403,10 @@ void Disassembler::VisitConditionalSelect(Instruction* instr) {
case CSINV_w:
case CSINV_x: {
mnemonic = "csinv";
if (rnm_is_zr) {
if (rnm_is_zr && invertible_cond) {
mnemonic = "csetm";
form = form_test;
} else if (rn_is_rm) {
} else if (rn_is_rm && invertible_cond) {
mnemonic = "cinv";
form = form_update;
}
@ -406,13 +415,13 @@ void Disassembler::VisitConditionalSelect(Instruction* instr) {
case CSNEG_w:
case CSNEG_x: {
mnemonic = "csneg";
if (rn_is_rm) {
if (rn_is_rm && invertible_cond) {
mnemonic = "cneg";
form = form_update;
}
break;
}
default: form = "(ConditionalSelect)";
default: UNREACHABLE();
}
Format(instr, mnemonic, form);
}
@ -423,8 +432,8 @@ void Disassembler::VisitBitfield(Instruction* instr) {
unsigned r = instr->ImmR();
unsigned rd_size_minus_1 =
((instr->SixtyFourBits() == 1) ? kXRegSize : kWRegSize) - 1;
const char *mnemonic = "unknown";
const char *form = "(Bitfield)";
const char *mnemonic = "";
const char *form = "";
const char *form_shift_right = "'Rd, 'Rn, 'IBr";
const char *form_extend = "'Rd, 'Wn";
const char *form_bfiz = "'Rd, 'Rn, 'IBZ-r, 'IBs+1";
@ -497,7 +506,7 @@ void Disassembler::VisitBitfield(Instruction* instr) {
void Disassembler::VisitExtract(Instruction* instr) {
const char *mnemonic = "unknown";
const char *mnemonic = "";
const char *form = "'Rd, 'Rn, 'Rm, 'IExtract";
switch (instr->Mask(ExtractMask)) {
@ -511,7 +520,7 @@ void Disassembler::VisitExtract(Instruction* instr) {
}
break;
}
default: form = "(Extract)";
default: UNREACHABLE();
}
Format(instr, mnemonic, form);
}
@ -521,7 +530,7 @@ void Disassembler::VisitPCRelAddressing(Instruction* instr) {
switch (instr->Mask(PCRelAddressingMask)) {
case ADR: Format(instr, "adr", "'Xd, 'AddrPCRelByte"); break;
// ADRP is not implemented.
default: Format(instr, "unknown", "(PCRelAddressing)");
default: Format(instr, "unimplemented", "(PCRelAddressing)");
}
}
@ -529,13 +538,13 @@ void Disassembler::VisitPCRelAddressing(Instruction* instr) {
void Disassembler::VisitConditionalBranch(Instruction* instr) {
switch (instr->Mask(ConditionalBranchMask)) {
case B_cond: Format(instr, "b.'CBrn", "'BImmCond"); break;
default: Format(instr, "unknown", "(ConditionalBranch)");
default: UNREACHABLE();
}
}
void Disassembler::VisitUnconditionalBranchToRegister(Instruction* instr) {
const char *mnemonic = "unknown";
const char *mnemonic = "unimplemented";
const char *form = "'Xn";
switch (instr->Mask(UnconditionalBranchToRegisterMask)) {
@ -555,20 +564,20 @@ void Disassembler::VisitUnconditionalBranchToRegister(Instruction* instr) {
void Disassembler::VisitUnconditionalBranch(Instruction* instr) {
const char *mnemonic = "unknown";
const char *mnemonic = "";
const char *form = "'BImmUncn";
switch (instr->Mask(UnconditionalBranchMask)) {
case B: mnemonic = "b"; break;
case BL: mnemonic = "bl"; break;
default: form = "(UnconditionalBranch)";
default: UNREACHABLE();
}
Format(instr, mnemonic, form);
}
void Disassembler::VisitDataProcessing1Source(Instruction* instr) {
const char *mnemonic = "unknown";
const char *mnemonic = "";
const char *form = "'Rd, 'Rn";
switch (instr->Mask(DataProcessing1SourceMask)) {
@ -582,14 +591,14 @@ void Disassembler::VisitDataProcessing1Source(Instruction* instr) {
FORMAT(CLS, "cls");
#undef FORMAT
case REV32_x: mnemonic = "rev32"; break;
default: form = "(DataProcessing1Source)";
default: UNREACHABLE();
}
Format(instr, mnemonic, form);
}
void Disassembler::VisitDataProcessing2Source(Instruction* instr) {
const char *mnemonic = "unknown";
const char *mnemonic = "unimplemented";
const char *form = "'Rd, 'Rn, 'Rm";
switch (instr->Mask(DataProcessing2SourceMask)) {
@ -611,7 +620,7 @@ void Disassembler::VisitDataProcessing2Source(Instruction* instr) {
void Disassembler::VisitDataProcessing3Source(Instruction* instr) {
bool ra_is_zr = RaIsZROrSP(instr);
const char *mnemonic = "unknown";
const char *mnemonic = "";
const char *form = "'Xd, 'Wn, 'Wm, 'Xa";
const char *form_rrr = "'Rd, 'Rn, 'Rm";
const char *form_rrrr = "'Rd, 'Rn, 'Rm, 'Ra";
@ -681,14 +690,14 @@ void Disassembler::VisitDataProcessing3Source(Instruction* instr) {
form = form_xxx;
break;
}
default: form = "(DataProcessing3Source)";
default: UNREACHABLE();
}
Format(instr, mnemonic, form);
}
void Disassembler::VisitCompareBranch(Instruction* instr) {
const char *mnemonic = "unknown";
const char *mnemonic = "";
const char *form = "'Rt, 'BImmCmpa";
switch (instr->Mask(CompareBranchMask)) {
@ -696,27 +705,31 @@ void Disassembler::VisitCompareBranch(Instruction* instr) {
case CBZ_x: mnemonic = "cbz"; break;
case CBNZ_w:
case CBNZ_x: mnemonic = "cbnz"; break;
default: form = "(CompareBranch)";
default: UNREACHABLE();
}
Format(instr, mnemonic, form);
}
void Disassembler::VisitTestBranch(Instruction* instr) {
const char *mnemonic = "unknown";
const char *form = "'Xt, 'IS, 'BImmTest";
const char *mnemonic = "";
// If the top bit of the immediate is clear, the tested register is
// disassembled as Wt, otherwise Xt. As the top bit of the immediate is
// encoded in bit 31 of the instruction, we can reuse the Rt form, which
// uses bit 31 (normally "sf") to choose the register size.
const char *form = "'Rt, 'IS, 'BImmTest";
switch (instr->Mask(TestBranchMask)) {
case TBZ: mnemonic = "tbz"; break;
case TBNZ: mnemonic = "tbnz"; break;
default: form = "(TestBranch)";
default: UNREACHABLE();
}
Format(instr, mnemonic, form);
}
void Disassembler::VisitMoveWideImmediate(Instruction* instr) {
const char *mnemonic = "unknown";
const char *mnemonic = "";
const char *form = "'Rd, 'IMoveImm";
// Print the shift separately for movk, to make it clear which half word will
@ -729,7 +742,7 @@ void Disassembler::VisitMoveWideImmediate(Instruction* instr) {
case MOVZ_x: mnemonic = "movz"; break;
case MOVK_w:
case MOVK_x: mnemonic = "movk"; form = "'Rd, 'IMoveLSL"; break;
default: form = "(MoveWideImmediate)";
default: UNREACHABLE();
}
Format(instr, mnemonic, form);
}
@ -755,7 +768,7 @@ void Disassembler::VisitMoveWideImmediate(Instruction* instr) {
V(LDR_d, "ldr", "'Dt")
void Disassembler::VisitLoadStorePreIndex(Instruction* instr) {
const char *mnemonic = "unknown";
const char *mnemonic = "unimplemented";
const char *form = "(LoadStorePreIndex)";
switch (instr->Mask(LoadStorePreIndexMask)) {
@ -769,7 +782,7 @@ void Disassembler::VisitLoadStorePreIndex(Instruction* instr) {
void Disassembler::VisitLoadStorePostIndex(Instruction* instr) {
const char *mnemonic = "unknown";
const char *mnemonic = "unimplemented";
const char *form = "(LoadStorePostIndex)";
switch (instr->Mask(LoadStorePostIndexMask)) {
@ -783,7 +796,7 @@ void Disassembler::VisitLoadStorePostIndex(Instruction* instr) {
void Disassembler::VisitLoadStoreUnsignedOffset(Instruction* instr) {
const char *mnemonic = "unknown";
const char *mnemonic = "unimplemented";
const char *form = "(LoadStoreUnsignedOffset)";
switch (instr->Mask(LoadStoreUnsignedOffsetMask)) {
@ -798,7 +811,7 @@ void Disassembler::VisitLoadStoreUnsignedOffset(Instruction* instr) {
void Disassembler::VisitLoadStoreRegisterOffset(Instruction* instr) {
const char *mnemonic = "unknown";
const char *mnemonic = "unimplemented";
const char *form = "(LoadStoreRegisterOffset)";
switch (instr->Mask(LoadStoreRegisterOffsetMask)) {
@ -813,7 +826,7 @@ void Disassembler::VisitLoadStoreRegisterOffset(Instruction* instr) {
void Disassembler::VisitLoadStoreUnscaledOffset(Instruction* instr) {
const char *mnemonic = "unknown";
const char *mnemonic = "unimplemented";
const char *form = "'Wt, ['Xns'ILS]";
const char *form_x = "'Xt, ['Xns'ILS]";
const char *form_s = "'St, ['Xns'ILS]";
@ -852,7 +865,7 @@ void Disassembler::VisitLoadLiteral(Instruction* instr) {
case LDR_x_lit: form = "'Xt, 'ILLiteral 'LValue"; break;
case LDR_s_lit: form = "'St, 'ILLiteral 'LValue"; break;
case LDR_d_lit: form = "'Dt, 'ILLiteral 'LValue"; break;
default: mnemonic = "unknown";
default: mnemonic = "unimplemented";
}
Format(instr, mnemonic, form);
}
@ -870,7 +883,7 @@ void Disassembler::VisitLoadLiteral(Instruction* instr) {
V(LDP_d, "ldp", "'Dt, 'Dt2", "8")
void Disassembler::VisitLoadStorePairPostIndex(Instruction* instr) {
const char *mnemonic = "unknown";
const char *mnemonic = "unimplemented";
const char *form = "(LoadStorePairPostIndex)";
switch (instr->Mask(LoadStorePairPostIndexMask)) {
@ -884,7 +897,7 @@ void Disassembler::VisitLoadStorePairPostIndex(Instruction* instr) {
void Disassembler::VisitLoadStorePairPreIndex(Instruction* instr) {
const char *mnemonic = "unknown";
const char *mnemonic = "unimplemented";
const char *form = "(LoadStorePairPreIndex)";
switch (instr->Mask(LoadStorePairPreIndexMask)) {
@ -898,7 +911,7 @@ void Disassembler::VisitLoadStorePairPreIndex(Instruction* instr) {
void Disassembler::VisitLoadStorePairOffset(Instruction* instr) {
const char *mnemonic = "unknown";
const char *mnemonic = "unimplemented";
const char *form = "(LoadStorePairOffset)";
switch (instr->Mask(LoadStorePairOffsetMask)) {
@ -912,7 +925,7 @@ void Disassembler::VisitLoadStorePairOffset(Instruction* instr) {
void Disassembler::VisitLoadStorePairNonTemporal(Instruction* instr) {
const char *mnemonic = "unknown";
const char *mnemonic = "unimplemented";
const char *form;
switch (instr->Mask(LoadStorePairNonTemporalMask)) {
@ -931,7 +944,7 @@ void Disassembler::VisitLoadStorePairNonTemporal(Instruction* instr) {
void Disassembler::VisitFPCompare(Instruction* instr) {
const char *mnemonic = "unknown";
const char *mnemonic = "unimplemented";
const char *form = "'Fn, 'Fm";
const char *form_zero = "'Fn, #0.0";
@ -947,12 +960,14 @@ void Disassembler::VisitFPCompare(Instruction* instr) {
void Disassembler::VisitFPConditionalCompare(Instruction* instr) {
const char *mnemonic = "unknown";
const char *mnemonic = "unmplemented";
const char *form = "'Fn, 'Fm, 'INzcv, 'Cond";
switch (instr->Mask(FPConditionalCompareMask)) {
case FCCMP_s:
case FCCMP_d: mnemonic = "fccmp"; break;
case FCCMPE_s:
case FCCMPE_d: mnemonic = "fccmpe"; break;
default: form = "(FPConditionalCompare)";
}
Format(instr, mnemonic, form);
@ -960,20 +975,20 @@ void Disassembler::VisitFPConditionalCompare(Instruction* instr) {
void Disassembler::VisitFPConditionalSelect(Instruction* instr) {
const char *mnemonic = "unknown";
const char *mnemonic = "";
const char *form = "'Fd, 'Fn, 'Fm, 'Cond";
switch (instr->Mask(FPConditionalSelectMask)) {
case FCSEL_s:
case FCSEL_d: mnemonic = "fcsel"; break;
default: form = "(FPConditionalSelect)";
default: UNREACHABLE();
}
Format(instr, mnemonic, form);
}
void Disassembler::VisitFPDataProcessing1Source(Instruction* instr) {
const char *mnemonic = "unknown";
const char *mnemonic = "unimplemented";
const char *form = "'Fd, 'Fn";
switch (instr->Mask(FPDataProcessing1SourceMask)) {
@ -1001,7 +1016,7 @@ void Disassembler::VisitFPDataProcessing1Source(Instruction* instr) {
void Disassembler::VisitFPDataProcessing2Source(Instruction* instr) {
const char *mnemonic = "unknown";
const char *mnemonic = "";
const char *form = "'Fd, 'Fn, 'Fm";
switch (instr->Mask(FPDataProcessing2SourceMask)) {
@ -1018,14 +1033,14 @@ void Disassembler::VisitFPDataProcessing2Source(Instruction* instr) {
FORMAT(FMINNM, "fminnm");
FORMAT(FNMUL, "fnmul");
#undef FORMAT
default: form = "(FPDataProcessing2Source)";
default: UNREACHABLE();
}
Format(instr, mnemonic, form);
}
void Disassembler::VisitFPDataProcessing3Source(Instruction* instr) {
const char *mnemonic = "unknown";
const char *mnemonic = "";
const char *form = "'Fd, 'Fn, 'Fm, 'Fa";
switch (instr->Mask(FPDataProcessing3SourceMask)) {
@ -1037,26 +1052,27 @@ void Disassembler::VisitFPDataProcessing3Source(Instruction* instr) {
FORMAT(FNMADD, "fnmadd");
FORMAT(FNMSUB, "fnmsub");
#undef FORMAT
default: form = "(FPDataProcessing3Source)";
default: UNREACHABLE();
}
Format(instr, mnemonic, form);
}
void Disassembler::VisitFPImmediate(Instruction* instr) {
const char *mnemonic = "unknown";
const char *mnemonic = "";
const char *form = "(FPImmediate)";
switch (instr->Mask(FPImmediateMask)) {
case FMOV_s_imm: mnemonic = "fmov"; form = "'Sd, 'IFPSingle"; break;
case FMOV_d_imm: mnemonic = "fmov"; form = "'Dd, 'IFPDouble"; break;
default: UNREACHABLE();
}
Format(instr, mnemonic, form);
}
void Disassembler::VisitFPIntegerConvert(Instruction* instr) {
const char *mnemonic = "unknown";
const char *mnemonic = "unimplemented";
const char *form = "(FPIntegerConvert)";
const char *form_rf = "'Rd, 'Fn";
const char *form_fr = "'Fd, 'Rn";
@ -1090,8 +1106,12 @@ void Disassembler::VisitFPIntegerConvert(Instruction* instr) {
case FCVTZS_wd:
case FCVTZS_xs:
case FCVTZS_ws: mnemonic = "fcvtzs"; form = form_rf; break;
case SCVTF_sw:
case SCVTF_sx:
case SCVTF_dw:
case SCVTF_dx: mnemonic = "scvtf"; form = form_fr; break;
case UCVTF_sw:
case UCVTF_sx:
case UCVTF_dw:
case UCVTF_dx: mnemonic = "ucvtf"; form = form_fr; break;
}
@ -1100,20 +1120,19 @@ void Disassembler::VisitFPIntegerConvert(Instruction* instr) {
void Disassembler::VisitFPFixedPointConvert(Instruction* instr) {
const char *mnemonic = "unknown";
const char *form = "(FPFixedPointConvert)";
const char *form_rf = "'Rd, 'Fn, 'IFPFBits";
const char *mnemonic = "";
const char *form = "'Rd, 'Fn, 'IFPFBits";
const char *form_fr = "'Fd, 'Rn, 'IFPFBits";
switch (instr->Mask(FPFixedPointConvertMask)) {
case FCVTZS_ws_fixed:
case FCVTZS_xs_fixed:
case FCVTZS_wd_fixed:
case FCVTZS_xd_fixed: mnemonic = "fcvtzs"; form = form_rf; break;
case FCVTZS_xd_fixed: mnemonic = "fcvtzs"; break;
case FCVTZU_ws_fixed:
case FCVTZU_xs_fixed:
case FCVTZU_wd_fixed:
case FCVTZU_xd_fixed: mnemonic = "fcvtzu"; form = form_rf; break;
case FCVTZU_xd_fixed: mnemonic = "fcvtzu"; break;
case SCVTF_sw_fixed:
case SCVTF_sx_fixed:
case SCVTF_dw_fixed:
@ -1122,6 +1141,7 @@ void Disassembler::VisitFPFixedPointConvert(Instruction* instr) {
case UCVTF_sx_fixed:
case UCVTF_dw_fixed:
case UCVTF_dx_fixed: mnemonic = "ucvtf"; form = form_fr; break;
default: UNREACHABLE();
}
Format(instr, mnemonic, form);
}
@ -1131,7 +1151,7 @@ void Disassembler::VisitSystem(Instruction* instr) {
// Some system instructions hijack their Op and Cp fields to represent a
// range of immediates instead of indicating a different instruction. This
// makes the decoding tricky.
const char *mnemonic = "unknown";
const char *mnemonic = "unimplemented";
const char *form = "(System)";
if (instr->Mask(SystemSysRegFMask) == SystemSysRegFixed) {
@ -1140,6 +1160,8 @@ void Disassembler::VisitSystem(Instruction* instr) {
mnemonic = "mrs";
switch (instr->ImmSystemRegister()) {
case NZCV: form = "'Xt, nzcv"; break;
case FPCR: form = "'Xt, fpcr"; break;
default: form = "'Xt, (unknown)"; break;
}
break;
}
@ -1147,6 +1169,8 @@ void Disassembler::VisitSystem(Instruction* instr) {
mnemonic = "msr";
switch (instr->ImmSystemRegister()) {
case NZCV: form = "nzcv, 'Xt"; break;
case FPCR: form = "fpcr, 'Xt"; break;
default: form = "(unknown), 'Xt"; break;
}
break;
}
@ -1167,16 +1191,31 @@ void Disassembler::VisitSystem(Instruction* instr) {
void Disassembler::VisitException(Instruction* instr) {
const char *mnemonic = "unimplemented";
const char *form = "'IDebug";
switch (instr->Mask(ExceptionMask)) {
case HLT: Format(instr, "hlt", "'IDebug"); break;
case BRK: Format(instr, "brk", "'IDebug"); break;
default: Format(instr, "unknown", "(Exception)");
case HLT: mnemonic = "hlt"; break;
case BRK: mnemonic = "brk"; break;
case SVC: mnemonic = "svc"; break;
case HVC: mnemonic = "hvc"; break;
case SMC: mnemonic = "smc"; break;
case DCPS1: mnemonic = "dcps1"; form = "{'IDebug}"; break;
case DCPS2: mnemonic = "dcps2"; form = "{'IDebug}"; break;
case DCPS3: mnemonic = "dcps3"; form = "{'IDebug}"; break;
default: form = "(Exception)";
}
Format(instr, mnemonic, form);
}
void Disassembler::VisitUnknown(Instruction* instr) {
Format(instr, "unknown", "(Unknown)");
void Disassembler::VisitUnimplemented(Instruction* instr) {
Format(instr, "unimplemented", "(Unimplemented)");
}
void Disassembler::VisitUnallocated(Instruction* instr) {
Format(instr, "unallocated", "(Unallocated)");
}
@ -1311,7 +1350,7 @@ int Disassembler::SubstituteImmediateField(Instruction* instr,
case 'L': {
switch (format[2]) {
case 'L': { // ILLiteral - Immediate Load Literal.
AppendToOutput("#%" PRId64,
AppendToOutput("pc%+" PRId64,
instr->ImmLLiteral() << kLiteralEntrySizeLog2);
return 9;
}
@ -1441,14 +1480,10 @@ int Disassembler::SubstituteLiteralField(Instruction* instr,
USE(format);
switch (instr->Mask(LoadLiteralMask)) {
case LDR_s_lit: AppendToOutput("(%.4f)", instr->LiteralFP32()); break;
case LDR_d_lit: AppendToOutput("(%.4f)", instr->LiteralFP64()); break;
case LDR_w_lit:
AppendToOutput("(0x%08" PRIx32 ")", instr->Literal32());
break;
case LDR_x_lit:
AppendToOutput("(0x%016" PRIx64 ")", instr->Literal64());
break;
case LDR_s_lit:
case LDR_d_lit: AppendToOutput("(addr %p)", instr->LiteralAddress()); break;
default: UNREACHABLE();
}

View File

@ -29,19 +29,6 @@
namespace vixl {
#ifdef DEBUG
uint32_t Instruction::SpacedBits(int num_bits, ...) const {
va_list bit_list;
va_start(bit_list, num_bits);
int32_t result = 0;
for (int i = 0; i < num_bits; i++) {
result = (result << 1) | Bit(va_arg(bit_list, int));
}
va_end(bit_list);
return result;
}
#endif
static uint64_t RotateRight(uint64_t value,
unsigned int rotate,
@ -67,6 +54,9 @@ static uint64_t RepeatBitsAcrossReg(unsigned reg_size,
}
// Logical immediates can't encode zero, so a return value of zero is used to
// indicate a failure case. Specifically, where the constraints on imm_s are
// not met.
uint64_t Instruction::ImmLogical() {
unsigned reg_size = SixtyFourBits() ? kXRegSize : kWRegSize;
int64_t n = BitN();
@ -91,15 +81,21 @@ uint64_t Instruction::ImmLogical() {
//
if (n == 1) {
ASSERT(imm_s != 0x3F);
if (imm_s == 0x3F) {
return 0;
}
uint64_t bits = (1UL << (imm_s + 1)) - 1;
return RotateRight(bits, imm_r, 64);
} else {
ASSERT((imm_s >> 1) != 0x1F);
if ((imm_s >> 1) == 0x1F) {
return 0;
}
for (int width = 0x20; width >= 0x2; width >>= 1) {
if ((imm_s & width) == 0) {
int mask = width - 1;
ASSERT((imm_s & mask) != mask);
if ((imm_s & mask) == mask) {
return 0;
}
uint64_t bits = (1UL << ((imm_s & mask) + 1)) - 1;
return RepeatBitsAcrossReg(reg_size,
RotateRight(bits, imm_r & mask, width),

View File

@ -72,6 +72,13 @@ const unsigned kLinkRegCode = 30;
const unsigned kZeroRegCode = 31;
const unsigned kSPRegInternalCode = 63;
const unsigned kRegCodeMask = 0x1f;
// AArch64 floating-point specifics. These match IEEE-754.
const unsigned kDoubleMantissaBits = 52;
const unsigned kDoubleExponentBits = 11;
const unsigned kFloatMantissaBits = 23;
const unsigned kFloatExponentBits = 8;
const float kFP32PositiveInfinity = rawbits_to_float(0x7f800000);
const float kFP32NegativeInfinity = rawbits_to_float(0xff800000);
const double kFP64PositiveInfinity = rawbits_to_double(0x7ff0000000000000UL);
@ -80,9 +87,11 @@ const double kFP64NegativeInfinity = rawbits_to_double(0xfff0000000000000UL);
// This value is a signalling NaN as both a double and as a float (taking the
// least-significant word).
static const double kFP64SignallingNaN = rawbits_to_double(0x7ff000007f800001);
static const float kFP32SignallingNaN = rawbits_to_float(0x7f800001);
// A similar value, but as a quiet NaN.
static const double kFP64QuietNaN = rawbits_to_double(0x7ff800007fc00001);
static const float kFP32QuietNaN = rawbits_to_float(0x7fc00001);
enum LSDataSize {
LSByte = 0,
@ -108,10 +117,14 @@ enum AddrMode {
};
enum FPRounding {
FPTieEven,
FPPositiveInfinity,
FPNegativeInfinity,
FPZero,
// The first four values are encodable directly by FPCR<RMode>.
FPTieEven = 0x0,
FPPositiveInfinity = 0x1,
FPNegativeInfinity = 0x2,
FPZero = 0x3,
// The final rounding mode is only available when explicitly specified by the
// instruction (such as with fcvta). It cannot be set in FPCR.
FPTieAway
};
@ -145,21 +158,17 @@ class Instruction {
return signed_bitextract_32(msb, lsb, bits);
}
#ifdef DEBUG
uint32_t SpacedBits(int num_bits, ...) const;
#endif
inline Instr Mask(uint32_t mask) const {
return InstructionBits() & mask;
}
#define DEFINE_GETTER(Name, HighBit, LowBit, Func) \
inline int64_t Name() const { return Func(HighBit, LowBit); }
FIELDS_LIST(DEFINE_GETTER)
INSTRUCTION_FIELDS_LIST(DEFINE_GETTER)
#undef DEFINE_GETTER
// ImmPCRel is a compound field (not present in FIELDS_LIST), formed from
// ImmPCRelLo and ImmPCRelHi.
// ImmPCRel is a compound field (not present in INSTRUCTION_FIELDS_LIST),
// formed from ImmPCRelLo and ImmPCRelHi.
int ImmPCRel() const {
int const offset = ((ImmPCRelHi() << ImmPCRelLo_width) | ImmPCRelLo());
int const width = ImmPCRelLo_width + ImmPCRelHi_width;
@ -212,6 +221,11 @@ class Instruction {
return Mask(LoadStoreAnyFMask) == LoadStoreAnyFixed;
}
inline bool IsMovn() const {
return (Mask(MoveWideImmediateMask) == MOVN_x) ||
(Mask(MoveWideImmediateMask) == MOVN_w);
}
// Indicate whether Rd can be the stack pointer or the zero register. This
// does not check that the instruction actually has an Rd field.
inline Reg31Mode RdMode() const {

638
src/a64/instrument-a64.cc Normal file
View File

@ -0,0 +1,638 @@
// 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 "a64/instrument-a64.h"
namespace vixl {
Counter::Counter(const char* name, CounterType type)
: count_(0), enabled_(false), type_(type) {
ASSERT(name != NULL);
strncpy(name_, name, kCounterNameMaxLength);
}
void Counter::Enable() {
enabled_ = true;
}
void Counter::Disable() {
enabled_ = false;
}
bool Counter::IsEnabled() {
return enabled_;
}
void Counter::Increment() {
if (enabled_) {
count_++;
}
}
uint64_t Counter::count() {
uint64_t result = count_;
if (type_ == Gauge) {
// If the counter is a Gauge, reset the count after reading.
count_ = 0;
}
return result;
}
const char* Counter::name() {
return name_;
}
CounterType Counter::type() {
return type_;
}
typedef struct {
const char* name;
CounterType type;
} CounterDescriptor;
static const CounterDescriptor kCounterList[] = {
{"Instruction", Cumulative},
{"Move Immediate", Gauge},
{"Add/Sub DP", Gauge},
{"Logical DP", Gauge},
{"Other Int DP", Gauge},
{"FP DP", Gauge},
{"Conditional Select", Gauge},
{"Conditional Compare", Gauge},
{"Unconditional Branch", Gauge},
{"Compare and Branch", Gauge},
{"Test and Branch", Gauge},
{"Conditional Branch", Gauge},
{"Load Integer", Gauge},
{"Load FP", Gauge},
{"Load Pair", Gauge},
{"Load Literal", Gauge},
{"Store Integer", Gauge},
{"Store FP", Gauge},
{"Store Pair", Gauge},
{"PC Addressing", Gauge},
{"Other", Gauge},
};
Instrument::Instrument(const char* datafile, uint64_t sample_period)
: output_stream_(stdout), sample_period_(sample_period) {
// Set up the output stream. If datafile is non-NULL, use that file. If it
// can't be opened, or datafile is NULL, use stdout.
if (datafile != NULL) {
output_stream_ = fopen(datafile, "w");
if (output_stream_ == NULL) {
printf("Can't open output file %s. Using stdout.\n", datafile);
output_stream_ = stdout;
}
}
static const int num_counters =
sizeof(kCounterList) / sizeof(CounterDescriptor);
// Dump an instrumentation description comment at the top of the file.
fprintf(output_stream_, "# counters=%d\n", num_counters);
fprintf(output_stream_, "# sample_period=%" PRIu64 "\n", sample_period_);
// Construct Counter objects from counter description array.
for (int i = 0; i < num_counters; i++) {
Counter* counter = new Counter(kCounterList[i].name, kCounterList[i].type);
counters_.push_back(counter);
}
DumpCounterNames();
}
Instrument::~Instrument() {
// Dump any remaining instruction data to the output file.
DumpCounters();
// Free all the counter objects.
std::list<Counter*>::iterator it;
for (it = counters_.begin(); it != counters_.end(); it++) {
free(*it);
}
if (output_stream_ != stdout) {
fclose(output_stream_);
}
}
void Instrument::Update() {
// Increment the instruction counter, and dump all counters if a sample period
// has elapsed.
static Counter* counter = GetCounter("Instruction");
ASSERT(counter->type() == Cumulative);
counter->Increment();
if (counter->IsEnabled() && (counter->count() % sample_period_) == 0) {
DumpCounters();
}
}
void Instrument::DumpCounters() {
// Iterate through the counter objects, dumping their values to the output
// stream.
std::list<Counter*>::const_iterator it;
for (it = counters_.begin(); it != counters_.end(); it++) {
fprintf(output_stream_, "%" PRIu64 ",", (*it)->count());
}
fprintf(output_stream_, "\n");
fflush(output_stream_);
}
void Instrument::DumpCounterNames() {
// Iterate through the counter objects, dumping the counter names to the
// output stream.
std::list<Counter*>::const_iterator it;
for (it = counters_.begin(); it != counters_.end(); it++) {
fprintf(output_stream_, "%s,", (*it)->name());
}
fprintf(output_stream_, "\n");
fflush(output_stream_);
}
void Instrument::HandleInstrumentationEvent(unsigned event) {
switch (event) {
case InstrumentStateEnable: Enable(); break;
case InstrumentStateDisable: Disable(); break;
default: DumpEventMarker(event);
}
}
void Instrument::DumpEventMarker(unsigned marker) {
// Dumpan event marker to the output stream as a specially formatted comment
// line.
static Counter* counter = GetCounter("Instruction");
fprintf(output_stream_, "# %c%c @ %" PRId64 "\n", marker & 0xff,
(marker >> 8) & 0xff, counter->count());
}
Counter* Instrument::GetCounter(const char* name) {
// Get a Counter object by name from the counter list.
std::list<Counter*>::const_iterator it;
for (it = counters_.begin(); it != counters_.end(); it++) {
if (strcmp((*it)->name(), name) == 0) {
return *it;
}
}
// A Counter by that name does not exist: print an error message to stderr
// and the output file, and exit.
static const char* error_message =
"# Error: Unknown counter \"%s\". Exiting.\n";
fprintf(stderr, error_message, name);
fprintf(output_stream_, error_message, name);
exit(1);
}
void Instrument::Enable() {
std::list<Counter*>::iterator it;
for (it = counters_.begin(); it != counters_.end(); it++) {
(*it)->Enable();
}
}
void Instrument::Disable() {
std::list<Counter*>::iterator it;
for (it = counters_.begin(); it != counters_.end(); it++) {
(*it)->Disable();
}
}
void Instrument::VisitPCRelAddressing(Instruction* instr) {
USE(instr);
Update();
static Counter* counter = GetCounter("PC Addressing");
counter->Increment();
}
void Instrument::VisitAddSubImmediate(Instruction* instr) {
USE(instr);
Update();
static Counter* counter = GetCounter("Add/Sub DP");
counter->Increment();
}
void Instrument::VisitLogicalImmediate(Instruction* instr) {
USE(instr);
Update();
static Counter* counter = GetCounter("Logical DP");
counter->Increment();
}
void Instrument::VisitMoveWideImmediate(Instruction* instr) {
Update();
static Counter* counter = GetCounter("Move Immediate");
if (instr->IsMovn() && (instr->Rd() == kZeroRegCode)) {
unsigned imm = instr->ImmMoveWide();
HandleInstrumentationEvent(imm);
} else {
counter->Increment();
}
}
void Instrument::VisitBitfield(Instruction* instr) {
USE(instr);
Update();
static Counter* counter = GetCounter("Other Int DP");
counter->Increment();
}
void Instrument::VisitExtract(Instruction* instr) {
USE(instr);
Update();
static Counter* counter = GetCounter("Other Int DP");
counter->Increment();
}
void Instrument::VisitUnconditionalBranch(Instruction* instr) {
USE(instr);
Update();
static Counter* counter = GetCounter("Unconditional Branch");
counter->Increment();
}
void Instrument::VisitUnconditionalBranchToRegister(Instruction* instr) {
USE(instr);
Update();
static Counter* counter = GetCounter("Unconditional Branch");
counter->Increment();
}
void Instrument::VisitCompareBranch(Instruction* instr) {
USE(instr);
Update();
static Counter* counter = GetCounter("Compare and Branch");
counter->Increment();
}
void Instrument::VisitTestBranch(Instruction* instr) {
USE(instr);
Update();
static Counter* counter = GetCounter("Test and Branch");
counter->Increment();
}
void Instrument::VisitConditionalBranch(Instruction* instr) {
USE(instr);
Update();
static Counter* counter = GetCounter("Conditional Branch");
counter->Increment();
}
void Instrument::VisitSystem(Instruction* instr) {
USE(instr);
Update();
static Counter* counter = GetCounter("Other");
counter->Increment();
}
void Instrument::VisitException(Instruction* instr) {
USE(instr);
Update();
static Counter* counter = GetCounter("Other");
counter->Increment();
}
void Instrument::InstrumentLoadStorePair(Instruction* instr) {
static Counter* load_pair_counter = GetCounter("Load Pair");
static Counter* store_pair_counter = GetCounter("Store Pair");
if (instr->Mask(LoadStorePairLBit) != 0) {
load_pair_counter->Increment();
} else {
store_pair_counter->Increment();
}
}
void Instrument::VisitLoadStorePairPostIndex(Instruction* instr) {
USE(instr);
Update();
InstrumentLoadStorePair(instr);
}
void Instrument::VisitLoadStorePairOffset(Instruction* instr) {
USE(instr);
Update();
InstrumentLoadStorePair(instr);
}
void Instrument::VisitLoadStorePairPreIndex(Instruction* instr) {
USE(instr);
Update();
InstrumentLoadStorePair(instr);
}
void Instrument::VisitLoadStorePairNonTemporal(Instruction* instr) {
USE(instr);
Update();
InstrumentLoadStorePair(instr);
}
void Instrument::VisitLoadLiteral(Instruction* instr) {
USE(instr);
Update();
static Counter* counter = GetCounter("Load Literal");
counter->Increment();
}
void Instrument::InstrumentLoadStore(Instruction* instr) {
static Counter* load_int_counter = GetCounter("Load Integer");
static Counter* store_int_counter = GetCounter("Store Integer");
static Counter* load_fp_counter = GetCounter("Load FP");
static Counter* store_fp_counter = GetCounter("Store FP");
switch (instr->Mask(LoadStoreOpMask)) {
case STRB_w: // Fall through.
case STRH_w: // Fall through.
case STR_w: // Fall through.
case STR_x: store_int_counter->Increment(); break;
case STR_s: // Fall through.
case STR_d: store_fp_counter->Increment(); break;
case LDRB_w: // Fall through.
case LDRH_w: // Fall through.
case LDR_w: // Fall through.
case LDR_x: // Fall through.
case LDRSB_x: // Fall through.
case LDRSH_x: // Fall through.
case LDRSW_x: // Fall through.
case LDRSB_w: // Fall through.
case LDRSH_w: load_int_counter->Increment(); break;
case LDR_s: // Fall through.
case LDR_d: load_fp_counter->Increment(); break;
}
}
void Instrument::VisitLoadStoreUnscaledOffset(Instruction* instr) {
Update();
InstrumentLoadStore(instr);
}
void Instrument::VisitLoadStorePostIndex(Instruction* instr) {
USE(instr);
Update();
InstrumentLoadStore(instr);
}
void Instrument::VisitLoadStorePreIndex(Instruction* instr) {
Update();
InstrumentLoadStore(instr);
}
void Instrument::VisitLoadStoreRegisterOffset(Instruction* instr) {
Update();
InstrumentLoadStore(instr);
}
void Instrument::VisitLoadStoreUnsignedOffset(Instruction* instr) {
Update();
InstrumentLoadStore(instr);
}
void Instrument::VisitLogicalShifted(Instruction* instr) {
USE(instr);
Update();
static Counter* counter = GetCounter("Logical DP");
counter->Increment();
}
void Instrument::VisitAddSubShifted(Instruction* instr) {
USE(instr);
Update();
static Counter* counter = GetCounter("Add/Sub DP");
counter->Increment();
}
void Instrument::VisitAddSubExtended(Instruction* instr) {
USE(instr);
Update();
static Counter* counter = GetCounter("Add/Sub DP");
counter->Increment();
}
void Instrument::VisitAddSubWithCarry(Instruction* instr) {
USE(instr);
Update();
static Counter* counter = GetCounter("Add/Sub DP");
counter->Increment();
}
void Instrument::VisitConditionalCompareRegister(Instruction* instr) {
USE(instr);
Update();
static Counter* counter = GetCounter("Conditional Compare");
counter->Increment();
}
void Instrument::VisitConditionalCompareImmediate(Instruction* instr) {
USE(instr);
Update();
static Counter* counter = GetCounter("Conditional Compare");
counter->Increment();
}
void Instrument::VisitConditionalSelect(Instruction* instr) {
USE(instr);
Update();
static Counter* counter = GetCounter("Conditional Select");
counter->Increment();
}
void Instrument::VisitDataProcessing1Source(Instruction* instr) {
USE(instr);
Update();
static Counter* counter = GetCounter("Other Int DP");
counter->Increment();
}
void Instrument::VisitDataProcessing2Source(Instruction* instr) {
USE(instr);
Update();
static Counter* counter = GetCounter("Other Int DP");
counter->Increment();
}
void Instrument::VisitDataProcessing3Source(Instruction* instr) {
USE(instr);
Update();
static Counter* counter = GetCounter("Other Int DP");
counter->Increment();
}
void Instrument::VisitFPCompare(Instruction* instr) {
USE(instr);
Update();
static Counter* counter = GetCounter("FP DP");
counter->Increment();
}
void Instrument::VisitFPConditionalCompare(Instruction* instr) {
USE(instr);
Update();
static Counter* counter = GetCounter("Conditional Compare");
counter->Increment();
}
void Instrument::VisitFPConditionalSelect(Instruction* instr) {
USE(instr);
Update();
static Counter* counter = GetCounter("Conditional Select");
counter->Increment();
}
void Instrument::VisitFPImmediate(Instruction* instr) {
USE(instr);
Update();
static Counter* counter = GetCounter("FP DP");
counter->Increment();
}
void Instrument::VisitFPDataProcessing1Source(Instruction* instr) {
USE(instr);
Update();
static Counter* counter = GetCounter("FP DP");
counter->Increment();
}
void Instrument::VisitFPDataProcessing2Source(Instruction* instr) {
USE(instr);
Update();
static Counter* counter = GetCounter("FP DP");
counter->Increment();
}
void Instrument::VisitFPDataProcessing3Source(Instruction* instr) {
USE(instr);
Update();
static Counter* counter = GetCounter("FP DP");
counter->Increment();
}
void Instrument::VisitFPIntegerConvert(Instruction* instr) {
USE(instr);
Update();
static Counter* counter = GetCounter("FP DP");
counter->Increment();
}
void Instrument::VisitFPFixedPointConvert(Instruction* instr) {
USE(instr);
Update();
static Counter* counter = GetCounter("FP DP");
counter->Increment();
}
void Instrument::VisitUnallocated(Instruction* instr) {
USE(instr);
Update();
static Counter* counter = GetCounter("Other");
counter->Increment();
}
void Instrument::VisitUnimplemented(Instruction* instr) {
USE(instr);
Update();
static Counter* counter = GetCounter("Other");
counter->Increment();
}
} // namespace v8::internal

108
src/a64/instrument-a64.h Normal file
View File

@ -0,0 +1,108 @@
// 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.
#ifndef VIXL_A64_INSTRUMENT_A64_H_
#define VIXL_A64_INSTRUMENT_A64_H_
#include "globals.h"
#include "utils.h"
#include "a64/decoder-a64.h"
#include "a64/constants-a64.h"
#include "a64/instrument-a64.h"
namespace vixl {
const int kCounterNameMaxLength = 256;
const uint64_t kDefaultInstrumentationSamplingPeriod = 1 << 22;
enum InstrumentState {
InstrumentStateDisable = 0,
InstrumentStateEnable = 1
};
enum CounterType {
Gauge = 0, // Gauge counters reset themselves after reading.
Cumulative = 1 // Cumulative counters keep their value after reading.
};
class Counter {
public:
Counter(const char* name, CounterType type = Gauge);
~Counter();
void Increment();
void Enable();
void Disable();
bool IsEnabled();
uint64_t count();
const char* name();
CounterType type();
private:
char name_[kCounterNameMaxLength];
uint64_t count_;
bool enabled_;
CounterType type_;
};
class Instrument: public DecoderVisitor {
public:
explicit Instrument(const char* datafile = NULL,
uint64_t sample_period = kDefaultInstrumentationSamplingPeriod);
~Instrument();
void Enable();
void Disable();
// Declare all Visitor functions.
#define DECLARE(A) void Visit##A(Instruction* instr);
VISITOR_LIST(DECLARE)
#undef DECLARE
private:
void Update();
void DumpCounters();
void DumpCounterNames();
void DumpEventMarker(unsigned marker);
void HandleInstrumentationEvent(unsigned event);
Counter* GetCounter(const char* name);
void InstrumentLoadStore(Instruction* instr);
void InstrumentLoadStorePair(Instruction* instr);
std::list<Counter*> counters_;
FILE *output_stream_;
uint64_t sample_period_;
};
} // namespace vixl
#endif // VIXL_A64_INSTRUMENT_A64_H_

View File

@ -338,6 +338,7 @@ void MacroAssembler::ConditionalCompareMacro(const Register& rn,
StatusFlags nzcv,
Condition cond,
ConditionalCompareOp op) {
ASSERT((cond != al) && (cond != nv));
if ((operand.IsShiftedRegister() && (operand.shift_amount() == 0)) ||
(operand.IsImmediate() && IsImmConditionalCompare(operand.immediate()))) {
// The immediate can be encoded in the instruction, or the operand is an
@ -1078,4 +1079,30 @@ void MacroAssembler::Log(TraceParameters parameters) {
#endif
}
void MacroAssembler::EnableInstrumentation() {
ASSERT(!isprint(InstrumentStateEnable));
InstructionAccurateScope scope(this, 1);
movn(xzr, InstrumentStateEnable);
}
void MacroAssembler::DisableInstrumentation() {
ASSERT(!isprint(InstrumentStateDisable));
InstructionAccurateScope scope(this, 1);
movn(xzr, InstrumentStateDisable);
}
void MacroAssembler::AnnotateInstrumentation(const char* marker_name) {
ASSERT(strlen(marker_name) == 2);
// We allow only printable characters in the marker names. Unprintable
// characters are reserved for controlling features of the instrumentation.
ASSERT(isprint(marker_name[0]) && isprint(marker_name[1]));
InstructionAccurateScope scope(this, 1);
movn(xzr, (marker_name[1] << 8) | marker_name[0]);
}
} // namespace vixl

View File

@ -296,8 +296,12 @@ class MacroAssembler : public Assembler {
ASSERT(!rm.IsZero());
asrv(rd, rn, rm);
}
void B(Label* label, Condition cond = al) {
void B(Label* label) {
b(label);
}
void B(Label* label, Condition cond) {
ASSERT(allow_macro_instructions_);
ASSERT((cond != al) && (cond != nv));
b(label, cond);
}
void Bfi(const Register& rd,
@ -388,6 +392,7 @@ class MacroAssembler : public Assembler {
ASSERT(!rd.IsZero());
ASSERT(!rn.IsZero());
ASSERT(!rm.IsZero());
ASSERT((cond != al) && (cond != nv));
csel(rd, rn, rm, cond);
}
void Cset(const Register& rd, Condition cond) {
@ -408,6 +413,7 @@ class MacroAssembler : public Assembler {
ASSERT(!rd.IsZero());
ASSERT(!rn.IsZero());
ASSERT(!rm.IsZero());
ASSERT((cond != al) && (cond != nv));
csinc(rd, rn, rm, cond);
}
void Csinv(const Register& rd,
@ -418,6 +424,7 @@ class MacroAssembler : public Assembler {
ASSERT(!rd.IsZero());
ASSERT(!rn.IsZero());
ASSERT(!rm.IsZero());
ASSERT((cond != al) && (cond != nv));
csinv(rd, rn, rm, cond);
}
void Csneg(const Register& rd,
@ -428,6 +435,7 @@ class MacroAssembler : public Assembler {
ASSERT(!rd.IsZero());
ASSERT(!rn.IsZero());
ASSERT(!rm.IsZero());
ASSERT((cond != al) && (cond != nv));
csneg(rd, rn, rm, cond);
}
void Extr(const Register& rd,
@ -453,6 +461,7 @@ class MacroAssembler : public Assembler {
StatusFlags nzcv,
Condition cond) {
ASSERT(allow_macro_instructions_);
ASSERT((cond != al) && (cond != nv));
fccmp(fn, fm, nzcv, cond);
}
void Fcmp(const FPRegister& fn, const FPRegister& fm) {
@ -474,8 +483,13 @@ class MacroAssembler : public Assembler {
const FPRegister& fm,
Condition cond) {
ASSERT(allow_macro_instructions_);
ASSERT((cond != al) && (cond != nv));
fcsel(fd, fn, fm, cond);
}
void Fcvt(const FPRegister& fd, const FPRegister& fn) {
ASSERT(allow_macro_instructions_);
fcvt(fd, fn);
}
void Fcvtms(const Register& rd, const FPRegister& fn) {
ASSERT(allow_macro_instructions_);
ASSERT(!rd.IsZero());
@ -569,10 +583,6 @@ class MacroAssembler : public Assembler {
ASSERT(allow_macro_instructions_);
fsqrt(fd, fn);
}
void Fcvt(const FPRegister& fd, const FPRegister& fn) {
ASSERT(allow_macro_instructions_);
fcvt(fd, fn);
}
void Fsub(const FPRegister& fd, const FPRegister& fn, const FPRegister& fm) {
ASSERT(allow_macro_instructions_);
fsub(fd, fn, fm);
@ -1066,7 +1076,17 @@ class MacroAssembler : public Assembler {
//
// __ Log(LOG_FLAGS)
// Will output the flags.
void Log(TraceParameters paramters);
void Log(TraceParameters parameters);
// Enable or disable instrumentation when an Instrument visitor is attached to
// the simulator.
void EnableInstrumentation();
void DisableInstrumentation();
// Add a marker to the instrumentation data produced by an Instrument visitor.
// The name is a two character string that will be attached to the marker in
// the output data.
void AnnotateInstrumentation(const char* marker_name);
private:
// The actual Push and Pop implementations. These don't generate any code

View File

@ -24,13 +24,38 @@
// 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 <cmath>
#include <math.h>
#include "a64/simulator-a64.h"
namespace vixl {
const Instruction* Simulator::kEndOfSimAddress = NULL;
void SimSystemRegister::SetBits(int msb, int lsb, uint32_t bits) {
int width = msb - lsb + 1;
ASSERT(is_uintn(width, bits) || is_intn(width, bits));
bits <<= lsb;
uint32_t mask = ((1 << width) - 1) << lsb;
ASSERT((mask & write_ignore_mask_) == 0);
value_ = (value_ & ~mask) | (bits & mask);
}
SimSystemRegister SimSystemRegister::DefaultValueFor(SystemRegister id) {
switch (id) {
case NZCV:
return SimSystemRegister(0x00000000, NZCVWriteIgnoreMask);
case FPCR:
return SimSystemRegister(0x00000000, FPCRWriteIgnoreMask);
default:
UNREACHABLE();
return SimSystemRegister();
}
}
Simulator::Simulator(Decoder* decoder, FILE* stream) {
// Ensure shift operations act as the simulator expects.
ASSERT((static_cast<int32_t>(-1) >> 1) == -1);
@ -53,12 +78,16 @@ Simulator::Simulator(Decoder* decoder, FILE* stream) {
print_disasm_ = new PrintDisassembler(stream_);
coloured_trace_ = false;
disasm_trace_ = false;
// Set the sample period to 10, as the VIXL examples and tests are short.
instrumentation_ = new Instrument("vixl_stats.csv", 10);
}
void Simulator::ResetState() {
// Reset the processor state.
psr_ = 0;
// Reset the system registers.
nzcv_ = SimSystemRegister::DefaultValueFor(NZCV);
fpcr_ = SimSystemRegister::DefaultValueFor(FPCR);
// Reset registers to 0.
pc_ = NULL;
@ -80,6 +109,9 @@ Simulator::~Simulator() {
// The decoder may outlive the simulator.
decoder_->RemoveVisitor(print_disasm_);
delete print_disasm_;
decoder_->RemoveVisitor(instrumentation_);
delete instrumentation_;
}
@ -97,14 +129,6 @@ void Simulator::RunFrom(Instruction* first) {
}
void Simulator::SetFlags(uint32_t new_flags) {
ASSERT((new_flags & ~kConditionFlagsMask) == 0);
psr_ &= ~kConditionFlagsMask;
// Set the new flags.
psr_ |= new_flags;
}
const char* Simulator::xreg_names[] = {
"x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7",
"x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15",
@ -197,14 +221,14 @@ int64_t Simulator::AddWithCarry(unsigned reg_size,
result = signed_sum & kWRegMask;
// Compute the C flag by comparing the sum to the max unsigned integer.
C = CFlag * (((kWMaxUInt - u1) < (u2 + carry_in)) ||
((kWMaxUInt - u1 - carry_in) < u2));
C = ((kWMaxUInt - u1) < (u2 + carry_in)) ||
((kWMaxUInt - u1 - carry_in) < u2);
// Overflow iff the sign bit is the same for the two inputs and different
// for the result.
int64_t s_src1 = src1 << (kXRegSize - kWRegSize);
int64_t s_src2 = src2 << (kXRegSize - kWRegSize);
int64_t s_result = result << (kXRegSize - kWRegSize);
V = VFlag * (((s_src1 ^ s_src2) >= 0) && ((s_src1 ^ s_result) < 0));
V = ((s_src1 ^ s_src2) >= 0) && ((s_src1 ^ s_result) < 0);
} else {
u1 = static_cast<uint64_t>(src1);
@ -212,17 +236,22 @@ int64_t Simulator::AddWithCarry(unsigned reg_size,
result = signed_sum;
// Compute the C flag by comparing the sum to the max unsigned integer.
C = CFlag * (((kXMaxUInt - u1) < (u2 + carry_in)) ||
((kXMaxUInt - u1 - carry_in) < u2));
C = ((kXMaxUInt - u1) < (u2 + carry_in)) ||
((kXMaxUInt - u1 - carry_in) < u2);
// Overflow iff the sign bit is the same for the two inputs and different
// for the result.
V = VFlag * (((src1 ^ src2) >= 0) && ((src1 ^ result) < 0));
V = ((src1 ^ src2) >= 0) && ((src1 ^ result) < 0);
}
N = CalcNFlag(result, reg_size);
Z = CalcZFlag(result);
if (set_flags) SetFlags(N | Z | C | V);
if (set_flags) {
nzcv().SetN(N);
nzcv().SetZ(Z);
nzcv().SetC(C);
nzcv().SetV(V);
}
return result;
}
@ -296,27 +325,26 @@ int64_t Simulator::ExtendValue(unsigned reg_size,
void Simulator::FPCompare(double val0, double val1) {
unsigned new_flags;
AssertSupportedFPCR();
// TODO: This assumes that the C++ implementation handles comparisons in the
// way that we expect (as per AssertSupportedFPCR()).
if ((isnan(val0) != 0) || (isnan(val1) != 0)) {
new_flags = CVFlag;
nzcv().SetRawValue(FPUnorderedFlag);
} else if (val0 < val1) {
nzcv().SetRawValue(FPLessThanFlag);
} else if (val0 > val1) {
nzcv().SetRawValue(FPGreaterThanFlag);
} else if (val0 == val1) {
nzcv().SetRawValue(FPEqualFlag);
} else {
if (val0 < val1) {
new_flags = NFlag;
} else {
new_flags = CFlag;
if (val0 == val1) {
new_flags |= ZFlag;
}
}
UNREACHABLE();
}
SetFlags(new_flags);
}
void Simulator::PrintFlags(bool print_all) {
void Simulator::PrintSystemRegisters(bool print_all) {
static bool first_run = true;
static uint32_t last_flags;
// Define some colour codes to use for the register dump.
// TODO: Find a more elegant way of defining these.
@ -324,14 +352,33 @@ void Simulator::PrintFlags(bool print_all) {
char const * const clr_flag_name = (coloured_trace_) ? ("\033[1;30m") : ("");
char const * const clr_flag_value = (coloured_trace_) ? ("\033[1;37m") : ("");
if (print_all || first_run || (last_flags != nzcv())) {
fprintf(stream_, "# %sFLAGS: %sN:%1d Z:%1d C:%1d V:%1d%s\n",
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,
N(), Z(), C(), V(),
clr_normal);
}
last_flags = nzcv();
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)"
};
ASSERT(fpcr().RMode() <= (sizeof(rmode) / sizeof(rmode[0])));
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);
}
last_fpcr = fpcr();
first_run = false;
}
@ -404,7 +451,7 @@ void Simulator::PrintFPRegisters(bool print_all_regs) {
void Simulator::PrintProcessorState() {
PrintFlags();
PrintSystemRegisters();
PrintRegisters();
PrintFPRegisters();
}
@ -412,8 +459,15 @@ void Simulator::PrintProcessorState() {
// Visitors---------------------------------------------------------------------
void Simulator::VisitUnknown(Instruction* instr) {
printf("Unknown instruction at 0x%p: 0x%08" PRIx32 "\n",
void Simulator::VisitUnimplemented(Instruction* instr) {
printf("Unimplemented instruction at 0x%p: 0x%08" PRIx32 "\n",
reinterpret_cast<void*>(instr), instr->InstructionBits());
UNIMPLEMENTED();
}
void Simulator::VisitUnallocated(Instruction* instr) {
printf("Unallocated instruction at 0x%p: 0x%08" PRIx32 "\n",
reinterpret_cast<void*>(instr), instr->InstructionBits());
UNIMPLEMENTED();
}
@ -613,7 +667,10 @@ void Simulator::LogicalHelper(Instruction* instr, int64_t op2) {
}
if (update_flags) {
SetFlags(CalcNFlag(result, reg_size) | CalcZFlag(result));
nzcv().SetN(CalcNFlag(result, reg_size));
nzcv().SetZ(CalcZFlag(result));
nzcv().SetC(0);
nzcv().SetV(0);
}
set_reg(reg_size, instr->Rd(), result, instr->RdMode());
@ -646,7 +703,7 @@ void Simulator::ConditionalCompareHelper(Instruction* instr, int64_t op2) {
}
} else {
// If the condition fails, set the status flags to the nzcv immediate.
SetFlags(instr->Nzcv() << Flags_offset);
nzcv().SetFlags(instr->Nzcv());
}
}
@ -1220,6 +1277,8 @@ void Simulator::VisitExtract(Instruction* instr) {
void Simulator::VisitFPImmediate(Instruction* instr) {
AssertSupportedFPCR();
unsigned dest = instr->Rd();
switch (instr->Mask(FPImmediateMask)) {
case FMOV_s_imm: set_sreg(dest, instr->ImmFP32()); break;
@ -1230,9 +1289,13 @@ void Simulator::VisitFPImmediate(Instruction* instr) {
void Simulator::VisitFPIntegerConvert(Instruction* instr) {
AssertSupportedFPCR();
unsigned dst = instr->Rd();
unsigned src = instr->Rn();
FPRounding round = RMode();
switch (instr->Mask(FPIntegerConvertMask)) {
case FCVTMS_ws:
set_wreg(dst, FPToInt32(sreg(src), FPNegativeInfinity));
@ -1279,57 +1342,69 @@ void Simulator::VisitFPIntegerConvert(Instruction* instr) {
case FMOV_sw: set_sreg_bits(dst, wreg(src)); break;
case FMOV_dx: set_dreg_bits(dst, xreg(src)); break;
// We only support conversions to double, and only when that double can
// exactly represent a given integer. This means all 32-bit integers, and a
// subset of 64-bit integers.
case SCVTF_dw: {
set_dreg(dst, wreg(src));
break;
}
case SCVTF_dx: {
double value = static_cast<double>(xreg(src));
ASSERT(static_cast<int64_t>(value) == xreg(src));
set_dreg(dst, static_cast<int64_t>(value));
break;
}
// A 32-bit input can be handled in the same way as a 64-bit input, since
// the sign- or zero-extension will not affect the conversion.
case SCVTF_dx: set_dreg(dst, FixedToDouble(xreg(src), 0, round)); break;
case SCVTF_dw: set_dreg(dst, FixedToDouble(wreg(src), 0, round)); break;
case UCVTF_dx: set_dreg(dst, UFixedToDouble(xreg(src), 0, round)); break;
case UCVTF_dw: {
set_dreg(dst, static_cast<uint32_t>(wreg(src)));
set_dreg(dst, UFixedToDouble(static_cast<uint32_t>(wreg(src)), 0, round));
break;
}
case UCVTF_dx: {
double value = static_cast<double>(static_cast<uint64_t>(xreg(src)));
ASSERT(static_cast<uint64_t>(value) == static_cast<uint64_t>(xreg(src)));
set_dreg(dst, static_cast<uint64_t>(value));
case SCVTF_sx: set_sreg(dst, FixedToFloat(xreg(src), 0, round)); break;
case SCVTF_sw: set_sreg(dst, FixedToFloat(wreg(src), 0, round)); break;
case UCVTF_sx: set_sreg(dst, UFixedToFloat(xreg(src), 0, round)); break;
case UCVTF_sw: {
set_sreg(dst, UFixedToFloat(static_cast<uint32_t>(wreg(src)), 0, round));
break;
}
default: UNIMPLEMENTED();
default: UNREACHABLE();
}
}
void Simulator::VisitFPFixedPointConvert(Instruction* instr) {
AssertSupportedFPCR();
unsigned dst = instr->Rd();
unsigned src = instr->Rn();
int fbits = 64 - instr->FPScale();
// We only support two cases: unsigned and signed conversion from fixed point
// values in X registers to floating point values in D registers. We rely on
// casting to convert from integer to floating point, and assert that the
// fractional part of the number is zero.
FPRounding round = RMode();
switch (instr->Mask(FPFixedPointConvertMask)) {
case UCVTF_dx_fixed: {
uint64_t value = static_cast<uint64_t>(xreg(src));
ASSERT((value & ((1UL << fbits) - 1)) == 0);
set_dreg(dst, static_cast<double>(value >> fbits));
// A 32-bit input can be handled in the same way as a 64-bit input, since
// the sign- or zero-extension will not affect the conversion.
case SCVTF_dx_fixed:
set_dreg(dst, FixedToDouble(xreg(src), fbits, round));
break;
case SCVTF_dw_fixed:
set_dreg(dst, FixedToDouble(wreg(src), fbits, round));
break;
case UCVTF_dx_fixed:
set_dreg(dst, UFixedToDouble(xreg(src), fbits, round));
break;
case UCVTF_dw_fixed: {
set_dreg(dst,
UFixedToDouble(static_cast<uint32_t>(wreg(src)), fbits, round));
break;
}
case SCVTF_dx_fixed: {
int64_t value = xreg(src);
ASSERT((value & ((1UL << fbits) - 1)) == 0);
set_dreg(dst, static_cast<double>(value >> fbits));
case SCVTF_sx_fixed:
set_sreg(dst, FixedToFloat(xreg(src), fbits, round));
break;
case SCVTF_sw_fixed:
set_sreg(dst, FixedToFloat(wreg(src), fbits, round));
break;
case UCVTF_sx_fixed:
set_sreg(dst, UFixedToFloat(xreg(src), fbits, round));
break;
case UCVTF_sw_fixed: {
set_sreg(dst,
UFixedToFloat(static_cast<uint32_t>(wreg(src)), fbits, round));
break;
}
default: UNIMPLEMENTED();
default: UNREACHABLE();
}
}
@ -1379,6 +1454,8 @@ uint64_t Simulator::FPToUInt64(double value, FPRounding rmode) {
void Simulator::VisitFPCompare(Instruction* instr) {
AssertSupportedFPCR();
unsigned reg_size = instr->FPType() == FP32 ? kSRegSize : kDRegSize;
double fn_val = fpreg(reg_size, instr->Rn());
@ -1393,6 +1470,8 @@ void Simulator::VisitFPCompare(Instruction* instr) {
void Simulator::VisitFPConditionalCompare(Instruction* instr) {
AssertSupportedFPCR();
switch (instr->Mask(FPConditionalCompareMask)) {
case FCCMP_s:
case FCCMP_d: {
@ -1403,7 +1482,7 @@ void Simulator::VisitFPConditionalCompare(Instruction* instr) {
FPCompare(fpreg(reg_size, instr->Rn()), fpreg(reg_size, instr->Rm()));
} else {
// If the condition fails, set the status flags to the nzcv immediate.
SetFlags(instr->Nzcv() << Flags_offset);
nzcv().SetFlags(instr->Nzcv());
}
break;
}
@ -1413,6 +1492,8 @@ void Simulator::VisitFPConditionalCompare(Instruction* instr) {
void Simulator::VisitFPConditionalSelect(Instruction* instr) {
AssertSupportedFPCR();
unsigned reg_size = instr->FPType() == FP32 ? kSRegSize : kDRegSize;
double selected_val;
@ -1431,6 +1512,8 @@ void Simulator::VisitFPConditionalSelect(Instruction* instr) {
void Simulator::VisitFPDataProcessing1Source(Instruction* instr) {
AssertSupportedFPCR();
unsigned fd = instr->Rd();
unsigned fn = instr->Rn();
@ -1447,12 +1530,256 @@ void Simulator::VisitFPDataProcessing1Source(Instruction* instr) {
case FRINTN_d: set_dreg(fd, FPRoundInt(dreg(fn), FPTieEven)); break;
case FRINTZ_s: set_sreg(fd, FPRoundInt(sreg(fn), FPZero)); break;
case FRINTZ_d: set_dreg(fd, FPRoundInt(dreg(fn), FPZero)); break;
case FCVT_ds: set_dreg(fd, sreg(fn)); break;
case FCVT_ds: set_dreg(fd, FPToDouble(sreg(fn))); break;
case FCVT_sd: set_sreg(fd, FPToFloat(dreg(fn), FPTieEven)); break;
default: UNIMPLEMENTED();
}
}
// Assemble the specified IEEE-754 components into the target type and apply
// appropriate rounding.
// sign: 0 = positive, 1 = negative
// exponent: Unbiased IEEE-754 exponent.
// mantissa: The mantissa of the input. The top bit (which is not encoded for
// normal IEEE-754 values) must not be omitted. This bit has the
// value 'pow(2, exponent)'.
//
// The input value is assumed to be a normalized value. That is, the input may
// not be infinity or NaN. If the source value is subnormal, it must be
// normalized before calling this function such that the highest set bit in the
// mantissa has the value 'pow(2, exponent)'.
//
// Callers should use FPRoundToFloat or FPRoundToDouble directly, rather than
// calling a templated FPRound.
template <class T, int ebits, int mbits>
static T FPRound(int64_t sign, int64_t exponent, uint64_t mantissa,
FPRounding round_mode) {
ASSERT((sign == 0) || (sign == 1));
// Only the FPTieEven rounding mode is implemented.
ASSERT(round_mode == FPTieEven);
USE(round_mode);
// Rounding can promote subnormals to normals, and normals to infinities. For
// example, a double with exponent 127 (FLT_MAX_EXP) would appear to be
// encodable as a float, but rounding based on the low-order mantissa bits
// could make it overflow. With ties-to-even rounding, this value would become
// an infinity.
// ---- Rounding Method ----
//
// The exponent is irrelevant in the rounding operation, so we treat the
// lowest-order bit that will fit into the result ('onebit') as having
// the value '1'. Similarly, the highest-order bit that won't fit into
// the result ('halfbit') has the value '0.5'. The 'point' sits between
// 'onebit' and 'halfbit':
//
// These bits fit into the result.
// |---------------------|
// mantissa = 0bxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
// ||
// / |
// / halfbit
// onebit
//
// For subnormal outputs, the range of representable bits is smaller and
// the position of onebit and halfbit depends on the exponent of the
// input, but the method is otherwise similar.
//
// onebit(frac)
// |
// | halfbit(frac) halfbit(adjusted)
// | / /
// | | |
// 0b00.0 (exact) -> 0b00.0 (exact) -> 0b00
// 0b00.0... -> 0b00.0... -> 0b00
// 0b00.1 (exact) -> 0b00.0111..111 -> 0b00
// 0b00.1... -> 0b00.1... -> 0b01
// 0b01.0 (exact) -> 0b01.0 (exact) -> 0b01
// 0b01.0... -> 0b01.0... -> 0b01
// 0b01.1 (exact) -> 0b01.1 (exact) -> 0b10
// 0b01.1... -> 0b01.1... -> 0b10
// 0b10.0 (exact) -> 0b10.0 (exact) -> 0b10
// 0b10.0... -> 0b10.0... -> 0b10
// 0b10.1 (exact) -> 0b10.0111..111 -> 0b10
// 0b10.1... -> 0b10.1... -> 0b11
// 0b11.0 (exact) -> 0b11.0 (exact) -> 0b11
// ... / | / |
// / | / |
// / |
// adjusted = frac - (halfbit(mantissa) & ~onebit(frac)); / |
//
// mantissa = (mantissa >> shift) + halfbit(adjusted);
static const int mantissa_offset = 0;
static const int exponent_offset = mantissa_offset + mbits;
static const int sign_offset = exponent_offset + ebits;
ASSERT(sign_offset == (sizeof(T) * 8 - 1));
// Bail out early for zero inputs.
if (mantissa == 0) {
return sign << sign_offset;
}
// If all bits in the exponent are set, the value is infinite or NaN.
// This is true for all binary IEEE-754 formats.
static const int infinite_exponent = (1 << ebits) - 1;
static const int max_normal_exponent = infinite_exponent - 1;
// Apply the exponent bias to encode it for the result. Doing this early makes
// it easy to detect values that will be infinite or subnormal.
exponent += max_normal_exponent >> 1;
if (exponent > max_normal_exponent) {
// Overflow: The input is too large for the result type to represent. The
// FPTieEven rounding mode handles overflows using infinities.
exponent = infinite_exponent;
mantissa = 0;
return (sign << sign_offset) |
(exponent << exponent_offset) |
(mantissa << mantissa_offset);
}
// Calculate the shift required to move the top mantissa bit to the proper
// place in the destination type.
const int highest_significant_bit = 63 - CountLeadingZeros(mantissa, 64);
int shift = highest_significant_bit - mbits;
if (exponent <= 0) {
// The output will be subnormal (before rounding).
// For subnormal outputs, the shift must be adjusted by the exponent. The +1
// is necessary because the exponent of a subnormal value (encoded as 0) is
// the same as the exponent of the smallest normal value (encoded as 1).
shift += -exponent + 1;
// Handle inputs that would produce a zero output.
//
// Shifts higher than highest_significant_bit+1 will always produce a zero
// result. A shift of exactly highest_significant_bit+1 might produce a
// non-zero result after rounding.
if (shift > (highest_significant_bit + 1)) {
// The result will always be +/-0.0.
return sign << sign_offset;
}
// Properly encode the exponent for a subnormal output.
exponent = 0;
} else {
// Clear the topmost mantissa bit, since this is not encoded in IEEE-754
// normal values.
mantissa &= ~(1UL << highest_significant_bit);
}
if (shift > 0) {
// We have to shift the mantissa to the right. Some precision is lost, so we
// need to apply rounding.
uint64_t onebit_mantissa = (mantissa >> (shift)) & 1;
uint64_t halfbit_mantissa = (mantissa >> (shift-1)) & 1;
uint64_t adjusted = mantissa - (halfbit_mantissa & ~onebit_mantissa);
T halfbit_adjusted = (adjusted >> (shift-1)) & 1;
T result = (sign << sign_offset) |
(exponent << exponent_offset) |
((mantissa >> shift) << mantissa_offset);
// A very large mantissa can overflow during rounding. If this happens, the
// exponent should be incremented and the mantissa set to 1.0 (encoded as
// 0). Applying halfbit_adjusted after assembling the float has the nice
// side-effect that this case is handled for free.
//
// This also handles cases where a very large finite value overflows to
// infinity, or where a very large subnormal value overflows to become
// normal.
return result + halfbit_adjusted;
} else {
// We have to shift the mantissa to the left (or not at all). The input
// mantissa is exactly representable in the output mantissa, so apply no
// rounding correction.
return (sign << sign_offset) |
(exponent << exponent_offset) |
((mantissa << -shift) << mantissa_offset);
}
}
// See FPRound for a description of this function.
static inline double FPRoundToDouble(int64_t sign, int64_t exponent,
uint64_t mantissa, FPRounding round_mode) {
int64_t bits =
FPRound<int64_t, kDoubleExponentBits, kDoubleMantissaBits>(sign,
exponent,
mantissa,
round_mode);
return rawbits_to_double(bits);
}
// See FPRound for a description of this function.
static inline float FPRoundToFloat(int64_t sign, int64_t exponent,
uint64_t mantissa, FPRounding round_mode) {
int32_t bits =
FPRound<int32_t, kFloatExponentBits, kFloatMantissaBits>(sign,
exponent,
mantissa,
round_mode);
return rawbits_to_float(bits);
}
double Simulator::FixedToDouble(int64_t src, int fbits, FPRounding round) {
if (src >= 0) {
return UFixedToDouble(src, fbits, round);
} else {
// This works for all negative values, including INT64_MIN.
return -UFixedToDouble(-src, fbits, round);
}
}
double Simulator::UFixedToDouble(uint64_t src, int fbits, FPRounding round) {
// An input of 0 is a special case because the result is effectively
// subnormal: The exponent is encoded as 0 and there is no implicit 1 bit.
if (src == 0) {
return 0.0;
}
// Calculate the exponent. The highest significant bit will have the value
// 2^exponent.
const int highest_significant_bit = 63 - CountLeadingZeros(src, 64);
const int64_t exponent = highest_significant_bit - fbits;
return FPRoundToDouble(0, exponent, src, round);
}
float Simulator::FixedToFloat(int64_t src, int fbits, FPRounding round) {
if (src >= 0) {
return UFixedToFloat(src, fbits, round);
} else {
// This works for all negative values, including INT64_MIN.
return -UFixedToFloat(-src, fbits, round);
}
}
float Simulator::UFixedToFloat(uint64_t src, int fbits, FPRounding round) {
// An input of 0 is a special case because the result is effectively
// subnormal: The exponent is encoded as 0 and there is no implicit 1 bit.
if (src == 0) {
return 0.0f;
}
// Calculate the exponent. The highest significant bit will have the value
// 2^exponent.
const int highest_significant_bit = 63 - CountLeadingZeros(src, 64);
const int32_t exponent = highest_significant_bit - fbits;
return FPRoundToFloat(0, exponent, src, round);
}
double Simulator::FPRoundInt(double value, FPRounding round_mode) {
if ((value == 0.0) || (value == kFP64PositiveInfinity) ||
(value == kFP64NegativeInfinity) || isnan(value)) {
@ -1489,7 +1816,97 @@ double Simulator::FPRoundInt(double value, FPRounding round_mode) {
}
double Simulator::FPToDouble(float value) {
switch (fpclassify(value)) {
case FP_NAN: {
// Convert NaNs as the processor would, assuming that FPCR.DN (default
// NaN) is not set:
// - The sign is propagated.
// - The payload (mantissa) is transferred entirely, except that the top
// bit is forced to '1', making the result a quiet NaN. The unused
// (low-order) payload bits are set to 0.
uint32_t raw = float_to_rawbits(value);
uint64_t sign = raw >> 31;
uint64_t exponent = (1 << 11) - 1;
uint64_t payload = unsigned_bitextract_64(21, 0, raw);
payload <<= (52 - 23); // The unused low-order bits should be 0.
payload |= (1L << 51); // Force a quiet NaN.
return rawbits_to_double((sign << 63) | (exponent << 52) | payload);
}
case FP_ZERO:
case FP_NORMAL:
case FP_SUBNORMAL:
case FP_INFINITE: {
// All other inputs are preserved in a standard cast, because every value
// representable using an IEEE-754 float is also representable using an
// IEEE-754 double.
return static_cast<double>(value);
}
}
UNREACHABLE();
return static_cast<double>(value);
}
float Simulator::FPToFloat(double value, FPRounding round_mode) {
// Only the FPTieEven rounding mode is implemented.
ASSERT(round_mode == FPTieEven);
USE(round_mode);
switch (fpclassify(value)) {
case FP_NAN: {
// Convert NaNs as the processor would, assuming that FPCR.DN (default
// NaN) is not set:
// - The sign is propagated.
// - The payload (mantissa) is transferred as much as possible, except
// that the top bit is forced to '1', making the result a quiet NaN.
uint64_t raw = double_to_rawbits(value);
uint32_t sign = raw >> 63;
uint32_t exponent = (1 << 8) - 1;
uint32_t payload = unsigned_bitextract_64(50, 52 - 23, raw);
payload |= (1 << 22); // Force a quiet NaN.
return rawbits_to_float((sign << 31) | (exponent << 23) | payload);
}
case FP_ZERO:
case FP_INFINITE: {
// In a C++ cast, any value representable in the target type will be
// unchanged. This is always the case for +/-0.0 and infinities.
return static_cast<float>(value);
}
case FP_NORMAL:
case FP_SUBNORMAL: {
// Convert double-to-float as the processor would, assuming that FPCR.FZ
// (flush-to-zero) is not set.
uint64_t raw = double_to_rawbits(value);
// Extract the IEEE-754 double components.
uint32_t sign = raw >> 63;
// Extract the exponent and remove the IEEE-754 encoding bias.
int32_t exponent = unsigned_bitextract_64(62, 52, raw) - 1023;
// Extract the mantissa and add the implicit '1' bit.
uint64_t mantissa = unsigned_bitextract_64(51, 0, raw);
if (fpclassify(value) == FP_NORMAL) {
mantissa |= (1UL << 52);
}
return FPRoundToFloat(sign, exponent, mantissa, round_mode);
}
}
UNREACHABLE();
return value;
}
void Simulator::VisitFPDataProcessing2Source(Instruction* instr) {
AssertSupportedFPCR();
unsigned fd = instr->Rd();
unsigned fn = instr->Rn();
unsigned fm = instr->Rm();
@ -1513,6 +1930,8 @@ void Simulator::VisitFPDataProcessing2Source(Instruction* instr) {
void Simulator::VisitFPDataProcessing3Source(Instruction* instr) {
AssertSupportedFPCR();
unsigned fd = instr->Rd();
unsigned fn = instr->Rn();
unsigned fm = instr->Rm();
@ -1575,16 +1994,16 @@ void Simulator::VisitSystem(Instruction* instr) {
switch (instr->Mask(SystemSysRegMask)) {
case MRS: {
switch (instr->ImmSystemRegister()) {
case NZCV: set_xreg(instr->Rt(), nzcv()); break;
case NZCV: set_xreg(instr->Rt(), nzcv().RawValue()); break;
case FPCR: set_xreg(instr->Rt(), fpcr().RawValue()); break;
default: UNIMPLEMENTED();
}
break;
}
case MSR: {
switch (instr->ImmSystemRegister()) {
case NZCV:
SetFlags(xreg(instr->Rt()) & kConditionFlagsMask);
break;
case NZCV: nzcv().SetRawValue(xreg(instr->Rt())); break;
case FPCR: fpcr().SetRawValue(xreg(instr->Rt())); break;
default: UNIMPLEMENTED();
}
break;

View File

@ -32,6 +32,7 @@
#include "a64/instructions-a64.h"
#include "a64/assembler-a64.h"
#include "a64/disasm-a64.h"
#include "a64/instrument-a64.h"
namespace vixl {
@ -60,6 +61,60 @@ const Instr kPrintfOpcode = 0xdeb1;
const unsigned kPrintfTypeOffset = 1 * kInstructionSize;
const unsigned kPrintfLength = 2 * kInstructionSize;
// The proper way to initialize a simulated system register (such as NZCV) is as
// follows:
// SimSystemRegister nzcv = SimSystemRegister::DefaultValueFor(NZCV);
class SimSystemRegister {
public:
// The default constructor represents a register which has no writable bits.
// It is not possible to set its value to anything other than 0.
SimSystemRegister() : value_(0), write_ignore_mask_(0xffffffff) { }
inline uint32_t RawValue() const {
return value_;
}
inline void SetRawValue(uint32_t new_value) {
value_ = (value_ & write_ignore_mask_) | (new_value & ~write_ignore_mask_);
}
inline uint32_t Bits(int msb, int lsb) const {
return unsigned_bitextract_32(msb, lsb, value_);
}
inline int32_t SignedBits(int msb, int lsb) const {
return signed_bitextract_32(msb, lsb, value_);
}
void SetBits(int msb, int lsb, uint32_t bits);
// Default system register values.
static SimSystemRegister DefaultValueFor(SystemRegister id);
#define DEFINE_GETTER(Name, HighBit, LowBit, Func) \
inline uint32_t Name() const { return Func(HighBit, LowBit); } \
inline void Set##Name(uint32_t bits) { SetBits(HighBit, LowBit, bits); }
#define DEFINE_WRITE_IGNORE_MASK(Name, Mask) \
static const uint32_t Name##WriteIgnoreMask = ~static_cast<uint32_t>(Mask);
SYSTEM_REGISTER_FIELDS_LIST(DEFINE_GETTER, DEFINE_WRITE_IGNORE_MASK)
#undef DEFINE_ZERO_BITS
#undef DEFINE_GETTER
protected:
// Most system registers only implement a few of the bits in the word. Other
// bits are "read-as-zero, write-ignored". The write_ignore_mask argument
// describes the bits which are not modifiable.
SimSystemRegister(uint32_t value, uint32_t write_ignore_mask)
: value_(value), write_ignore_mask_(write_ignore_mask) { }
uint32_t value_;
uint32_t write_ignore_mask_;
};
class Simulator : public DecoderVisitor {
public:
explicit Simulator(Decoder* decoder, FILE* stream = stdout);
@ -278,14 +333,19 @@ class Simulator : public DecoderVisitor {
REGISTER_CODE_LIST(FPREG_ACCESSORS)
#undef FPREG_ACCESSORS
bool N() { return (psr_ & NFlag) != 0; }
bool Z() { return (psr_ & ZFlag) != 0; }
bool C() { return (psr_ & CFlag) != 0; }
bool V() { return (psr_ & VFlag) != 0; }
uint32_t nzcv() { return psr_ & (NFlag | ZFlag | CFlag | VFlag); }
bool N() { return nzcv_.N() != 0; }
bool Z() { return nzcv_.Z() != 0; }
bool C() { return nzcv_.C() != 0; }
bool V() { return nzcv_.V() != 0; }
SimSystemRegister& nzcv() { return nzcv_; }
// TODO(jbramley): Find a way to make the fpcr_ members return the proper
// types, so this accessor is not necessary.
FPRounding RMode() { return static_cast<FPRounding>(fpcr_.RMode()); }
SimSystemRegister& fpcr() { return fpcr_; }
// Debug helpers
void PrintFlags(bool print_all = false);
void PrintSystemRegisters(bool print_all = false);
void PrintRegisters(bool print_all_regs = false);
void PrintFPRegisters(bool print_all_regs = false);
void PrintProcessorState();
@ -312,6 +372,16 @@ class Simulator : public DecoderVisitor {
disasm_trace_ = value;
}
}
inline void set_instruction_stats(bool value) {
if (value != instruction_stats_) {
if (value) {
decoder_->AppendVisitor(instrumentation_);
} else {
decoder_->RemoveVisitor(instrumentation_);
}
instruction_stats_ = value;
}
}
protected:
// Simulation helpers ------------------------------------
@ -345,6 +415,7 @@ class Simulator : public DecoderVisitor {
return !Z() && (N() == V());
case le:
return !(!Z() && (N() == V()));
case nv: // Fall through.
case al:
return true;
default:
@ -405,6 +476,12 @@ class Simulator : public DecoderVisitor {
void FPCompare(double val0, double val1);
double FPRoundInt(double value, FPRounding round_mode);
double FPToDouble(float value);
float FPToFloat(double value, FPRounding round_mode);
double FixedToDouble(int64_t src, int fbits, FPRounding round_mode);
double UFixedToDouble(uint64_t src, int fbits, FPRounding round_mode);
float FixedToFloat(int64_t src, int fbits, FPRounding round_mode);
float UFixedToFloat(uint64_t src, int fbits, FPRounding round_mode);
int32_t FPToInt32(double value, FPRounding rmode);
int64_t FPToInt64(double value, FPRounding rmode);
uint32_t FPToUInt32(double value, FPRounding rmode);
@ -421,6 +498,9 @@ class Simulator : public DecoderVisitor {
FILE* stream_;
PrintDisassembler* print_disasm_;
// Instruction statistics instrumentation.
Instrument* instrumentation_;
// General purpose registers. Register 31 is the stack pointer.
SimRegister registers_[kNumberOfRegisters];
@ -430,17 +510,33 @@ class Simulator : public DecoderVisitor {
// Program Status Register.
// bits[31, 27]: Condition flags N, Z, C, and V.
// (Negative, Zero, Carry, Overflow)
uint32_t psr_;
SimSystemRegister nzcv_;
// Condition flags.
void SetFlags(uint32_t new_flags);
// Floating-Point Control Register
SimSystemRegister fpcr_;
static inline uint32_t CalcNFlag(int64_t result, unsigned reg_size) {
return ((result >> (reg_size - 1)) & 1) * NFlag;
// Only a subset of FPCR features are supported by the simulator. This helper
// checks that the FPCR settings are supported.
//
// This is checked when floating-point instructions are executed, not when
// FPCR is set. This allows generated code to modify FPCR for external
// functions, or to save and restore it when entering and leaving generated
// code.
void AssertSupportedFPCR() {
ASSERT(fpcr().DN() == 0); // No default-NaN support.
ASSERT(fpcr().FZ() == 0); // No flush-to-zero support.
ASSERT(fpcr().RMode() == FPTieEven); // Ties-to-even rounding only.
// The simulator does not support half-precision operations so fpcr().AHP()
// is irrelevant, and is not checked here.
}
static inline uint32_t CalcZFlag(int64_t result) {
return (result == 0) ? static_cast<uint32_t>(ZFlag) : 0;
static inline int CalcNFlag(uint64_t result, unsigned reg_size) {
return (result >> (reg_size - 1)) & 1;
}
static inline int CalcZFlag(uint64_t result) {
return result == 0;
}
static const uint32_t kConditionFlagsMask = 0xf0000000;
@ -468,8 +564,12 @@ class Simulator : public DecoderVisitor {
private:
bool coloured_trace_;
// Indicates whether the disassembly trace is active.
bool disasm_trace_;
// Indicates whether the instruction instrumentation is active.
bool instruction_stats_;
};
} // namespace vixl

View File

@ -79,7 +79,7 @@ inline uint32_t unsigned_bitextract_32(int msb, int lsb, uint32_t x) {
}
inline uint64_t unsigned_bitextract_64(int msb, int lsb, uint64_t x) {
return (x >> lsb) & ((1 << (1 + msb - lsb)) - 1);
return (x >> lsb) & ((static_cast<uint64_t>(1) << (1 + msb - lsb)) - 1);
}
inline int32_t signed_bitextract_32(int msb, int lsb, int32_t x) {

View File

@ -43,6 +43,9 @@ bool vixl::Cctest::trace_reg_ = false;
// No colour highlight by default.
bool vixl::Cctest::coloured_trace_ = false;
// No instruction statistics by default.
bool vixl::Cctest::instruction_stats_ = false;
// Instantiate a Cctest and add append it to the linked list.
vixl::Cctest::Cctest(const char* name, CctestFunction* callback)
: name_(name), callback_(callback), next_(NULL) {
@ -76,33 +79,28 @@ bool IsSpecialArgument(const char* arg) {
(strcmp(arg, "--debugger") == 0) ||
(strcmp(arg, "--trace_sim") == 0) ||
(strcmp(arg, "--trace_reg") == 0) ||
(strcmp(arg, "--coloured_trace") == 0);
(strcmp(arg, "--coloured_trace") == 0) ||
(strcmp(arg, "--instruction_stats") == 0);
}
void PrintHelpMessage() {
printf("Usage: ./cctest [options] [test names]\n"
"Run all tests specified on the command line.\n"
"--help print this help message.\n"
"--list list all available tests.\n"
"--run_all run all available tests.\n"
"--debugger run in the debugger.\n"
"--trace_sim generate a trace of simulated instructions.\n"
"--trace_reg generate a trace of simulated registers. "
"--help print this help message.\n"
"--list list all available tests.\n"
"--run_all run all available tests.\n"
"--debugger run in the debugger.\n"
"--trace_sim generate a trace of simulated instructions.\n"
"--trace_reg generate a trace of simulated registers. "
"Implies --debugger.\n"
"--coloured_trace generate coloured trace.\n");
"--coloured_trace generate coloured trace.\n"
"--instruction_stats log instruction statistics to vixl_stats.csv.\n");
}
int main(int argc, char* argv[]) {
// Parse the arguments, with the following priority:
// --help
// --list
// --run_all
// --debugger
// --trace_sim
// --trace_reg
// --coloured_trace
// test names
// Parse the arguments. Option flags must appear first, followed by an
// optional list of tests to run.
if (IsInArgs("--coloured_trace", argc, argv)) {
vixl::Cctest::set_coloured_trace(true);
@ -120,6 +118,10 @@ int main(int argc, char* argv[]) {
vixl::Cctest::set_trace_sim(true);
}
if (IsInArgs("--instruction_stats", argc, argv)) {
vixl::Cctest::set_instruction_stats(true);
}
if (IsInArgs("--help", argc, argv)) {
PrintHelpMessage();

View File

@ -52,6 +52,8 @@ class Cctest {
static void set_trace_reg(bool value) { trace_reg_ = value; }
static bool coloured_trace() { return coloured_trace_; }
static void set_coloured_trace(bool value) { coloured_trace_ = value; }
static bool instruction_stats() { return instruction_stats_; }
static void set_instruction_stats(bool value) { instruction_stats_ = value; }
// The debugger is needed to trace register values.
static bool run_debugger() { return debug_ || trace_reg_; }
@ -67,6 +69,7 @@ class Cctest {
static bool trace_sim_;
static bool trace_reg_;
static bool coloured_trace_;
static bool instruction_stats_;
};
// Define helper macros for cctest files.

View File

@ -140,10 +140,16 @@ void GenerateTestWrapper(MacroAssembler* masm, RegisterDump *regs) {
Debugger simulator(&decoder); \
simulator.set_coloured_trace(Cctest::coloured_trace()); \
PrintDisassembler* pdis = NULL; \
Instrument* inst = NULL; \
if (Cctest::trace_sim()) { \
pdis = new PrintDisassembler(stdout); \
decoder.PrependVisitor(pdis); \
} \
if (Cctest::instruction_stats()) { \
inst = new Instrument("vixl_stats.csv", 10); \
inst->Enable(); \
decoder.AppendVisitor(inst); \
} \
RegisterDump regs; \
\
Label test; \

View File

@ -26,7 +26,8 @@
#include <stdio.h>
#include <string.h>
#include <cmath>
#include <math.h>
#include <float.h>
#include "cctest.h"
#include "test-utils-a64.h"
@ -81,7 +82,7 @@ namespace vixl {
//
// e.g. ASSERT_EQUAL_64(0.5, d30);
//
// If more advance computation is required before the assert then access the
// If more advanced computation is required before the assert then access the
// RegisterDump named core directly:
//
// ASSERT_EQUAL_64(0x1234, core->reg_x0() & 0xffff);
@ -109,6 +110,7 @@ namespace vixl {
simulator->set_disasm_trace(Cctest::trace_sim()); \
} \
simulator->set_coloured_trace(Cctest::coloured_trace()); \
simulator->set_instruction_stats(Cctest::instruction_stats()); \
RegisterDump core
#define START() \
@ -122,9 +124,15 @@ namespace vixl {
if (Cctest::trace_sim()) { \
__ Trace(LOG_DISASM, TRACE_ENABLE); \
} \
} \
if (Cctest::instruction_stats()) { \
__ EnableInstrumentation(); \
}
#define END() \
if (Cctest::instruction_stats()) { \
__ DisableInstrumentation(); \
} \
if (Cctest::run_debugger()) { \
__ Trace(LOG_ALL, TRACE_DISABLE); \
} \
@ -1449,7 +1457,7 @@ TEST(branch_cond) {
// For each 'cmp' instruction below, condition codes other than the ones
// following it would branch.
__ Cmp(x1, Operand(0));
__ Cmp(x1, 0);
__ B(&wrong, eq);
__ B(&wrong, lo);
__ B(&wrong, mi);
@ -1462,7 +1470,7 @@ TEST(branch_cond) {
__ Mov(x0, 0x0);
__ Bind(&ok_1);
__ Cmp(x1, Operand(1));
__ Cmp(x1, 1);
__ B(&wrong, ne);
__ B(&wrong, lo);
__ B(&wrong, mi);
@ -1475,7 +1483,7 @@ TEST(branch_cond) {
__ Mov(x0, 0x0);
__ Bind(&ok_2);
__ Cmp(x1, Operand(2));
__ Cmp(x1, 2);
__ B(&wrong, eq);
__ B(&wrong, hs);
__ B(&wrong, pl);
@ -1488,7 +1496,7 @@ TEST(branch_cond) {
__ Mov(x0, 0x0);
__ Bind(&ok_3);
__ Cmp(x2, Operand(1));
__ Cmp(x2, 1);
__ B(&wrong, eq);
__ B(&wrong, lo);
__ B(&wrong, mi);
@ -1500,6 +1508,17 @@ TEST(branch_cond) {
__ B(&ok_4, le);
__ Mov(x0, 0x0);
__ Bind(&ok_4);
Label ok_5;
__ b(&ok_5, al);
__ Mov(x0, 0x0);
__ Bind(&ok_5);
Label ok_6;
__ b(&ok_6, nv);
__ Mov(x0, 0x0);
__ Bind(&ok_6);
END();
__ Bind(&wrong);
@ -3389,21 +3408,28 @@ TEST(ccmp) {
START();
__ Mov(w16, 0);
__ Mov(w17, 1);
__ Cmp(w16, Operand(w16));
__ Ccmp(w16, Operand(w17), NCFlag, eq);
__ Cmp(w16, w16);
__ Ccmp(w16, w17, NCFlag, eq);
__ Mrs(x0, NZCV);
__ Cmp(w16, Operand(w16));
__ Ccmp(w16, Operand(w17), NCFlag, ne);
__ Cmp(w16, w16);
__ Ccmp(w16, w17, NCFlag, ne);
__ Mrs(x1, NZCV);
__ Cmp(x16, Operand(x16));
__ Ccmn(x16, Operand(2), NZCVFlag, eq);
__ Cmp(x16, x16);
__ Ccmn(x16, 2, NZCVFlag, eq);
__ Mrs(x2, NZCV);
__ Cmp(x16, Operand(x16));
__ Ccmn(x16, Operand(2), NZCVFlag, ne);
__ Cmp(x16, x16);
__ Ccmn(x16, 2, NZCVFlag, ne);
__ Mrs(x3, NZCV);
__ ccmp(x16, x16, NZCVFlag, al);
__ Mrs(x4, NZCV);
__ ccmp(x16, x16, NZCVFlag, nv);
__ Mrs(x5, NZCV);
END();
RUN();
@ -3412,6 +3438,8 @@ TEST(ccmp) {
ASSERT_EQUAL_32(NCFlag, w1);
ASSERT_EQUAL_32(NoFlag, w2);
ASSERT_EQUAL_32(NZCVFlag, w3);
ASSERT_EQUAL_32(ZCFlag, w4);
ASSERT_EQUAL_32(ZCFlag, w5);
TEARDOWN();
}
@ -3498,6 +3526,9 @@ TEST(csel) {
__ Csinc(w2, w24, w25, mi);
__ Csinc(w3, w24, w25, pl);
__ csel(w13, w24, w25, al);
__ csel(x14, x24, x25, nv);
__ Cmp(x16, Operand(1));
__ Csinv(x4, x24, x25, gt);
__ Csinv(x5, x24, x25, le);
@ -3509,6 +3540,10 @@ TEST(csel) {
__ Cinc(x10, x25, ne);
__ Cinv(x11, x24, ne);
__ Cneg(x12, x24, ne);
__ csel(w15, w24, w25, al);
__ csel(x17, x24, x25, nv);
END();
RUN();
@ -3526,6 +3561,10 @@ TEST(csel) {
ASSERT_EQUAL_64(0x0000001f00000020UL, x10);
ASSERT_EQUAL_64(0xfffffff0fffffff0UL, x11);
ASSERT_EQUAL_64(0xfffffff0fffffff1UL, x12);
ASSERT_EQUAL_64(0x0000000f, x13);
ASSERT_EQUAL_64(0x0000000f0000000fUL, x14);
ASSERT_EQUAL_64(0x0000000f, x15);
ASSERT_EQUAL_64(0x0000000f0000000fUL, x17);
TEARDOWN();
}
@ -4443,37 +4482,43 @@ TEST(fccmp) {
__ Fmov(d19, -1.0);
__ Mov(x20, 0);
__ Cmp(x20, Operand(0));
__ Cmp(x20, 0);
__ Fccmp(s16, s16, NoFlag, eq);
__ Mrs(x0, NZCV);
__ Cmp(x20, Operand(0));
__ Cmp(x20, 0);
__ Fccmp(s16, s16, VFlag, ne);
__ Mrs(x1, NZCV);
__ Cmp(x20, Operand(0));
__ Cmp(x20, 0);
__ Fccmp(s16, s17, CFlag, ge);
__ Mrs(x2, NZCV);
__ Cmp(x20, Operand(0));
__ Cmp(x20, 0);
__ Fccmp(s16, s17, CVFlag, lt);
__ Mrs(x3, NZCV);
__ Cmp(x20, Operand(0));
__ Cmp(x20, 0);
__ Fccmp(d18, d18, ZFlag, le);
__ Mrs(x4, NZCV);
__ Cmp(x20, Operand(0));
__ Cmp(x20, 0);
__ Fccmp(d18, d18, ZVFlag, gt);
__ Mrs(x5, NZCV);
__ Cmp(x20, Operand(0));
__ Cmp(x20, 0);
__ Fccmp(d18, d19, ZCVFlag, ls);
__ Mrs(x6, NZCV);
__ Cmp(x20, Operand(0));
__ Cmp(x20, 0);
__ Fccmp(d18, d19, NFlag, hi);
__ Mrs(x7, NZCV);
__ fccmp(s16, s16, NFlag, al);
__ Mrs(x8, NZCV);
__ fccmp(d18, d18, NFlag, nv);
__ Mrs(x9, NZCV);
END();
RUN();
@ -4486,6 +4531,8 @@ TEST(fccmp) {
ASSERT_EQUAL_32(ZVFlag, w5);
ASSERT_EQUAL_32(CFlag, w6);
ASSERT_EQUAL_32(NFlag, w7);
ASSERT_EQUAL_32(ZCFlag, w8);
ASSERT_EQUAL_32(ZCFlag, w9);
TEARDOWN();
}
@ -4567,11 +4614,13 @@ TEST(fcsel) {
__ Fmov(d18, 3.0);
__ Fmov(d19, 4.0);
__ Cmp(x16, Operand(0));
__ Cmp(x16, 0);
__ Fcsel(s0, s16, s17, eq);
__ Fcsel(s1, s16, s17, ne);
__ Fcsel(d2, d18, d19, eq);
__ Fcsel(d3, d18, d19, ne);
__ fcsel(s4, s16, s17, al);
__ fcsel(d5, d18, d19, nv);
END();
RUN();
@ -4580,6 +4629,8 @@ TEST(fcsel) {
ASSERT_EQUAL_FP32(2.0, s1);
ASSERT_EQUAL_FP64(3.0, d2);
ASSERT_EQUAL_FP64(4.0, d3);
ASSERT_EQUAL_FP32(1.0, s4);
ASSERT_EQUAL_FP64(3.0, d5);
TEARDOWN();
}
@ -4879,7 +4930,7 @@ TEST(frintz) {
}
TEST(fcvt) {
TEST(fcvt_ds) {
SETUP();
START();
@ -4894,6 +4945,10 @@ TEST(fcvt) {
__ Fmov(s24, kFP32NegativeInfinity);
__ Fmov(s25, 0.0);
__ Fmov(s26, -0.0);
__ Fmov(s27, FLT_MAX);
__ Fmov(s28, FLT_MIN);
__ Fmov(s29, rawbits_to_float(0x7fc12345)); // Quiet NaN.
__ Fmov(s30, rawbits_to_float(0x7f812345)); // Signalling NaN.
__ Fcvt(d0, s16);
__ Fcvt(d1, s17);
@ -4906,6 +4961,10 @@ TEST(fcvt) {
__ Fcvt(d8, s24);
__ Fcvt(d9, s25);
__ Fcvt(d10, s26);
__ Fcvt(d11, s27);
__ Fcvt(d12, s28);
__ Fcvt(d13, s29);
__ Fcvt(d14, s30);
END();
RUN();
@ -4921,11 +4980,135 @@ TEST(fcvt) {
ASSERT_EQUAL_FP64(kFP64NegativeInfinity, d8);
ASSERT_EQUAL_FP64(0.0f, d9);
ASSERT_EQUAL_FP64(-0.0f, d10);
ASSERT_EQUAL_FP64(FLT_MAX, d11);
ASSERT_EQUAL_FP64(FLT_MIN, d12);
// Check that the NaN payload is preserved according to A64 conversion rules:
// - The sign bit is preserved.
// - The top bit of the mantissa is forced to 1 (making it a quiet NaN).
// - The remaining mantissa bits are copied until they run out.
// - The low-order bits that haven't already been assigned are set to 0.
ASSERT_EQUAL_FP64(rawbits_to_double(0x7ff82468a0000000), d13);
ASSERT_EQUAL_FP64(rawbits_to_double(0x7ff82468a0000000), d14);
TEARDOWN();
}
TEST(fcvt_sd) {
// There are a huge number of corner-cases to check, so this test iterates
// through a list. The list is then negated and checked again (since the sign
// is irrelevant in ties-to-even rounding), so the list shouldn't include any
// negative values.
//
// Note that this test only checks ties-to-even rounding, because that is all
// that the simulator supports.
struct {double in; float expected;} test[] = {
// Check some simple conversions.
{0.0, 0.0f},
{1.0, 1.0f},
{1.5, 1.5f},
{2.0, 2.0f},
{FLT_MAX, FLT_MAX},
// - The smallest normalized float.
{pow(2, -126), pow(2, -126)},
// - Normal floats that need (ties-to-even) rounding.
// For normalized numbers:
// bit 29 (0x0000000020000000) is the lowest-order bit which will
// fit in the float's mantissa.
{rawbits_to_double(0x3ff0000000000000), rawbits_to_float(0x3f800000)},
{rawbits_to_double(0x3ff0000000000001), rawbits_to_float(0x3f800000)},
{rawbits_to_double(0x3ff0000010000000), rawbits_to_float(0x3f800000)},
{rawbits_to_double(0x3ff0000010000001), rawbits_to_float(0x3f800001)},
{rawbits_to_double(0x3ff0000020000000), rawbits_to_float(0x3f800001)},
{rawbits_to_double(0x3ff0000020000001), rawbits_to_float(0x3f800001)},
{rawbits_to_double(0x3ff0000030000000), rawbits_to_float(0x3f800002)},
{rawbits_to_double(0x3ff0000030000001), rawbits_to_float(0x3f800002)},
{rawbits_to_double(0x3ff0000040000000), rawbits_to_float(0x3f800002)},
{rawbits_to_double(0x3ff0000040000001), rawbits_to_float(0x3f800002)},
{rawbits_to_double(0x3ff0000050000000), rawbits_to_float(0x3f800002)},
{rawbits_to_double(0x3ff0000050000001), rawbits_to_float(0x3f800003)},
{rawbits_to_double(0x3ff0000060000000), rawbits_to_float(0x3f800003)},
// - A mantissa that overflows into the exponent during rounding.
{rawbits_to_double(0x3feffffff0000000), rawbits_to_float(0x3f800000)},
// - The largest double that rounds to a normal float.
{rawbits_to_double(0x47efffffefffffff), rawbits_to_float(0x7f7fffff)},
// Doubles that are too big for a float.
{kFP64PositiveInfinity, kFP32PositiveInfinity},
{DBL_MAX, kFP32PositiveInfinity},
// - The smallest exponent that's too big for a float.
{pow(2, 128), kFP32PositiveInfinity},
// - This exponent is in range, but the value rounds to infinity.
{rawbits_to_double(0x47effffff0000000), kFP32PositiveInfinity},
// Doubles that are too small for a float.
// - The smallest (subnormal) double.
{DBL_MIN, 0.0},
// - The largest double which is too small for a subnormal float.
{rawbits_to_double(0x3690000000000000), rawbits_to_float(0x00000000)},
// Normal doubles that become subnormal floats.
// - The largest subnormal float.
{rawbits_to_double(0x380fffffc0000000), rawbits_to_float(0x007fffff)},
// - The smallest subnormal float.
{rawbits_to_double(0x36a0000000000000), rawbits_to_float(0x00000001)},
// - Subnormal floats that need (ties-to-even) rounding.
// For these subnormals:
// bit 34 (0x0000000400000000) is the lowest-order bit which will
// fit in the float's mantissa.
{rawbits_to_double(0x37c159e000000000), rawbits_to_float(0x00045678)},
{rawbits_to_double(0x37c159e000000001), rawbits_to_float(0x00045678)},
{rawbits_to_double(0x37c159e200000000), rawbits_to_float(0x00045678)},
{rawbits_to_double(0x37c159e200000001), rawbits_to_float(0x00045679)},
{rawbits_to_double(0x37c159e400000000), rawbits_to_float(0x00045679)},
{rawbits_to_double(0x37c159e400000001), rawbits_to_float(0x00045679)},
{rawbits_to_double(0x37c159e600000000), rawbits_to_float(0x0004567a)},
{rawbits_to_double(0x37c159e600000001), rawbits_to_float(0x0004567a)},
{rawbits_to_double(0x37c159e800000000), rawbits_to_float(0x0004567a)},
{rawbits_to_double(0x37c159e800000001), rawbits_to_float(0x0004567a)},
{rawbits_to_double(0x37c159ea00000000), rawbits_to_float(0x0004567a)},
{rawbits_to_double(0x37c159ea00000001), rawbits_to_float(0x0004567b)},
{rawbits_to_double(0x37c159ec00000000), rawbits_to_float(0x0004567b)},
// - The smallest double which rounds up to become a subnormal float.
{rawbits_to_double(0x3690000000000001), rawbits_to_float(0x00000001)},
// Check NaN payload preservation.
{rawbits_to_double(0x7ff82468a0000000), rawbits_to_float(0x7fc12345)},
{rawbits_to_double(0x7ff82468bfffffff), rawbits_to_float(0x7fc12345)},
// - Signalling NaNs become quiet NaNs.
{rawbits_to_double(0x7ff02468a0000000), rawbits_to_float(0x7fc12345)},
{rawbits_to_double(0x7ff02468bfffffff), rawbits_to_float(0x7fc12345)},
{rawbits_to_double(0x7ff000001fffffff), rawbits_to_float(0x7fc00000)},
};
int count = sizeof(test) / sizeof(test[0]);
for (int i = 0; i < count; i++) {
double in = test[i].in;
float expected = test[i].expected;
// We only expect positive input.
ASSERT(signbit(in) == 0);
ASSERT(signbit(expected) == 0);
SETUP();
START();
__ Fmov(d10, in);
__ Fcvt(s20, d10);
__ Fmov(d11, -in);
__ Fcvt(s21, d11);
END();
RUN();
ASSERT_EQUAL_FP32(expected, s20);
ASSERT_EQUAL_FP32(-expected, s21);
TEARDOWN();
}
}
TEST(fcvtms) {
SETUP();
@ -5540,133 +5723,328 @@ TEST(fcvtzu) {
}
TEST(scvtf_ucvtf) {
// Test that scvtf and ucvtf can convert the 64-bit input into the expected
// value. All possible values of 'fbits' are tested. The expected value is
// modified accordingly in each case.
//
// The expected value is specified as the bit encoding of the expected double
// produced by scvtf (expected_scvtf_bits) as well as ucvtf
// (expected_ucvtf_bits).
//
// Where the input value is representable by int32_t or uint32_t, conversions
// from W registers will also be tested.
static void TestUScvtfHelper(uint64_t in,
uint64_t expected_scvtf_bits,
uint64_t expected_ucvtf_bits) {
uint64_t u64 = in;
uint32_t u32 = u64 & 0xffffffff;
int64_t s64 = static_cast<int64_t>(in);
int32_t s32 = s64 & 0x7fffffff;
bool cvtf_s32 = (s64 == s32);
bool cvtf_u32 = (u64 == u32);
double results_scvtf_x[65];
double results_ucvtf_x[65];
double results_scvtf_w[33];
double results_ucvtf_w[33];
SETUP();
START();
__ Mov(w0, 42424242);
__ Mov(x1, 0x7ffffffffffffc00UL); // Largest double < INT64_MAX.
__ Mov(w2, 0xffffffff); // 32-bit -1.
__ Mov(x3, 0xffffffffffffffffUL); // 64-bit -1.
__ Mov(x4, 0xfffffffffffff800UL); // Largest double < UINT64_MAX.
__ Scvtf(d0, w0);
__ Scvtf(d1, x1);
__ Scvtf(d2, w2);
__ Scvtf(d3, x2);
__ Scvtf(d4, x3);
__ Scvtf(d5, x4);
__ Ucvtf(d6, w0);
__ Ucvtf(d7, x1);
__ Ucvtf(d8, w2);
__ Ucvtf(d9, x2);
__ Ucvtf(d10, x4);
END();
__ Mov(x0, reinterpret_cast<int64_t>(results_scvtf_x));
__ Mov(x1, reinterpret_cast<int64_t>(results_ucvtf_x));
__ Mov(x2, reinterpret_cast<int64_t>(results_scvtf_w));
__ Mov(x3, reinterpret_cast<int64_t>(results_ucvtf_w));
__ Mov(x10, s64);
// Corrupt the top word, in case it is accidentally used during W-register
// conversions.
__ Mov(x11, 0x5555555555555555);
__ Bfi(x11, x10, 0, kWRegSize);
// Test integer conversions.
__ Scvtf(d0, x10);
__ Ucvtf(d1, x10);
__ Scvtf(d2, w11);
__ Ucvtf(d3, w11);
__ Str(d0, MemOperand(x0));
__ Str(d1, MemOperand(x1));
__ Str(d2, MemOperand(x2));
__ Str(d3, MemOperand(x3));
// Test all possible values of fbits.
for (int fbits = 1; fbits <= 32; fbits++) {
__ Scvtf(d0, x10, fbits);
__ Ucvtf(d1, x10, fbits);
__ Scvtf(d2, w11, fbits);
__ Ucvtf(d3, w11, fbits);
__ Str(d0, MemOperand(x0, fbits * kDRegSizeInBytes));
__ Str(d1, MemOperand(x1, fbits * kDRegSizeInBytes));
__ Str(d2, MemOperand(x2, fbits * kDRegSizeInBytes));
__ Str(d3, MemOperand(x3, fbits * kDRegSizeInBytes));
}
// Conversions from W registers can only handle fbits values <= 32, so just
// test conversions from X registers for 32 < fbits <= 64.
for (int fbits = 33; fbits <= 64; fbits++) {
__ Scvtf(d0, x10, fbits);
__ Ucvtf(d1, x10, fbits);
__ Str(d0, MemOperand(x0, fbits * kDRegSizeInBytes));
__ Str(d1, MemOperand(x1, fbits * kDRegSizeInBytes));
}
END();
RUN();
ASSERT_EQUAL_FP64(42424242.0, d0);
ASSERT_EQUAL_FP64(9223372036854774784.0, d1);
ASSERT_EQUAL_FP64(-1.0, d2);
ASSERT_EQUAL_FP64(4294967295.0, d3);
ASSERT_EQUAL_FP64(-1.0, d4);
ASSERT_EQUAL_FP64(-2048.0, d5);
ASSERT_EQUAL_FP64(42424242.0, d6);
ASSERT_EQUAL_FP64(9223372036854774784.0, d7);
ASSERT_EQUAL_FP64(4294967295.0, d8);
ASSERT_EQUAL_FP64(4294967295.0, d9);
ASSERT_EQUAL_FP64(18446744073709549568.0, d10);
// Check the results.
double expected_scvtf_base = rawbits_to_double(expected_scvtf_bits);
double expected_ucvtf_base = rawbits_to_double(expected_ucvtf_bits);
for (int fbits = 0; fbits <= 32; fbits++) {
double expected_scvtf = expected_scvtf_base / pow(2, fbits);
double expected_ucvtf = expected_ucvtf_base / pow(2, fbits);
ASSERT_EQUAL_FP64(expected_scvtf, results_scvtf_x[fbits]);
ASSERT_EQUAL_FP64(expected_ucvtf, results_ucvtf_x[fbits]);
if (cvtf_s32) ASSERT_EQUAL_FP64(expected_scvtf, results_scvtf_w[fbits]);
if (cvtf_u32) ASSERT_EQUAL_FP64(expected_ucvtf, results_ucvtf_w[fbits]);
}
for (int fbits = 33; fbits <= 64; fbits++) {
double expected_scvtf = expected_scvtf_base / pow(2, fbits);
double expected_ucvtf = expected_ucvtf_base / pow(2, fbits);
ASSERT_EQUAL_FP64(expected_scvtf, results_scvtf_x[fbits]);
ASSERT_EQUAL_FP64(expected_ucvtf, results_ucvtf_x[fbits]);
}
TEARDOWN();
}
TEST(scvtf_ucvtf_fixed) {
TEST(scvtf_ucvtf_double) {
// Simple conversions of positive numbers which require no rounding; the
// results should not depened on the rounding mode, and ucvtf and scvtf should
// produce the same result.
TestUScvtfHelper(0x0000000000000000, 0x0000000000000000, 0x0000000000000000);
TestUScvtfHelper(0x0000000000000001, 0x3ff0000000000000, 0x3ff0000000000000);
TestUScvtfHelper(0x0000000040000000, 0x41d0000000000000, 0x41d0000000000000);
TestUScvtfHelper(0x0000000100000000, 0x41f0000000000000, 0x41f0000000000000);
TestUScvtfHelper(0x4000000000000000, 0x43d0000000000000, 0x43d0000000000000);
// Test mantissa extremities.
TestUScvtfHelper(0x4000000000000400, 0x43d0000000000001, 0x43d0000000000001);
// The largest int32_t that fits in a double.
TestUScvtfHelper(0x000000007fffffff, 0x41dfffffffc00000, 0x41dfffffffc00000);
// Values that would be negative if treated as an int32_t.
TestUScvtfHelper(0x00000000ffffffff, 0x41efffffffe00000, 0x41efffffffe00000);
TestUScvtfHelper(0x0000000080000000, 0x41e0000000000000, 0x41e0000000000000);
TestUScvtfHelper(0x0000000080000001, 0x41e0000000200000, 0x41e0000000200000);
// The largest int64_t that fits in a double.
TestUScvtfHelper(0x7ffffffffffffc00, 0x43dfffffffffffff, 0x43dfffffffffffff);
// Check for bit pattern reproduction.
TestUScvtfHelper(0x0123456789abcde0, 0x43723456789abcde, 0x43723456789abcde);
TestUScvtfHelper(0x0000000012345678, 0x41b2345678000000, 0x41b2345678000000);
// Simple conversions of negative int64_t values. These require no rounding,
// and the results should not depend on the rounding mode.
TestUScvtfHelper(0xffffffffc0000000, 0xc1d0000000000000, 0x43effffffff80000);
TestUScvtfHelper(0xffffffff00000000, 0xc1f0000000000000, 0x43efffffffe00000);
TestUScvtfHelper(0xc000000000000000, 0xc3d0000000000000, 0x43e8000000000000);
// Conversions which require rounding.
TestUScvtfHelper(0x1000000000000000, 0x43b0000000000000, 0x43b0000000000000);
TestUScvtfHelper(0x1000000000000001, 0x43b0000000000000, 0x43b0000000000000);
TestUScvtfHelper(0x1000000000000080, 0x43b0000000000000, 0x43b0000000000000);
TestUScvtfHelper(0x1000000000000081, 0x43b0000000000001, 0x43b0000000000001);
TestUScvtfHelper(0x1000000000000100, 0x43b0000000000001, 0x43b0000000000001);
TestUScvtfHelper(0x1000000000000101, 0x43b0000000000001, 0x43b0000000000001);
TestUScvtfHelper(0x1000000000000180, 0x43b0000000000002, 0x43b0000000000002);
TestUScvtfHelper(0x1000000000000181, 0x43b0000000000002, 0x43b0000000000002);
TestUScvtfHelper(0x1000000000000200, 0x43b0000000000002, 0x43b0000000000002);
TestUScvtfHelper(0x1000000000000201, 0x43b0000000000002, 0x43b0000000000002);
TestUScvtfHelper(0x1000000000000280, 0x43b0000000000002, 0x43b0000000000002);
TestUScvtfHelper(0x1000000000000281, 0x43b0000000000003, 0x43b0000000000003);
TestUScvtfHelper(0x1000000000000300, 0x43b0000000000003, 0x43b0000000000003);
// Check rounding of negative int64_t values (and large uint64_t values).
TestUScvtfHelper(0x8000000000000000, 0xc3e0000000000000, 0x43e0000000000000);
TestUScvtfHelper(0x8000000000000001, 0xc3e0000000000000, 0x43e0000000000000);
TestUScvtfHelper(0x8000000000000200, 0xc3e0000000000000, 0x43e0000000000000);
TestUScvtfHelper(0x8000000000000201, 0xc3dfffffffffffff, 0x43e0000000000000);
TestUScvtfHelper(0x8000000000000400, 0xc3dfffffffffffff, 0x43e0000000000000);
TestUScvtfHelper(0x8000000000000401, 0xc3dfffffffffffff, 0x43e0000000000001);
TestUScvtfHelper(0x8000000000000600, 0xc3dffffffffffffe, 0x43e0000000000001);
TestUScvtfHelper(0x8000000000000601, 0xc3dffffffffffffe, 0x43e0000000000001);
TestUScvtfHelper(0x8000000000000800, 0xc3dffffffffffffe, 0x43e0000000000001);
TestUScvtfHelper(0x8000000000000801, 0xc3dffffffffffffe, 0x43e0000000000001);
TestUScvtfHelper(0x8000000000000a00, 0xc3dffffffffffffe, 0x43e0000000000001);
TestUScvtfHelper(0x8000000000000a01, 0xc3dffffffffffffd, 0x43e0000000000001);
TestUScvtfHelper(0x8000000000000c00, 0xc3dffffffffffffd, 0x43e0000000000002);
// Round up to produce a result that's too big for the input to represent.
TestUScvtfHelper(0x7ffffffffffffe00, 0x43e0000000000000, 0x43e0000000000000);
TestUScvtfHelper(0x7fffffffffffffff, 0x43e0000000000000, 0x43e0000000000000);
TestUScvtfHelper(0xfffffffffffffc00, 0xc090000000000000, 0x43f0000000000000);
TestUScvtfHelper(0xffffffffffffffff, 0xbff0000000000000, 0x43f0000000000000);
}
// The same as TestUScvtfHelper, but convert to floats.
static void TestUScvtf32Helper(uint64_t in,
uint32_t expected_scvtf_bits,
uint32_t expected_ucvtf_bits) {
uint64_t u64 = in;
uint32_t u32 = u64 & 0xffffffff;
int64_t s64 = static_cast<int64_t>(in);
int32_t s32 = s64 & 0x7fffffff;
bool cvtf_s32 = (s64 == s32);
bool cvtf_u32 = (u64 == u32);
float results_scvtf_x[65];
float results_ucvtf_x[65];
float results_scvtf_w[33];
float results_ucvtf_w[33];
SETUP();
START();
__ Mov(x0, 0);
__ Mov(x1, 0x0000000000010000UL);
__ Mov(x2, 0x7fffffffffff0000UL);
__ Mov(x3, 0x8000000000000000UL);
__ Mov(x4, 0xffffffffffff0000UL);
__ Mov(x5, 0x0000000100000000UL);
__ Mov(x6, 0x7fffffff00000000UL);
__ Mov(x7, 0xffffffff00000000UL);
__ Mov(x8, 0x1000000000000000UL);
__ Mov(x9, 0x7000000000000000UL);
__ Mov(x10, 0xf000000000000000UL);
__ Scvtf(d0, x0, 16);
__ Scvtf(d1, x1, 16);
__ Scvtf(d2, x2, 16);
__ Scvtf(d3, x3, 16);
__ Scvtf(d4, x4, 16);
__ Scvtf(d5, x0, 32);
__ Scvtf(d6, x5, 32);
__ Scvtf(d7, x6, 32);
__ Scvtf(d8, x3, 32);
__ Scvtf(d9, x7, 32);
__ Scvtf(d10, x0, 60);
__ Scvtf(d11, x8, 60);
__ Scvtf(d12, x9, 60);
__ Scvtf(d13, x3, 60);
__ Scvtf(d14, x10, 60);
__ Ucvtf(d15, x0, 16);
__ Ucvtf(d16, x1, 16);
__ Ucvtf(d17, x2, 16);
__ Ucvtf(d18, x3, 16);
__ Ucvtf(d19, x4, 16);
__ Ucvtf(d20, x0, 32);
__ Ucvtf(d21, x5, 32);
__ Ucvtf(d22, x6, 32);
__ Ucvtf(d23, x3, 32);
__ Ucvtf(d24, x7, 32);
__ Ucvtf(d25, x0, 60);
__ Ucvtf(d26, x8, 60);
__ Ucvtf(d27, x9, 60);
__ Ucvtf(d28, x3, 60);
__ Ucvtf(d29, x10, 60);
__ Mov(x0, reinterpret_cast<int64_t>(results_scvtf_x));
__ Mov(x1, reinterpret_cast<int64_t>(results_ucvtf_x));
__ Mov(x2, reinterpret_cast<int64_t>(results_scvtf_w));
__ Mov(x3, reinterpret_cast<int64_t>(results_ucvtf_w));
__ Mov(x10, s64);
// Corrupt the top word, in case it is accidentally used during W-register
// conversions.
__ Mov(x11, 0x5555555555555555);
__ Bfi(x11, x10, 0, kWRegSize);
// Test integer conversions.
__ Scvtf(s0, x10);
__ Ucvtf(s1, x10);
__ Scvtf(s2, w11);
__ Ucvtf(s3, w11);
__ Str(s0, MemOperand(x0));
__ Str(s1, MemOperand(x1));
__ Str(s2, MemOperand(x2));
__ Str(s3, MemOperand(x3));
// Test all possible values of fbits.
for (int fbits = 1; fbits <= 32; fbits++) {
__ Scvtf(s0, x10, fbits);
__ Ucvtf(s1, x10, fbits);
__ Scvtf(s2, w11, fbits);
__ Ucvtf(s3, w11, fbits);
__ Str(s0, MemOperand(x0, fbits * kSRegSizeInBytes));
__ Str(s1, MemOperand(x1, fbits * kSRegSizeInBytes));
__ Str(s2, MemOperand(x2, fbits * kSRegSizeInBytes));
__ Str(s3, MemOperand(x3, fbits * kSRegSizeInBytes));
}
// Conversions from W registers can only handle fbits values <= 32, so just
// test conversions from X registers for 32 < fbits <= 64.
for (int fbits = 33; fbits <= 64; fbits++) {
__ Scvtf(s0, x10, fbits);
__ Ucvtf(s1, x10, fbits);
__ Str(s0, MemOperand(x0, fbits * kSRegSizeInBytes));
__ Str(s1, MemOperand(x1, fbits * kSRegSizeInBytes));
}
END();
RUN();
ASSERT_EQUAL_FP64(0.0, d0);
ASSERT_EQUAL_FP64(1.0, d1);
ASSERT_EQUAL_FP64(140737488355327.0, d2);
ASSERT_EQUAL_FP64(-140737488355328.0, d3);
ASSERT_EQUAL_FP64(-1.0, d4);
ASSERT_EQUAL_FP64(0.0, d5);
ASSERT_EQUAL_FP64(1.0, d6);
ASSERT_EQUAL_FP64(2147483647.0, d7);
ASSERT_EQUAL_FP64(-2147483648.0, d8);
ASSERT_EQUAL_FP64(-1.0, d9);
ASSERT_EQUAL_FP64(0.0, d10);
ASSERT_EQUAL_FP64(1.0, d11);
ASSERT_EQUAL_FP64(7.0, d12);
ASSERT_EQUAL_FP64(-8.0, d13);
ASSERT_EQUAL_FP64(-1.0, d14);
// Check the results.
float expected_scvtf_base = rawbits_to_float(expected_scvtf_bits);
float expected_ucvtf_base = rawbits_to_float(expected_ucvtf_bits);
ASSERT_EQUAL_FP64(0.0, d15);
ASSERT_EQUAL_FP64(1.0, d16);
ASSERT_EQUAL_FP64(140737488355327.0, d17);
ASSERT_EQUAL_FP64(140737488355328.0, d18);
ASSERT_EQUAL_FP64(281474976710655.0, d19);
ASSERT_EQUAL_FP64(0.0, d20);
ASSERT_EQUAL_FP64(1.0, d21);
ASSERT_EQUAL_FP64(2147483647.0, d22);
ASSERT_EQUAL_FP64(2147483648.0, d23);
ASSERT_EQUAL_FP64(4294967295.0, d24);
ASSERT_EQUAL_FP64(0.0, d25);
ASSERT_EQUAL_FP64(1.0, d26);
ASSERT_EQUAL_FP64(7.0, d27);
ASSERT_EQUAL_FP64(8.0, d28);
ASSERT_EQUAL_FP64(15.0, d29);
for (int fbits = 0; fbits <= 32; fbits++) {
float expected_scvtf = expected_scvtf_base / pow(2, fbits);
float expected_ucvtf = expected_ucvtf_base / pow(2, fbits);
ASSERT_EQUAL_FP32(expected_scvtf, results_scvtf_x[fbits]);
ASSERT_EQUAL_FP32(expected_ucvtf, results_ucvtf_x[fbits]);
if (cvtf_s32) ASSERT_EQUAL_FP32(expected_scvtf, results_scvtf_w[fbits]);
if (cvtf_u32) ASSERT_EQUAL_FP32(expected_ucvtf, results_ucvtf_w[fbits]);
break;
}
for (int fbits = 33; fbits <= 64; fbits++) {
break;
float expected_scvtf = expected_scvtf_base / pow(2, fbits);
float expected_ucvtf = expected_ucvtf_base / pow(2, fbits);
ASSERT_EQUAL_FP32(expected_scvtf, results_scvtf_x[fbits]);
ASSERT_EQUAL_FP32(expected_ucvtf, results_ucvtf_x[fbits]);
}
TEARDOWN();
}
TEST(scvtf_ucvtf_float) {
// Simple conversions of positive numbers which require no rounding; the
// results should not depened on the rounding mode, and ucvtf and scvtf should
// produce the same result.
TestUScvtf32Helper(0x0000000000000000, 0x00000000, 0x00000000);
TestUScvtf32Helper(0x0000000000000001, 0x3f800000, 0x3f800000);
TestUScvtf32Helper(0x0000000040000000, 0x4e800000, 0x4e800000);
TestUScvtf32Helper(0x0000000100000000, 0x4f800000, 0x4f800000);
TestUScvtf32Helper(0x4000000000000000, 0x5e800000, 0x5e800000);
// Test mantissa extremities.
TestUScvtf32Helper(0x0000000000800001, 0x4b000001, 0x4b000001);
TestUScvtf32Helper(0x4000008000000000, 0x5e800001, 0x5e800001);
// The largest int32_t that fits in a float.
TestUScvtf32Helper(0x000000007fffff80, 0x4effffff, 0x4effffff);
// Values that would be negative if treated as an int32_t.
TestUScvtf32Helper(0x00000000ffffff00, 0x4f7fffff, 0x4f7fffff);
TestUScvtf32Helper(0x0000000080000000, 0x4f000000, 0x4f000000);
TestUScvtf32Helper(0x0000000080000100, 0x4f000001, 0x4f000001);
// The largest int64_t that fits in a float.
TestUScvtf32Helper(0x7fffff8000000000, 0x5effffff, 0x5effffff);
// Check for bit pattern reproduction.
TestUScvtf32Helper(0x0000000000876543, 0x4b076543, 0x4b076543);
// Simple conversions of negative int64_t values. These require no rounding,
// and the results should not depend on the rounding mode.
TestUScvtf32Helper(0xfffffc0000000000, 0xd4800000, 0x5f7ffffc);
TestUScvtf32Helper(0xc000000000000000, 0xde800000, 0x5f400000);
// Conversions which require rounding.
TestUScvtf32Helper(0x0000800000000000, 0x57000000, 0x57000000);
TestUScvtf32Helper(0x0000800000000001, 0x57000000, 0x57000000);
TestUScvtf32Helper(0x0000800000800000, 0x57000000, 0x57000000);
TestUScvtf32Helper(0x0000800000800001, 0x57000001, 0x57000001);
TestUScvtf32Helper(0x0000800001000000, 0x57000001, 0x57000001);
TestUScvtf32Helper(0x0000800001000001, 0x57000001, 0x57000001);
TestUScvtf32Helper(0x0000800001800000, 0x57000002, 0x57000002);
TestUScvtf32Helper(0x0000800001800001, 0x57000002, 0x57000002);
TestUScvtf32Helper(0x0000800002000000, 0x57000002, 0x57000002);
TestUScvtf32Helper(0x0000800002000001, 0x57000002, 0x57000002);
TestUScvtf32Helper(0x0000800002800000, 0x57000002, 0x57000002);
TestUScvtf32Helper(0x0000800002800001, 0x57000003, 0x57000003);
TestUScvtf32Helper(0x0000800003000000, 0x57000003, 0x57000003);
// Check rounding of negative int64_t values (and large uint64_t values).
TestUScvtf32Helper(0x8000000000000000, 0xdf000000, 0x5f000000);
TestUScvtf32Helper(0x8000000000000001, 0xdf000000, 0x5f000000);
TestUScvtf32Helper(0x8000004000000000, 0xdf000000, 0x5f000000);
TestUScvtf32Helper(0x8000004000000001, 0xdeffffff, 0x5f000000);
TestUScvtf32Helper(0x8000008000000000, 0xdeffffff, 0x5f000000);
TestUScvtf32Helper(0x8000008000000001, 0xdeffffff, 0x5f000001);
TestUScvtf32Helper(0x800000c000000000, 0xdefffffe, 0x5f000001);
TestUScvtf32Helper(0x800000c000000001, 0xdefffffe, 0x5f000001);
TestUScvtf32Helper(0x8000010000000000, 0xdefffffe, 0x5f000001);
TestUScvtf32Helper(0x8000010000000001, 0xdefffffe, 0x5f000001);
TestUScvtf32Helper(0x8000014000000000, 0xdefffffe, 0x5f000001);
TestUScvtf32Helper(0x8000014000000001, 0xdefffffd, 0x5f000001);
TestUScvtf32Helper(0x8000018000000000, 0xdefffffd, 0x5f000002);
// Round up to produce a result that's too big for the input to represent.
TestUScvtf32Helper(0x000000007fffffc0, 0x4f000000, 0x4f000000);
TestUScvtf32Helper(0x000000007fffffff, 0x4f000000, 0x4f000000);
TestUScvtf32Helper(0x00000000ffffff80, 0x4f800000, 0x4f800000);
TestUScvtf32Helper(0x00000000ffffffff, 0x4f800000, 0x4f800000);
TestUScvtf32Helper(0x7fffffc000000000, 0x5f000000, 0x5f000000);
TestUScvtf32Helper(0x7fffffffffffffff, 0x5f000000, 0x5f000000);
TestUScvtf32Helper(0xffffff8000000000, 0xd3000000, 0x5f800000);
TestUScvtf32Helper(0xffffffffffffffff, 0xbf800000, 0x5f800000);
}
TEST(system_mrs) {
SETUP();
@ -5686,22 +6064,35 @@ TEST(system_mrs) {
// Set the Z, C and V flags.
__ Add(w0, w2, w2, SetFlags);
__ Mrs(x5, NZCV);
// Read the default FPCR.
__ Mrs(x6, FPCR);
END();
RUN();
// TODO: The assertions below should be ASSERT_EQUAL_64(flag, X register), but
// the flag (enum) will be sign extended, since the assertion's argument type
// is int64_t.
// NZCV
ASSERT_EQUAL_32(ZCFlag, w3);
ASSERT_EQUAL_32(NFlag, w4);
ASSERT_EQUAL_32(ZCVFlag, w5);
// FPCR
// The default FPCR on Linux-based platforms is 0.
ASSERT_EQUAL_32(0, w6);
TEARDOWN();
}
TEST(system_msr) {
// All FPCR fields that must be implemented: AHP, DN, FZ, RMode
const uint64_t fpcr_core = 0x07c00000;
// All FPCR fields (including fields which may be read-as-zero):
// Stride, Len
// IDE, IXE, UFE, OFE, DZE, IOE
const uint64_t fpcr_all = fpcr_core | 0x00379f00;
SETUP();
START();
@ -5728,6 +6119,27 @@ TEST(system_msr) {
__ Cinc(x7, x7, hs); // C
__ Cinc(x7, x7, vc); // !V
// All core FPCR fields must be writable.
__ Mov(x8, fpcr_core);
__ Msr(FPCR, x8);
__ Mrs(x8, FPCR);
// All FPCR fields, including optional ones. This part of the test doesn't
// achieve much other than ensuring that supported fields can be cleared by
// the next test.
__ Mov(x9, fpcr_all);
__ Msr(FPCR, x9);
__ Mrs(x9, FPCR);
__ And(x9, x9, fpcr_core);
// The undefined bits must ignore writes.
// It's conceivable that a future version of the architecture could use these
// fields (making this test fail), but in the meantime this is a useful test
// for the simulator.
__ Mov(x10, ~fpcr_all);
__ Msr(FPCR, x10);
__ Mrs(x10, FPCR);
END();
RUN();
@ -5735,6 +6147,10 @@ TEST(system_msr) {
// We should have incremented x7 (from 0) exactly 8 times.
ASSERT_EQUAL_64(8, x7);
ASSERT_EQUAL_64(fpcr_core, x8);
ASSERT_EQUAL_64(fpcr_core, x9);
ASSERT_EQUAL_64(0, x10);
TEARDOWN();
}

View File

@ -57,6 +57,18 @@
abort(); \
}
#define COMPARE_PREFIX(ASM, EXP) \
masm->Reset(); \
masm->ASM; \
masm->FinalizeCode(); \
decoder->Decode(reinterpret_cast<Instruction*>(buf)); \
encoding = *reinterpret_cast<uint32_t*>(buf); \
if (strncmp(disasm->GetOutput(), EXP, strlen(EXP)) != 0) { \
printf("Encoding: %08" PRIx32 "\nExpected: %s\nFound: %s\n", \
encoding, EXP, disasm->GetOutput()); \
abort(); \
}
#define CLEANUP() \
delete disasm; \
delete decoder; \
@ -723,6 +735,8 @@ TEST(branch) {
COMPARE(b(INST_OFF(-0x8000000)), "b #-0x8000000");
COMPARE(b(INST_OFF(0xffffc), eq), "b.eq #+0xffffc");
COMPARE(b(INST_OFF(-0x100000), mi), "b.mi #-0x100000");
COMPARE(b(INST_OFF(0xffffc), al), "b.al #+0xffffc");
COMPARE(b(INST_OFF(-0x100000), nv), "b.nv #-0x100000");
COMPARE(bl(INST_OFF(0x4)), "bl #+0x4");
COMPARE(bl(INST_OFF(-0x4)), "bl #-0x4");
COMPARE(bl(INST_OFF(0xffffc)), "bl #+0xffffc");
@ -731,9 +745,9 @@ TEST(branch) {
COMPARE(cbz(x1, INST_OFF(-0x100000)), "cbz x1, #-0x100000");
COMPARE(cbnz(w2, INST_OFF(0xffffc)), "cbnz w2, #+0xffffc");
COMPARE(cbnz(x3, INST_OFF(-0x100000)), "cbnz x3, #-0x100000");
COMPARE(tbz(x4, 0, INST_OFF(0x7ffc)), "tbz x4, #0, #+0x7ffc");
COMPARE(tbz(x4, 0, INST_OFF(0x7ffc)), "tbz w4, #0, #+0x7ffc");
COMPARE(tbz(x5, 63, INST_OFF(-0x8000)), "tbz x5, #63, #-0x8000");
COMPARE(tbnz(x6, 0, INST_OFF(0x7ffc)), "tbnz x6, #0, #+0x7ffc");
COMPARE(tbnz(x6, 0, INST_OFF(0x7ffc)), "tbnz w6, #0, #+0x7ffc");
COMPARE(tbnz(x7, 63, INST_OFF(-0x8000)), "tbnz x7, #63, #-0x8000");
COMPARE(br(x0), "br x0");
@ -1173,10 +1187,10 @@ TEST(load_store_pair_nontemp) {
TEST(load_literal) {
SETUP();
COMPARE(ldr(x10, 0x1234567890abcdefUL), "ldr x10, #8 (0x1234567890abcdef)");
COMPARE(ldr(w20, 0xfedcba09), "ldr w20, #8 (0xfedcba09)");
COMPARE(ldr(d11, 1.234), "ldr d11, #8 (1.2340)");
COMPARE(ldr(s22, 2.5), "ldr s22, #8 (2.5000)");
COMPARE_PREFIX(ldr(x10, 0x1234567890abcdefUL), "ldr x10, pc+8");
COMPARE_PREFIX(ldr(w20, 0xfedcba09), "ldr w20, pc+8");
COMPARE_PREFIX(ldr(d11, 1.234), "ldr d11, pc+8");
COMPARE_PREFIX(ldr(s22, 2.5), "ldr s22, pc+8");
CLEANUP();
}
@ -1203,20 +1217,31 @@ TEST(cond_select) {
COMPARE(cneg(w5, w6, hs), "cneg w5, w6, hs");
COMPARE(cneg(x7, x8, lo), "cneg x7, x8, lo");
COMPARE(csel(x0, x1, x2, al), "csel x0, x1, x2, al");
COMPARE(csel(x1, x2, x3, nv), "csel x1, x2, x3, nv");
COMPARE(csinc(x2, x3, x4, al), "csinc x2, x3, x4, al");
COMPARE(csinc(x3, x4, x5, nv), "csinc x3, x4, x5, nv");
COMPARE(csinv(x4, x5, x6, al), "csinv x4, x5, x6, al");
COMPARE(csinv(x5, x6, x7, nv), "csinv x5, x6, x7, nv");
COMPARE(csneg(x6, x7, x8, al), "csneg x6, x7, x8, al");
COMPARE(csneg(x7, x8, x9, nv), "csneg x7, x8, x9, nv");
CLEANUP();
}
TEST(cond_cmp) {
SETUP();
COMPARE(ccmn(w0, Operand(w1), NZCVFlag, eq), "ccmn w0, w1, #NZCV, eq");
COMPARE(ccmn(x2, Operand(x3), NZCFlag, ne), "ccmn x2, x3, #NZCv, ne");
COMPARE(ccmp(w4, Operand(w5), NZVFlag, hs), "ccmp w4, w5, #NZcV, hs");
COMPARE(ccmp(x6, Operand(x7), NZFlag, lo), "ccmp x6, x7, #NZcv, lo");
COMPARE(ccmn(w8, Operand(31), NFlag, mi), "ccmn w8, #31, #Nzcv, mi");
COMPARE(ccmn(x9, Operand(30), NCFlag, pl), "ccmn x9, #30, #NzCv, pl");
COMPARE(ccmp(w10, Operand(29), NVFlag, vs), "ccmp w10, #29, #NzcV, vs");
COMPARE(ccmp(x11, Operand(28), NFlag, vc), "ccmp x11, #28, #Nzcv, vc");
COMPARE(ccmn(w0, w1, NZCVFlag, eq), "ccmn w0, w1, #NZCV, eq");
COMPARE(ccmn(x2, x3, NZCFlag, ne), "ccmn x2, x3, #NZCv, ne");
COMPARE(ccmp(w4, w5, NZVFlag, hs), "ccmp w4, w5, #NZcV, hs");
COMPARE(ccmp(x6, x7, NZFlag, lo), "ccmp x6, x7, #NZcv, lo");
COMPARE(ccmn(w8, 31, NFlag, mi), "ccmn w8, #31, #Nzcv, mi");
COMPARE(ccmn(x9, 30, NCFlag, pl), "ccmn x9, #30, #NzCv, pl");
COMPARE(ccmp(w10, 29, NVFlag, vs), "ccmp w10, #29, #NzcV, vs");
COMPARE(ccmp(x11, 28, NFlag, vc), "ccmp x11, #28, #Nzcv, vc");
COMPARE(ccmn(w12, w13, NoFlag, al), "ccmn w12, w13, #nzcv, al");
COMPARE(ccmp(x14, 27, ZVFlag, nv), "ccmp x14, #27, #nZcV, nv");
CLEANUP();
}
@ -1331,6 +1356,8 @@ TEST(fp_cond_compare) {
COMPARE(fccmp(d6, d7, NFlag, vs), "fccmp d6, d7, #Nzcv, vs");
COMPARE(fccmp(d30, d0, NZFlag, vc), "fccmp d30, d0, #NZcv, vc");
COMPARE(fccmp(d31, d31, ZFlag, hs), "fccmp d31, d31, #nZcv, hs");
COMPARE(fccmp(s14, s15, CVFlag, al), "fccmp s14, s15, #nzCV, al");
COMPARE(fccmp(d16, d17, CFlag, nv), "fccmp d16, d17, #nzCv, nv");
CLEANUP();
}
@ -1343,6 +1370,8 @@ TEST(fp_select) {
COMPARE(fcsel(s31, s31, s30, ne), "fcsel s31, s31, s30, ne");
COMPARE(fcsel(d0, d1, d2, mi), "fcsel d0, d1, d2, mi");
COMPARE(fcsel(d31, d30, d31, pl), "fcsel d31, d30, d31, pl");
COMPARE(fcsel(s14, s15, s16, al), "fcsel s14, s15, s16, al");
COMPARE(fcsel(d17, d18, d19, nv), "fcsel d17, d18, d19, nv");
CLEANUP();
}
@ -1368,15 +1397,27 @@ TEST(fcvt_scvtf_ucvtf) {
COMPARE(fcvtzs(x20, s21), "fcvtzs x20, s21");
COMPARE(fcvtzs(w22, s23), "fcvtzs w22, s23");
COMPARE(scvtf(d24, w25), "scvtf d24, w25");
COMPARE(scvtf(d26, x27), "scvtf d26, x27");
COMPARE(scvtf(s24, w25), "scvtf s24, w25");
COMPARE(scvtf(d26, x0), "scvtf d26, x0");
COMPARE(scvtf(s26, x0), "scvtf s26, x0");
COMPARE(ucvtf(d28, w29), "ucvtf d28, w29");
COMPARE(ucvtf(s28, w29), "ucvtf s28, w29");
COMPARE(ucvtf(d0, x1), "ucvtf d0, x1");
COMPARE(ucvtf(s0, x1), "ucvtf s0, x1");
COMPARE(ucvtf(d0, x1, 0), "ucvtf d0, x1");
COMPARE(ucvtf(s0, x1, 0), "ucvtf s0, x1");
COMPARE(scvtf(d1, x2, 1), "scvtf d1, x2, #1");
COMPARE(scvtf(s1, x2, 1), "scvtf s1, x2, #1");
COMPARE(scvtf(d3, x4, 15), "scvtf d3, x4, #15");
COMPARE(scvtf(s3, x4, 15), "scvtf s3, x4, #15");
COMPARE(scvtf(d5, x6, 32), "scvtf d5, x6, #32");
COMPARE(scvtf(s5, x6, 32), "scvtf s5, x6, #32");
COMPARE(ucvtf(d7, x8, 2), "ucvtf d7, x8, #2");
COMPARE(ucvtf(s7, x8, 2), "ucvtf s7, x8, #2");
COMPARE(ucvtf(d9, x10, 16), "ucvtf d9, x10, #16");
COMPARE(ucvtf(s9, x10, 16), "ucvtf s9, x10, #16");
COMPARE(ucvtf(d11, x12, 33), "ucvtf d11, x12, #33");
COMPARE(ucvtf(s11, x12, 33), "ucvtf s11, x12, #33");
COMPARE(fcvtms(w0, s1), "fcvtms w0, s1");
COMPARE(fcvtms(x2, s3), "fcvtms x2, s3");
COMPARE(fcvtms(w4, d5), "fcvtms w4, d5");
@ -1395,6 +1436,7 @@ TEST(system_mrs) {
COMPARE(mrs(x0, NZCV), "mrs x0, nzcv");
COMPARE(mrs(x30, NZCV), "mrs x30, nzcv");
COMPARE(mrs(x15, FPCR), "mrs x15, fpcr");
CLEANUP();
}
@ -1405,6 +1447,7 @@ TEST(system_msr) {
COMPARE(msr(NZCV, x0), "msr nzcv, x0");
COMPARE(msr(NZCV, x30), "msr nzcv, x30");
COMPARE(msr(FPCR, x15), "msr fpcr, x15");
CLEANUP();
}
@ -1456,7 +1499,7 @@ TEST(log) {
// All Log calls should produce the same instruction.
COMPARE(Log(LOG_ALL), "hlt #0xdeb3");
COMPARE(Log(LOG_FLAGS), "hlt #0xdeb3");
COMPARE(Log(LOG_SYS_REGS), "hlt #0xdeb3");
CLEANUP();
}

119
test/test-fuzz-a64.cc Normal file
View File

@ -0,0 +1,119 @@
// 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 <stdlib.h>
#include "cctest.h"
#include "a64/decoder-a64.h"
#include "a64/disasm-a64.h"
#define TEST(name) TEST_(FUZZ_##name)
namespace vixl {
TEST(decoder) {
// Feed noise into the decoder to check that it doesn't crash.
// 43 million = ~1% of the instruction space.
static const int instruction_count = 43 * 1024 * 1024;
uint16_t seed[3] = {1, 2, 3};
seed48(seed);
Decoder decoder;
Instruction buffer[kInstructionSize];
for (int i = 0; i < instruction_count; i++) {
uint32_t instr = mrand48();
buffer->SetInstructionBits(instr);
decoder.Decode(buffer);
}
}
TEST(disasm) {
// Feed noise into the disassembler to check that it doesn't crash.
// 9 million = ~0.2% of the instruction space.
static const int instruction_count = 9 * 1024 * 1024;
uint16_t seed[3] = {42, 43, 44};
seed48(seed);
Decoder decoder;
Disassembler disasm;
Instruction buffer[kInstructionSize];
decoder.AppendVisitor(&disasm);
for (int i = 0; i < instruction_count; i++) {
uint32_t instr = mrand48();
buffer->SetInstructionBits(instr);
decoder.Decode(buffer);
}
}
#if 0
// These tests are commented out as they take a long time to run, causing the
// test script to timeout. After enabling them, they are best run individually
// using cctest:
//
// cctest_sim FUZZ_decoder_pedantic
// cctest_sim FUZZ_disasm_pedantic
//
// or cctest_sim_g for debug builds.
TEST(decoder_pedantic) {
// Test the entire instruction space.
Decoder decoder;
Instruction buffer[kInstructionSize];
for (uint64_t i = 0; i < (1UL << 32); i++) {
if ((i & 0xffffff) == 0) {
fprintf(stderr, "0x%08" PRIx32 "\n", static_cast<uint32_t>(i));
}
buffer->SetInstructionBits(static_cast<uint32_t>(i));
decoder.Decode(buffer);
}
}
TEST(disasm_pedantic) {
// Test the entire instruction space. Warning: takes about 30 minutes on a
// high-end CPU.
Decoder decoder;
PrintDisassembler disasm(stdout);
Instruction buffer[kInstructionSize];
decoder.AppendVisitor(&disasm);
for (uint64_t i = 0; i < (1UL << 32); i++) {
if ((i & 0xffff) == 0) {
fprintf(stderr, "0x%08" PRIx32 "\n", static_cast<uint32_t>(i));
}
buffer->SetInstructionBits(static_cast<uint32_t>(i));
decoder.Decode(buffer);
}
}
#endif
} // namespace vixl

View File

@ -59,20 +59,38 @@ bool Equal64(uint64_t expected, const RegisterDump*, uint64_t result) {
bool EqualFP32(float expected, const RegisterDump*, float result) {
if (result != expected) {
printf("Expected %.20f\t Found %.20f\n", expected, result);
if (float_to_rawbits(expected) == float_to_rawbits(result)) {
return true;
} else {
if (isnan(expected) || (expected == 0.0)) {
printf("Expected 0x%08" PRIx32 "\t Found 0x%08" PRIx32 "\n",
float_to_rawbits(expected), float_to_rawbits(result));
} else {
printf("Expected %.9f (0x%08" PRIx32 ")\t "
"Found %.9f (0x%08" PRIx32 ")\n",
expected, float_to_rawbits(expected),
result, float_to_rawbits(result));
}
return false;
}
return expected == result;
}
bool EqualFP64(double expected, const RegisterDump*, double result) {
if (result != expected) {
printf("Expected %.20f\t Found %.20f\n", expected, result);
if (double_to_rawbits(expected) == double_to_rawbits(result)) {
return true;
}
return expected == result;
if (isnan(expected) || (expected == 0.0)) {
printf("Expected 0x%016" PRIx64 "\t Found 0x%016" PRIx64 "\n",
double_to_rawbits(expected), double_to_rawbits(result));
} else {
printf("Expected %.17f (0x%016" PRIx64 ")\t "
"Found %.17f (0x%016" PRIx64 ")\n",
expected, double_to_rawbits(expected),
result, double_to_rawbits(result));
}
return false;
}
@ -112,15 +130,8 @@ bool EqualFP32(float expected,
float_to_rawbits(expected), expected, result_64);
return false;
}
if (expected == 0.0) {
return Equal32(float_to_rawbits(expected), core,
core->sreg_bits(fpreg.code()));
} else if (isnan(expected)) {
return isnan(core->sreg(fpreg.code()));
} else {
float result = core->sreg(fpreg.code());
return EqualFP32(expected, core, result);
}
return EqualFP32(expected, core, core->sreg(fpreg.code()));
}
@ -128,15 +139,7 @@ bool EqualFP64(double expected,
const RegisterDump* core,
const FPRegister& fpreg) {
ASSERT(fpreg.Is64Bits());
if (expected == 0.0) {
return Equal64(double_to_rawbits(expected), core,
core->dreg_bits(fpreg.code()));
} else if (isnan(expected)) {
return isnan(core->dreg(fpreg.code()));
} else {
double result = core->dreg(fpreg.code());
return EqualFP64(expected, core, result);
}
return EqualFP64(expected, core, core->dreg(fpreg.code()));
}
@ -177,9 +180,10 @@ bool EqualNzcv(uint32_t expected, uint32_t result) {
printf("Expected: %c%c%c%c\t Found: %c%c%c%c\n",
FlagN(expected), FlagZ(expected), FlagC(expected), FlagV(expected),
FlagN(result), FlagZ(result), FlagC(result), FlagV(result));
return false;
}
return result == expected;
return true;
}

View File

@ -116,7 +116,7 @@ class CCtest:
def Command(self):
command = '%s %s' % (CCtest.cctest, self.name)
if self.options is not None:
command = '%s %s' % (commnad, ' '.join(options))
command = '%s %s' % (command, ' '.join(self.options))
return command