ppc/svp64: introduce SVP64 opcode lookup
authorDmitry Selyutin <ghostmansd@gmail.com>
Sun, 28 May 2023 22:04:57 +0000 (01:04 +0300)
committerDmitry Selyutin <ghostmansd@gmail.com>
Tue, 14 Nov 2023 19:53:35 +0000 (22:53 +0300)
include/opcode/ppc.h
opcodes/ppc-dis.c
opcodes/ppc-svp64-dis.c [new file with mode: 0644]

index e9e1c6316d8f03f4a89f7cd00b1b44cc0ba14144..e7ca06e23c167158f71e04ebcd8080f1c0c83085 100644 (file)
@@ -281,6 +281,10 @@ extern const unsigned int spe2_num_opcodes;
 /* A macro used to hash 8-byte PREFIX instructions.  */
 #define PPC_PREFIX_SEG(i) (PPC_OP (i) >> 1)
 
+/* A macro to extract the major opcode from an SVP64 instruction.
+   This is PPC-compatible due to the way SVP64 remaps instructions.  */
+#define SVP64_OP(i) PPC_OP (PPC_GET_SUFFIX (i))
+
 \f
 /* The operands table is an array of struct powerpc_operand.  */
 
index 75472730ac8084ee1ec1e24c1979bcc643aa3e8b..81b9f5bcb126958f2a337610b5c71f0b588f0f49 100644 (file)
@@ -28,6 +28,8 @@
 #include "opcode/ppc.h"
 #include "libiberty.h"
 
+#include "ppc-svp64-dis.c"
+
 /* This file provides several disassembler functions, all of which use
    the disassembler interface defined in dis-asm.h.  Several functions
    are provided because this file handles disassembly for the PowerPC
@@ -528,6 +530,8 @@ disassemble_init_powerpc (struct disassemble_info *info)
                break;
            }
        }
+
+      svp64_disassemble_init ();
     }
 
   powerpc_init_dialect (info);
@@ -1165,10 +1169,11 @@ print_insn_powerpc (bfd_vma memaddr,
 
   /* Get the major opcode of the insn.  */
   opcode = NULL;
-  if ((dialect & PPC_OPCODE_POWER10) != 0
+  if ((dialect & (PPC_OPCODE_POWER10 | PPC_OPCODE_SVP64)) != 0
       && PPC_OP (insn) == 0x1)
     {
       uint64_t temp_insn, suffix;
+
       status = (*info->read_memory_func) (memaddr + 4, buffer, 4, info);
       if (status == 0)
        {
@@ -1177,7 +1182,19 @@ print_insn_powerpc (bfd_vma memaddr,
          else
            suffix = bfd_getl32 (buffer);
          temp_insn = (insn << 32) | suffix;
-         opcode = lookup_prefix (temp_insn, dialect & ~PPC_OPCODE_ANY);
+
+         if ((dialect & PPC_OPCODE_SVP64) != 0)
+           {
+             opcode = svp64_lookup (temp_insn, dialect);
+             if (opcode != NULL)
+               {
+                 insn = temp_insn;
+                 insn_length = 8;
+               }
+           }
+
+         if (opcode == NULL)
+           opcode = lookup_prefix (temp_insn, dialect & ~PPC_OPCODE_ANY);
          if (opcode == NULL && (dialect & PPC_OPCODE_ANY) != 0)
            opcode = lookup_prefix (temp_insn, dialect);
          if (opcode != NULL)
diff --git a/opcodes/ppc-svp64-dis.c b/opcodes/ppc-svp64-dis.c
new file mode 100644 (file)
index 0000000..0d50997
--- /dev/null
@@ -0,0 +1,116 @@
+/* ppc-dis.c -- Disassemble PowerPC instructions
+   Copyright (C) 1994-2022 Free Software Foundation, Inc.
+   Written by Dmitry Selyutin (ghostmansd).
+   Sponsored by NLnet and NGI POINTER under EU Grants 871528 and 957073.
+
+   This file is part of the GNU opcodes library.
+
+   This library is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3, or (at your option)
+   any later version.
+
+   It is distributed in the hope that it will be useful, but WITHOUT
+   ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+   or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
+   License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this file; see the file COPYING.  If not, write to the
+   Free Software Foundation, 51 Franklin Street - Fifth Floor, Boston,
+   MA 02110-1301, USA.  */
+
+#include <stdlib.h>
+
+#define SVP64_RECORD_SEGS (1 + SVP64_OP (-1))
+static unsigned short svp64_record_indices[SVP64_RECORD_SEGS + 1];
+
+static const struct powerpc_opcode *
+lookup_powerpc (uint64_t insn, ppc_cpu_t dialect);
+
+static void
+svp64_disassemble_init (void)
+{
+  size_t seg;
+  size_t idx;
+
+  for (seg = 0, idx = 0; seg <= SVP64_RECORD_SEGS; seg++)
+    {
+      svp64_record_indices[seg] = idx;
+      for (; idx < svp64_nr_records; idx++)
+       {
+         const struct svp64_opcode *opcode;
+         const struct svp64_opcode *opcode_end;
+
+         opcode = svp64_records[idx].opcodes;
+         opcode_end = (opcode + svp64_records[idx].nr_opcodes);
+
+         if (seg < SVP64_OP (opcode->value))
+           break;
+
+         /* Check that the segment doesn't break in the middle. */
+         for (; opcode < opcode_end; ++opcode)
+           {
+             if (seg < SVP64_OP (opcode->value))
+             abort ();
+           }
+       }
+    }
+}
+
+static const struct svp64_record *
+svp64_lookup_record (uint32_t insn,
+  const struct svp64_record *start,
+  const struct svp64_record *end)
+{
+  const struct svp64_record *record;
+
+  for (record = start; record < end; ++record)
+    {
+      const struct svp64_opcode *opcode;
+      const struct svp64_opcode *opcode_end;
+
+      opcode = record->opcodes;
+      opcode_end = (opcode + record->nr_opcodes);
+      for (; opcode < opcode_end; ++opcode)
+       {
+         if ((opcode->value & opcode->mask) ==
+             (insn & opcode->mask))
+         return record;
+       }
+    }
+
+  return NULL;
+}
+
+static const struct powerpc_opcode *
+svp64_lookup (uint64_t insn, ppc_cpu_t dialect)
+{
+  uint32_t suffix;
+  unsigned long op;
+  const struct powerpc_opcode *opcode;
+  const struct svp64_record *record;
+  const struct svp64_record *record_start;
+  const struct svp64_record *record_end;
+  struct svp64_insn svp64_insn = {insn};
+
+  if ((svp64_insn_get_prefix_po (&svp64_insn) != 0x1) ||
+      (svp64_insn_get_prefix_id (&svp64_insn) != 0x3))
+    return NULL;
+
+  suffix = (uint32_t)svp64_insn_get_suffix (&svp64_insn);
+  opcode = lookup_powerpc (suffix, dialect & ~PPC_OPCODE_ANY);
+  if (opcode == NULL && (dialect & PPC_OPCODE_ANY) != 0)
+    opcode = lookup_powerpc (suffix, dialect);
+  if (opcode == NULL)
+    return NULL;
+
+  op = SVP64_OP (suffix);
+  record_start = svp64_records + svp64_record_indices[op];
+  record_end = (svp64_records + svp64_record_indices[op + 1]);
+  record = svp64_lookup_record (suffix, record_start, record_end);
+  if (record == NULL)
+    return NULL;
+
+  return opcode;
+}