Quick hacked-up ARM VFP disassembler. Buggy.

This commit is contained in:
Henrik Rydgard 2013-07-30 21:39:04 +02:00
parent 7e373c206d
commit 65f8430c32
4 changed files with 258 additions and 139 deletions

View File

@ -920,14 +920,8 @@ void ARMXEmitter::VSUB(IntegerSize Size, ARMReg Vd, ARMReg Vn, ARMReg Vm)
| ((Vm & 0x10) << 2) | (Vm & 0xF));
}
// VFP Specific
struct VFPEnc
{
s16 opc1;
s16 opc2;
};
// Double/single, Neon
const VFPEnc VFPOps[][2] = {
extern const VFPEnc VFPOps[16][2] = {
{{0xE0, 0xA0}, {0x20, 0xD1}}, // 0: VMLA
{{0xE1, 0xA4}, { -1, -1}}, // 1: VNMLA
{{0xE0, 0xA4}, {0x22, 0xD1}}, // 2: VMLS
@ -944,7 +938,8 @@ const VFPEnc VFPOps[][2] = {
{{0xEB, 0xAC}, { -1, -1}}, // 13: VCMPE (Vn(0x4 | #0 ? 1 : 0) used for encoding)
{{ -1, -1}, {0x3B, 0x30}}, // 14: VABSi
};
const char *VFPOpNames[] = {
extern const char *VFPOpNames[16] = {
"VMLA",
"VNMLA",
"VMLS",

View File

@ -656,6 +656,14 @@ public:
}
};
// VFP Specific
struct VFPEnc {
s16 opc1;
s16 opc2;
};
extern const VFPEnc VFPOps[16][2];
extern const char *VFPOpNames[16];
} // namespace
#endif // _DOLPHIN_INTEL_CODEGEN_

View File

@ -63,6 +63,194 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "base/basictypes.h"
#include "Common/ArmEmitter.h"
static const char *CCFlagsStr[] = {
"EQ", // Equal
"NEQ", // Not equal
"CS", // Carry Set
"CC", // Carry Clear
"MI", // Minus (Negative)
"PL", // Plus
"VS", // Overflow
"VC", // No Overflow
"HI", // Unsigned higher
"LS", // Unsigned lower or same
"GE", // Signed greater than or equal
"LT", // Signed less than
"GT", // Signed greater than
"LE", // Signed less than or equal
"", // Always (unconditional) 14
};
int GetVd(uint32_t op, bool quad = false, bool dbl = false) {
if (!quad && !dbl) {
return ((op >> 22) & 1) | ((op >> 11) & 0x1E);
}
return 0;
}
int GetVn(uint32_t op, bool quad = false, bool dbl = false) {
if (!quad && !dbl) {
return ((op >> 7) & 1) | ((op >> 15) & 0x1E);
} else if (dbl) {
return ((op >> 16) & 0xF) | ((op >> 3) & 0x10);
}
return 0;
}
int GetVm(uint32_t op, bool quad = false, bool dbl = false) {
if (!quad && !dbl) {
return ((op >> 5) & 1) | ((op << 1) & 0x1E);
}
return 0;
}
// Modern VFP disassembler, written entirely separately because I can't figure out the old stuff :P
// Horrible array of hacks but hey. Can be cleaned up later.
bool DisasmVFP(uint32_t op, char *text) {
const char *cond = CCFlagsStr[op >> 28];
switch ((op >> 24) & 0xF) {
case 0xD:
// VLDR/VSTR
{
int base = (op >> 16) & 0xF;
bool add = (op >> 23) & 1;
int freg = ((op >> 11) & 0x1E) | ((op >> 22) & 1);
int offset = (op & 0xFF) << 2;
if (!add) offset = -offset;
bool vldr = (op >> 20) & 1;
bool single_reg = ((op >> 8) & 0xF) == 10;
sprintf(text, "%s%s s%i, [r%i, #%i]", vldr ? "VLDR" : "VSTR", cond, freg, base, offset);
return true;
}
case 0xE:
{
switch ((op >> 20) & 0xF) {
case 0xE: // VMSR
if ((op & 0xFFF) != 0xA10)
break;
sprintf(text, "VMSR%s r%i", cond, (op >> 12) & 0xF);
return true;
case 0xF: // VMRS
if ((op & 0xFFF) != 0xA10)
break;
if (op == 0xEEF1FA10) {
sprintf(text, "VMRS APSR", cond);
} else {
sprintf(text, "VMRS%s r%i", cond, (op >> 12) & 0xF);
}
return true;
default:
break;
}
if (((op >> 19) & 0x7) == 0x7) {
// VCVT
sprintf(text, "VCVT ...");
return true;
}
int part1 = ((op >> 23) & 0x1F);
int part2 = ((op >> 9) & 0x7) ;
int part3 = ((op >> 20) & 0x3) ;
if (part3 == 3 && part2 == 5 && part1 == 0x1D && (op & (1<<6))) {
// VMOV
int vn = GetVn(op);
if (vn != 1 && vn != 3) {
int vm = GetVm(op);
int vd = GetVd(op);
sprintf(text, "VMOV%s s%i, s%i", cond, vd, vm);
return true;
}
}
// Arithmetic (buggy!)
bool quad_reg = (op >> 6) & 1;
bool double_reg = (op >> 8) & 1;
int opnum = -1;
int opc1 = (op >> 20) & 0xFB;
int opc2 = (op >> 4) & 0xAC;
for (int i = 0; i < 16; i++) {
if (ArmGen::VFPOps[i][0].opc1 == opc1 && ArmGen::VFPOps[i][0].opc2 == opc2) {
opnum = i;
break;
}
}
if (opnum < 0)
return false;
switch (opnum) {
case 8:
case 10:
case 11:
case 12:
case 13:
case 14:
{
quad_reg = false;
int vd = GetVd(op, quad_reg, double_reg);
int vn = GetVn(op, quad_reg, true);
int vm = GetVm(op, quad_reg, double_reg);
if (opnum == 8 && vn == 0x11) opnum += 3;
sprintf(text, "%s%s s%i, s%i", ArmGen::VFPOpNames[opnum], cond, vd, vm);
return true;
}
default:
{
quad_reg = false;
int vd = GetVd(op, quad_reg, double_reg);
int vn = GetVn(op, quad_reg, double_reg);
int vm = GetVm(op, quad_reg, double_reg);
sprintf(text, "%s%s s%i, s%i, s%i", ArmGen::VFPOpNames[opnum], cond, vd, vn, vm);
return true;
}
}
return true;
}
break;
}
return false;
}
typedef unsigned int word;
typedef unsigned int address;
@ -489,132 +677,17 @@ lMaybeLDRHetc:
break;
case 12:
case 13:
/* STC or LDC */
if (CP_is(1)) {
/* copro 1: FPU. This is STF or LDF. */
mnemonic = "STF\0LDF" + ((instr&Lbit) >> 18);
format = "8,/";
*flagp++ = "SDEP"[fpn];
poss_tt = (eTargetType)(target_FloatS+fpn);
}
else if (CP_is(2)) {
/* copro 2: this is LFM or SFM. */
mnemonic = "SFM\0LFM" + ((instr&Lbit) >> 18);
if (!fpn) fpn=4;
if (RN_is(13) && BitsDiffer(23,24)) {
if ((instr&255)!=fpn) goto lNonStackLFM;
/* r13 and U!=P, so treat as stack */
if (BitsDiffer(20,24)) {
/* L != P, so FD */
*flagp++ = 'F'; *flagp++ = 'D';
}
else {
/* L == P, so EA */
*flagp++ = 'E'; *flagp++ = 'A';
}
format = "8,(,[4]'";
}
else {
lNonStackLFM:
/* not r13 or U=P or wrong offset, so don't treat as stack */
format = "8,(,/";
poss_tt = target_FloatE;
}
}
else {
/* some other copro number: STC or LDC. */
mnemonic = "STC\0LDC" + ((instr&Lbit) >> 18);
format = ";,\004,/";
if (instr&(1<<22)) *flagp++ = 'L';
poss_tt = target_Unknown;
}
break;
case 14:
/* CDP or MRC/MCR */
if (instr&(1<<4)) {
/* MRC/MCR. */
if (CP_is(1)) {
/* copro 1: FPU. */
if ((instr&Lbit) && RD_is(15)) {
/* MCR in FPU with Rd=r15: comparison (ugh) */
if (!(instr&(1<<23))) goto lUndefined; /* unused operation */
mnemonic = "CMF\0\0CNF\0\0CMFE\0CNFE" + (5*(instr&(3<<21)) >> 21);
format = "9,+";
if (instr&((1<<19)+(7<<5)))
result.badbits=1; /* size,rmode reseved */
}
else {
/* normal FPU MCR/MRC */
word op20 = instr&(15<<20);
if (op20>=6<<20) goto lUndefined;
mnemonic = "FLT\0FIX\0WFS\0RFS\0WFC\0RFC" + (op20>>18);
if (op20==0) {
/* FLT instruction */
format = "9,3";
{ char c = "SDE*"[((instr>>7)&1) + ((instr>>18)&2)];
if (c=='*') goto lUndefined; else *flagp++=c;
}
if (instr&15) result.oddbits=1; /* Fm and const flag unused */
}
else {
/* not FLT instruction */
if (instr&((1<<7)+(1<<19)))
result.badbits=1; /* size bits reserved */
if (op20==1<<20) {
/* FIX instruction */
format = "3,+";
if (opts->flags&disopt_FIXS)
*flagp++ = "SDEP"[((instr>>7)&1) + ((instr>>18)&2)];
*flagp++ = "\0PMZ"[(instr&(3<<5))>>5];
if (instr&(7<<15)) result.oddbits=1; /* Fn unused */
if (instr&(1<<3)) result.badbits=1; /* no immediate consts */
}
else {
/* neither FLT nor FIX */
format = "3";
if (instr&(3<<5)) result.badbits=1; /* rmode reserved */
if (instr&(15+(7<<15))) result.oddbits=1;/* iFm, Fn unused */
}
}
}
}
else {
/* some other copro number. Not FPU. */
/* NB that ObjAsm documentation gets MCR and MRC the wrong way round!
*/
mnemonic = "MCR\0MRC";
mnemonic += (instr&Lbit) >> 18;
format = ";,:,3,\005,\001-";
}
}
else {
/* CDP. */
if (CP_is(1)) {
/* copro 1: FPU. */
mnemonic = /* dyadics: */
"ADF\0MUF\0SUF\0RSF\0"
"DVF\0RDF\0POW\0RPW\0"
"RMF\0FML\0FDV\0FRD\0"
"POL\0***\0***\0***\0"
/* monadics: */
"MVF\0MNF\0ABS\0RND\0"
"SQT\0LOG\0LGN\0EXP\0"
"SIN\0COS\0TAN\0ASN\0"
"ACS\0ATN\0URD\0NRM\0"
+ ((instr&(15<<20)) >> 18) /* opcode -> bits 5432 */
+ ((instr&(1<<15)) >> 9); /* monadicP -> bit 6 */
format = (instr&(1<<15)) ? "8,+" : "8,9,+";
*flagp++ = "SDE*"[((instr>>7)&1) + ((instr>>18)&2)];
*flagp++ = "\0PMZ"[(instr&(3<<5))>>5];
/* NB that foregoing relies on this being the last flag! */
if (*mnemonic=='*' || *flagchars=='*') goto lUndefined;
}
else {
/* some other copro number. Not FPU. */
mnemonic = "CDP";
format = ";,),\004,\005,\001-";
}
}
case 14: // FPU
{
char text[256];
if (!DisasmVFP(instr, text)) {
goto lUndefined;
break;
}
strcpy(result.text, text);
result.undefined = 0;
return &result;
}
break;
case 15:
/* SWI */

View File

@ -37,7 +37,20 @@
#define EXPECT_TRUE(a) if (!(a)) { printf(__FUNCTION__ ":%i: Test Fail\n", __LINE__); return false; }
#define EXPECT_FALSE(a) if ((a)) { printf(__FUNCTION__ ":%i: Test Fail\n", __LINE__); return false; }
#define EXPECT_EQ_FLOAT(a, b) if ((a) != (b)) { printf(__FUNCTION__ ":" __LINE__ ": Test Fail\n%f\nvs\n%f\n", a, b); return false; }
#define EXPECT_EQ_STR(a, b) if ((a) != (b)) { printf(__FUNCTION__ ": Test Fail\n%s\nvs\n%s\n", a.c_str(), b.c_str()); return false; }
#define EXPECT_EQ_STR(a, b) if (a != b) { printf(__FUNCTION__ ": Test Fail\n%s\nvs\n%s\n", a.c_str(), b.c_str()); return false; }
#define RET(a) if (!(a)) { return false; }
bool CheckLast(ArmGen::ARMXEmitter &emit, const char *comp) {
u32 instr;
memcpy(&instr, emit.GetCodePtr() - 4, 4);
char disasm[512];
ArmDis(0, instr, disasm);
EXPECT_EQ_STR(std::string(disasm), std::string(comp));
return true;
}
bool TestArmEmitter() {
using namespace ArmGen;
@ -45,12 +58,42 @@ bool TestArmEmitter() {
u32 code[512];
ARMXEmitter emitter((u8 *)code);
emitter.LDR(R3, R7);
RET(CheckLast(emitter, "e5973000 LDR r3, [r7, #0]"));
emitter.VLDR(S3, R8, 48);
RET(CheckLast(emitter, "edd81a0c VLDR s3, [r8, #48]"));
emitter.VSTR(S5, R12, -36);
RET(CheckLast(emitter, "ed4c2a09 VSTR s5, [r12, #-36]"));
emitter.VADD(S1, S2, S3);
RET(CheckLast(emitter, "ee710a21 VADD s1, s2, s3"));
emitter.VMUL(S7, S8, S9);
RET(CheckLast(emitter, "ee643a24 VMUL s7, s8, s9"));
emitter.VMLA(S7, S8, S9);
RET(CheckLast(emitter, "ee443a24 VMLA s7, s8, s9"));
emitter.VNMLA(S7, S8, S9);
RET(CheckLast(emitter, "ee543a64 VNMLA s7, s8, s9"));
emitter.VABS(S1, S2);
RET(CheckLast(emitter, "eef00ac1 VABS s1, s2"));
emitter.VSQRT(S1, S2);
RET(CheckLast(emitter, "eef10ac1 VSQRT s1, s2"));
emitter.VDIV(S1, S2, S3);
RET(CheckLast(emitter, "eec10a21 VDIV s1, s2, s3"));
emitter.VMRS(R1);
RET(CheckLast(emitter, "eef11a10 VMRS r1"));
emitter.VMSR(R7);
RET(CheckLast(emitter, "eee17a10 VMSR r7"));
emitter.VMRS_APSR();
RET(CheckLast(emitter, "eef1fa10 VMRS APSR"));
emitter.VCVT(S0, S1, TO_INT | IS_SIGNED);
RET(CheckLast(emitter, "eebd0a60 VCVT ..."));
char disasm[512];
ArmDis(0, code[0] & 0xFFFFFFFF, disasm);
std::string dis(disasm);
EXPECT_EQ_STR(dis, std::string("e4973000 LDR r3, [r7, #0]"));
// WTF?
//emitter.VSUB(S4, S5, S6);
//RET(CheckLast(emitter, "ee322ac3 VSUB s4, s5, s6"));
emitter.VMOV(S3, S6);
RET(CheckLast(emitter, "eef01a43 VMOV s3, s6"));
return true;
}