Skip to content

Commit

Permalink
arm64: Adding SIMD move instructions.
Browse files Browse the repository at this point in the history
  • Loading branch information
mikeakohn committed Jan 27, 2024
1 parent 726ec15 commit 3618f4b
Show file tree
Hide file tree
Showing 4 changed files with 257 additions and 9 deletions.
140 changes: 131 additions & 9 deletions asm/arm64.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -79,9 +79,10 @@ enum
struct _operand
{
int value;
int type;
int attribute;
int offset_imm;
uint8_t type;
uint8_t attribute;
uint8_t element;
uint8_t index_type;
};

Expand Down Expand Up @@ -339,29 +340,29 @@ static int get_register_arm64(
case SIZE_8B:
case SIZE_16B:
case SIZE_B:
if (element < 1 || element > 16)
if (element < 0 || element > 15)
{
print_error_range(asm_context, "Element", 1, 16);
print_error_range(asm_context, "Element", 0, 15);
return -2;
}
size = SIZE_B;
break;
case SIZE_4H:
case SIZE_8H:
case SIZE_H:
if (element < 1 || element > 8)
if (element < 0 || element > 7)
{
print_error_range(asm_context, "Element", 1, 8);
print_error_range(asm_context, "Element", 0, 7);
return -2;
}
size = SIZE_H;
break;
case SIZE_2S:
case SIZE_4S:
case SIZE_S:
if (element < 1 || element > 4)
if (element < 0 || element > 3)
{
print_error_range(asm_context, "Element", 1, 4);
print_error_range(asm_context, "Element", 0, 3);
return -2;
}
size = SIZE_S;
Expand All @@ -371,7 +372,7 @@ static int get_register_arm64(
case SIZE_D:
if (element < 1 || element > 2)
{
print_error_range(asm_context, "Element", 1, 2);
print_error_range(asm_context, "Element", 0, 1);
return -2;
}
size = SIZE_D;
Expand All @@ -385,6 +386,7 @@ static int get_register_arm64(
operand->type = OPERAND_REG_VECTOR_ELEMENT;
operand->value = num;
operand->attribute = size;
operand->element = element;

return 0;
}
Expand Down Expand Up @@ -1122,6 +1124,56 @@ static int op_ld_literal(
return 4;
}

static bool reg_matches(int operand, int wanted)
{
switch (wanted)
{
case ARM64_REG_V: return operand == OPERAND_REG_VECTOR;
case ARM64_REG_V_SCALAR: return operand == OPERAND_REG_SCALAR;
case ARM64_REG_V_ELEMENT: return operand == OPERAND_REG_VECTOR_ELEMENT;
case ARM64_REG_B:
return operand == OPERAND_REG_32 || operand == OPERAND_REG_64;
default:
return false;
}
}

static bool match_vector_size(int attribute, int type)
{
if (attribute == SIZE_D)
{
return type == OPERAND_REG_64;
}
else
{
return type == OPERAND_REG_32;
}
}

static bool encode_vector_element(_operand *operand, int &imm)
{
switch (operand->attribute)
{
case SIZE_B:
imm = 1 | (operand->element << 1);
return true;
case SIZE_H:
imm = 2 | (operand->element << 2);
return true;
case SIZE_S:
imm = 4 | (operand->element << 3);
return true;
case SIZE_D:
imm = 8 | (operand->element << 4);
return true;
default:
imm = 0;
return false;
}

return true;
}

int parse_instruction_arm64(AsmContext *asm_context, char *instr)
{
char instr_case_mem[TOKENLEN];
Expand Down Expand Up @@ -1346,6 +1398,76 @@ int parse_instruction_arm64(AsmContext *asm_context, char *instr)
}
}

if (operand_count == 2)
{
for (n = 0; table_arm64_simd_copy[n].instr != NULL; n++)
{
if (strcmp(table_arm64_simd_copy[n].instr, instr_case) != 0) { continue; }

found = 1;

if (!reg_matches(operands[0].type, table_arm64_simd_copy[n].reg_rd))
{
continue;
}

if (!reg_matches(operands[1].type, table_arm64_simd_copy[n].reg_rn))
{
continue;
}

int imm5 = 0;
int imm4 = table_arm64_simd_copy[n].imm4;

if (operands[0].type == OPERAND_REG_VECTOR_ELEMENT &&
operands[1].type == OPERAND_REG_VECTOR_ELEMENT)
{
if (operands[0].attribute != operands[1].attribute)
{
print_error(asm_context, "Mismatched vector sizes.");
return -1;
}

encode_vector_element(&operands[0], imm5);
encode_vector_element(&operands[1], imm4);
}
else
if (operands[0].type == OPERAND_REG_VECTOR_ELEMENT)
{
if (!match_vector_size(operands[0].attribute, operands[1].type))
{
print_error(asm_context, "Mismatched register sizes.");
return -1;
}

encode_vector_element(&operands[0], imm5);
}
else
if (operands[1].type == OPERAND_REG_VECTOR_ELEMENT)
{
if (!match_vector_size(operands[1].attribute, operands[0].type))
{
print_error(asm_context, "Mismatched register sizes.");
return -1;
}

encode_vector_element(&operands[1], imm5);
}

opcode = 0x0e000400 |
(table_arm64_simd_copy[n].q << 31) |
(table_arm64_simd_copy[n].op << 29) |
(imm5 << 16) |
(imm4 << 11) |
(operands[1].value << 5) |
operands[0].value;

add_bin32(asm_context, opcode, IS_OPCODE);

return 4;
}
}

for (n = 0; table_arm64[n].instr != NULL; n++)
{
if (strcmp(table_arm64[n].instr, instr_case) == 0)
Expand Down
95 changes: 95 additions & 0 deletions disasm/arm64.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,57 @@ static const char *get_at(int value)
return "???";
}

static const char *decode_imm5_scalar(int imm, int q)
{
int size = 0;

if ((imm & 0x1) == 1) { size = 0; }
else if ((imm & 0x3) == 2) { size = 2; }
else if ((imm & 0x7) == 4) { size = 4; }
else if ((imm & 0xf) == 8) { size = 6; }

size = size + q;

return vec_size[size];
}

static void decode_imm5_element(int imm, char &size, int &element)
{
size = '?';
element = 0;

if ((imm & 0x1) == 1) { size = 'b'; element = (imm >> 1) & 0xf; }
else if ((imm & 0x3) == 2) { size = 'h'; element = (imm >> 2) & 0x7; }
else if ((imm & 0x7) == 4) { size = 's'; element = (imm >> 3) & 0x3; }
else if ((imm & 0xf) == 8) { size = 'd'; element = (imm >> 4) & 0x1; }
}

static void get_reg_name(char *s, int length, int num, int imm, int type, int q)
{
char size;
int element;

switch (type)
{
case ARM64_REG_B:
snprintf(s, length, "%c%d", (imm & 8) == 0 ? 'w' : 'x', num);
break;
case ARM64_REG_V:
snprintf(s, length, "v%d", num);
break;
case ARM64_REG_V_SCALAR:
snprintf(s, length, "v%d.%s", num, decode_imm5_scalar(imm, q));
break;
case ARM64_REG_V_ELEMENT:
decode_imm5_element(imm, size, element);
snprintf(s, length, "v%d.%c[%d]", num, size, element);
break;
default:
memset(s, 0, length);
break;
}
}

int disasm_arm64(
Memory *memory,
uint32_t address,
Expand All @@ -70,6 +121,50 @@ int disasm_arm64(
sf = (opcode >> 31) & 0x1;
v = (opcode >> 26) & 0x1;

if ((opcode & 0x9fe08400) == 0x0e000400)
{
for (n = 0; table_arm64_simd_copy[n].instr != NULL; n++)
{
int q = opcode >> 31;
int op = (opcode >> 29) & 1;
int imm5 = (opcode >> 16) & 0x1f;
int imm4 = (opcode >> 11) & 0xf;

if (table_arm64_simd_copy[n].q != q ||
table_arm64_simd_copy[n].op != op)
{
continue;
}

char rn_name[16];
char rd_name[16];

if (table_arm64_simd_copy[n].q == 1 &&
table_arm64_simd_copy[n].op == 1)
{
get_reg_name(rn_name, sizeof(rn_name), rn, imm4, table_arm64_simd_copy[n].reg_rn, q);
get_reg_name(rd_name, sizeof(rd_name), rd, imm5, table_arm64_simd_copy[n].reg_rd, q);
}
else
{
if (table_arm64_simd_copy[n].imm4 != imm4) { continue; }

get_reg_name(rn_name, sizeof(rn_name), rn, imm5, table_arm64_simd_copy[n].reg_rn, q);
get_reg_name(rd_name, sizeof(rd_name), rd, imm5, table_arm64_simd_copy[n].reg_rd, q);
}

snprintf(instruction, length, "%s %s, %s",
table_arm64_simd_copy[n].instr,
rd_name,
rn_name);

return 4;
}

strcpy(instruction, "???");
return 4;
}

for (n = 0; table_arm64[n].instr != NULL; n++)
{
if ((opcode & table_arm64[n].mask) == table_arm64[n].opcode)
Expand Down
12 changes: 12 additions & 0 deletions table/arm64.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -334,7 +334,19 @@ struct _table_arm64_uncond_branch table_arm64_uncond_branch[] =
// C3.6.2 AdvSIMD TBL/TBX.
// C3.6.3 AdvSIMD ZIP/UZP/TRN.
// C3.6.4 AdvSIMD across lanes.

// C3.6.5 AdvSIMD copy.
struct _table_arm64_simd_copy table_arm64_simd_copy[] =
{
{ "dup", 0, 0, 0x0, ARM64_REG_V, ARM64_REG_V_ELEMENT },
{ "dup", 0, 0, 0x1, ARM64_REG_V_SCALAR, ARM64_REG_V_ELEMENT },
{ "smov", 0, 0, 0x5, ARM64_REG_B, ARM64_REG_V_ELEMENT },
{ "umov", 0, 0, 0x7, ARM64_REG_B, ARM64_REG_V_ELEMENT },
{ "ins", 1, 0, 0x3, ARM64_REG_V_ELEMENT, ARM64_REG_V_ELEMENT },
{ "ins", 1, 1, 0x0, ARM64_REG_V_ELEMENT, ARM64_REG_B },
{ NULL, 0, 0, 0x0, 0, 0 },
};

// C3.6.6 AdvSIMD modified immediate.
// C3.6.7 AdvSIMD scalar copy.
// C3.6.9 AdvSIMD scalar shift by immediate.
Expand Down
19 changes: 19 additions & 0 deletions table/arm64.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,14 @@ enum
OP_LD_LITERAL,
};

enum
{
ARM64_REG_B,
ARM64_REG_V,
ARM64_REG_V_SCALAR,
ARM64_REG_V_ELEMENT,
};

#define ARM64_IMM 0xff
#define ARM64_REG_31 0xfe
#define ARM64_REG_ANY 0xfd
Expand Down Expand Up @@ -109,6 +117,16 @@ struct _table_arm64_test_branch
uint8_t op;
};

struct _table_arm64_simd_copy
{
const char *instr;
uint8_t q;
uint8_t op;
uint8_t imm4;
uint8_t reg_rd;
uint8_t reg_rn;
};

#if 0
struct _table_arm64_uncond_branch
{
Expand All @@ -128,6 +146,7 @@ extern struct _table_arm64_exception table_arm64_exception[];
extern struct _table_arm64_system table_arm64_system[];
extern struct _table_arm64_test_branch table_arm64_test_branch[];
//extern struct _table_arm64_uncond_branch table_arm64_uncond_branch[];
extern struct _table_arm64_simd_copy table_arm64_simd_copy[];

#endif

0 comments on commit 3618f4b

Please sign in to comment.