From 4965eb9fed617cf7746d4689fc93f41bf3c6aa4e Mon Sep 17 00:00:00 2001
From: Michael Kohn <mike@mikekohn.net>
Date: Sun, 28 Jan 2024 10:49:23 -0600
Subject: [PATCH] arm64: Some fixes for simd move.

---
 asm/arm64.cpp    | 76 +++++++++++++++++++++++++++++++++++++++---------
 common/version.h |  2 +-
 disasm/arm64.cpp | 13 +++++----
 table/arm64.cpp  |  7 +++--
 table/arm64.h    |  2 +-
 5 files changed, 76 insertions(+), 24 deletions(-)

diff --git a/asm/arm64.cpp b/asm/arm64.cpp
index 67190928..5f9e0391 100644
--- a/asm/arm64.cpp
+++ b/asm/arm64.cpp
@@ -1128,7 +1128,7 @@ static bool reg_matches(int operand, int wanted)
 {
   switch (wanted)
   {
-    case ARM64_REG_V:         return operand == OPERAND_REG_VECTOR;
+    case ARM64_REG_V_DOT:     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:
@@ -1138,18 +1138,68 @@ static bool reg_matches(int operand, int wanted)
   }
 }
 
-static bool match_vector_size(int attribute, int type)
+static int get_register_size(_operand *operand)
 {
-  if (attribute == SIZE_D)
+  switch (operand->type)
   {
-    return type == OPERAND_REG_64;
-  }
-    else
-  {
-    return type == OPERAND_REG_32;
+    case OPERAND_REG_32: return 32;
+    case OPERAND_REG_64: return 64;
+    case OPERAND_REG_VECTOR:
+      switch (operand->attribute)
+      {
+        case SIZE_8B: return 8;
+        case SIZE_16B: return 8;
+        case SIZE_4H: return 16;
+        case SIZE_8H: return 16;
+        case SIZE_2S: return 32;
+        case SIZE_4S: return 32;
+        case SIZE_1D: return 64;
+        case SIZE_2D: return 64;
+        case SIZE_B: return 8;
+        case SIZE_H: return 16;
+        case SIZE_S: return 32;
+        case SIZE_D: return 64;
+        default: return -4;
+      }
+    case OPERAND_REG_VECTOR_ELEMENT:
+      switch (operand->attribute)
+      {
+        case SIZE_8B: return 8;
+        case SIZE_16B: return 8;
+        case SIZE_4H: return 16;
+        case SIZE_8H: return 16;
+        case SIZE_2S: return 32;
+        case SIZE_4S: return 32;
+        case SIZE_1D: return 64;
+        case SIZE_2D: return 64;
+        case SIZE_B: return 8;
+        case SIZE_H: return 16;
+        case SIZE_S: return 32;
+        case SIZE_D: return 64;
+        default: return -4;
+      }
+    case OPERAND_REG_SCALAR:
+      switch (operand->attribute)
+      {
+        case 0: return 8;
+        case 1: return 16;
+        case 2: return 32;
+        case 3: return 64;
+        default: return -3;
+      }
+    default:
+      return -1;
   }
 }
 
+static bool match_vector_size(_operand *operands)
+{
+  int size_d = get_register_size(&operands[0]);
+  int size_n = get_register_size(&operands[1]);
+
+  return size_d == size_n;
+}
+
 static bool encode_vector_element(_operand *operand, int &imm)
 {
   switch (operand->attribute)
@@ -1434,9 +1484,9 @@ int parse_instruction_arm64(AsmContext *asm_context, char *instr)
         else
       if (operands[0].type == OPERAND_REG_VECTOR_ELEMENT)
       {
-        if (!match_vector_size(operands[0].attribute, operands[1].type))
+        if (!match_vector_size(operands))
         {
-          print_error(asm_context, "Mismatched register sizes.");
+          print_error(asm_context, "Mismatched register sizes");
           return -1;
         }
 
@@ -1445,9 +1495,9 @@ int parse_instruction_arm64(AsmContext *asm_context, char *instr)
         else
       if (operands[1].type == OPERAND_REG_VECTOR_ELEMENT)
       {
-        if (!match_vector_size(operands[1].attribute, operands[0].type))
+        if (!match_vector_size(operands))
         {
-          print_error(asm_context, "Mismatched register sizes.");
+          print_error(asm_context, "Mismatched register sizes");
           return -1;
         }
 
@@ -1456,7 +1506,7 @@ int parse_instruction_arm64(AsmContext *asm_context, char *instr)
 
       opcode = 0x0e000400 |
         (table_arm64_simd_copy[n].q << 31) |
-        (table_arm64_simd_copy[n].op << 29) |
+        (table_arm64_simd_copy[n].op << 28) |
         (imm5 << 16) |
         (imm4 << 11) |
         (operands[1].value << 5) |
diff --git a/common/version.h b/common/version.h
index 2006f072..c1e4fad9 100644
--- a/common/version.h
+++ b/common/version.h
@@ -1,7 +1,7 @@
 #ifndef NAKEN_ASM_VERSION_H
 #define NAKEN_ASM_VERSION_H
 
-#define VERSION "January 11, 2024"
+#define VERSION "January 28, 2024"
 
 #endif
 
diff --git a/disasm/arm64.cpp b/disasm/arm64.cpp
index 5609974a..60db1eaf 100644
--- a/disasm/arm64.cpp
+++ b/disasm/arm64.cpp
@@ -44,7 +44,7 @@ static const char *get_at(int value)
   return "???";
 }
 
-static const char *decode_imm5_scalar(int imm, int q)
+static const char *decode_imm5_vector(int imm, int q)
 {
   int size = 0;
 
@@ -79,11 +79,12 @@ static void get_reg_name(char *s, int length, int num, int imm, int type, int q)
     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);
+    case ARM64_REG_V_DOT:
+      snprintf(s, length, "v%d.%s", num, decode_imm5_vector(imm, q));
       break;
     case ARM64_REG_V_SCALAR:
-      snprintf(s, length, "v%d.%s", num, decode_imm5_scalar(imm, q));
+      decode_imm5_element(imm, size, element);
+      snprintf(s, length, "%c%d", size, num);
       break;
     case ARM64_REG_V_ELEMENT:
       decode_imm5_element(imm, size, element);
@@ -121,12 +122,12 @@ int disasm_arm64(
   sf = (opcode >> 31) & 0x1;
   v = (opcode >> 26) & 0x1;
 
-  if ((opcode & 0x9fe08400) == 0x0e000400)
+  if ((opcode & 0x8fe08400) == 0x0e000400)
   {
     for (n = 0; table_arm64_simd_copy[n].instr != NULL; n++)
     {
       int q = opcode >> 31;
-      int op = (opcode >> 29) & 1;
+      int op = (opcode >> 28) & 3;
       int imm5 = (opcode >> 16) & 0x1f;
       int imm4 = (opcode >> 11) & 0xf;
 
diff --git a/table/arm64.cpp b/table/arm64.cpp
index ae501e1f..ca1ba50f 100644
--- a/table/arm64.cpp
+++ b/table/arm64.cpp
@@ -338,12 +338,13 @@ struct _table_arm64_uncond_branch table_arm64_uncond_branch[] =
 // 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 },
+  { "dup",  0, 0, 0x0, ARM64_REG_V_DOT,     ARM64_REG_V_ELEMENT },
+  { "dup",  0, 1, 0x0, ARM64_REG_V_SCALAR,  ARM64_REG_V_ELEMENT },
+  { "dup",  0, 0, 0x1, ARM64_REG_V_DOT,     ARM64_REG_B         },
   { "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         },
+  { "ins",  1, 2, 0x0, ARM64_REG_V_ELEMENT, ARM64_REG_B         },
   {  NULL,  0, 0, 0x0, 0,                   0                   },
 };
 
diff --git a/table/arm64.h b/table/arm64.h
index a4a05c95..0ca145cd 100644
--- a/table/arm64.h
+++ b/table/arm64.h
@@ -48,8 +48,8 @@ enum
 enum
 {
   ARM64_REG_B,
-  ARM64_REG_V,
   ARM64_REG_V_SCALAR,
+  ARM64_REG_V_DOT,
   ARM64_REG_V_ELEMENT,
 };