xtensa-relax.c   [plain text]


/* Table of relaxations for Xtensa assembly.
   Copyright 2003 Free Software Foundation, Inc.

   This file is part of GAS, the GNU Assembler.

   GAS 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 2, or (at your option)
   any later version.

   GAS 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 GAS; see the file COPYING.  If not, write to
   the Free Software Foundation, 59 Temple Place - Suite 330, Boston, 
   MA 02111-1307, USA.  */

/* This file contains the code for generating runtime data structures
   for relaxation pattern matching from statically specified strings.
   Each action contains an instruction pattern to match and
   preconditions for the match as well as an expansion if the pattern
   matches.  The preconditions can specify that two operands are the
   same or an operand is a specific constant.  The expansion uses the
   bound variables from the pattern to specify that specific operands
   from the pattern should be used in the result.  

   The patterns match a language like:

   INSN_PATTERN ::= INSN_TEMPL ( '|' PRECOND )*
   INSN_TEMPL   ::= OPCODE ' ' [ OPERAND (',' OPERAND)* ]
   OPCODE       ::=  id
   OPERAND      ::= CONSTANT | VARIABLE | SPECIALFN '(' VARIABLE ')'
   SPECIALFN    ::= 'HI24S' | 'F32MINUS' | 'LOW8'
   VARIABLE     ::= '%' id
   PRECOND      ::= OPERAND CMPOP OPERAND
   CMPOP        ::= '==' | '!='

   The replacement language 
   INSN_REPL      ::= INSN_LABEL_LIT ( ';' INSN_LABEL_LIT )*
   INSN_LABEL_LIT ::= INSN_TEMPL 
                      | 'LABEL' num 
                      | 'LITERAL' num ' ' VARIABLE

   The operands in a PRECOND must be constants or variables bound by
   the INSN_PATTERN.

   The operands in the INSN_REPL must be constants, variables bound in
   the associated INSN_PATTERN, special variables that are bound in
   the INSN_REPL by LABEL or LITERAL definitions, or special value
   manipulation functions.

   A simple example of a replacement pattern:
   {"movi.n %as,%imm", "movi %as,%imm"} would convert the narrow
   movi.n instruction to the wide movi instruction.

   A more complex example of a branch around:
   {"beqz %as,%label", "bnez %as,%LABEL0;j %label;LABEL0"}
   would convert a branch to a negated branch to the following instruction
   with a jump to the original label.
   
   An Xtensa-specific example that generates a literal:
   {"movi %at,%imm", "LITERAL0 %imm; l32r %at,%LITERAL0"}
   will convert a movi instruction to an l32r of a literal
   literal defined in the literal pool.

   Even more complex is a conversion of a load with immediate offset
   to a load of a freshly generated literal, an explicit add and
   a load with 0 offset.  This transformation is only valid, though
   when the first and second operands are not the same as specified
   by the "| %at!=%as" precondition clause.
   {"l32i %at,%as,%imm | %at!=%as",
   "LITERAL0 %imm; l32r %at,%LITERAL0; add %at,%at,%as; l32i %at,%at,0"}

   There is special case for loop instructions here, but because we do
   not currently have the ability to represent the difference of two
   symbols, the conversion requires special code in the assembler to
   write the operands of the addi/addmi pair representing the
   difference of the old and new loop end label.  */

#include "as.h"
#include "xtensa-isa.h"
#include "xtensa-relax.h"
#include <stddef.h>

/* Imported from bfd.  */
extern xtensa_isa xtensa_default_isa;


/* The opname_list is a small list of names that we use for opcode and
   operand variable names to simplify ownership of these commonly used
   strings.  Strings entered in the table can be compared by pointer
   equality.  */

typedef struct opname_list_struct opname_list;
typedef opname_list opname_e;

struct opname_list_struct
{
  char *opname;
  opname_list *next;
};

static opname_list *local_opnames = NULL;


/* The "opname_map" and its element structure "opname_map_e" are used
   for binding an operand number to a name or a constant.  */

typedef struct opname_map_e_struct opname_map_e;
typedef struct opname_map_struct opname_map;

struct opname_map_e_struct
{
  const char *operand_name;	/* If null, then use constant_value.  */
  size_t operand_num;
  unsigned constant_value;
  opname_map_e *next;
};

struct opname_map_struct
{
  opname_map_e *head;
  opname_map_e **tail;
};

/* The "precond_list" and its element structure "precond_e" represents
   explicit preconditions comparing operand variables and constants.
   In the "precond_e" structure, a variable is identified by the name
   in the "opname" field.   If that field is NULL, then the operand
   is the constant in field "opval".  */

typedef struct precond_e_struct precond_e;
typedef struct precond_list_struct precond_list;

struct precond_e_struct
{
  const char *opname1;
  unsigned opval1;
  CmpOp cmpop;
  const char *opname2;
  unsigned opval2;
  precond_e *next;
};

struct precond_list_struct
{
  precond_e *head;
  precond_e **tail;
};


/* The insn_templ represents the INSN_TEMPL instruction template.  It
   is an opcode name with a list of operands.  These are used for
   instruction patterns and replacement patterns.  */

typedef struct insn_templ_struct insn_templ;
struct insn_templ_struct
{
  const char *opcode_name;
  opname_map operand_map;
};


/* The insn_pattern represents an INSN_PATTERN instruction pattern.
   It is an instruction template with preconditions that specify when
   it actually matches a given instruction.  */

typedef struct insn_pattern_struct insn_pattern;
struct insn_pattern_struct
{
  insn_templ t;
  precond_list preconds;
};


/* The "insn_repl" and associated element structure "insn_repl_e"
   instruction replacement list is a list of
   instructions/LITERALS/LABELS with constant operands or operands
   with names bound to the operand names in the associated pattern.  */

typedef struct insn_repl_e_struct insn_repl_e;
struct insn_repl_e_struct
{
  insn_templ t;
  insn_repl_e *next;
};

typedef struct insn_repl_struct insn_repl;
struct insn_repl_struct
{
  insn_repl_e *head;
  insn_repl_e **tail;
};


/* The split_rec is a vector of allocated char * pointers.  */

typedef struct split_rec_struct split_rec;
struct split_rec_struct
{
  char **vec;
  size_t count;
};

/* The "string_pattern_pair" is a set of pairs containing instruction
   patterns and replacement strings.  */

typedef struct string_pattern_pair_struct string_pattern_pair;
struct string_pattern_pair_struct
{
  const char *pattern;
  const char *replacement;
};


/* The widen_spec_list is a list of valid substitutions that generate
   wider representations.  These are generally used to specify
   replacements for instructions whose immediates do not fit their
   encodings.  A valid transition may require multiple steps of
   one-to-one instruction replacements with a final multiple
   instruction replacement.  As an example, here are the transitions
   required to replace an 'addi.n' with an 'addi', 'addmi'.

     addi.n a4, 0x1010
     => addi a4, 0x1010
     => addmi a4, 0x1010
     => addmi a4, 0x1000, addi a4, 0x10.  */

static string_pattern_pair widen_spec_list[] =
{
  {"add.n %ar,%as,%at", "add %ar,%as,%at"},
  {"addi.n %ar,%as,%imm", "addi %ar,%as,%imm"},
  {"beqz.n %as,%label", "beqz %as,%label"},
  {"bnez.n %as,%label", "bnez %as,%label"},
  {"l32i.n %at,%as,%imm", "l32i %at,%as,%imm"},
  {"mov.n %at,%as", "or %at,%as,%as"},
  {"movi.n %as,%imm", "movi %as,%imm"},
  {"nop.n", "or 1,1,1"},
  {"ret.n", "ret"},
  {"retw.n", "retw"},
  {"s32i.n %at,%as,%imm", "s32i %at,%as,%imm"},
  {"srli %at,%as,%imm", "extui %at,%as,%imm,F32MINUS(%imm)"},
  {"slli %ar,%as,0", "or %ar,%as,%as"},
  /* Widening with literals */
  {"movi %at,%imm", "LITERAL0 %imm; l32r %at,%LITERAL0"},
  {"addi %ar,%as,%imm", "addmi %ar,%as,%imm"},
  /* LOW8 is the low 8 bits of the Immed
     MID8S is the middle 8 bits of the Immed */
  {"addmi %ar,%as,%imm", "addmi %ar,%as,HI24S(%imm); addi %ar,%ar,LOW8(%imm)"},
  {"addmi %ar,%as,%imm | %ar!=%as",
   "LITERAL0 %imm; l32r %ar,%LITERAL0; add %ar,%as,%ar"},

  /* Widening the load instructions with too-large immediates */
  {"l8ui %at,%as,%imm | %at!=%as",
   "LITERAL0 %imm; l32r %at,%LITERAL0; add %at,%at,%as; l8ui %at,%at,0"},
  {"l16si %at,%as,%imm | %at!=%as",
   "LITERAL0 %imm; l32r %at,%LITERAL0; add %at,%at,%as; l16si %at,%at,0"},
  {"l16ui %at,%as,%imm | %at!=%as",
   "LITERAL0 %imm; l32r %at,%LITERAL0; add %at,%at,%as; l16ui %at,%at,0"},
#if 0 /* Xtensa Synchronization Option not yet available */
  {"l32ai %at,%as,%imm",
   "LITERAL0 %imm; l32r %at,%LITERAL0; add.n %at,%at,%as; l32ai %at,%at,0"},
#endif
#if 0 /* Xtensa Speculation Option not yet available */
  {"l32is %at,%as,%imm",
   "LITERAL0 %imm; l32r %at,%LITERAL0; add.n %at,%at,%as; l32is %at,%at,0"},
#endif
  {"l32i %at,%as,%imm | %at!=%as",
   "LITERAL0 %imm; l32r %at,%LITERAL0; add %at,%at,%as; l32i %at,%at,0"},

  /* This is only PART of the loop instruction.  In addition, hard
     coded into it's use is a modification of the final operand in the
     instruction in bytes 9 and 12.  */
  {"loop %as,%label",
   "loop %as,%LABEL0;"
   "rsr     %as, 1;"		/* LEND */
   "wsr     %as, 0;"		/* LBEG */
   "addi    %as, %as, 0;"	/* lo8(%label-%LABEL1) */
   "addmi   %as, %as, 0;"	/* mid8(%label-%LABEL1) */
   "wsr     %as, 1;"
   "isync;"
   "rsr     %as, 2;"		/* LCOUNT */
   "addi    %as, %as, 1;"	/* density -> addi.n %as, %as, 1 */
   "LABEL0"},
  {"loopgtz %as,%label",
   "beqz     %as,%label;"
   "bltz     %as,%label;"
   "loopgtz %as,%LABEL0;"
   "rsr     %as, 1;"		/* LEND */
   "wsr     %as, 0;"		/* LBEG */
   "addi    %as, %as, 0;"	/* lo8(%label-%LABEL1) */
   "addmi   %as, %as, 0;"	/* mid8(%label-%LABEL1) */
   "wsr     %as, 1;"
   "isync;"
   "rsr     %as, 2;"		/* LCOUNT */
   "addi    %as, %as, 1;"	/* density -> addi.n %as, %as, 1 */
   "LABEL0"},
  {"loopnez %as,%label",
   "beqz     %as,%label;"
   "loopnez %as,%LABEL0;"
   "rsr     %as, 1;"		/* LEND */
   "wsr     %as, 0;"		/* LBEG */
   "addi    %as, %as, 0;"	/* lo8(%label-%LABEL1) */
   "addmi   %as, %as, 0;"	/* mid8(%label-%LABEL1) */
   "wsr     %as, 1;"
   "isync;"
   "rsr     %as, 2;"		/* LCOUNT */
   "addi    %as, %as, 1;"	/* density -> addi.n %as, %as, 1 */
   "LABEL0"},

#if 0 /* no mechanism here to determine if Density Option is available */
  {"beqz %as,%label", "bnez.n %as,%LABEL0;j %label;LABEL0"},
  {"bnez %as,%label", "beqz.n %as,%LABEL0;j %label;LABEL0"},
#else
  {"beqz %as,%label", "bnez %as,%LABEL0;j %label;LABEL0"},
  {"bnez %as,%label", "beqz %as,%LABEL0;j %label;LABEL0"},
#endif

  {"bgez %as,%label", "bltz %as,%LABEL0;j %label;LABEL0"},
  {"bltz %as,%label", "bgez %as,%LABEL0;j %label;LABEL0"},
  {"beqi %as,%imm,%label", "bnei %as,%imm,%LABEL0;j %label;LABEL0"},
  {"bnei %as,%imm,%label", "beqi %as,%imm,%LABEL0;j %label;LABEL0"},
  {"bgei %as,%imm,%label", "blti %as,%imm,%LABEL0;j %label;LABEL0"},
  {"blti %as,%imm,%label", "bgei %as,%imm,%LABEL0;j %label;LABEL0"},
  {"bgeui %as,%imm,%label", "bltui %as,%imm,%LABEL0;j %label;LABEL0"},
  {"bltui %as,%imm,%label", "bgeui %as,%imm,%LABEL0;j %label;LABEL0"},
  {"bbci %as,%imm,%label", "bbsi %as,%imm,%LABEL0;j %label;LABEL0"},
  {"bbsi %as,%imm,%label", "bbci %as,%imm,%LABEL0;j %label;LABEL0"},
  {"beq %as,%at,%label", "bne %as,%at,%LABEL0;j %label;LABEL0"},
  {"bne %as,%at,%label", "beq %as,%at,%LABEL0;j %label;LABEL0"},
  {"bge %as,%at,%label", "blt %as,%at,%LABEL0;j %label;LABEL0"},
  {"blt %as,%at,%label", "bge %as,%at,%LABEL0;j %label;LABEL0"},
  {"bgeu %as,%at,%label", "bltu %as,%at,%LABEL0;j %label;LABEL0"},
  {"bltu %as,%at,%label", "bgeu %as,%at,%LABEL0;j %label;LABEL0"},
  {"bany %as,%at,%label", "bnone %as,%at,%LABEL0;j %label;LABEL0"},
#if 1 /* provide relaxations for Boolean Option */
  {"bt %bs,%label", "bf %bs,%LABEL0;j %label;LABEL0"},
  {"bf %bs,%label", "bt %bs,%LABEL0;j %label;LABEL0"},
#endif
  {"bnone %as,%at,%label", "bany %as,%at,%LABEL0;j %label;LABEL0"},
  {"ball %as,%at,%label", "bnall %as,%at,%LABEL0;j %label;LABEL0"},
  {"bnall %as,%at,%label", "ball %as,%at,%LABEL0;j %label;LABEL0"},
  {"bbc %as,%at,%label", "bbs %as,%at,%LABEL0;j %label;LABEL0"},
  {"bbs %as,%at,%label", "bbc %as,%at,%LABEL0;j %label;LABEL0"},
  {"call0 %label", "LITERAL0 %label; l32r a0,%LITERAL0; callx0 a0"},
  {"call4 %label", "LITERAL0 %label; l32r a4,%LITERAL0; callx4 a4"},
  {"call8 %label", "LITERAL0 %label; l32r a8,%LITERAL0; callx8 a8"},
  {"call12 %label", "LITERAL0 %label; l32r a12,%LITERAL0; callx12 a12"}
};

#define WIDEN_COUNT (sizeof (widen_spec_list) / sizeof (string_pattern_pair))


/* The simplify_spec_list specifies simplifying transformations that
   will reduce the instruction width or otherwise simplify an
   instruction.  These are usually applied before relaxation in the
   assembler.  It is always legal to simplify.  Even for "addi as, 0",
   the "addi.n as, 0" will eventually be widened back to an "addi 0"
   after the widening table is applied.  Note: The usage of this table
   has changed somewhat so that it is entirely specific to "narrowing"
   instructions to use the density option.  This table is not used at
   all when the density option is not available.  */

string_pattern_pair simplify_spec_list[] =
{
  {"add %ar,%as,%at", "add.n %ar,%as,%at"},
  {"addi.n %ar,%as,0", "mov.n %ar,%as"},
  {"addi %ar,%as,0", "mov.n %ar,%as"},
  {"addi %ar,%as,%imm", "addi.n %ar,%as,%imm"},
  {"addmi %ar,%as,%imm", "addi.n %ar,%as,%imm"},
  {"beqz %as,%label", "beqz.n %as,%label"},
  {"bnez %as,%label", "bnez.n %as,%label"},
  {"l32i %at,%as,%imm", "l32i.n %at,%as,%imm"},
  {"movi %as,%imm", "movi.n %as,%imm"},
  {"or %ar,%as,%at | %as==%at", "mov.n %ar,%as"},
  {"ret", "ret.n"},
  {"retw", "retw.n"},
  {"s32i %at,%as,%imm", "s32i.n %at,%as,%imm"},
  {"slli %ar,%as,0", "mov.n %ar,%as"}
};

#define SIMPLIFY_COUNT \
  (sizeof (simplify_spec_list) / sizeof (string_pattern_pair))


/* Transition generation helpers.  */

static void append_transition 
  PARAMS ((TransitionTable *, xtensa_opcode, TransitionRule *));
static void append_condition 
  PARAMS ((TransitionRule *, Precondition *));
static void append_value_condition 
  PARAMS ((TransitionRule *, CmpOp, unsigned, unsigned));
static void append_constant_value_condition 
  PARAMS ((TransitionRule *, CmpOp, unsigned, unsigned));
static void append_build_insn
  PARAMS ((TransitionRule *, BuildInstr *));
static void append_op
  PARAMS ((BuildInstr *, BuildOp *));
static void append_literal_op 
  PARAMS ((BuildInstr *, unsigned, unsigned));
static void append_label_op 
  PARAMS ((BuildInstr *, unsigned, unsigned));
static void append_constant_op 
  PARAMS ((BuildInstr *, unsigned, unsigned));
static void append_field_op 
  PARAMS ((BuildInstr *, unsigned, unsigned));
static void append_user_fn_field_op 
  PARAMS ((BuildInstr *, unsigned, OpType, unsigned));
static long operand_function_HI24S
  PARAMS ((long));
static long operand_function_F32MINUS
  PARAMS ((long));
static long operand_function_LOW8
  PARAMS ((long));

/* Externally visible functions.  */

extern bfd_boolean xg_has_userdef_op_fn
  PARAMS ((OpType));
extern long xg_apply_userdef_op_fn
  PARAMS ((OpType, long));

/* Parsing helpers.  */

static const char *enter_opname_n
  PARAMS ((const char *, size_t));
static const char *enter_opname
  PARAMS ((const char *));

/* Construction and destruction.  */

static void init_opname_map
  PARAMS ((opname_map *));
static void clear_opname_map
  PARAMS ((opname_map *));
static void init_precond_list
  PARAMS ((precond_list *));
static void clear_precond_list
  PARAMS ((precond_list *));
static void init_insn_templ
  PARAMS ((insn_templ *));
static void clear_insn_templ
  PARAMS ((insn_templ *));
static void init_insn_pattern
  PARAMS ((insn_pattern *));
static void clear_insn_pattern
  PARAMS ((insn_pattern *));
static void init_insn_repl
  PARAMS ((insn_repl *));
static void clear_insn_repl
  PARAMS ((insn_repl *));
static void init_split_rec
  PARAMS ((split_rec *));
static void clear_split_rec
  PARAMS ((split_rec *));

/* Operand and insn_templ helpers.  */

static bfd_boolean same_operand_name
  PARAMS ((const opname_map_e *, const opname_map_e *));
static opname_map_e *get_opmatch
  PARAMS ((opname_map *, const char *));
static bfd_boolean op_is_constant
  PARAMS ((const opname_map_e *));
static unsigned op_get_constant
  PARAMS ((const opname_map_e *));
static size_t insn_templ_operand_count
  PARAMS ((const insn_templ *));

/* parsing helpers.  */

static const char *skip_white
  PARAMS ((const char *));
static void trim_whitespace
  PARAMS ((char *));
static void split_string 
  PARAMS ((split_rec *, const char *, char,  bfd_boolean));

/* Language parsing.  */

static bfd_boolean parse_insn_pattern 
  PARAMS ((const char *, insn_pattern *));
static bfd_boolean parse_insn_repl 
  PARAMS ((const char *, insn_repl *));
static bfd_boolean parse_insn_templ 
  PARAMS ((const char *, insn_templ *));
static bfd_boolean parse_special_fn 
  PARAMS ((const char *, const char **, const char **));
static bfd_boolean parse_precond
  PARAMS ((const char *, precond_e *));
static bfd_boolean parse_constant
  PARAMS ((const char *, unsigned *));
static bfd_boolean parse_id_constant 
  PARAMS ((const char *, const char *, unsigned *));

/* Transition table building code.  */

static TransitionRule *build_transition 
  PARAMS ((insn_pattern *, insn_repl *, const char *, const char *));
static TransitionTable *build_transition_table 
  PARAMS ((const string_pattern_pair *, size_t));


void
append_transition (tt, opcode, t)
     TransitionTable *tt;
     xtensa_opcode opcode;
     TransitionRule *t;
{
  TransitionList *tl = (TransitionList *) xmalloc (sizeof (TransitionList));
  TransitionList *prev;
  TransitionList *nxt;
  assert (tt != NULL);
  assert (opcode < tt->num_opcodes);

  prev = tt->table[opcode];
  tl->rule = t;
  tl->next = NULL;
  if (prev == NULL)
    {
      tt->table[opcode] = tl;
      return;
    }
  nxt = prev->next;
  while (nxt != NULL)
    {
      prev = nxt;
      nxt = nxt->next;
    }
  prev->next = tl;
}


void
append_condition (tr, cond)
     TransitionRule *tr;
     Precondition *cond;
{
  PreconditionList *pl =
    (PreconditionList *) xmalloc (sizeof (PreconditionList));
  PreconditionList *prev = tr->conditions;
  PreconditionList *nxt;

  pl->precond = cond;
  pl->next = NULL;
  if (prev == NULL)
    {
      tr->conditions = pl;
      return;
    }
  nxt = prev->next;
  while (nxt != NULL)
    {
      prev = nxt;
      nxt = nxt->next;
    }
  prev->next = pl;
}


void
append_value_condition (tr, cmp, op1, op2)
     TransitionRule *tr;
     CmpOp cmp;
     unsigned op1;
     unsigned op2;
{
  Precondition *cond = (Precondition *) xmalloc (sizeof (Precondition));

  cond->cmp = cmp;
  cond->op_num = op1;
  cond->typ = OP_OPERAND;
  cond->op_data = op2;
  append_condition (tr, cond);
}


void
append_constant_value_condition (tr, cmp, op1, cnst)
     TransitionRule *tr;
     CmpOp cmp;
     unsigned op1;
     unsigned cnst;
{
  Precondition *cond = (Precondition *) xmalloc (sizeof (Precondition));

  cond->cmp = cmp;
  cond->op_num = op1;
  cond->typ = OP_CONSTANT;
  cond->op_data = cnst;
  append_condition (tr, cond);
}


void
append_build_insn (tr, bi)
     TransitionRule *tr;
     BuildInstr *bi;
{
  BuildInstr *prev = tr->to_instr;
  BuildInstr *nxt;

  bi->next = NULL;
  if (prev == NULL)
    {
      tr->to_instr = bi;
      return;
    }
  nxt = prev->next;
  while (nxt != 0)
    {
      prev = nxt;
      nxt = prev->next;
    }
  prev->next = bi;
}


void
append_op (bi, b_op)
     BuildInstr *bi;
     BuildOp *b_op;
{
  BuildOp *prev = bi->ops;
  BuildOp *nxt;

  if (prev == NULL)
    {
      bi->ops = b_op;
      return;
    }
  nxt = prev->next;
  while (nxt != NULL)
    {
      prev = nxt;
      nxt = nxt->next;
    }
  prev->next = b_op;
}


void
append_literal_op (bi, op1, litnum)
     BuildInstr *bi;
     unsigned op1;
     unsigned litnum;
{
  BuildOp *b_op = (BuildOp *) xmalloc (sizeof (BuildOp));

  b_op->op_num = op1;
  b_op->typ = OP_LITERAL;
  b_op->op_data = litnum;
  b_op->next = NULL;
  append_op (bi, b_op);
}


void
append_label_op (bi, op1, labnum)
     BuildInstr *bi;
     unsigned op1;
     unsigned labnum;
{
  BuildOp *b_op = (BuildOp *) xmalloc (sizeof (BuildOp));

  b_op->op_num = op1;
  b_op->typ = OP_LABEL;
  b_op->op_data = labnum;
  b_op->next = NULL;
  append_op (bi, b_op);
}


void
append_constant_op (bi, op1, cnst)
     BuildInstr *bi;
     unsigned op1;
     unsigned cnst;
{
  BuildOp *b_op = (BuildOp *) xmalloc (sizeof (BuildOp));

  b_op->op_num = op1;
  b_op->typ = OP_CONSTANT;
  b_op->op_data = cnst;
  b_op->next = NULL;
  append_op (bi, b_op);
}


void
append_field_op (bi, op1, src_op)
     BuildInstr *bi;
     unsigned op1;
     unsigned src_op;
{
  BuildOp *b_op = (BuildOp *) xmalloc (sizeof (BuildOp));

  b_op->op_num = op1;
  b_op->typ = OP_OPERAND;
  b_op->op_data = src_op;
  b_op->next = NULL;
  append_op (bi, b_op);
}


/* These could be generated but are not currently.  */

void
append_user_fn_field_op (bi, op1, typ, src_op)
     BuildInstr *bi;
     unsigned op1;
     OpType typ;
     unsigned src_op;
{
  BuildOp *b_op = (BuildOp *) xmalloc (sizeof (BuildOp));

  b_op->op_num = op1;
  b_op->typ = typ;
  b_op->op_data = src_op;
  b_op->next = NULL;
  append_op (bi, b_op);
}


/* These operand functions are the semantics of user-defined
   operand functions.  */

long
operand_function_HI24S (a)
     long a;
{
  if (a & 0x80)
    return (a & (~0xff)) + 0x100;
  else
    return (a & (~0xff));
}


long
operand_function_F32MINUS (a)
     long a;
{
  return (32 - a);
}


long
operand_function_LOW8 (a)
     long a;
{
  if (a & 0x80)
    return (a & 0xff) | ~0xff;
  else
    return (a & 0xff);
}


bfd_boolean
xg_has_userdef_op_fn (op)
     OpType op;
{
  switch (op)
    {
    case OP_OPERAND_F32MINUS:
    case OP_OPERAND_LOW8:
    case OP_OPERAND_HI24S:
      return TRUE;
    default:
      break;
    }
  return FALSE;
}


long
xg_apply_userdef_op_fn (op, a)
     OpType op;
     long a;
{
  switch (op)
    {
    case OP_OPERAND_F32MINUS:
      return operand_function_F32MINUS (a);
    case OP_OPERAND_LOW8:
      return operand_function_LOW8 (a);
    case OP_OPERAND_HI24S:
      return operand_function_HI24S (a);
    default:
      break;
    }
  return FALSE;
}


/* Generate a transition table.  */

const char *
enter_opname_n (name, len)
     const char *name;
     size_t len;
{
  opname_e *op;

  for (op = local_opnames; op != NULL; op = op->next)
    {
      if (strlen (op->opname) == len && strncmp (op->opname, name, len) == 0)
	return op->opname;
    }
  op = (opname_e *) xmalloc (sizeof (opname_e));
  op->opname = (char *) xmalloc (len + 1);
  strncpy (op->opname, name, len);
  op->opname[len] = '\0';
  return op->opname;
}


static const char *
enter_opname (name)
     const char *name;
{
  opname_e *op;

  for (op = local_opnames; op != NULL; op = op->next)
    {
      if (strcmp (op->opname, name) == 0)
	return op->opname;
    }
  op = (opname_e *) xmalloc (sizeof (opname_e));
  op->opname = strdup (name);
  return op->opname;
}


void
init_opname_map (m)
     opname_map *m;
{
  m->head = NULL;
  m->tail = &m->head;
}


void
clear_opname_map (m)
     opname_map *m;
{
  opname_map_e *e;

  while (m->head != NULL)
    {
      e = m->head;
      m->head = e->next;
      free (e);
    }
  m->tail = &m->head;
}


static bfd_boolean
same_operand_name (m1, m2)
     const opname_map_e *m1;
     const opname_map_e *m2;
{
  if (m1->operand_name == NULL || m1->operand_name == NULL)
    return FALSE;
  return (m1->operand_name == m2->operand_name);
}


opname_map_e *
get_opmatch (map, operand_name)
     opname_map *map;
     const char *operand_name;
{
  opname_map_e *m;

  for (m = map->head; m != NULL; m = m->next)
    {
      if (strcmp (m->operand_name, operand_name) == 0)
	return m;
    }
  return NULL;
}


bfd_boolean
op_is_constant (m1)
     const opname_map_e *m1;
{
  return (m1->operand_name == NULL);
}


static unsigned
op_get_constant (m1)
     const opname_map_e *m1;
{
  assert (m1->operand_name == NULL);
  return m1->constant_value;
}


void
init_precond_list (l)
     precond_list *l;
{
  l->head = NULL;
  l->tail = &l->head;
}


void
clear_precond_list (l)
     precond_list *l;
{
  precond_e *e;

  while (l->head != NULL)
    {
      e = l->head;
      l->head = e->next;
      free (e);
    }
  l->tail = &l->head;
}


void
init_insn_templ (t)
     insn_templ *t;
{
  t->opcode_name = NULL;
  init_opname_map (&t->operand_map);
}


void
clear_insn_templ (t)
     insn_templ *t;
{
  clear_opname_map (&t->operand_map);
}


void
init_insn_pattern (p)
     insn_pattern *p;
{
  init_insn_templ (&p->t);
  init_precond_list (&p->preconds);
}


void
clear_insn_pattern (p)
     insn_pattern *p;
{
  clear_insn_templ (&p->t);
  clear_precond_list (&p->preconds);
}


void
init_insn_repl (r)
     insn_repl *r;
{
  r->head = NULL;
  r->tail = &r->head;
}


void
clear_insn_repl (r)
     insn_repl *r;
{
  insn_repl_e *e;

  while (r->head != NULL)
    {
      e = r->head;
      r->head = e->next;
      clear_insn_templ (&e->t);
    }
  r->tail = &r->head;
}


static size_t
insn_templ_operand_count (t)
     const insn_templ *t;
{
  size_t i = 0;
  const opname_map_e *op;

  for (op = t->operand_map.head; op != NULL; op = op->next, ++i)
    ;
  return i;
}


/* Convert a string to a number.  E.G.: parse_constant("10", &num) */

bfd_boolean
parse_constant (in, val_p)
     const char *in;
     unsigned *val_p;
{
  unsigned val = 0;
  const char *p;

  if (in == NULL)
    return FALSE;
  p = in;

  while (*p != '\0')
    {
      if (*p >= '0' && *p <= '9')
	val = val * 10 + (*p - '0');
      else
	return FALSE;
      ++p;
    }
  *val_p = val;
  return TRUE;
}


/* Match a pattern like "foo1" with
   parse_id_constant("foo1", "foo", &num).
   This may also be used to just match a number.  */

bfd_boolean
parse_id_constant (in, name, val_p)
     const char *in;
     const char *name;
     unsigned *val_p;
{
  unsigned namelen = 0;
  const char *p;

  if (in == NULL)
    return FALSE;

  if (name != NULL)
    namelen = strlen (name);

  if (name != NULL && strncmp (in, name, namelen) != 0)
    return FALSE;

  p = &in[namelen];
  return parse_constant (p, val_p);
}


static bfd_boolean
parse_special_fn (name, fn_name_p, arg_name_p)
     const char *name;
     const char **fn_name_p;
     const char **arg_name_p;
{
  char *p_start;
  const char *p_end;

  p_start = strchr (name, '(');
  if (p_start == NULL)
    return FALSE;

  p_end = strchr (p_start, ')');

  if (p_end == NULL)
    return FALSE;

  if (p_end[1] != '\0')
    return FALSE;

  *fn_name_p = enter_opname_n (name, p_start - name);
  *arg_name_p = enter_opname_n (p_start + 1, p_end - p_start - 1);
  return TRUE;
}


const char *
skip_white (p)
     const char *p;
{
  if (p == NULL)
    return p;
  while (*p == ' ')
    ++p;
  return p;
}


void
trim_whitespace (in)
     char *in;
{
  char *last_white = NULL;
  char *p = in;

  while (p && *p != '\0')
    {
      while (*p == ' ')
	{
	  if (last_white == NULL)
	    last_white = p;
	  p++;
	}
      if (*p != '\0')
	{
	  last_white = NULL;
	  p++;
	}
    }
  if (last_white)
    *last_white = '\0';
}


/* Split a string into component strings where "c" is the
   delimiter.  Place the result in the split_rec.  */

void
split_string (rec, in, c, elide_whitespace)
     split_rec *rec;
     const char *in;
     char c;
     bfd_boolean elide_whitespace;
{
  size_t cnt = 0;
  size_t i;
  const char *p = in;

  while (p != NULL && *p != '\0')
    {
      cnt++;
      p = strchr (p, c);
      if (p)
	p++;
    }
  rec->count = cnt;
  rec->vec = NULL;

  if (rec->count == 0)
    return;

  rec->vec = (char **) xmalloc (sizeof (char *) * cnt);
  for (i = 0; i < cnt; i++)
    rec->vec[i] = 0;

  p = in;
  for (i = 0; i < cnt; i++)
    {
      const char *q;
      size_t len;

      q = p;
      if (elide_whitespace)
	q = skip_white (q);

      p = strchr (q, c);
      if (p == NULL)
	rec->vec[i] = strdup (q);
      else
	{
	  len = p - q;
	  rec->vec[i] = (char *) xmalloc (sizeof (char) * (len + 1));
	  strncpy (rec->vec[i], q, len);
	  rec->vec[i][len] = '\0';
	  p++;
	}

      if (elide_whitespace)
	trim_whitespace (rec->vec[i]);
    }
}


void
clear_split_rec (rec)
     split_rec *rec;
{
  size_t i;

  for (i = 0; i < rec->count; ++i)
    free (rec->vec[i]);

  if (rec->count > 0)
    free (rec->vec);
}


void
init_split_rec (rec)
     split_rec *rec;
{
  rec->vec = NULL;
  rec->count = 0;
}


/* Parse an instruction template like "insn op1, op2, op3".  */

bfd_boolean
parse_insn_templ (s, t)
     const char *s;
     insn_templ *t;
{
  const char *p = s;
  /* First find the first whitespace.  */
  size_t insn_name_len;
  split_rec oprec;
  size_t i;

  init_split_rec (&oprec);

  p = skip_white (p);
  insn_name_len = strcspn (s, " ");
  if (insn_name_len == 0)
    return FALSE;

  init_insn_templ (t);
  t->opcode_name = enter_opname_n (p, insn_name_len);

  p = p + insn_name_len;

  /* Split by ',' and skip beginning and trailing whitespace.  */
  split_string (&oprec, p, ',', TRUE);

  for (i = 0; i < oprec.count; i++)
    {
      const char *opname = oprec.vec[i];
      opname_map_e *e = (opname_map_e *) xmalloc (sizeof (opname_map_e));
      e->next = NULL;
      e->operand_name = NULL;
      e->constant_value = 0;
      e->operand_num = i;

      /* If it begins with a number, assume that it is a number.  */
      if (opname && opname[0] >= '0' && opname[0] <= '9')
	{
	  unsigned val;

	  if (parse_constant (opname, &val))
	    e->constant_value = val;
	  else
	    {
	      free (e);
	      clear_split_rec (&oprec);
	      clear_insn_templ (t);
	      return FALSE;
	    }
	}
      else
	e->operand_name = enter_opname (oprec.vec[i]);

      *t->operand_map.tail = e;
      t->operand_map.tail = &e->next;
    }
  clear_split_rec (&oprec);
  return TRUE;
}


bfd_boolean
parse_precond (s, precond)
     const char *s;
     precond_e *precond;
{
  /* All preconditions are currently of the form:
     a == b or a != b or a == k (where k is a constant).
     Later we may use some special functions like DENSITY == 1
     to identify when density is available.  */

  const char *p = s;
  size_t len;
  precond->opname1 = NULL;
  precond->opval1 = 0;
  precond->cmpop = OP_EQUAL;
  precond->opname2 = NULL;
  precond->opval2 = 0;
  precond->next = NULL;

  p = skip_white (p);

  len = strcspn (p, " !=");

  if (len == 0)
    return FALSE;

  precond->opname1 = enter_opname_n (p, len);
  p = p + len;
  p = skip_white (p);

  /* Check for "==" and "!=".  */
  if (strncmp (p, "==", 2) == 0)
    precond->cmpop = OP_EQUAL;
  else if (strncmp (p, "!=", 2) == 0)
    precond->cmpop = OP_NOTEQUAL;
  else
    return FALSE;

  p = p + 2;
  p = skip_white (p);

  /* No trailing whitespace from earlier parsing.  */
  if (p[0] >= '0' && p[0] <= '9')
    {
      unsigned val;
      if (parse_constant (p, &val))
	precond->opval2 = val;
      else
	return FALSE;
    }
  else
    precond->opname2 = enter_opname (p);
  return TRUE;
}


/* Parse a string like:
   "insn op1, op2, op3, op4 | op1 != op2 | op2 == op3 | op4 == 1".
   I.E., instruction "insn" with 4 operands where operand 1 and 2 are not
   the same and operand 2 and 3 are the same and operand 4 is 1.  */

bfd_boolean
parse_insn_pattern (in, insn)
     const char *in;
     insn_pattern *insn;
{

  split_rec rec;
  size_t i;

  init_split_rec (&rec);
  init_insn_pattern (insn);

  split_string (&rec, in, '|', TRUE);

  if (rec.count == 0)
    {
      clear_split_rec (&rec);
      return FALSE;
    }

  if (!parse_insn_templ (rec.vec[0], &insn->t))
    {
      clear_split_rec (&rec);
      return FALSE;
    }

  for (i = 1; i < rec.count; i++)
    {
      precond_e *cond = (precond_e *) xmalloc (sizeof (precond_e));

      if (!parse_precond (rec.vec[i], cond))
	{
	  clear_split_rec (&rec);
	  clear_insn_pattern (insn);
	  return FALSE;
	}

      /* Append the condition.  */
      *insn->preconds.tail = cond;
      insn->preconds.tail = &cond->next;
    }

  clear_split_rec (&rec);
  return TRUE;
}


bfd_boolean
parse_insn_repl (in, r_p)
     const char *in;
     insn_repl *r_p;
{
  /* This is a list of instruction templates separated by ';'.  */
  split_rec rec;
  size_t i;

  split_string (&rec, in, ';', TRUE);

  for (i = 0; i < rec.count; i++)
    {
      insn_repl_e *e = (insn_repl_e *) xmalloc (sizeof (insn_repl_e));

      e->next = NULL;

      if (!parse_insn_templ (rec.vec[i], &e->t))
	{
	  free (e);
	  clear_insn_repl (r_p);
	  return FALSE;
	}
      *r_p->tail = e;
      r_p->tail = &e->next;
    }
  return TRUE;
}


TransitionRule *
build_transition (initial_insn, replace_insns, from_string, to_string)
     insn_pattern *initial_insn;
     insn_repl *replace_insns;
     const char *from_string;
     const char *to_string;
{
  TransitionRule *tr = NULL;
  xtensa_opcode opcode;
  xtensa_isa isa = xtensa_default_isa;

  opname_map_e *op1;
  opname_map_e *op2;

  precond_e *precond;
  insn_repl_e *r;
  unsigned label_count = 0;
  unsigned max_label_count = 0;
  bfd_boolean has_label = FALSE;
  unsigned literal_count = 0;

  opcode = xtensa_opcode_lookup (isa, initial_insn->t.opcode_name);
  if (opcode == XTENSA_UNDEFINED)
    {
      /* It is OK to not be able to translate some of these opcodes.  */
#if 0
      as_warn (_("Invalid opcode '%s' in transition rule '%s'\n"),
	       initial_insn->t.opcode_name, to_string);
#endif
      return NULL;
    }


  if (xtensa_num_operands (isa, opcode)
      != (int) insn_templ_operand_count (&initial_insn->t))
    {
      /* This is also OK because there are opcodes that
	 have different numbers of operands on different
	 architecture variations.  */
#if 0
      as_fatal (_("opcode %s mismatched operand count %d != expected %d"),
		xtensa_opcode_name (isa, opcode),
		xtensa_num_operands (isa, opcode),
		insn_templ_operand_count (&initial_insn->t));
#endif
      return NULL;
    }

  tr = (TransitionRule *) xmalloc (sizeof (TransitionRule));
  tr->opcode = opcode;
  tr->conditions = NULL;
  tr->to_instr = NULL;

  /* Build the conditions. First, equivalent operand condition....  */
  for (op1 = initial_insn->t.operand_map.head; op1 != NULL; op1 = op1->next)
    {
      for (op2 = op1->next; op2 != NULL; op2 = op2->next)
	{
	  if (same_operand_name (op1, op2))
	    {
	      append_value_condition (tr, OP_EQUAL,
				      op1->operand_num, op2->operand_num);
	    }
	}
    }

  /* Now the condition that an operand value must be a constant....  */
  for (op1 = initial_insn->t.operand_map.head; op1 != NULL; op1 = op1->next)
    {
      if (op_is_constant (op1))
	{
	  append_constant_value_condition (tr,
					   OP_EQUAL,
					   op1->operand_num,
					   op_get_constant (op1));
	}
    }


  /* Now add the explicit preconditions listed after the "|" in the spec.
     These are currently very limited, so we do a special case
     parse for them.  We expect spaces, opname != opname.  */
  for (precond = initial_insn->preconds.head;
       precond != NULL;
       precond = precond->next)
    {
      op1 = NULL;
      op2 = NULL;

      if (precond->opname1)
	{
	  op1 = get_opmatch (&initial_insn->t.operand_map, precond->opname1);
	  if (op1 == NULL)
	    {
	      as_fatal (_("opcode '%s': no bound opname '%s' "
			  "for precondition in '%s'"),
			xtensa_opcode_name (isa, opcode),
			precond->opname1, from_string);
	      return NULL;
	    }
	}

      if (precond->opname2)
	{
	  op2 = get_opmatch (&initial_insn->t.operand_map, precond->opname2);
	  if (op2 == NULL)
	    {
	      as_fatal (_("opcode '%s': no bound opname '%s' "
			  "for precondition in %s"),
		       xtensa_opcode_name (isa, opcode),
		       precond->opname2, from_string);
	      return NULL;
	    }
	}

      if (op1 == NULL && op2 == NULL)
	{
	  as_fatal (_("opcode '%s': precondition only contains "
		      "constants in '%s'"),
		    xtensa_opcode_name (isa, opcode), from_string);
	  return NULL;
	}
      else if (op1 != NULL && op2 != NULL)
	append_value_condition (tr, precond->cmpop,
				op1->operand_num, op2->operand_num);
      else if (op2 == NULL)
	append_constant_value_condition (tr, precond->cmpop,
					 op1->operand_num, precond->opval1);
      else
	append_constant_value_condition (tr, precond->cmpop,
					 op2->operand_num, precond->opval2);
    }

  /* Generate the replacement instructions.  Some of these
     "instructions" are actually labels and literals.  The literals
     must be defined in order 0..n and a literal must be defined
     (e.g., "LITERAL0 %imm") before use (e.g., "%LITERAL0").  The
     labels must be defined in order, but they can be used before they
     are defined.  Also there are a number of special operands (e.g.,
     HI24S).  */

  for (r = replace_insns->head; r != NULL; r = r->next)
    {
      BuildInstr *bi;
      const char *opcode_name;
      size_t operand_count;
      opname_map_e *op;
      unsigned idnum = 0;
      const char *fn_name;
      const char *operand_arg_name;

      bi = (BuildInstr *) xmalloc (sizeof (BuildInstr));
      append_build_insn (tr, bi);

      bi->id = 0;
      bi->opcode = XTENSA_UNDEFINED;
      bi->ops = NULL;
      bi->next = NULL;

      opcode_name = r->t.opcode_name;
      operand_count = insn_templ_operand_count (&r->t);

      if (parse_id_constant (opcode_name, "LITERAL", &idnum))
	{
	  bi->typ = INSTR_LITERAL_DEF;
	  bi->id = idnum;
	  if (idnum != literal_count)
	    as_fatal (_("generated literals must be numbered consecutively"));
	  ++literal_count;
	  if (operand_count != 1)
	    as_fatal (_("expected one operand for generated literal"));

	}
      else if (parse_id_constant (opcode_name, "LABEL", &idnum))
	{
	  bi->typ = INSTR_LABEL_DEF;
	  bi->id = idnum;
	  if (idnum != label_count)
	    as_fatal (_("generated labels must be numbered consecutively"));
	  ++label_count;
	  if (operand_count != 0)
	    as_fatal (_("expected 0 operands for generated label"));
	}
      else
	{
	  bi->typ = INSTR_INSTR;
	  bi->opcode = xtensa_opcode_lookup (isa, r->t.opcode_name);
	  if (bi->opcode == XTENSA_UNDEFINED)
	    return NULL;
	  /* Check for the right number of ops.  */
	  if (xtensa_num_operands (isa, bi->opcode) 
	      != (int) operand_count)
	    as_fatal (_("opcode '%s': replacement does not have %d ops"),
		      opcode_name, xtensa_num_operands (isa, bi->opcode));
	}

      for (op = r->t.operand_map.head; op != NULL; op = op->next)
	{
	  unsigned idnum;

	  if (op_is_constant (op))
	    append_constant_op (bi, op->operand_num, op_get_constant (op));
	  else if (parse_id_constant (op->operand_name, "%LITERAL", &idnum))
	    {
	      if (idnum >= literal_count)
		as_fatal (_("opcode %s: replacement "
			    "literal %d >= literal_count(%d)"),
			  opcode_name, idnum, literal_count);
	      append_literal_op (bi, op->operand_num, idnum);
	    }
	  else if (parse_id_constant (op->operand_name, "%LABEL", &idnum))
	    {
	      has_label = TRUE;
	      if (idnum > max_label_count)
		max_label_count = idnum;
	      append_label_op (bi, op->operand_num, idnum);
	    }
	  else if (parse_id_constant (op->operand_name, "a", &idnum))
	    append_constant_op (bi, op->operand_num, idnum);
	  else if (op->operand_name[0] == '%')
	    {
	      opname_map_e *orig_op;
	      orig_op = get_opmatch (&initial_insn->t.operand_map,
				     op->operand_name);
	      if (orig_op == NULL)
		{
		  as_fatal (_("opcode %s: unidentified operand '%s' in '%s'"),
			    opcode_name, op->operand_name, to_string);

		  append_constant_op (bi, op->operand_num, 0);
		}
	      else
		append_field_op (bi, op->operand_num, orig_op->operand_num);
	    }
	  else if (parse_special_fn (op->operand_name,
				     &fn_name, &operand_arg_name))
	    {
	      opname_map_e *orig_op;
	      OpType typ = OP_CONSTANT;

	      if (strcmp (fn_name, "LOW8") == 0)
		typ = OP_OPERAND_LOW8;
	      else if (strcmp (fn_name, "HI24S") == 0)
		typ = OP_OPERAND_HI24S;
	      else if (strcmp (fn_name, "F32MINUS") == 0)
		typ = OP_OPERAND_F32MINUS;
	      else
		as_fatal (_("unknown user defined function %s"), fn_name);

	      orig_op = get_opmatch (&initial_insn->t.operand_map,
				     operand_arg_name);
	      if (orig_op == NULL)
		{
		  as_fatal (_("opcode %s: unidentified operand '%s' in '%s'"),
			    opcode_name, op->operand_name, to_string);
		  append_constant_op (bi, op->operand_num, 0);
		}
	      else
		append_user_fn_field_op (bi, op->operand_num,
					 typ, orig_op->operand_num);
	    }
	  else
	    {
	      as_fatal (_("opcode %s: could not parse operand '%s' in '%s'"),
			opcode_name, op->operand_name, to_string);
	      append_constant_op (bi, op->operand_num, 0);
	    }
	}
    }
  if (has_label && max_label_count >= label_count)
    {
      as_fatal (_("opcode %s: replacement label %d >= label_count(%d)"),
		xtensa_opcode_name (isa, opcode),
		max_label_count, label_count);
      return NULL;
    }

  return tr;
}


TransitionTable *
build_transition_table (transitions, transition_count)
     const string_pattern_pair *transitions;
     size_t transition_count;
{
  TransitionTable *table = NULL;
  int num_opcodes = xtensa_num_opcodes (xtensa_default_isa);
  int i;
  size_t tnum;

  if (table != NULL)
    return table;

  /* Otherwise, build it now.  */
  table = (TransitionTable *) xmalloc (sizeof (TransitionTable));
  table->num_opcodes = num_opcodes;
  table->table =
    (TransitionList **) xmalloc (sizeof (TransitionTable *) * num_opcodes);

  for (i = 0; i < num_opcodes; i++)
    table->table[i] = NULL;

  for (tnum = 0; tnum < transition_count; tnum++)
    {
      const char *from_string = transitions[tnum].pattern;
      const char *to_string = transitions[tnum].replacement;

      insn_pattern initial_insn;
      insn_repl replace_insns;
      TransitionRule *tr;

      init_insn_pattern (&initial_insn);
      if (!parse_insn_pattern (from_string, &initial_insn))
	{
	  as_fatal (_("could not parse INSN_PATTERN '%s'"), from_string);
	  clear_insn_pattern (&initial_insn);
	  continue;
	}

      init_insn_repl (&replace_insns);
      if (!parse_insn_repl (to_string, &replace_insns))
	{
	  as_fatal (_("could not parse INSN_REPL '%s'"), to_string);
	  clear_insn_pattern (&initial_insn);
	  clear_insn_repl (&replace_insns);
	  continue;
	}

      tr = build_transition (&initial_insn, &replace_insns,
			     from_string, to_string);
      if (tr)
	append_transition (table, tr->opcode, tr);

      clear_insn_repl (&replace_insns);
      clear_insn_pattern (&initial_insn);
    }
  return table;
}


extern TransitionTable *
xg_build_widen_table ()
{
  static TransitionTable *table = NULL;
  if (table == NULL)
    table = build_transition_table (widen_spec_list, WIDEN_COUNT);
  return table;
}


extern TransitionTable *
xg_build_simplify_table ()
{
  static TransitionTable *table = NULL;
  if (table == NULL)
    table = build_transition_table (simplify_spec_list, SIMPLIFY_COUNT);
  return table;
}