ARM64: Guess what? more emitter & disasm

This commit is contained in:
Henrik Rydgard 2015-03-21 18:26:53 +01:00
parent 1e9fdf08c5
commit 5a5f3c94fd
4 changed files with 122 additions and 53 deletions

View file

@ -1485,6 +1485,14 @@ void ARM64XEmitter::UBFM(ARM64Reg Rd, ARM64Reg Rn, u32 immr, u32 imms)
{
EncodeBitfieldMOVInst(2, Rd, Rn, immr, imms);
}
void ARM64XEmitter::EXTR(ARM64Reg Rd, ARM64Reg Rn, ARM64Reg Rm, u32 shift) {
bool sf = Is64Bit(Rd);
bool N = sf;
Rd = DecodeReg(Rd);
Rn = DecodeReg(Rn);
Rm = DecodeReg(Rm);
Write32((sf << 31) | (0x27 << 23) | (N << 22) | (Rm << 16) | (shift << 10) | (Rm << 5) | Rd);
}
void ARM64XEmitter::SXTB(ARM64Reg Rd, ARM64Reg Rn)
{
SBFM(Rd, Rn, 0, 7);
@ -1496,7 +1504,6 @@ void ARM64XEmitter::SXTH(ARM64Reg Rd, ARM64Reg Rn)
void ARM64XEmitter::SXTW(ARM64Reg Rd, ARM64Reg Rn)
{
_assert_msg_(DYNA_REC, Is64Bit(Rd), "%s requires 64bit register as destination", __FUNCTION__);
SBFM(Rd, Rn, 0, 31);
}
void ARM64XEmitter::UXTB(ARM64Reg Rd, ARM64Reg Rn)
@ -1817,8 +1824,8 @@ void ARM64XEmitter::MOVI2R(ARM64Reg Rd, u64 imm, bool optimize)
if (!imm)
{
// Zero immediate, just clear the register
EOR(Rd, Rd, Rd, ArithOption(Rd, ST_LSL, 0));
// Zero immediate, just clear the register. EOR is pointless when we have MOVZ, which looks clearer in disasm too.
MOVZ(Rd, 0, SHIFT_0);
return;
}
@ -2638,10 +2645,30 @@ void ARM64FloatEmitter::ST1(u8 size, u8 count, ARM64Reg Rt, ARM64Reg Rn)
}
// Scalar - 1 Source
void ARM64FloatEmitter::FMOV(ARM64Reg Rd, ARM64Reg Rn)
void ARM64FloatEmitter::FMOV(ARM64Reg Rd, ARM64Reg Rn, bool top)
{
EmitScalar1Source(0, 0, IsDouble(Rd), 0, Rd, Rn);
if (IsScalar(Rd) && IsScalar(Rn)) {
EmitScalar1Source(0, 0, IsDouble(Rd), 0, Rd, Rn);
} else {
int type = 0;
int rmode = 0;
int opcode = 6;
int sf = 0;
if (IsSingle(Rd) && !Is64Bit(Rn) && !top) {
// GPR to scalar single
opcode |= 1;
} else if (!Is64Bit(Rd) && IsSingle(Rn) && !top) {
// Scalar single to GPR - defaults are correct
} else {
// TODO
_assert_msg_(JIT, 0, "FMOV: Unhandled case");
}
Rd = DecodeReg(Rd);
Rn = DecodeReg(Rn);
Write32((sf << 31) | (0x1e2 << 20) | (rmode << 19) | (opcode << 16) | (Rn << 5) | Rd);
}
}
void ARM64FloatEmitter::FABS(ARM64Reg Rd, ARM64Reg Rn)
{
EmitScalar1Source(0, 0, IsDouble(Rd), 1, Rd, Rn);
@ -2949,41 +2976,41 @@ void ARM64FloatEmitter::FCVT(u8 size_to, u8 size_from, ARM64Reg Rd, ARM64Reg Rn)
Emit1Source(0, 0, src_encoding, 4 | dst_encoding, Rd, Rn);
}
// Conversion between float and integer
void ARM64FloatEmitter::FMOV(u8 size, bool top, ARM64Reg Rd, ARM64Reg Rn)
{
bool sf = size == 64 ? true : false;
u32 type = 0;
u32 rmode = top ? 1 : 0;
if (size == 64)
{
if (top)
type = 2;
else
type = 1;
}
EmitConversion(sf, 0, type, rmode, IsVector(Rd) ? 7 : 6, Rd, Rn);
}
void ARM64FloatEmitter::SCVTF(ARM64Reg Rd, ARM64Reg Rn)
{
bool sf = Is64Bit(Rn);
u32 type = 0;
if (IsDouble(Rd))
type = 1;
EmitConversion(sf, 0, type, 0, 2, Rd, Rn);
if (IsScalar(Rn)) {
// Source is in FP register (like destination!). We must use a vector encoding.
bool sign = false;
Rd = DecodeReg(Rd);
Rn = DecodeReg(Rn);
int sz = IsDouble(Rn);
Write32((0x5e << 24) | (sign << 29) | (sz << 22) | (0x876 << 10) | (Rn << 5) | Rd);
} else {
bool sf = Is64Bit(Rn);
u32 type = 0;
if (IsDouble(Rd))
type = 1;
EmitConversion(sf, 0, type, 0, 2, Rd, Rn);
}
}
void ARM64FloatEmitter::UCVTF(ARM64Reg Rd, ARM64Reg Rn)
{
bool sf = Is64Bit(Rn);
u32 type = 0;
if (IsDouble(Rd))
type = 1;
if (IsScalar(Rn)) {
// Source is in FP register (like destination!). We must use a vector encoding.
bool sign = true;
Rd = DecodeReg(Rd);
Rn = DecodeReg(Rn);
int sz = IsDouble(Rn);
Write32((0x5e << 24) | (sign << 29) | (sz << 22) | (0x876 << 10) | (Rn << 5) | Rd);
} else {
bool sf = Is64Bit(Rn);
u32 type = 0;
if (IsDouble(Rd))
type = 1;
EmitConversion(sf, 0, type, 0, 3, Rd, Rn);
EmitConversion(sf, 0, type, 0, 3, Rd, Rn);
}
}
void ARM64FloatEmitter::SCVTF(ARM64Reg Rd, ARM64Reg Rn, int scale)

View file

@ -548,6 +548,8 @@ public:
void MOV(ARM64Reg Rd, ARM64Reg Rm, ArithOption Shift);
void MOV(ARM64Reg Rd, ARM64Reg Rm);
void MVN(ARM64Reg Rd, ARM64Reg Rm);
// TODO: These are "slow" as they use arith+shift, should be replaced with UBFM/EXTR variants.
void LSR(ARM64Reg Rd, ARM64Reg Rm, int shift);
void LSL(ARM64Reg Rd, ARM64Reg Rm, int shift);
void ASR(ARM64Reg Rd, ARM64Reg Rm, int shift);
@ -576,12 +578,21 @@ public:
void BFM(ARM64Reg Rd, ARM64Reg Rn, u32 immr, u32 imms);
void SBFM(ARM64Reg Rd, ARM64Reg Rn, u32 immr, u32 imms);
void UBFM(ARM64Reg Rd, ARM64Reg Rn, u32 immr, u32 imms);
// Extract register (ROR with two inputs, if same then faster on A67)
void EXTR(ARM64Reg Rd, ARM64Reg Rn, ARM64Reg Rm, u32 shift);
// Aliases
void SXTB(ARM64Reg Rd, ARM64Reg Rn);
void SXTH(ARM64Reg Rd, ARM64Reg Rn);
void SXTW(ARM64Reg Rd, ARM64Reg Rn);
void UXTB(ARM64Reg Rd, ARM64Reg Rn);
void UXTH(ARM64Reg Rd, ARM64Reg Rn);
void UBFX(ARM64Reg Rd, ARM64Reg Rn, int lsb, int width) {
UBFM(Rd, Rn, lsb, lsb + width <= (Is64Bit(Rn) ? 64 : 32));
}
// Load Register (Literal)
void LDR(ARM64Reg Rt, u32 imm);
void LDRSW(ARM64Reg Rt, u32 imm);
@ -743,10 +754,10 @@ public:
void ST1(u8 size, u8 count, ARM64Reg Rt, ARM64Reg Rn);
// Scalar - 1 Source
void FMOV(ARM64Reg Rd, ARM64Reg Rn);
void FABS(ARM64Reg Rd, ARM64Reg Rn);
void FNEG(ARM64Reg Rd, ARM64Reg Rn);
void FSQRT(ARM64Reg Rd, ARM64Reg Rn);
void FMOV(ARM64Reg Rd, ARM64Reg Rn, bool top = false); // Also generalized move between GPR/FP
// Scalar - 2 Source
void FADD(ARM64Reg Rd, ARM64Reg Rn, ARM64Reg Rm);
@ -791,9 +802,6 @@ public:
// One source
void FCVT(u8 size_to, u8 size_from, ARM64Reg Rd, ARM64Reg Rn);
// Conversion or movement between float and integer
void FMOV(u8 size, bool top, ARM64Reg Rd, ARM64Reg Rn);
// Scalar convert float to int, in a lot of variants.
// Note that the scalar version of this operation has two encodings, one that goes to an integer register
// and one that outputs to a scalar fp register.

View file

@ -169,6 +169,13 @@ static void DataProcessingImmediate(uint32_t w, uint64_t addr, Instruction *inst
snprintf(instr->text, sizeof(instr->text), "%s %c%d, %c%d, #0x%x%08x", opname[opc], r, Rd, r, Rn, (wmask >> 32), (wmask & 0xFFFFFFFF));
else
snprintf(instr->text, sizeof(instr->text), "%s %c%d, %c%d, #0x%x", opname[opc], r, Rd, r, Rn, (uint32_t)wmask);
} else if (((w >> 23) & 0x3f) == 0x26) {
int N = (w >> 22) & 1;
int opc = (w >> 29) & 3;
int immr = (w >> 16) & 0x3f;
int imms = (w >> 10) & 0x3f;
const char *opname[4] = { "sbfm", "bfm", "ubfm" };
snprintf(instr->text, sizeof(instr->text), "%s %c%d, %c%d, #%d, #%d", opname[opc], r, Rd, r, Rn, immr, imms);
} else {
snprintf(instr->text, sizeof(instr->text), "(DPI %08x)", w);
}
@ -458,6 +465,7 @@ static void FPandASIMD2(uint32_t w, uint64_t addr, Instruction *instr) {
snprintf(instr->text, sizeof(instr->text), "(float<->fixed %08x)", w);
}
} else if (((w >> 21) & 0x2F9) == 0xF1) {
int opcode = (w >> 16) & 7;
if (((w >> 10) & 3) == 0) {
if (((w >> 10) & 7) == 4) {
snprintf(instr->text, sizeof(instr->text), "(float imm %08x)", w);
@ -476,14 +484,28 @@ static void FPandASIMD2(uint32_t w, uint64_t addr, Instruction *instr) {
const char *opnames[4] = { "fmov", "fabs", "fneg", "fsqrt" };
int opc = (w >> 15) & 0x3;
snprintf(instr->text, sizeof(instr->text), "%s s%d, s%d", opnames[opc], Rd, Rn); // TODO: Support doubles too
} else if (((w >> 10) & 0x3f) == 0x0) {
} else if (((w >> 10) & 0x1bf) == 0x180) {
// Generalized FMOV
char ir = sf ? 'x' : 'w';
bool tosimd = (opcode & 0x1);
char fr = ((w >> 22) & 1) ? 'd' : 's';
if (tosimd) {
snprintf(instr->text, sizeof(instr->text), "fmov %c%d, %c%d", fr, Rd, ir, Rn);
} else {
snprintf(instr->text, sizeof(instr->text), "fmov %c%d, %c%d", ir, Rd, fr, Rn);
}
} else if (((w >> 10) & 0x3f) == 0x0 && opcode == 0) {
char ir = sf ? 'x' : 'w';
char roundmode = "npmz"[(w >> 19) & 3];
int opcode = (w >> 16) & 7;
if (opcode & 0x4)
roundmode = 'a';
char fr = ((w >> 22) & 1) ? 'd' : 's';
snprintf(instr->text, sizeof(instr->text), "fcvt%cs %c%d, %c%d", roundmode, ir, Rd, fr, Rn);
} else if ((opcode & 6) == 2) {
char ir = sf ? 'x' : 'w';
char fr = ((w >> 22) & 1) ? 'd' : 's';
char sign = (opcode & 1) ? 'u' : 's';
snprintf(instr->text, sizeof(instr->text), "%ccvtf %c%d, %c%d", sign, fr, Rd, ir, Rn);
}
} else if (((w >> 10) & 3) == 1) {
snprintf(instr->text, sizeof(instr->text), "(float cond compare %08x)", w);
@ -567,8 +589,10 @@ static void FPandASIMD2(uint32_t w, uint64_t addr, Instruction *instr) {
} else if (sign_prefix) {
char sign = U ? 'u' : 's';
snprintf(instr->text, sizeof(instr->text), "%c%s %c%d, %c%d", sign, opname, r, Rd, r, Rn);
} else {
snprintf(instr->text, sizeof(instr->text), "%s (asimd scalar two-reg misc %08x)", opname, w);
} else if (!zero) {
snprintf(instr->text, sizeof(instr->text), "%s %c%d, %c%d", opname, r, Rd, r, Rn);
} else if (zero) {
snprintf(instr->text, sizeof(instr->text), "%s %c%d, #0.0", opname, r, Rd);
}
} else {
snprintf(instr->text, sizeof(instr->text), "(asimd scalar two-reg misc %08x)", w);

View file

@ -35,10 +35,19 @@ bool TestArm64Emitter() {
u32 code[512];
ARM64XEmitter emitter((u8 *)code);
ARM64FloatEmitter fp(&emitter);
//fp.FMOV(32, false, S1, X3);
//RET(CheckLast(emitter, "aa023be1 fmov s1, w3"));
//fp.FMOV(32, false, X1, S3);
//RET(CheckLast(emitter, "aa023be1 fmov x1, s3"));
//emitter.EXTR(W1, W3, 0, 7);
//RET(CheckLast(emitter, "53033061 extr w1, w3, w7"));
fp.SCVTF(S13, W7);
RET(CheckLast(emitter, "1e2200ed scvtf s13, w7"));
emitter.UBFM(W1, W3, 0, 7);
RET(CheckLast(emitter, "53001c61 ubfm w1, w3, #0, #7"));
fp.FMOV(W1, S3);
RET(CheckLast(emitter, "1e260061 fmov w1, s3"));
fp.FMOV(S1, W3);
RET(CheckLast(emitter, "1e270061 fmov s1, w3"));
fp.SCVTF(S13, S12);
RET(CheckLast(emitter, "5e21d98d scvtf s13, s12"));
fp.FCVTS(S13, S12, ROUND_N);
RET(CheckLast(emitter, "5e21a98d fcvtns s13, s12"));
fp.FCVTS(D13, D12, ROUND_P);
@ -46,13 +55,15 @@ bool TestArm64Emitter() {
fp.FCVTS(W13, S12, ROUND_N);
RET(CheckLast(emitter, "1e20018d fcvtns w13, s12"));
fp.FCVTS(S22, S12, ROUND_Z);
RET(CheckLast(emitter, "9e03e06c fcvtzs s22, s12"));
RET(CheckLast(emitter, "5ea1b996 fcvtzs s22, s12"));
fp.FCVTS(X3, D2, ROUND_M);
RET(CheckLast(emitter, "9e03e06c fcvtms x3, d2"));
RET(CheckLast(emitter, "9e700043 fcvtms x3, d2"));
fp.UCVTF(S12, X3, 8);
RET(CheckLast(emitter, "9e03e06c ucvtf s12, x3"));
//fp.FCVTZS(W12, S3, 12);
//RET(CheckLast(emitter, "b86c6877 fcvtzs w12, s3, #12"));
RET(CheckLast(emitter, "9e03e06c ucvtf s12, x3, #8"));
fp.SCVTF(S12, W13, 12);
RET(CheckLast(emitter, "1e02d1ac scvtf s12, w13, #12"));
fp.FCVTS(W12, S3, ROUND_Z);
RET(CheckLast(emitter, "1e38006c fcvtzs w12, s3"));
emitter.LSLV(W1, W2, W3);
RET(CheckLast(emitter, "1ac32041 lslv w1, w2, w3"));
emitter.UDIV(W1, W2, W3);
@ -103,7 +114,6 @@ bool TestArm64Emitter() {
RET(CheckLast(emitter, "6a1e0041 ands w1, w2, w30"));
emitter.ORR(X1, X2, X30);
RET(CheckLast(emitter, "aa1e0041 orr x1, x2, x30"));
fp.FMUL(S0, S12, S22);
RET(CheckLast(emitter, "1e360980 fmul s0, s12, s22"));
emitter.ADD(X23, X30, 123, false);
@ -119,9 +129,9 @@ bool TestArm64Emitter() {
fp.FMOV(S20, S25);
RET(CheckLast(emitter, "1e204334 fmov s20, s25"));
fp.FCMP(S7);
RET(CheckLast(emitter, "1e6320e0 fcmp s7, #0.0"));
RET(CheckLast(emitter, "1e2020e8 fcmp s7, #0.0"));
fp.FCMP(D7, D3);
RET(CheckLast(emitter, "1e2020e8 fcmp d7, d3"));
RET(CheckLast(emitter, "1e6320e0 fcmp d7, d3"));
emitter.ORI2R(X1, X3, 0x3F, INVALID_REG);
RET(CheckLast(emitter, "b2401461 orr x1, x3, #0x3f"));
emitter.EORI2R(X1, X3, 0x3F0000003F0, INVALID_REG);