view gcc/genoutput.c @ 138:fc828634a951

merge
author Shinji KONO <kono@ie.u-ryukyu.ac.jp>
date Thu, 08 Nov 2018 14:17:14 +0900
parents 84e7813d76e9
children 1830386684a0
line wrap: on
line source

/* Generate code from to output assembler insns as recognized from rtl.
   Copyright (C) 1987-2018 Free Software Foundation, Inc.

This file is part of GCC.

GCC 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.

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


/* This program reads the machine description for the compiler target machine
   and produces a file containing these things:

   1. An array of `struct insn_data_d', which is indexed by insn code number,
   which contains:

     a. `name' is the name for that pattern.  Nameless patterns are
     given a name.

     b. `output' hold either the output template, an array of output
     templates, or an output function.

     c. `genfun' is the function to generate a body for that pattern,
     given operands as arguments.

     d. `n_operands' is the number of distinct operands in the pattern
     for that insn,

     e. `n_dups' is the number of match_dup's that appear in the insn's
     pattern.  This says how many elements of `recog_data.dup_loc' are
     significant after an insn has been recognized.

     f. `n_alternatives' is the number of alternatives in the constraints
     of each pattern.

     g. `output_format' tells what type of thing `output' is.

     h. `operand' is the base of an array of operand data for the insn.

   2. An array of `struct insn_operand data', used by `operand' above.

     a. `predicate', an int-valued function, is the match_operand predicate
     for this operand.

     b. `constraint' is the constraint for this operand.

     c. `address_p' indicates that the operand appears within ADDRESS
     rtx's.

     d. `mode' is the machine mode that that operand is supposed to have.

     e. `strict_low', is nonzero for operands contained in a STRICT_LOW_PART.

     f. `eliminable', is nonzero for operands that are matched normally by
     MATCH_OPERAND; it is zero for operands that should not be changed during
     register elimination such as MATCH_OPERATORs.

     g. `allows_mem', is true for operands that accept MEM rtxes.

  The code number of an insn is simply its position in the machine
  description; code numbers are assigned sequentially to entries in
  the description, starting with code number 0.

  Thus, the following entry in the machine description

    (define_insn "clrdf"
      [(set (match_operand:DF 0 "general_operand" "")
	    (const_int 0))]
      ""
      "clrd %0")

  assuming it is the 25th entry present, would cause
  insn_data[24].template to be "clrd %0", and
  insn_data[24].n_operands to be 1.  */

#include "bconfig.h"
#include "system.h"
#include "coretypes.h"
#include "tm.h"
#include "rtl.h"
#include "errors.h"
#include "read-md.h"
#include "gensupport.h"

/* No instruction can have more operands than this.  Sorry for this
   arbitrary limit, but what machine will have an instruction with
   this many operands?  */

#define MAX_MAX_OPERANDS 40

static char general_mem[] = { TARGET_MEM_CONSTRAINT, 0 };

static int n_occurrences		(int, const char *);
static const char *strip_whitespace	(const char *);

/* This counts all operands used in the md file.  The first is null.  */

static int next_operand_number = 1;

/* Record in this chain all information about the operands we will output.  */

struct operand_data
{
  struct operand_data *next;
  int index;
  const char *predicate;
  const char *constraint;
  machine_mode mode;
  unsigned char n_alternatives;
  char address_p;
  char strict_low;
  char eliminable;
  char seen;
};

/* Begin with a null operand at index 0.  */

static struct operand_data null_operand =
{
  0, 0, "", "", E_VOIDmode, 0, 0, 0, 0, 0
};

static struct operand_data *odata = &null_operand;
static struct operand_data **odata_end = &null_operand.next;

/* Must match the constants in recog.h.  */

#define INSN_OUTPUT_FORMAT_NONE         0       /* abort */
#define INSN_OUTPUT_FORMAT_SINGLE       1       /* const char * */
#define INSN_OUTPUT_FORMAT_MULTI        2       /* const char * const * */
#define INSN_OUTPUT_FORMAT_FUNCTION     3       /* const char * (*)(...) */

/* Record in this chain all information that we will output,
   associated with the code number of the insn.  */

struct data
{
  struct data *next;
  const char *name;
  const char *template_code;
  file_location loc;
  int code_number;
  int n_generator_args;		/* Number of arguments passed to generator */
  int n_operands;		/* Number of operands this insn recognizes */
  int n_dups;			/* Number times match_dup appears in pattern */
  int n_alternatives;		/* Number of alternatives in each constraint */
  int operand_number;		/* Operand index in the big array.  */
  int output_format;		/* INSN_OUTPUT_FORMAT_*.  */
  struct operand_data operand[MAX_MAX_OPERANDS];
};

/* This variable points to the first link in the insn chain.  */
static struct data *idata;

/* This variable points to the end of the insn chain.  This is where
   everything relevant from the machien description is appended to.  */
static struct data **idata_end;


static void output_prologue (void);
static void output_operand_data (void);
static void output_insn_data (void);
static void output_get_insn_name (void);
static void scan_operands (struct data *, rtx, int, int);
static int compare_operands (struct operand_data *,
			     struct operand_data *);
static void place_operands (struct data *);
static void process_template (struct data *, const char *);
static void validate_insn_alternatives (struct data *);
static void validate_insn_operands (struct data *);

struct constraint_data
{
  struct constraint_data *next_this_letter;
  file_location loc;
  unsigned int namelen;
  char name[1];
};

/* All machine-independent constraint characters (except digits) that
   are handled outside the define*_constraint mechanism.  */
static const char indep_constraints[] = ",=+%*?!^$#&g";

static struct constraint_data *
constraints_by_letter_table[1 << CHAR_BIT];

static int mdep_constraint_len (const char *, file_location, int);
static void note_constraint (md_rtx_info *);

static void
output_prologue (void)
{
  printf ("/* Generated automatically by the program `genoutput'\n\
   from the machine description file `md'.  */\n\n");

  printf ("#define IN_TARGET_CODE 1\n");
  printf ("#include \"config.h\"\n");
  printf ("#include \"system.h\"\n");
  printf ("#include \"coretypes.h\"\n");
  printf ("#include \"backend.h\"\n");
  printf ("#include \"predict.h\"\n");
  printf ("#include \"tree.h\"\n");
  printf ("#include \"rtl.h\"\n");
  printf ("#include \"flags.h\"\n");
  printf ("#include \"alias.h\"\n");
  printf ("#include \"varasm.h\"\n");
  printf ("#include \"stor-layout.h\"\n");
  printf ("#include \"calls.h\"\n");
  printf ("#include \"insn-config.h\"\n");
  printf ("#include \"expmed.h\"\n");
  printf ("#include \"dojump.h\"\n");
  printf ("#include \"explow.h\"\n");
  printf ("#include \"memmodel.h\"\n");
  printf ("#include \"emit-rtl.h\"\n");
  printf ("#include \"stmt.h\"\n");
  printf ("#include \"expr.h\"\n");
  printf ("#include \"insn-codes.h\"\n");
  printf ("#include \"tm_p.h\"\n");
  printf ("#include \"regs.h\"\n");
  printf ("#include \"conditions.h\"\n");
  printf ("#include \"insn-attr.h\"\n\n");
  printf ("#include \"recog.h\"\n\n");
  printf ("#include \"diagnostic-core.h\"\n");
  printf ("#include \"output.h\"\n");
  printf ("#include \"target.h\"\n");
  printf ("#include \"tm-constrs.h\"\n");
}

static void
output_operand_data (void)
{
  struct operand_data *d;

  printf ("\nstatic const struct insn_operand_data operand_data[] = \n{\n");

  for (d = odata; d; d = d->next)
    {
      struct pred_data *pred;

      printf ("  {\n");

      printf ("    %s,\n",
	      d->predicate && d->predicate[0] ? d->predicate : "0");

      printf ("    \"%s\",\n", d->constraint ? d->constraint : "");

      printf ("    E_%smode,\n", GET_MODE_NAME (d->mode));

      printf ("    %d,\n", d->strict_low);

      printf ("    %d,\n", d->constraint == NULL ? 1 : 0);

      printf ("    %d,\n", d->eliminable);

      pred = NULL;
      if (d->predicate)
	pred = lookup_predicate (d->predicate);
      printf ("    %d\n", pred && pred->codes[MEM]);

      printf ("  },\n");
    }
  printf ("};\n\n\n");
}

static void
output_insn_data (void)
{
  struct data *d;
  int name_offset = 0;
  int next_name_offset;
  const char * last_name = 0;
  const char * next_name = 0;
  struct data *n;

  for (n = idata, next_name_offset = 1; n; n = n->next, next_name_offset++)
    if (n->name)
      {
	next_name = n->name;
	break;
      }

  printf ("#if GCC_VERSION >= 2007\n__extension__\n#endif\n");
  printf ("\nconst struct insn_data_d insn_data[] = \n{\n");

  for (d = idata; d; d = d->next)
    {
      printf ("  /* %s:%d */\n", d->loc.filename, d->loc.lineno);
      printf ("  {\n");

      if (d->name)
	{
	  printf ("    \"%s\",\n", d->name);
	  name_offset = 0;
	  last_name = d->name;
	  next_name = 0;
	  for (n = d->next, next_name_offset = 1; n;
	       n = n->next, next_name_offset++)
	    {
	      if (n->name)
		{
		  next_name = n->name;
		  break;
		}
	    }
	}
      else
	{
	  name_offset++;
	  if (next_name && (last_name == 0
			    || name_offset > next_name_offset / 2))
	    printf ("    \"%s-%d\",\n", next_name,
		    next_name_offset - name_offset);
	  else
	    printf ("    \"%s+%d\",\n", last_name, name_offset);
	}

      switch (d->output_format)
	{
	case INSN_OUTPUT_FORMAT_NONE:
	  printf ("#if HAVE_DESIGNATED_UNION_INITIALIZERS\n");
	  printf ("    { 0 },\n");
	  printf ("#else\n");
	  printf ("    { 0, 0, 0 },\n");
	  printf ("#endif\n");
	  break;
	case INSN_OUTPUT_FORMAT_SINGLE:
	  {
	    const char *p = d->template_code;
	    char prev = 0;

	    printf ("#if HAVE_DESIGNATED_UNION_INITIALIZERS\n");
	    printf ("    { .single =\n");
	    printf ("#else\n");
	    printf ("    {\n");
	    printf ("#endif\n");
	    printf ("    \"");
	    while (*p)
	      {
		if (IS_VSPACE (*p) && prev != '\\')
		  {
		    /* Preserve two consecutive \n's or \r's, but treat \r\n
		       as a single newline.  */
		    if (*p == '\n' && prev != '\r')
		      printf ("\\n\\\n");
		  }
		else
		  putchar (*p);
		prev = *p;
		++p;
	      }
	    printf ("\",\n");
	    printf ("#if HAVE_DESIGNATED_UNION_INITIALIZERS\n");
	    printf ("    },\n");
	    printf ("#else\n");
	    printf ("    0, 0 },\n");
	    printf ("#endif\n");
	  }
	  break;
	case INSN_OUTPUT_FORMAT_MULTI:
	  printf ("#if HAVE_DESIGNATED_UNION_INITIALIZERS\n");
	  printf ("    { .multi = output_%d },\n", d->code_number);
	  printf ("#else\n");
	  printf ("    { 0, output_%d, 0 },\n", d->code_number);
	  printf ("#endif\n");
	  break;
	case INSN_OUTPUT_FORMAT_FUNCTION:
	  printf ("#if HAVE_DESIGNATED_UNION_INITIALIZERS\n");
	  printf ("    { .function = output_%d },\n", d->code_number);
	  printf ("#else\n");
	  printf ("    { 0, 0, output_%d },\n", d->code_number);
	  printf ("#endif\n");
	  break;
	default:
	  gcc_unreachable ();
	}

      if (d->name && d->name[0] != '*')
	printf ("    { (insn_gen_fn::stored_funcptr) gen_%s },\n", d->name);
      else
	printf ("    { 0 },\n");

      printf ("    &operand_data[%d],\n", d->operand_number);
      printf ("    %d,\n", d->n_generator_args);
      printf ("    %d,\n", d->n_operands);
      printf ("    %d,\n", d->n_dups);
      printf ("    %d,\n", d->n_alternatives);
      printf ("    %d\n", d->output_format);

      printf ("  },\n");
    }
  printf ("};\n\n\n");
}

static void
output_get_insn_name (void)
{
  printf ("const char *\n");
  printf ("get_insn_name (int code)\n");
  printf ("{\n");
  printf ("  if (code == NOOP_MOVE_INSN_CODE)\n");
  printf ("    return \"NOOP_MOVE\";\n");
  printf ("  else\n");
  printf ("    return insn_data[code].name;\n");
  printf ("}\n");
}


/* Stores the operand data into `d->operand[i]'.

   THIS_ADDRESS_P is nonzero if the containing rtx was an ADDRESS.
   THIS_STRICT_LOW is nonzero if the containing rtx was a STRICT_LOW_PART.  */

static void
scan_operands (struct data *d, rtx part, int this_address_p,
	       int this_strict_low)
{
  int i, j;
  const char *format_ptr;
  int opno;

  if (part == 0)
    return;

  switch (GET_CODE (part))
    {
    case MATCH_OPERAND:
      opno = XINT (part, 0);
      if (opno >= MAX_MAX_OPERANDS)
	{
	  error_at (d->loc, "maximum number of operands exceeded");
	  return;
	}
      if (d->operand[opno].seen)
	error_at (d->loc, "repeated operand number %d\n", opno);

      d->operand[opno].seen = 1;
      d->operand[opno].mode = GET_MODE (part);
      d->operand[opno].strict_low = this_strict_low;
      d->operand[opno].predicate = XSTR (part, 1);
      d->operand[opno].constraint = strip_whitespace (XSTR (part, 2));
      d->operand[opno].n_alternatives
	= n_occurrences (',', d->operand[opno].constraint) + 1;
      d->operand[opno].address_p = this_address_p;
      d->operand[opno].eliminable = 1;
      return;

    case MATCH_SCRATCH:
      opno = XINT (part, 0);
      if (opno >= MAX_MAX_OPERANDS)
	{
	  error_at (d->loc, "maximum number of operands exceeded");
	  return;
	}
      if (d->operand[opno].seen)
	error_at (d->loc, "repeated operand number %d\n", opno);

      d->operand[opno].seen = 1;
      d->operand[opno].mode = GET_MODE (part);
      d->operand[opno].strict_low = 0;
      d->operand[opno].predicate = "scratch_operand";
      d->operand[opno].constraint = strip_whitespace (XSTR (part, 1));
      d->operand[opno].n_alternatives
	= n_occurrences (',', d->operand[opno].constraint) + 1;
      d->operand[opno].address_p = 0;
      d->operand[opno].eliminable = 0;
      return;

    case MATCH_OPERATOR:
    case MATCH_PARALLEL:
      opno = XINT (part, 0);
      if (opno >= MAX_MAX_OPERANDS)
	{
	  error_at (d->loc, "maximum number of operands exceeded");
	  return;
	}
      if (d->operand[opno].seen)
	error_at (d->loc, "repeated operand number %d\n", opno);

      d->operand[opno].seen = 1;
      d->operand[opno].mode = GET_MODE (part);
      d->operand[opno].strict_low = 0;
      d->operand[opno].predicate = XSTR (part, 1);
      d->operand[opno].constraint = 0;
      d->operand[opno].address_p = 0;
      d->operand[opno].eliminable = 0;
      for (i = 0; i < XVECLEN (part, 2); i++)
	scan_operands (d, XVECEXP (part, 2, i), 0, 0);
      return;

    case STRICT_LOW_PART:
      scan_operands (d, XEXP (part, 0), 0, 1);
      return;

    default:
      break;
    }

  format_ptr = GET_RTX_FORMAT (GET_CODE (part));

  for (i = 0; i < GET_RTX_LENGTH (GET_CODE (part)); i++)
    switch (*format_ptr++)
      {
      case 'e':
      case 'u':
	scan_operands (d, XEXP (part, i), 0, 0);
	break;
      case 'E':
	if (XVEC (part, i) != NULL)
	  for (j = 0; j < XVECLEN (part, i); j++)
	    scan_operands (d, XVECEXP (part, i, j), 0, 0);
	break;
      }
}

/* Compare two operands for content equality.  */

static int
compare_operands (struct operand_data *d0, struct operand_data *d1)
{
  const char *p0, *p1;

  p0 = d0->predicate;
  if (!p0)
    p0 = "";
  p1 = d1->predicate;
  if (!p1)
    p1 = "";
  if (strcmp (p0, p1) != 0)
    return 0;

  p0 = d0->constraint;
  if (!p0)
    p0 = "";
  p1 = d1->constraint;
  if (!p1)
    p1 = "";
  if (strcmp (p0, p1) != 0)
    return 0;

  if (d0->mode != d1->mode)
    return 0;

  if (d0->strict_low != d1->strict_low)
    return 0;

  if (d0->eliminable != d1->eliminable)
    return 0;

  return 1;
}

/* Scan the list of operands we've already committed to output and either
   find a subsequence that is the same, or allocate a new one at the end.  */

static void
place_operands (struct data *d)
{
  struct operand_data *od, *od2;
  int i;

  if (d->n_operands == 0)
    {
      d->operand_number = 0;
      return;
    }

  /* Brute force substring search.  */
  for (od = odata, i = 0; od; od = od->next, i = 0)
    if (compare_operands (od, &d->operand[0]))
      {
	od2 = od->next;
	i = 1;
	while (1)
	  {
	    if (i == d->n_operands)
	      goto full_match;
	    if (od2 == NULL)
	      goto partial_match;
	    if (! compare_operands (od2, &d->operand[i]))
	      break;
	    ++i, od2 = od2->next;
	  }
      }

  /* Either partial match at the end of the list, or no match.  In either
     case, we tack on what operands are remaining to the end of the list.  */
 partial_match:
  d->operand_number = next_operand_number - i;
  for (; i < d->n_operands; ++i)
    {
      od2 = &d->operand[i];
      *odata_end = od2;
      odata_end = &od2->next;
      od2->index = next_operand_number++;
    }
  *odata_end = NULL;
  return;

 full_match:
  d->operand_number = od->index;
  return;
}


/* Process an assembler template from a define_insn or a define_peephole.
   It is either the assembler code template, a list of assembler code
   templates, or C code to generate the assembler code template.  */

static void
process_template (struct data *d, const char *template_code)
{
  const char *cp;
  int i;

  /* Templates starting with * contain straight code to be run.  */
  if (template_code[0] == '*')
    {
      d->template_code = 0;
      d->output_format = INSN_OUTPUT_FORMAT_FUNCTION;

      puts ("\nstatic const char *");
      printf ("output_%d (rtx *operands ATTRIBUTE_UNUSED, rtx_insn *insn ATTRIBUTE_UNUSED)\n",
	      d->code_number);
      puts ("{");
      rtx_reader_ptr->print_md_ptr_loc (template_code);
      puts (template_code + 1);
      puts ("}");
    }

  /* If the assembler code template starts with a @ it is a newline-separated
     list of assembler code templates, one for each alternative.  */
  else if (template_code[0] == '@')
    {
      int found_star = 0;

      for (cp = &template_code[1]; *cp; )
	{
	  while (ISSPACE (*cp))
	    cp++;
	  if (*cp == '*')
	    found_star = 1;
	  while (!IS_VSPACE (*cp) && *cp != '\0')
	    ++cp;
	}
      d->template_code = 0;
      if (found_star)
	{
	  d->output_format = INSN_OUTPUT_FORMAT_FUNCTION;
	  puts ("\nstatic const char *");
	  printf ("output_%d (rtx *operands ATTRIBUTE_UNUSED, "
		  "rtx_insn *insn ATTRIBUTE_UNUSED)\n", d->code_number);
	  puts ("{");
	  puts ("  switch (which_alternative)\n    {");
	}
      else
	{
	  d->output_format = INSN_OUTPUT_FORMAT_MULTI;
	  printf ("\nstatic const char * const output_%d[] = {\n",
		  d->code_number);
	}

      for (i = 0, cp = &template_code[1]; *cp; )
	{
	  const char *ep, *sp, *bp;

	  while (ISSPACE (*cp))
	    cp++;

	  bp = cp;
	  if (found_star)
	    {
	      printf ("    case %d:", i);
	      if (*cp == '*')
		{
		  printf ("\n      ");
		  cp++;
		}
	      else
		printf (" return \"");
	    }
	  else
	    printf ("  \"");

	  for (ep = sp = cp; !IS_VSPACE (*ep) && *ep != '\0'; ++ep)
	    if (!ISSPACE (*ep))
	      sp = ep + 1;

	  if (sp != ep)
	    message_at (d->loc, "trailing whitespace in output template");

	  while (cp < sp)
	    {
	      putchar (*cp);
	      cp++;
	    }

	  if (!found_star)
	    puts ("\",");
	  else if (*bp != '*')
	    puts ("\";");
	  else
	    {
	      /* The usual action will end with a return.
		 If there is neither break or return at the end, this is
		 assumed to be intentional; this allows to have multiple
		 consecutive alternatives share some code.  */
	      puts ("");
	    }
	  i++;
	}
      if (i == 1)
	message_at (d->loc, "'@' is redundant for output template with"
		    " single alternative");
      if (i != d->n_alternatives)
	error_at (d->loc, "wrong number of alternatives in the output"
		  " template");

      if (found_star)
	puts ("      default: gcc_unreachable ();\n    }\n}");
      else
	printf ("};\n");
    }
  else
    {
      d->template_code = template_code;
      d->output_format = INSN_OUTPUT_FORMAT_SINGLE;
    }
}

/* Check insn D for consistency in number of constraint alternatives.  */

static void
validate_insn_alternatives (struct data *d)
{
  int n = 0, start;

  /* Make sure all the operands have the same number of alternatives
     in their constraints.  Let N be that number.  */
  for (start = 0; start < d->n_operands; start++)
    if (d->operand[start].n_alternatives > 0)
      {
	int len, i;
	const char *p;
	char c;
	int which_alternative = 0;
	int alternative_count_unsure = 0;
	bool seen_write = false;

	for (p = d->operand[start].constraint; (c = *p); p += len)
	  {
	    if ((c == '%' || c == '=' || c == '+')
		&& p != d->operand[start].constraint)
	      error_at (d->loc, "character '%c' can only be used at the"
			" beginning of a constraint string", c);

	    if (c == '=' || c == '+')
	      seen_write = true;

	    /* Earlyclobber operands must always be marked write-only
	       or read/write.  */
	    if (!seen_write && c == '&')
	      error_at (d->loc, "earlyclobber operands may not be"
			" read-only in alternative %d", which_alternative);

	    if (ISSPACE (c) || strchr (indep_constraints, c))
	      len = 1;
	    else if (ISDIGIT (c))
	      {
		const char *q = p;
		do
		  q++;
		while (ISDIGIT (*q));
		len = q - p;
	      }
	    else
	      len = mdep_constraint_len (p, d->loc, start);

	    if (c == ',')
	      {
	        which_alternative++;
		continue;
	      }

	    for (i = 1; i < len; i++)
	      if (p[i] == '\0')
		{
		  error_at (d->loc, "NUL in alternative %d of operand %d",
			    which_alternative, start);
		  alternative_count_unsure = 1;
		  break;
		}
	      else if (strchr (",#*", p[i]))
		{
		  error_at (d->loc, "'%c' in alternative %d of operand %d",
			    p[i], which_alternative, start);
		  alternative_count_unsure = 1;
		}
	  }
	if (!alternative_count_unsure)
	  {
	    if (n == 0)
	      n = d->operand[start].n_alternatives;
	    else if (n != d->operand[start].n_alternatives)
	      error_at (d->loc, "wrong number of alternatives in operand %d",
			start);
	  }
      }

  /* Record the insn's overall number of alternatives.  */
  d->n_alternatives = n;
}

/* Verify that there are no gaps in operand numbers for INSNs.  */

static void
validate_insn_operands (struct data *d)
{
  int i;

  for (i = 0; i < d->n_operands; ++i)
    if (d->operand[i].seen == 0)
      error_at (d->loc, "missing operand %d", i);
}

static void
validate_optab_operands (struct data *d)
{
  if (!d->name || d->name[0] == '\0' || d->name[0] == '*')
    return;

  /* Miscellaneous tests.  */
  if (strncmp (d->name, "cstore", 6) == 0
      && d->name[strlen (d->name) - 1] == '4'
      && d->operand[0].mode == VOIDmode)
    {
      message_at (d->loc, "missing mode for operand 0 of cstore");
      have_error = 1;
    }
}

/* Look at a define_insn just read.  Assign its code number.  Record
   on idata the template and the number of arguments.  If the insn has
   a hairy output action, output a function for now.  */

static void
gen_insn (md_rtx_info *info)
{
  struct pattern_stats stats;
  rtx insn = info->def;
  data *d = new data;
  int i;

  d->code_number = info->index;
  d->loc = info->loc;
  if (XSTR (insn, 0)[0])
    d->name = XSTR (insn, 0);
  else
    d->name = 0;

  /* Build up the list in the same order as the insns are seen
     in the machine description.  */
  d->next = 0;
  *idata_end = d;
  idata_end = &d->next;

  memset (d->operand, 0, sizeof (d->operand));

  for (i = 0; i < XVECLEN (insn, 1); i++)
    scan_operands (d, XVECEXP (insn, 1, i), 0, 0);

  get_pattern_stats (&stats, XVEC (insn, 1));
  d->n_generator_args = stats.num_generator_args;
  d->n_operands = stats.num_insn_operands;
  d->n_dups = stats.num_dups;

  validate_insn_operands (d);
  validate_insn_alternatives (d);
  validate_optab_operands (d);
  place_operands (d);
  process_template (d, XTMPL (insn, 3));
}

/* Look at a define_peephole just read.  Assign its code number.
   Record on idata the template and the number of arguments.
   If the insn has a hairy output action, output it now.  */

static void
gen_peephole (md_rtx_info *info)
{
  struct pattern_stats stats;
  data *d = new data;
  int i;

  d->code_number = info->index;
  d->loc = info->loc;
  d->name = 0;

  /* Build up the list in the same order as the insns are seen
     in the machine description.  */
  d->next = 0;
  *idata_end = d;
  idata_end = &d->next;

  memset (d->operand, 0, sizeof (d->operand));

  /* Get the number of operands by scanning all the patterns of the
     peephole optimizer.  But ignore all the rest of the information
     thus obtained.  */
  rtx peep = info->def;
  for (i = 0; i < XVECLEN (peep, 0); i++)
    scan_operands (d, XVECEXP (peep, 0, i), 0, 0);

  get_pattern_stats (&stats, XVEC (peep, 0));
  d->n_generator_args = 0;
  d->n_operands = stats.num_insn_operands;
  d->n_dups = 0;

  validate_insn_alternatives (d);
  place_operands (d);
  process_template (d, XTMPL (peep, 2));
}

/* Process a define_expand just read.  Assign its code number,
   only for the purposes of `insn_gen_function'.  */

static void
gen_expand (md_rtx_info *info)
{
  struct pattern_stats stats;
  rtx insn = info->def;
  data *d = new data;
  int i;

  d->code_number = info->index;
  d->loc = info->loc;
  if (XSTR (insn, 0)[0])
    d->name = XSTR (insn, 0);
  else
    d->name = 0;

  /* Build up the list in the same order as the insns are seen
     in the machine description.  */
  d->next = 0;
  *idata_end = d;
  idata_end = &d->next;

  memset (d->operand, 0, sizeof (d->operand));

  /* Scan the operands to get the specified predicates and modes,
     since expand_binop needs to know them.  */

  if (XVEC (insn, 1))
    for (i = 0; i < XVECLEN (insn, 1); i++)
      scan_operands (d, XVECEXP (insn, 1, i), 0, 0);

  get_pattern_stats (&stats, XVEC (insn, 1));
  d->n_generator_args = stats.num_generator_args;
  d->n_operands = stats.num_insn_operands;
  d->n_dups = stats.num_dups;
  d->template_code = 0;
  d->output_format = INSN_OUTPUT_FORMAT_NONE;

  validate_insn_alternatives (d);
  validate_optab_operands (d);
  place_operands (d);
}

static void
init_insn_for_nothing (void)
{
  idata = XCNEW (struct data);
  new (idata) data ();
  idata->name = "*placeholder_for_nothing";
  idata->loc = file_location ("<internal>", 0, 0);
  idata_end = &idata->next;
}

extern int main (int, const char **);

int
main (int argc, const char **argv)
{
  progname = "genoutput";

  init_insn_for_nothing ();

  if (!init_rtx_reader_args (argc, argv))
    return (FATAL_EXIT_CODE);

  output_prologue ();

  /* Read the machine description.  */

  md_rtx_info info;
  while (read_md_rtx (&info))
    switch (GET_CODE (info.def))
      {
      case DEFINE_INSN:
	gen_insn (&info);
	break;

      case DEFINE_PEEPHOLE:
	gen_peephole (&info);
	break;

      case DEFINE_EXPAND:
	gen_expand (&info);
	break;

      case DEFINE_CONSTRAINT:
      case DEFINE_REGISTER_CONSTRAINT:
      case DEFINE_ADDRESS_CONSTRAINT:
      case DEFINE_MEMORY_CONSTRAINT:
      case DEFINE_SPECIAL_MEMORY_CONSTRAINT:
	note_constraint (&info);
	break;

      default:
	break;
      }

  printf ("\n\n");
  output_operand_data ();
  output_insn_data ();
  output_get_insn_name ();

  fflush (stdout);
  return (ferror (stdout) != 0 || have_error
	? FATAL_EXIT_CODE : SUCCESS_EXIT_CODE);
}

/* Return the number of occurrences of character C in string S or
   -1 if S is the null string.  */

static int
n_occurrences (int c, const char *s)
{
  int n = 0;

  if (s == 0 || *s == '\0')
    return -1;

  while (*s)
    n += (*s++ == c);

  return n;
}

/* Remove whitespace in `s' by moving up characters until the end.
   Return a new string.  */

static const char *
strip_whitespace (const char *s)
{
  char *p, *q;
  char ch;

  if (s == 0)
    return 0;

  p = q = XNEWVEC (char, strlen (s) + 1);
  while ((ch = *s++) != '\0')
    if (! ISSPACE (ch))
      *p++ = ch;

  *p = '\0';
  return q;
}

/* Record just enough information about the constraint in *INFO to allow
   checking of operand constraint strings above, in validate_insn_alternatives.
   Does not validate most properties of the constraint itself; does enforce
   no duplicate names, no overlap with MI constraints, and no prefixes.  */
static void
note_constraint (md_rtx_info *info)
{
  rtx exp = info->def;
  const char *name = XSTR (exp, 0);
  struct constraint_data **iter, **slot, *new_cdata;

  if (strcmp (name, "TARGET_MEM_CONSTRAINT") == 0)
    name = general_mem;
  unsigned int namelen = strlen (name);

  if (strchr (indep_constraints, name[0]))
    {
      if (name[1] == '\0')
	error_at (info->loc, "constraint letter '%s' cannot be "
		  "redefined by the machine description", name);
      else
	error_at (info->loc, "constraint name '%s' cannot be defined by "
		  "the machine description, as it begins with '%c'",
		  name, name[0]);
      return;
    }

  slot = &constraints_by_letter_table[(unsigned int)name[0]];
  for (iter = slot; *iter; iter = &(*iter)->next_this_letter)
    {
      /* This causes slot to end up pointing to the
	 next_this_letter field of the last constraint with a name
	 of equal or greater length than the new constraint; hence
	 the new constraint will be inserted after all previous
	 constraints with names of the same length.  */
      if ((*iter)->namelen >= namelen)
	slot = iter;

      if (!strcmp ((*iter)->name, name))
	{
	  error_at (info->loc, "redefinition of constraint '%s'", name);
	  message_at ((*iter)->loc, "previous definition is here");
	  return;
	}
      else if (!strncmp ((*iter)->name, name, (*iter)->namelen))
	{
	  error_at (info->loc, "defining constraint '%s' here", name);
	  message_at ((*iter)->loc, "renders constraint '%s' "
		      "(defined here) a prefix", (*iter)->name);
	  return;
	}
      else if (!strncmp ((*iter)->name, name, namelen))
	{
	  error_at (info->loc, "constraint '%s' is a prefix", name);
	  message_at ((*iter)->loc, "of constraint '%s' "
		      "(defined here)", (*iter)->name);
	  return;
	}
    }
  new_cdata = XNEWVAR (struct constraint_data,
		       sizeof (struct constraint_data) + namelen);
  new (new_cdata) constraint_data ();
  strcpy (CONST_CAST (char *, new_cdata->name), name);
  new_cdata->namelen = namelen;
  new_cdata->loc = info->loc;
  new_cdata->next_this_letter = *slot;
  *slot = new_cdata;
}

/* Return the length of the constraint name beginning at position S
   of an operand constraint string, or issue an error message if there
   is no such constraint.  Does not expect to be called for generic
   constraints.  */
static int
mdep_constraint_len (const char *s, file_location loc, int opno)
{
  struct constraint_data *p;

  p = constraints_by_letter_table[(unsigned int)s[0]];

  if (p)
    for (; p; p = p->next_this_letter)
      if (!strncmp (s, p->name, p->namelen))
	return p->namelen;

  error_at (loc, "error: undefined machine-specific constraint "
	    "at this point: \"%s\"", s);
  message_at (loc, "note:  in operand %d", opno);
  return 1; /* safe */
}