/* aarch64-gen.c -- Generate tables and routines for opcode lookup and
   instruction encoding and decoding.
   Copyright 2012, 2013  Free Software Foundation, Inc.
   Contributed by ARM Ltd.

   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 program; see the file COPYING3. If not,
   see <http://www.gnu.org/licenses/>.  */

#include "sysdep.h"
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>

#include "libiberty.h"
#include "getopt.h"
#include "opcode/aarch64.h"

#include "aarch64-tbl.h"

static int debug = 0;

/* Structure used in the decoding tree to group a list of aarch64_opcode
   entries.  */

struct opcode_node
{
  aarch64_insn opcode;
  aarch64_insn mask;
  /* Index of the entry in the original table; the top 2 bits help
     determine the table.  */
  unsigned int index;
  struct opcode_node *next;
};

typedef struct opcode_node opcode_node;

/* Head of the list of the opcode_node after read_table.  */
static opcode_node opcode_nodes_head;

/* Node in the decoding tree.  */

struct bittree
{
  unsigned int bitno;
  /* 0, 1, and X (don't care).  */
  struct bittree *bits[2];
  /* List of opcodes; only valid for the leaf node.  */
  opcode_node *list;
};

/* Allocate and initialize an opcode_node.  */
static opcode_node*
new_opcode_node (void)
{
  opcode_node* ent = malloc (sizeof (opcode_node));

  if (!ent)
    abort ();

  ent->opcode = 0;
  ent->mask = 0;
  ent->index = -1;
  ent->next = NULL;

  return ent;
}

/* Multiple tables are supported, although currently only one table is
   in use.  N.B. there are still some functions have the table name
   'aarch64_opcode_table' hard-coded in, e.g. print_find_next_opcode;
   therefore some amount of work needs to be done if the full support
   for multiple tables needs to be enabled.  */
static const struct aarch64_opcode *aarch64_opcode_tables[] =
{aarch64_opcode_table};

/* Use top 2 bits to indiate which table.  */
static unsigned int
initialize_index (const struct aarch64_opcode* table)
{
  int i;
  const int num_of_tables = sizeof (aarch64_opcode_tables)
    / sizeof (struct aarch64_opcode *);
  for (i = 0; i < num_of_tables; ++i)
    if (table == aarch64_opcode_tables [i])
      break;
  if (i == num_of_tables)
    abort ();
  return (unsigned int)i << 30;
}

static inline const struct aarch64_opcode *
index2table (unsigned int index)
{
  return aarch64_opcode_tables[(index >> 30) & 0x3];
}

static inline unsigned int
real_index (unsigned int index)
{
  return index & ((1 << 30) - 1);
}

/* Given OPCODE_NODE, return the corresponding aarch64_opcode*.  */
static const aarch64_opcode*
get_aarch64_opcode (const opcode_node *opcode_node)
{
  if (opcode_node == NULL)
    return NULL;
  return &index2table (opcode_node->index)[real_index (opcode_node->index)];
}

static void
read_table (const struct aarch64_opcode* table)
{
  const struct aarch64_opcode *ent = table;
  opcode_node **new_ent;
  unsigned int index = initialize_index (table);

  if (!ent->name)
    return;

  new_ent = &opcode_nodes_head.next;

  while (*new_ent)
    new_ent = &(*new_ent)->next;

  do
    {
      /* F_PSEUDO needs to be used together with F_ALIAS to indicate an alias
	 opcode is a programmer friendly pseudo instruction available only in
	 the assembly code (thus will not show up in the disassembly).  */
      assert (pseudo_opcode_p (ent) == FALSE || alias_opcode_p (ent) == TRUE);
      /* Skip alias (inc. pseudo) opcode.  */
      if (alias_opcode_p (ent) == TRUE)
	{
	  index++;
	  continue;
	}
      *new_ent = new_opcode_node ();
      (*new_ent)->opcode = ent->opcode;
      (*new_ent)->mask = ent->mask;
      (*new_ent)->index = index++;
      new_ent = &((*new_ent)->next);
    } while ((++ent)->name);
}

static inline void
print_one_opcode_node (opcode_node* ent)
{
  printf ("%s\t%08x\t%08x\t%d\n", get_aarch64_opcode (ent)->name,
	  get_aarch64_opcode (ent)->opcode, get_aarch64_opcode (ent)->mask,
	  (int)real_index (ent->index));
}

/* As an internal debugging utility, print out the list of nodes pointed
   by opcode_nodes_head.  */
static void
print_opcode_nodes (void)
{
  opcode_node* ent = opcode_nodes_head.next;
  printf ("print_opcode_nodes table:\n");
  while (ent)
    {
      print_one_opcode_node (ent);
      ent = ent->next;
    }
}

static struct bittree*
new_bittree_node (void)
{
  struct bittree* node;
  node = malloc (sizeof (struct bittree));
  if (!node)
    abort ();
  node->bitno = -1;
  node->bits[0] = NULL;
  node->bits[1] = NULL;
  return node;
}

/* The largest number of opcode entries that exist at a leaf node of the
   decoding decision tree.  The reason that there can be more than one
   opcode entry is because some opcodes have shared field that is partially
   constrained and thus cannot be fully isolated using the algorithm
   here.  */
static int max_num_opcodes_at_leaf_node = 0;

/* Given a list of opcodes headed by *OPCODE, try to establish one bit that
   is shared by all the opcodes in the list as one of base opcode bits.  If
   such a bit is found, divide the list of the opcodes into two based on the
   value of the bit.

   Store the bit number in BITTREE->BITNO if the division succeeds.  If unable
   to determine such a bit or there is only one opcode in the list, the list
   is decided to be undividable and OPCODE will be assigned to BITTREE->LIST.

   The function recursively call itself until OPCODE is undividable.
   
   N.B. the nature of this algrithm determines that given any value in the
   32-bit space, the computed decision tree will always be able to find one or
   more opcodes entries for it, regardless whether there is a valid instruction
   defined for this value or not.  In order to detect the undefined values,
   when the caller obtains the opcode entry/entries, it should at least compare
   the bit-wise AND result of the value and the mask with the base opcode
   value; if the two are different, it means that the value is undefined
   (although the value may be still undefined when the comparison is the same,
   in which case call aarch64_opcode_decode to carry out further checks).  */

static void
divide_table_1 (struct bittree *bittree, opcode_node *opcode)
{
  aarch64_insn mask_and;
  opcode_node *ent;
  unsigned int bitno;
  aarch64_insn bitmask;
  opcode_node list0, list1, **ptr0, **ptr1;
  static int depth = 0;

  ++depth;

  if (debug)
    printf ("Enter into depth %d\n", depth);

  assert (opcode != NULL);

  /* Succeed when there is only one opcode left.  */
  if (!opcode->next)
    {
      if (debug)
	{
	  printf ("opcode isolated:\n");
	  print_one_opcode_node (opcode);
	}
      goto divide_table_1_finish;
    }

divide_table_1_try_again:
  mask_and = -1;
  ent = opcode;
  while (ent)
    {
      mask_and &= ent->mask;
      ent = ent->next;
    }

  if (debug)
    printf ("mask and result: %08x\n", (unsigned int)mask_and);

  /* If no more bit to look into, we have to accept the reality then.  */
  if (!mask_and)
    {
      int i;
      opcode_node *ptr;
      if (debug)
	{
	  ptr = opcode;
	  printf ("Isolated opcode group:\n");
	  do {
	      print_one_opcode_node (ptr);
	      ptr = ptr->next;
	  } while (ptr);
	}
      /* Count the number of opcodes.  */
      for (i = 0, ptr = opcode; ptr; ++i)
	ptr = ptr->next;
      if (i > max_num_opcodes_at_leaf_node)
	max_num_opcodes_at_leaf_node = i;
      goto divide_table_1_finish;
    }

  /* Pick up the right most bit that is 1.  */
  bitno = 0;
  while (!(mask_and & (1 << bitno)))
    ++bitno;
  bitmask = (1 << bitno);

  if (debug)
    printf ("use bit %d\n", bitno);

  /* Record in the bittree.  */
  bittree->bitno = bitno;

  /* Get two new opcode lists; adjust their masks.  */
  list0.next = NULL;
  list1.next = NULL;
  ptr0 = &list0.next;
  ptr1 = &list1.next;
  ent = opcode;
  while (ent)
    {
      if (ent->opcode & bitmask)
	{
	  ent->mask &= (~bitmask);
	  *ptr1 = ent;
	  ent = ent->next;
	  (*ptr1)->next = NULL;
	  ptr1 = &(*ptr1)->next;
	}
      else
	{
	  ent->mask &= (~bitmask);
	  *ptr0 = ent;
	  ent = ent->next;
	  (*ptr0)->next = NULL;
	  ptr0 = &(*ptr0)->next;
	}
    }

  /* If BITNO can NOT divide the opcode group, try next bit.  */
  if (list0.next == NULL)
    {
      opcode = list1.next;
      goto divide_table_1_try_again;
    }
  else if (list1.next == NULL)
    {
      opcode = list0.next;
      goto divide_table_1_try_again;
    }

  /* Further divide.  */
  bittree->bits[0] = new_bittree_node ();
  bittree->bits[1] = new_bittree_node ();
  divide_table_1 (bittree->bits[0], list0.next);
  divide_table_1 (bittree->bits[1], list1.next);

divide_table_1_finish:
  if (debug)
    printf ("Leave from depth %d\n", depth);
  --depth;

  /* Record the opcode entries on this leaf node.  */
  bittree->list = opcode;

  return;
}

/* Call divide_table_1 to divide the all the opcodes and thus create the
   decoding decision tree.  */
static struct bittree *
divide_table (void)
{
  struct bittree *bittree = new_bittree_node ();
  divide_table_1 (bittree, opcode_nodes_head.next);
  return bittree;
}

/* Read in all of the tables, create the decoding decision tree and return
   the tree root.  */
static struct bittree *
initialize_decoder_tree (void)
{
  int i;
  const int num_of_tables = (sizeof (aarch64_opcode_tables)
			     / sizeof (struct aarch64_opcode *));
  for (i = 0; i < num_of_tables; ++i)
    read_table (aarch64_opcode_tables [i]);
  if (debug)
    print_opcode_nodes ();
  return divide_table ();
}

static void __attribute__ ((format (printf, 2, 3)))
indented_print (unsigned int indent, const char *format, ...)
{
  /* 80 number of spaces pluc a NULL terminator.  */
  static const char spaces[81] =
    "                                                                                ";
  va_list ap;
  va_start (ap, format);
  assert (indent <= 80);
  printf ("%s", &spaces[80 - indent]);
  vprintf (format, ap);
  va_end (ap);
}

/* N.B. read the comment above divide_table_1 for the reason why the generated
   decision tree function never returns NULL.  */

static void
print_decision_tree_1 (unsigned int indent, struct bittree* bittree)
{
  /* PATTERN is only used to generate comment in the code.  */
  static char pattern[33] = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
  assert (bittree != NULL);

  /* Leaf node located.  */
  if (bittree->bits[0] == NULL && bittree->bits[1] == NULL)
    {
      assert (bittree->list != NULL);
      indented_print (indent, "/* 33222222222211111111110000000000\n");
      indented_print (indent, "   10987654321098765432109876543210\n");
      indented_print (indent, "   %s\n", pattern);
      indented_print (indent, "   %s.  */\n",
		      get_aarch64_opcode (bittree->list)->name);
      indented_print (indent, "return %u;\n",
		      real_index (bittree->list->index));
      return;
    }

  /* Walk down the decoder tree.  */
  indented_print (indent, "if (((word >> %d) & 0x1) == 0)\n", bittree->bitno);
  indented_print (indent, "  {\n");
  pattern[bittree->bitno] = '0';
  print_decision_tree_1 (indent + 4, bittree->bits[0]);
  indented_print (indent, "  }\n");
  indented_print (indent, "else\n");
  indented_print (indent, "  {\n");
  pattern[bittree->bitno] = '1';
  print_decision_tree_1 (indent + 4, bittree->bits[1]);
  indented_print (indent, "  }\n");
  pattern[bittree->bitno] = 'x';
}

/* Generate aarch64_opcode_lookup in C code to the standard output.  */

static void
print_decision_tree (struct bittree* bittree)
{
  if (debug)
    printf ("Enter print_decision_tree\n");

  printf ("/* Called by aarch64_opcode_lookup.  */\n\n");

  printf ("static int\n");
  printf ("aarch64_opcode_lookup_1 (uint32_t word)\n");
  printf ("{\n");

  print_decision_tree_1 (2, bittree);

  printf ("}\n\n");


  printf ("/* Lookup opcode WORD in the opcode table.  N.B. all alias\n");
  printf ("   opcodes are ignored here.  */\n\n");

  printf ("const aarch64_opcode *\n");
  printf ("aarch64_opcode_lookup (uint32_t word)\n");
  printf ("{\n");
  printf ("  return aarch64_opcode_table + aarch64_opcode_lookup_1 (word);\n");
  printf ("}\n");
}

static void
print_find_next_opcode_1 (struct bittree* bittree)
{
  assert (bittree != NULL);

  /* Leaf node located.  */
  if (bittree->bits[0] == NULL && bittree->bits[1] == NULL)
    {
      assert (bittree->list != NULL);
      /* Find multiple opcode entries in one leaf node.  */
      if (bittree->list->next != NULL)
	{
	  opcode_node *list = bittree->list;
	  while (list != NULL)
	    {
	      const aarch64_opcode *curr = get_aarch64_opcode (list);
	      const aarch64_opcode *next = get_aarch64_opcode (list->next);

	      printf ("    case %u: ",
		      (unsigned int)(curr - aarch64_opcode_table));
	      if (list->next != NULL)
		{
		  printf ("value = %u; break;\t", real_index (list->next->index));
		  printf ("/* %s --> %s.  */\n", curr->name, next->name);
		}
	      else
		{
		  printf ("return NULL;\t\t");
		  printf ("/* %s --> NULL.  */\n", curr->name);
		}

	      list = list->next;
	    }
	}
      return;
    }

  /* Walk down the decoder tree.  */
  print_find_next_opcode_1 (bittree->bits[0]);
  print_find_next_opcode_1 (bittree->bits[1]);
}

/* Generate aarch64_find_next_opcode in C code to the standard output.  */

static void
print_find_next_opcode (struct bittree* bittree)
{
  if (debug)
    printf ("Enter print_find_next_opcode\n");

  printf ("\n");
  printf ("const aarch64_opcode *\n");
  printf ("aarch64_find_next_opcode (const aarch64_opcode *opcode)\n");
  printf ("{\n");
  printf ("  /* Use the index as the key to locate the next opcode.  */\n");
  printf ("  int key = opcode - aarch64_opcode_table;\n");
  printf ("  int value;\n");
  printf ("  switch (key)\n");
  printf ("    {\n");

  print_find_next_opcode_1 (bittree);

  printf ("    default: return NULL;\n");
  printf ("    }\n\n");

  printf ("  return aarch64_opcode_table + value;\n");
  printf ("}\n");
}

/* Release the dynamic memory resource allocated for the generation of the
   decoder tree.  */

static void
release_resource_decoder_tree (struct bittree* bittree)
{
  assert (bittree != NULL);

  /* Leaf node located.  */
  if (bittree->bits[0] == NULL && bittree->bits[1] == NULL)
    {
      assert (bittree->list != NULL);
      /* Free opcode_nodes.  */
      opcode_node *list = bittree->list;
      while (list != NULL)
	{
	  opcode_node *next = list->next;
	  free (list);
	  list = next;
	}
      /* Free the tree node.  */
      free (bittree);
      return;
    }

  /* Walk down the decoder tree.  */
  release_resource_decoder_tree (bittree->bits[0]);
  release_resource_decoder_tree (bittree->bits[1]);

  /* Free the tree node.  */
  free (bittree);
}

/* Generate aarch64_find_real_opcode in C code to the standard output.
   TABLE points to the alias info table, while NUM indicates the number of
   entries in the table.  */

static void
print_find_real_opcode (const opcode_node *table, int num)
{
  int i;

  if (debug)
    printf ("Enter print_find_real_opcode\n");

  printf ("\n");
  printf ("const aarch64_opcode *\n");
  printf ("aarch64_find_real_opcode (const aarch64_opcode *opcode)\n");
  printf ("{\n");
  printf ("  /* Use the index as the key to locate the real opcode.  */\n");
  printf ("  int key = opcode - aarch64_opcode_table;\n");
  printf ("  int value;\n");
  printf ("  switch (key)\n");
  printf ("    {\n");

  for (i = 0; i < num; ++i)
    {
      const opcode_node *real = table + i;
      const opcode_node *alias = real->next;
      for (; alias; alias = alias->next)
	printf ("    case %u:\t/* %s */\n", real_index (alias->index),
		get_aarch64_opcode (alias)->name);
      printf ("      value = %u;\t/* --> %s.  */\n", real_index (real->index),
	      get_aarch64_opcode (real)->name);
      printf ("      break;\n");
    }

  printf ("    default: return NULL;\n");
  printf ("    }\n\n");

  printf ("  return aarch64_opcode_table + value;\n");
  printf ("}\n");
}

/* Generate aarch64_find_alias_opcode in C code to the standard output.
   TABLE points to the alias info table, while NUM indicates the number of
   entries in the table.  */

static void
print_find_alias_opcode (const opcode_node *table, int num)
{
  int i;

  if (debug)
    printf ("Enter print_find_alias_opcode\n");

  printf ("\n");
  printf ("const aarch64_opcode *\n");
  printf ("aarch64_find_alias_opcode (const aarch64_opcode *opcode)\n");
  printf ("{\n");
  printf ("  /* Use the index as the key to locate the alias opcode.  */\n");
  printf ("  int key = opcode - aarch64_opcode_table;\n");
  printf ("  int value;\n");
  printf ("  switch (key)\n");
  printf ("    {\n");

  for (i = 0; i < num; ++i)
    {
      const opcode_node *node = table + i;
      assert (node->next);
      printf ("    case %u: value = %u; break;", real_index (node->index),
	      real_index (node->next->index));
      printf ("\t/* %s --> %s.  */\n", get_aarch64_opcode (node)->name,
	      get_aarch64_opcode (node->next)->name);
    }

  printf ("    default: return NULL;\n");
  printf ("    }\n\n");

  printf ("  return aarch64_opcode_table + value;\n");
  printf ("}\n");
}

/* Generate aarch64_find_next_alias_opcode in C code to the standard output.
   TABLE points to the alias info table, while NUM indicates the number of
   entries in the table.  */

static void
print_find_next_alias_opcode (const opcode_node *table, int num)
{
  int i;

  if (debug)
    printf ("Enter print_find_next_alias_opcode\n");

  printf ("\n");
  printf ("const aarch64_opcode *\n");
  printf ("aarch64_find_next_alias_opcode (const aarch64_opcode *opcode)\n");
  printf ("{\n");
  printf ("  /* Use the index as the key to locate the next opcode.  */\n");
  printf ("  int key = opcode - aarch64_opcode_table;\n");
  printf ("  int value;\n");
  printf ("  switch (key)\n");
  printf ("    {\n");

  for (i = 0; i < num; ++i)
    {
      const opcode_node *node = table + i;
      assert (node->next);
      if (node->next->next == NULL)
	continue;
      while (node->next->next)
	{
	  printf ("    case %u: value = %u; break;", real_index (node->next->index),
		 real_index (node->next->next->index));
	  printf ("\t/* %s --> %s.  */\n",
		  get_aarch64_opcode (node->next)->name,
		  get_aarch64_opcode (node->next->next)->name);
	  node = node->next;
	}
    }

  printf ("    default: return NULL;\n");
  printf ("    }\n\n");

  printf ("  return aarch64_opcode_table + value;\n");
  printf ("}\n");
}

/* Given OPCODE, establish and return a link list of alias nodes in the
   preferred order.  */

opcode_node *
find_alias_opcode (const aarch64_opcode *opcode)
{
  int i;
  /* Assume maximum of 8 disassemble preference candidates.  */
  const int max_num_aliases = 8;
  const aarch64_opcode *ent;
  const aarch64_opcode *preferred[max_num_aliases];
  opcode_node head, **next;

  assert (opcode_has_alias (opcode));

  i = 0;
  ent = aarch64_opcode_table;
  while (ent->name != NULL)
    {
      /* The mask of an alias opcode must be equal to or a super-set (i.e.
	 more constrained) of that of the aliased opcode; so is the base
	 opcode value.  */
      if (alias_opcode_p (ent) == TRUE
	  && (ent->mask & opcode->mask) == opcode->mask
	  && (opcode->mask & ent->opcode) == (opcode->mask & opcode->opcode))
	{
	  assert (i < max_num_aliases);
	  preferred[i++] = ent;
	  if (debug)
	    printf ("found %s for %s.", ent->name, opcode->name);
	}
      ++ent;
    }

  if (debug)
    {
      int m;
      printf ("un-orderd list: ");
      for (m = 0; m < i; ++m)
	printf ("%s, ", preferred[m]->name);
      printf ("\n");
    }

  /* There must be at least one alias.  */
  assert (i >= 1);

  /* Sort preferred array according to the priority (from the lowest to the
     highest.  */
  if (i > 1)
    {
      int j, k;
      for (j = 0; j < i - 1; ++j)
	{
	  for (k = 0; k < i - 1 - j; ++k)
	    {
	      const aarch64_opcode *t;
	      t = preferred [k+1];
	      if (opcode_priority (t) < opcode_priority (preferred [k]))
		{
		  preferred [k+1] = preferred [k];
		  preferred [k] = t;
		}
	    }
	}
    }

  if (debug)
    {
      int m;
      printf ("orderd list: ");
      for (m = 0; m < i; ++m)
	printf ("%s, ", preferred[m]->name);
      printf ("\n");
    }

  /* Create a link-list of opcode_node with disassemble preference from
     higher to lower.  */
  next = &head.next;
  --i;
  while (i >= 0)
    {
      const aarch64_opcode *alias = preferred [i];
      opcode_node *node = new_opcode_node ();

      if (debug)
	printf ("add %s.\n", alias->name);

      node->index = alias - aarch64_opcode_table;
      *next = node;
      next = &node->next;

      --i;
    }
  *next = NULL;

  return head.next;
}

/* Create and return alias information.
   Return the address of the created alias info table; return the number
   of table entries in *NUM_PTR.  */

opcode_node *
create_alias_info (int *num_ptr)
{
  int i, num;
  opcode_node *ret;
  const aarch64_opcode *ent;

  /* Calculate the total number of opcodes that have alias.  */
  num = 0;
  ent = aarch64_opcode_table;
  while (ent->name != NULL)
    {
      if (opcode_has_alias (ent))
	{
	  /* Assert the alias relationship be flat-structured to keep
	     algorithms simple; not allow F_ALIAS and F_HAS_ALIAS both
	     specified.  */
	  assert (!alias_opcode_p (ent));
	  ++num;
	}
      ++ent;
    }
  assert (num_ptr);
  *num_ptr = num;

  /* The array of real opcodes that have alias(es).  */
  ret = malloc (sizeof (opcode_node) * num);

  /* For each opcode, establish a list of alias nodes in a preferred
     order.  */
  for (i = 0, ent = aarch64_opcode_table; i < num; ++i, ++ent)
    {
      opcode_node *node = ret + i;
      while (ent->name != NULL && !opcode_has_alias (ent))
	++ent;
      assert (ent->name != NULL);
      node->index = ent - aarch64_opcode_table;
      node->next = find_alias_opcode (ent);
      assert (node->next);
    }
  assert (i == num);

  return ret;
}

/* Release the dynamic memory resource allocated for the generation of the
   alias information.  */

void
release_resource_alias_info (opcode_node *alias_info, int num)
{
  int i = 0;
  opcode_node *node = alias_info;

  /* Free opcode_node list.  */
  for (; i < num; ++i, ++node)
    {
      opcode_node *list = node->next;
      do
	{
	  opcode_node *next = list->next;
	  free (list);
	  list = next;
	} while (list != NULL);
    }

  /* Free opcode_node array.  */
  free (alias_info);
}

/* As a debugging utility, print out the result of the table division, although
   it is not doing much this moment.  */
static void
print_divide_result (const struct bittree *bittree ATTRIBUTE_UNUSED)
{
  printf ("max_num_opcodes_at_leaf_node: %d\n", max_num_opcodes_at_leaf_node);
  return;
}

/* Structure to help generate the operand table.  */
struct operand
{
  const char *class;
  const char *inserter;
  const char *extractor;
  const char *str;
  const char *flags;
  const char *fields;
  const char *desc;
  unsigned processed : 1;
  unsigned has_inserter : 1;
  unsigned has_extractor : 1;
};

typedef struct operand operand;

#ifdef X
#undef X
#endif

#ifdef Y
#undef Y
#endif

#ifdef F
#undef F
#endif

/* Get the operand information in strings.  */

static operand operands[] =
{
    {"NIL", "0", "0", "", "0", "{0}", "<none>", 0, 0, 0},
#define F(...)	#__VA_ARGS__
#define X(a,b,c,d,e,f,g)	\
    {#a, #b, #c, d, #e, "{"f"}", g, 0, 0, 0},
#define Y(a,b,d,e,f,g)		\
    {#a, "ins_"#b, "ext_"#b, d, #e, "{"f"}", g, 0, 0, 0},
    AARCH64_OPERANDS
    {"NIL", "0", "0", "", "0", "{0}", "DUMMY", 0, 0, 0},
};

#undef F
#undef X

static void
process_operand_table (void)
{
  int i;
  operand *opnd;
  const int num = sizeof (operands) / sizeof (operand);

  for (i = 0, opnd = operands; i < num; ++i, ++opnd)
    {
      opnd->has_inserter = opnd->inserter[0] != '0';
      opnd->has_extractor = opnd->extractor[0] != '0';
    }
}

/* Generate aarch64_operands in C to the standard output.  */

static void
print_operand_table (void)
{
  int i;
  operand *opnd;
  const int num = sizeof (operands) / sizeof (operand);

  if (debug)
    printf ("Enter print_operand_table\n");

  printf ("\n");
  printf ("const struct aarch64_operand aarch64_operands[] =\n");
  printf ("{\n");

  for (i = 0, opnd = operands; i < num; ++i, ++opnd)
    {
      char flags[256];
      flags[0] = '\0';
      if (opnd->flags[0] != '0')
	sprintf (flags, "%s", opnd->flags);
      if (opnd->has_inserter)
	{
	  if (flags[0] != '\0')
	    strcat (flags, " | ");
	  strcat (flags, "OPD_F_HAS_INSERTER");
	}
      if (opnd->has_extractor)
	{
	  if (flags[0] != '\0')
	    strcat (flags, " | ");
	  strcat (flags, "OPD_F_HAS_EXTRACTOR");
	}
      if (flags[0] == '\0')
	{
	  flags[0] = '0';
	  flags[1] = '\0';
	}
    printf ("  {AARCH64_OPND_CLASS_%s, \"%s\", %s, %s, \"%s\"},\n",
	    opnd->class, opnd->str, flags, opnd->fields, opnd->desc);
    }
  printf ("};\n");
}

/* Generate aarch64_insert_operand in C to the standard output.  */

static void
print_operand_inserter (void)
{
  int i;
  operand *opnd;
  const int num = sizeof (operands) / sizeof (operand);

  if (debug)
    printf ("Enter print_operand_inserter\n");

  printf ("\n");
  printf ("const char*\n");
  printf ("aarch64_insert_operand (const aarch64_operand *self,\n\
			   const aarch64_opnd_info *info,\n\
			   aarch64_insn *code, const aarch64_inst *inst)\n");
  printf ("{\n");
  printf ("  /* Use the index as the key.  */\n");
  printf ("  int key = self - aarch64_operands;\n");
  printf ("  switch (key)\n");
  printf ("    {\n");

  for (i = 0, opnd = operands; i < num; ++i, ++opnd)
    opnd->processed = 0;

  for (i = 0, opnd = operands; i < num; ++i, ++opnd)
    {
      if (!opnd->processed && opnd->has_inserter)
	{
	  int j = i + 1;
	  const int len = strlen (opnd->inserter);
	  operand *opnd2 = opnd + 1;
	  printf ("    case %u:\n", (unsigned int)(opnd - operands));
	  opnd->processed = 1;
	  for (; j < num; ++j, ++opnd2)
	    {
	      if (!opnd2->processed
		  && opnd2->has_inserter
		  && len == strlen (opnd2->inserter)
		  && strncmp (opnd->inserter, opnd2->inserter, len) == 0)
		{
		  printf ("    case %u:\n", (unsigned int)(opnd2 - operands));
		  opnd2->processed = 1;
		}
	    }
	  printf ("      return aarch64_%s (self, info, code, inst);\n",
		  opnd->inserter);
	}
    }

  printf ("    default: assert (0); abort ();\n");
  printf ("    }\n");
  printf ("}\n");
}

/* Generate aarch64_extract_operand in C to the standard output.  */

static void
print_operand_extractor (void)
{
  int i;
  operand *opnd;
  const int num = sizeof (operands) / sizeof (operand);

  if (debug)
    printf ("Enter print_operand_extractor\n");

  printf ("\n");
  printf ("int\n");
  printf ("aarch64_extract_operand (const aarch64_operand *self,\n\
			   aarch64_opnd_info *info,\n\
			   aarch64_insn code, const aarch64_inst *inst)\n");
  printf ("{\n");
  printf ("  /* Use the index as the key.  */\n");
  printf ("  int key = self - aarch64_operands;\n");
  printf ("  switch (key)\n");
  printf ("    {\n");

  for (i = 0, opnd = operands; i < num; ++i, ++opnd)
    opnd->processed = 0;

  for (i = 0, opnd = operands; i < num; ++i, ++opnd)
    {
      if (!opnd->processed && opnd->has_extractor)
	{
	  int j = i + 1;
	  const int len = strlen (opnd->extractor);
	  operand *opnd2 = opnd + 1;
	  printf ("    case %u:\n", (unsigned int)(opnd - operands));
	  opnd->processed = 1;
	  for (; j < num; ++j, ++opnd2)
	    {
	      if (!opnd2->processed
		  && opnd2->has_extractor
		  && len == strlen (opnd2->extractor)
		  && strncmp (opnd->extractor, opnd2->extractor, len) == 0)
		{
		  printf ("    case %u:\n", (unsigned int)(opnd2 - operands));
		  opnd2->processed = 1;
		}
	    }
	  printf ("      return aarch64_%s (self, info, code, inst);\n",
		  opnd->extractor);
	}
    }

  printf ("    default: assert (0); abort ();\n");
  printf ("    }\n");
  printf ("}\n");
}

/* Table indexed by opcode enumerator stores the index of the corresponding
   opcode entry in aarch64_opcode_table.  */
static unsigned op_enum_table [OP_TOTAL_NUM];

/* Print out the routine which, given the opcode enumerator, returns the
   corresponding opcode entry pointer.  */

static void
print_get_opcode (void)
{
  int i;
  const int num = OP_TOTAL_NUM;
  const aarch64_opcode *opcode;

  if (debug)
    printf ("Enter print_get_opcode\n");

  /* Fill in the internal table.  */
  opcode = aarch64_opcode_table;
  while (opcode->name != NULL)
    {
      if (opcode->op != OP_NIL)
	{
	  /* Assert opcode enumerator be unique, in other words, no shared by
	     different opcodes.  */
	  if (op_enum_table[opcode->op] != 0)
	    {
	      fprintf (stderr, "Opcode %u is shared by different %s and %s.\n",
		       opcode->op,
		       aarch64_opcode_table[op_enum_table[opcode->op]].name,
		       opcode->name);
	      assert (0);
	      abort ();
	    }
	  assert (opcode->op < OP_TOTAL_NUM);
	  op_enum_table[opcode->op] = opcode - aarch64_opcode_table;
	}
      ++opcode;
    }

  /* Print the table.  */
  printf ("\n");
  printf ("/* Indexed by an enum aarch64_op enumerator, the value is the offset of\n\
   the corresponding aarch64_opcode entry in the aarch64_opcode_table.  */\n\n");
  printf ("static const unsigned op_enum_table [] =\n");
  printf ("{\n");
  for (i = 0; i < num; ++i)
    printf ("  %u,\n", op_enum_table[i]);
  printf ("};\n");

  /* Print the function.  */
  printf ("\n");
  printf ("/* Given the opcode enumerator OP, return the pointer to the corresponding\n");
  printf ("   opcode entry.  */\n");
  printf ("\n");
  printf ("const aarch64_opcode *\n");
  printf ("aarch64_get_opcode (enum aarch64_op op)\n");
  printf ("{\n");
  printf ("  return aarch64_opcode_table + op_enum_table[op];\n");
  printf ("}\n");
}

/* Print out the content of an opcode table (not in use).  */
static void ATTRIBUTE_UNUSED
print_table (struct aarch64_opcode* table)
{
  struct aarch64_opcode *ent = table;
  do
    {
      printf ("%s\t%08x\t%08x\n", ent->name, (unsigned int)ent->opcode,
	      (unsigned int)ent->mask);
    } while ((++ent)->name);
}

static const char * program_name = NULL;

/* Program options.  */
struct option long_options[] =
{
  {"debug",   no_argument,       NULL, 'd'},
  {"version", no_argument,       NULL, 'V'},
  {"help",    no_argument,       NULL, 'h'},
  {"gen-opc", no_argument,       NULL, 'c'},
  {"gen-asm", no_argument,       NULL, 'a'},
  {"gen-dis", no_argument,       NULL, 's'},
  {0,         no_argument,       NULL, 0}
};

static void
print_version (void)
{
  printf ("%s: version 1.0\n", program_name);
  xexit (0);
}

static void
usage (FILE * stream, int status)
{
  fprintf (stream, "Usage: %s [-V | --version] [-d | --debug] [--help]\n",
	   program_name);
  fprintf (stream, "\t[ [-c | --gen-opc] | [-a | --gen-asm] | [-s | --gen-dis] ]\n");
  xexit (status);
}

int
main (int argc, char **argv)
{
  extern int chdir (char *);
  int c;
  int gen_opcode_p = 0;
  int gen_assembler_p = 0;
  int gen_disassembler_p = 0;

  program_name = *argv;
  xmalloc_set_program_name (program_name);

  while ((c = getopt_long (argc, argv, "vVdhacs", long_options, 0)) != EOF)
    switch (c)
      {
      case 'V':
      case 'v':
	print_version ();
	break;
      case 'd':
	debug = 1;
	break;
      case 'h':
      case '?':
	usage (stderr, 0);
	break;
      case 'c':
	gen_opcode_p = 1;
	break;
      case 'a':
	gen_assembler_p = 1;
	break;
      case 's':
	gen_disassembler_p = 1;
	break;
      default:
      case 0:
	break;
      }

  if (argc == 1 || optind != argc)
    usage (stdout, 1);

  if (gen_opcode_p + gen_assembler_p + gen_disassembler_p > 1)
    {
      printf ("Please specify only one of the following options\n\
	      [-c | --gen-opc] [-a | --gen-asm] [-s | --gen-dis]\n");
      xexit (2);
    }

  struct bittree *decoder_tree;

  decoder_tree = initialize_decoder_tree ();
  if (debug)
    print_divide_result (decoder_tree);

  printf ("/* This file is automatically generated by aarch64-gen.  Do not edit!  */\n");
  printf ("/* Copyright 2012, 2013  Free Software Foundation, Inc.\n\
   Contributed by ARM Ltd.\n\
\n\
   This file is part of the GNU opcodes library.\n\
\n\
   This library is free software; you can redistribute it and/or modify\n\
   it under the terms of the GNU General Public License as published by\n\
   the Free Software Foundation; either version 3, or (at your option)\n\
   any later version.\n\
\n\
   It is distributed in the hope that it will be useful, but WITHOUT\n\
   ANY WARRANTY; without even the implied warranty of MERCHANTABILITY\n\
   or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public\n\
   License for more details.\n\
\n\
   You should have received a copy of the GNU General Public License\n\
   along with this program; see the file COPYING3. If not,\n\
   see <http://www.gnu.org/licenses/>.  */\n");

  printf ("\n");
  printf ("#include \"sysdep.h\"\n");
  if (gen_opcode_p)
    printf ("#include \"aarch64-opc.h\"\n");
  if (gen_assembler_p)
    printf ("#include \"aarch64-asm.h\"\n");
  if (gen_disassembler_p)
    printf ("#include \"aarch64-dis.h\"\n");
  printf ("\n");

  /* Generate opcode entry lookup for the disassembler.  */
  if (gen_disassembler_p)
    {
      print_decision_tree (decoder_tree);
      print_find_next_opcode (decoder_tree);
      release_resource_decoder_tree (decoder_tree);
    }

  /* Generate alias opcode handling for the assembler or the disassembler.  */
  if (gen_assembler_p || gen_disassembler_p)
    {
      int num;
      opcode_node *alias_info = create_alias_info (&num);

      if (gen_assembler_p)
	print_find_real_opcode (alias_info, num);

      if (gen_disassembler_p)
	{
	  print_find_alias_opcode (alias_info, num);
	  print_find_next_alias_opcode (alias_info, num);
	}

      release_resource_alias_info (alias_info, num);
    }

  /* Generate operand table.  */
  process_operand_table ();

  if (gen_assembler_p)
    print_operand_inserter ();

  if (gen_disassembler_p)
    print_operand_extractor ();

  if (gen_opcode_p)
    print_operand_table ();

  /* Generate utility to return aarch64_opcode entry given an enumerator.  */
  if (gen_opcode_p)
    print_get_opcode ();

  exit (0);
}
