mirror of
https://github.com/FEX-Emu/vixl.git
synced 2024-11-23 14:40:17 +00:00
VIXL Release 1.1
Refer to the README.md and LICENCE files for details.
This commit is contained in:
parent
ad96eda894
commit
578645f14e
18
README.md
18
README.md
@ -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."
|
||||
|
@ -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
12
doc/changelog.md
Normal 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.
|
@ -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));
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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_
|
||||
|
@ -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 {
|
||||
|
@ -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();
|
||||
|
@ -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); \
|
||||
|
@ -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
|
||||
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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),
|
||||
|
@ -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
638
src/a64/instrument-a64.cc
Normal 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
108
src/a64/instrument-a64.h
Normal 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_
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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; \
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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
119
test/test-fuzz-a64.cc
Normal 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
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user