diff mbox

[PR68432,05/22] Add support attributes that operate on insn codes

Message ID 87fuzui7q6.fsf@e105548-lin.cambridge.arm.com
State New
Headers show

Commit Message

Richard Sandiford Nov. 25, 2015, 12:28 p.m. UTC
This patch adds a new type of attribute whose function takes only
the instruction code and alternative number as argument, rather than
an rtx_insn *.  This makes it possible to call the functions from
the gimple level without having to construct rtl first.  For convenience
(and consistency) the new attributes still provide an rtx_insn * interface
too.

The patch adds a new optional type operand to DEFINE_ATTR and
DEFINE_ENUM_ATTR.  "" or "insn" selects the general rtx_insn * style
and "code,alternative" selects the new more limited form.

The patch also uses _Pragma ("GCC error ...") to trap uses of recog_data
in the new functions.  This isn't a particularly strong safety net but
should be much better than nothing (and what we have now).

enabled, preferred_for_size and preferred_for_speed were already supposed
to be suitable for "code,alternative", we just never enforced it until now.
Later patches in the series make this change to each port in turn.

Tested as described in the covering note.  I think this comes under
the gen* maintainership, but I'd welcome comments all the same.

gcc/
	* doc/md.texi: Document new define_attr field.
	* rtl.def (DEFINE_ATTR, DEFINE_ENUM_ATTR): Add a string type.
	* genattr.c (declare_function): New function.
	(gen_attr): Use it.  Call parse_attr_type.  Declare a code/alternative
	function if appropriate.  Do not output a prototype for constant
	attributes.
	* genattrtab.c (attr_desc): Replace the boolean is_const field with
	an attr_type field.
	(attr_desc::is_const): New function.
	(check_attr_value): Update accordingly.  Use get_attr_type_name.
	Make sure that attributes don't refer to other attributes with
	a higher type.
	(match_attr_test): Likewise.  Make sure that only AT_INSN attributes
	use match_operand.
	(make_canonical): Update after changes to attr_desc.
	(fill_attr, make_length_attrs, get_attr_order, make_internal_attr)
	(find_tune_attr, make_automaton_attrs, main): Likewise.
	(gen_attr): Use parse_attr_type.
	(find_attrs_to_cache): Only cache AT_INSN attributes.
	(write_test_expr): Pass the code and alternative number when
	calling an AT_CODE_ALT function.
	(walk_attr_value): Require an alternative number when calling
	an AT_CODE_ALT function.
	(write_attr_return_type): New function.
	(write_attr_get): Remove constant handling and assert that it
	isn't needed instead.  Handle AT_CODE_ALT functions.
	Use write_attr_return_type.  Make sure that the instruction
	code is always available in a variable called "icode".
	(write_attr_case): Limit asm handling to AT_INSN.  Likewise
	must_constrain and must_extract.
	(write_attr_value): Use write_attr_return_type.
	(find_attr): Use memset to clear the structure, rather than
	writing zero values.  Initialize the type field.
	(gen_insn_reserv): Initialize the type field.
	* read-rtl.c (add_define_attr_for_define_subst): Initialize the
	fourth DEFINE_ATTR field.
	* gensupport.h (attr_type): New enum.
	(parse_attr_type, get_attr_type_name): Declare.
	* gensupport.c (add_define_attr): Take a type argument and use it
	to initialize the fourth DEFINE_ATTR field.
	(alter_attrs_for_insn): Set the ce_enabled and noce_enabled
	attributes to the same type as "enabled".
	(parse_attr_type, get_attr_type_name): New functions.
diff mbox

Patch

diff --git a/gcc/doc/md.texi b/gcc/doc/md.texi
index de1b58a..5d3736c 100644
--- a/gcc/doc/md.texi
+++ b/gcc/doc/md.texi
@@ -8070,7 +8070,7 @@  The @code{define_attr} expression is used to define each attribute required
 by the target machine.  It looks like:
 
 @smallexample
-(define_attr @var{name} @var{list-of-values} @var{default})
+(define_attr @var{name} @var{list-of-values} @var{default} @var{type})
 @end smallexample
 
 @var{name} is a string specifying the name of the attribute being
@@ -8095,6 +8095,25 @@  an explicit value for this attribute.  @xref{Attr Example}, for more
 information on the handling of defaults.  @xref{Constant Attributes},
 for information on attributes that do not depend on any particular insn.
 
+@var{type} is an optional string that specifies the type of the attribute.
+The possible values are:
+
+@table @code
+@item code,alternative
+The attribute is a function of the instruction code and the alternative
+number.  It does not need need access to a particular rtx instruction
+or to a particular set of operands.
+
+@item insn
+The attribute is a function of an rtx instruction.  It can depend on
+things like operand values.
+@end table
+
+If @var{type} is omitted, the default type is @code{insn}.
+@code{insn} attributes may refer to both @code{insn} and
+@code{code,alternative} attributes, but @code{code,alternative} attributes
+may only refer to other @code{code,alternative} attributes.
+
 @findex insn-attr.h
 For each defined attribute, a number of definitions are written to the
 @file{insn-attr.h} file.  For cases where an explicit set of values is
@@ -8110,14 +8129,21 @@  elements of the form @samp{@var{upper-name}_@var{upper-value}} where
 the attribute name and value are first converted to uppercase.
 
 @item
-A function @samp{get_attr_@var{name}} is defined that is passed an insn and
+For @code{code,alternative} attributes, a function @samp{get_attr_@var{name}}
+is defined that takes an @code{int} instruction code number and an
+@code{int} alternative number and returns the associated attribute value.
+
+@item
+For both @code{code,alternative} and @var{insn} attributes, a function
+@samp{get_attr_@var{name}} is defined that is passed an insn and
 returns the attribute value for that insn.
 @end itemize
 
 For example, if the following is present in the @file{md} file:
 
 @smallexample
-(define_attr "type" "branch,fp,load,store,arith" @dots{})
+(define_attr "type" "branch,fp,load,store,arith" @dots{}
+  "code,alternative")
 @end smallexample
 
 @noindent
@@ -8127,7 +8153,8 @@  the following lines will be written to the file @file{insn-attr.h}.
 #define HAVE_ATTR_type 1
 enum attr_type @{TYPE_BRANCH, TYPE_FP, TYPE_LOAD,
                  TYPE_STORE, TYPE_ARITH@};
-extern enum attr_type get_attr_type ();
+extern enum attr_type get_attr_type (int, int);
+extern enum attr_type get_attr_type (rtx_insn *);
 @end smallexample
 
 If the attribute takes numeric values, no @code{enum} type will be
@@ -8163,7 +8190,7 @@  attribute is not defined; in that case, it is defined as @samp{0}.
 Another way of defining an attribute is to use:
 
 @smallexample
-(define_enum_attr "@var{attr}" "@var{enum}" @var{default})
+(define_enum_attr "@var{attr}" "@var{enum}" @var{default} @var{type})
 @end smallexample
 
 This works in just the same way as @code{define_attr}, except that
diff --git a/gcc/genattr.c b/gcc/genattr.c
index e45f87c..c08c4af 100644
--- a/gcc/genattr.c
+++ b/gcc/genattr.c
@@ -31,34 +31,42 @@  along with GCC; see the file COPYING3.  If not see
 
 static vec<rtx> const_attrs, reservations;
 
+/* Declare a get_attr_*-style function for ATTR with the argument
+   types specified in string PROTOTYPE.  */
+
+static void
+declare_function (rtx attr, const char *prototype)
+{
+  if (GET_CODE (attr) == DEFINE_ENUM_ATTR)
+    printf ("extern enum %s get_attr_%s (%s);\n",
+	    XSTR (attr, 1), XSTR (attr, 0), prototype);
+  else
+    {
+      const char *p = XSTR (attr, 1);
+      if (*p == '\0')
+	printf ("extern int get_attr_%s (%s);\n", XSTR (attr, 0), prototype);
+      else
+	printf ("extern enum attr_%s get_attr_%s (%s);\n",
+		XSTR (attr, 0), XSTR (attr, 0), prototype);
+    }
+}
+
 
 static void
 gen_attr (md_rtx_info *info)
 {
-  const char *p;
   rtx attr = info->def;
-  int is_const = GET_CODE (XEXP (attr, 2)) == CONST;
-
-  if (is_const)
-    const_attrs.safe_push (attr);
+  attr_type type = parse_attr_type (info->loc, attr);
 
   printf ("#define HAVE_ATTR_%s 1\n", XSTR (attr, 0));
 
-  /* If numeric attribute, don't need to write an enum.  */
-  if (GET_CODE (attr) == DEFINE_ENUM_ATTR)
-    printf ("extern enum %s get_attr_%s (%s);\n\n",
-	    XSTR (attr, 1), XSTR (attr, 0),
-	    (is_const ? "void" : "rtx_insn *"));
+  if (type == AT_CONST)
+    const_attrs.safe_push (attr);
   else
     {
-      p = XSTR (attr, 1);
-      if (*p == '\0')
-	printf ("extern int get_attr_%s (%s);\n", XSTR (attr, 0),
-		(is_const ? "void" : "rtx_insn *"));
-      else
-	printf ("extern enum attr_%s get_attr_%s (%s);\n\n",
-		XSTR (attr, 0), XSTR (attr, 0),
-		(is_const ? "void" : "rtx_insn *"));
+      if (type == AT_CODE_ALT)
+	declare_function (attr, "int, int");
+      declare_function (attr, "rtx_insn *");
     }
 
   /* If `length' attribute, write additional function definitions and define
diff --git a/gcc/genattrtab.c b/gcc/genattrtab.c
index 36fdf8b..6f797bf 100644
--- a/gcc/genattrtab.c
+++ b/gcc/genattrtab.c
@@ -177,9 +177,12 @@  struct attr_desc
   struct attr_value *first_value; /* First value of this attribute.  */
   struct attr_value *default_val; /* Default value for this attribute.  */
   file_location loc;		/* Where in the .md files it occurs.  */
-  unsigned is_numeric	: 1;	/* Values of this attribute are numeric.  */
-  unsigned is_const	: 1;	/* Attribute value constant for each run.  */
-  unsigned is_special	: 1;	/* Don't call `write_attr_set'.  */
+  ENUM_BITFIELD (attr_type) type : 2;
+				/* Type of attribute.  */
+  unsigned is_numeric  : 1;	/* Values of this attribute are numeric.  */
+  unsigned is_special  : 1;	/* Don't call `write_attr_set'.  */
+
+  bool is_const () const { return type == AT_CONST; }
 };
 
 /* Structure for each DEFINE_DELAY.  */
@@ -772,9 +775,10 @@  check_attr_test (file_location loc, rtx exp, attr_desc *attr)
 			  " attribute `%s'", XSTR (exp, 0), attr->name);
 	    }
 
-	  if (attr->is_const && ! attr2->is_const)
-	    fatal_at (loc, "constant attribute `%s' cannot test non-constant"
-		      " attribute `%s'", attr->name, attr2->name);
+	  if (attr->type < attr2->type)
+	    fatal_at (loc, "%s attribute `%s' cannot test %s attribute `%s'",
+		      get_attr_type_name (attr->type), attr->name,
+		      get_attr_type_name (attr2->type), attr2->name);
 
 	  /* Copy this just to make it permanent,
 	     so expressions using it can be permanent too.  */
@@ -783,7 +787,7 @@  check_attr_test (file_location loc, rtx exp, attr_desc *attr)
 	  /* It shouldn't be possible to simplify the value given to a
 	     constant attribute, so don't expand this until it's time to
 	     write the test expression.  */
-	  if (attr2->is_const)
+	  if (attr2->is_const ())
 	    ATTR_IND_SIMPLIFIED_P (exp) = 1;
 
 	  if (attr2->is_numeric)
@@ -859,10 +863,10 @@  check_attr_test (file_location loc, rtx exp, attr_desc *attr)
       break;
 
     case MATCH_OPERAND:
-      if (attr->is_const)
-	fatal_at (loc, "invalid operator `%s' in definition of constant"
+      if (attr->type < AT_INSN)
+	fatal_at (loc, "invalid operator `%s' in definition of %s"
 		  " attribute `%s'", GET_RTX_NAME (GET_CODE (exp)),
-		  attr->name);
+		  get_attr_type_name (attr->type), attr->name);
       /* These cases can't be simplified.  */
       ATTR_IND_SIMPLIFIED_P (exp) = 1;
       break;
@@ -880,7 +884,7 @@  check_attr_test (file_location loc, rtx exp, attr_desc *attr)
       break;
 
     case SYMBOL_REF:
-      if (attr->is_const)
+      if (attr->is_const ())
 	{
 	  /* These cases are valid for constant attributes, but can't be
 	     simplified.  */
@@ -1017,10 +1021,11 @@  check_attr_value (file_location loc, rtx exp, struct attr_desc *attr)
 	if (attr2 == NULL)
 	  error_at (loc, "unknown attribute `%s' in ATTR",
 		    XSTR (exp, 0));
-	else if (attr->is_const && ! attr2->is_const)
-	  error_at (attr->loc,
-		    "constant attribute `%s' cannot refer to non-constant"
-		    " attribute `%s'", attr->name, attr2->name);
+	else if (attr->type < attr2->type)
+	  error_at (loc,
+		    "%s attribute `%s' cannot refer to %s attribute `%s'",
+		    get_attr_type_name (attr->type), attr->name,
+		    get_attr_type_name (attr2->type), attr2->name);
 	else if (attr->is_numeric != attr2->is_numeric)
 	  error_at (loc,
 		    "numeric attribute mismatch calling `%s' from `%s'",
@@ -1199,7 +1204,7 @@  make_canonical (file_location loc, struct attr_desc *attr, rtx exp)
       break;
 
     case SYMBOL_REF:
-      if (!attr->is_const || ATTR_IND_SIMPLIFIED_P (exp))
+      if (!attr->is_const () || ATTR_IND_SIMPLIFIED_P (exp))
 	break;
       /* The SYMBOL_REF is constant for a given run, so mark it as unchanging.
 	 This makes the COND something that won't be considered an arbitrary
@@ -1425,7 +1430,7 @@  fill_attr (struct attr_desc *attr)
 
   /* Don't fill constant attributes.  The value is independent of
      any particular insn.  */
-  if (attr->is_const)
+  if (attr->is_const ())
     return;
 
   for (id = defs; id; id = id->next)
@@ -1557,7 +1562,7 @@  make_length_attrs (void)
   if (! length_attr->is_numeric)
     fatal_at (length_attr->loc, "length attribute must be numeric");
 
-  length_attr->is_const = 0;
+  length_attr->type = AT_INSN;
   length_attr->is_special = 1;
 
   /* Make each new attribute, in turn.  */
@@ -2889,7 +2894,7 @@  get_attr_order (struct attr_desc ***ret)
 
   j = 0;
   for (i = 0; i < num; i++)
-    if (all[i]->is_const)
+    if (all[i]->is_const ())
       handled[i] = 1, sorted[j++] = all[i];
 
   /* We have only few attributes hence we can live with the inner
@@ -2917,7 +2922,7 @@  get_attr_order (struct attr_desc ***ret)
 		}
 	    if (k == num)
 	      {
-		/* Nothing in I depended on anything intersting, so
+		/* Nothing in I depended on anything interesting, so
 		   it's done.  */
 		handled[i] = 1;
 		sorted[j++] = all[i];
@@ -2935,7 +2940,7 @@  get_attr_order (struct attr_desc ***ret)
 	fprintf (stderr, "%s depends on: ", attr->name);
 	for (i = 0; i < MAX_ATTRS_INDEX; ++i)
 	  for (attr2 = attrs[i]; attr2; attr2 = attr2->next)
-	    if (!attr2->is_const)
+	    if (!attr2->is_const ())
 	      for (av = attr->first_value; av; av = av->next)
 		if (av->num_insns != 0)
 		  if (tests_attr_p (av->value, attr2))
@@ -3152,6 +3157,8 @@  gen_attr (md_rtx_info *info)
     }
   attr->loc = info->loc;
 
+  attr->type = parse_attr_type (info->loc, def);
+
   if (GET_CODE (def) == DEFINE_ENUM_ATTR)
     {
       attr->enum_name = XSTR (def, 1);
@@ -3172,9 +3179,8 @@  gen_attr (md_rtx_info *info)
 	add_attr_value (attr, p);
     }
 
-  if (GET_CODE (XEXP (def, 2)) == CONST)
+  if (attr->type == AT_CONST)
     {
-      attr->is_const = 1;
       if (attr->is_numeric)
 	error_at (info->loc,
 		  "constant attributes may not take numeric values");
@@ -3389,7 +3395,7 @@  find_attrs_to_cache (rtx exp, bool create)
 	return;
       attr = find_attr (&name, 0);
       gcc_assert (attr);
-      if (attr->is_const)
+      if (attr->type != AT_INSN)
 	return;
       if (cached_attr_count == 32)
 	return;
@@ -3688,13 +3694,17 @@  write_test_expr (FILE *outf, rtx exp, unsigned int attrs_cached, int flags)
       gcc_assert (attr);
 
       /* Now is the time to expand the value of a constant attribute.  */
-      if (attr->is_const)
+      if (attr->type == AT_CONST)
+	write_test_expr (outf,
+			 evaluate_eq_attr (exp, attr,
+					   attr->default_val->value,
+					   -2, -2),
+			 attrs_cached, 0);
+      else if (attr->type == AT_CODE_ALT)
 	{
-	  write_test_expr (outf,
-			   evaluate_eq_attr (exp, attr,
-					     attr->default_val->value,
-					     -2, -2),
-			   attrs_cached, 0);
+	  fprintf (outf, "get_attr_%s (icode, which_alternative)", attr->name);
+	  fprintf (outf, " == ");
+	  write_attr_valueq (outf, attr, XSTR (exp, 1));
 	}
       else
 	{
@@ -3932,6 +3942,7 @@  walk_attr_value (rtx exp)
   int i, j;
   const char *fmt;
   RTX_CODE code;
+  attr_desc *attr;
 
   if (exp == NULL)
     return;
@@ -3957,6 +3968,9 @@  walk_attr_value (rtx exp)
       break;
 
     case EQ_ATTR:
+      attr = find_attr (&XSTR (exp, 0), 0);
+      if (attr->type == AT_CODE_ALT)
+	must_constrain = 1;
       if (XSTR (exp, 0) == alternative_name)
 	must_extract = must_constrain = 1;
       else if (strcmp_check (XSTR (exp, 0), length_str) == 0)
@@ -3995,6 +4009,19 @@  walk_attr_value (rtx exp)
       }
 }
 
+/* Write the return type of ATTR's function to OUTF.  */
+
+static void
+write_attr_return_type (FILE *outf, struct attr_desc *attr)
+{
+ if (attr->enum_name)
+    fprintf (outf, "enum %s", attr->enum_name);
+  else if (!attr->is_numeric)
+    fprintf (outf, "enum attr_%s", attr->name);
+  else
+    fprintf (outf, "int");
+}
+
 /* Write out a function to obtain the attribute for a given INSN.  */
 
 static void
@@ -4009,39 +4036,30 @@  write_attr_get (FILE *outf, struct attr_desc *attr)
 
   /* Write out start of function, then all values with explicit `case' lines,
      then a `default', then the value with the most uses.  */
-  if (attr->enum_name)
-    fprintf (outf, "enum %s\n", attr->enum_name);
-  else if (!attr->is_numeric)
-    fprintf (outf, "enum attr_%s\n", attr->name);
+  write_attr_return_type (outf, attr);
+
+  if (attr->type == AT_CONST)
+    gcc_unreachable ();
+  else if (attr->type == AT_CODE_ALT)
+    fprintf (outf, "\nget_attr_%s (int icode,"
+	     " int which_alternative ATTRIBUTE_UNUSED)\n",
+	     attr->name);
+  else if (attr->name[0] == '*')
+    /* If the attribute name starts with a star, the remainder is the name of
+       the subroutine to use, instead of `get_attr_...'.  */
+    fprintf (outf, "\n%s (rtx_insn *insn ATTRIBUTE_UNUSED)\n", &attr->name[1]);
   else
-    fprintf (outf, "int\n");
-
-  /* If the attribute name starts with a star, the remainder is the name of
-     the subroutine to use, instead of `get_attr_...'.  */
-  if (attr->name[0] == '*')
-    fprintf (outf, "%s (rtx_insn *insn ATTRIBUTE_UNUSED)\n", &attr->name[1]);
-  else if (attr->is_const == 0)
-    fprintf (outf, "get_attr_%s (rtx_insn *insn ATTRIBUTE_UNUSED)\n", attr->name);
-  else
-    {
-      fprintf (outf, "get_attr_%s (void)\n", attr->name);
-      fprintf (outf, "{\n");
-
-      for (av = attr->first_value; av; av = av->next)
-	if (av->num_insns == 1)
-	  write_attr_set (outf, attr, 2, av->value, "return", ";",
-			  true_rtx, av->first_insn->def->insn_code,
-			  av->first_insn->def->insn_index, 0);
-	else if (av->num_insns != 0)
-	  write_attr_set (outf, attr, 2, av->value, "return", ";",
-			  true_rtx, -2, 0, 0);
-
-      fprintf (outf, "}\n\n");
-      return;
-    }
+    fprintf (outf, "\nget_attr_%s (rtx_insn *insn ATTRIBUTE_UNUSED)\n",
+	     attr->name);
 
   fprintf (outf, "{\n");
 
+  if (attr->type == AT_CODE_ALT)
+    fprintf (outf, "#define recog_data _Pragma ("
+	     "\"GCC error \\\"recog_data cannot be used"
+	     " in %s attributes\\\"\") recog_data\n",
+	     get_attr_type_name (attr->type));
+
   /* Find attributes that are worth caching in the conditions.  */
   cached_attr_count = 0;
   attrs_seen_more_than_once = 0;
@@ -4059,13 +4077,9 @@  write_attr_get (FILE *outf, struct attr_desc *attr)
 	if (i != j)
 	  cached_attrs[j] = name;
 	cached_attr = find_attr (&name, 0);
-	gcc_assert (cached_attr && cached_attr->is_const == 0);
-	if (cached_attr->enum_name)
-	  fprintf (outf, "  enum %s", cached_attr->enum_name);
-	else if (!cached_attr->is_numeric)
-	  fprintf (outf, "  enum attr_%s", cached_attr->name);
-	else
-	  fprintf (outf, "  int");
+	gcc_assert (cached_attr && cached_attr->type == AT_INSN);
+	fprintf (outf, "  ");
+	write_attr_return_type (outf, cached_attr);
 	fprintf (outf, " cached_%s ATTRIBUTE_UNUSED;\n", name);
 	j++;
       }
@@ -4073,7 +4087,9 @@  write_attr_get (FILE *outf, struct attr_desc *attr)
   if (cached_attr_count)
     fprintf (outf, "\n");
 
-  fprintf (outf, "  switch (recog_memoized (insn))\n");
+  if (attr->type == AT_INSN)
+    fprintf (outf, "  int icode = recog_memoized (insn);\n");
+  fprintf (outf, "  switch (icode)\n");
   fprintf (outf, "    {\n");
 
   for (av = attr->first_value; av; av = av->next)
@@ -4081,8 +4097,22 @@  write_attr_get (FILE *outf, struct attr_desc *attr)
       write_attr_case (outf, attr, av, 1, "return", ";", 4, true_rtx);
 
   write_attr_case (outf, attr, common_av, 0, "return", ";", 4, true_rtx);
-  fprintf (outf, "    }\n}\n\n");
+  fprintf (outf, "    }\n");
+  if (attr->type == AT_CODE_ALT)
+    fprintf (outf, "#undef recog_data\n");
+  fprintf (outf, "}\n\n");
   cached_attr_count = 0;
+  if (attr->type == AT_CODE_ALT)
+    {
+      write_attr_return_type (outf, attr);
+      fprintf (outf, "\nget_attr_%s (rtx_insn *insn)\n", attr->name);
+      fprintf (outf, "{\n");
+      fprintf (outf, "  int which_alternative = "
+	       "extract_constrain_insn_cached (insn);\n");
+      fprintf (outf, "  return get_attr_%s (INSN_CODE (insn),"
+	       " which_alternative);\n", attr->name);
+      fprintf (outf, "}\n\n");
+    }
 }
 
 /* Given an AND tree of known true terms (because we are inside an `if' with
@@ -4254,7 +4284,7 @@  write_attr_case (FILE *outf, struct attr_desc *attr, struct attr_value *av,
   if (av->num_insns == 0)
     return;
 
-  if (av->has_asm_insn)
+  if (attr->type == AT_INSN && av->has_asm_insn)
     {
       write_indent (outf, indent);
       fprintf (outf, "case -1:\n");
@@ -4279,19 +4309,22 @@  write_attr_case (FILE *outf, struct attr_desc *attr, struct attr_value *av,
   walk_attr_value (av->value);
 
   int code_indent = indent + 2;
-  if (must_constrain)
-    {
-      code_indent += 2;
-      write_indent (outf, indent + 2);
-      fprintf (outf, "{\n");
-      write_indent (outf, code_indent);
-      fprintf (outf, "int which_alternative ATTRIBUTE_UNUSED = "
-	       "extract_constrain_insn_cached (insn);\n");
-    }
-  else if (must_extract)
+  if (attr->type == AT_INSN)
     {
-      write_indent (outf, code_indent);
-      fprintf (outf, "extract_insn_cached (insn);\n");
+      if (must_constrain)
+	{
+	  code_indent += 2;
+	  write_indent (outf, indent + 2);
+	  fprintf (outf, "{\n");
+	  write_indent (outf, code_indent);
+	  fprintf (outf, "int which_alternative ATTRIBUTE_UNUSED = "
+		   "extract_constrain_insn_cached (insn);\n");
+	}
+      else if (must_extract)
+	{
+	  write_indent (outf, code_indent);
+	  fprintf (outf, "extract_insn_cached (insn);\n");
+	}
     }
 
   attrs_to_cache = 0;
@@ -4360,15 +4393,17 @@  write_attr_value (FILE *outf, struct attr_desc *attr, rtx value)
     case ATTR:
       {
 	struct attr_desc *attr2 = find_attr (&XSTR (value, 0), 0);
-	if (attr->enum_name)
-	  fprintf (outf, "(enum %s)", attr->enum_name);
-	else if (!attr->is_numeric)
-	  fprintf (outf, "(enum attr_%s)", attr->name);
-	else if (!attr2->is_numeric)
-	  fprintf (outf, "(int)");
-
-	fprintf (outf, "get_attr_%s (%s)", attr2->name,
-		 (attr2->is_const ? "" : "insn"));
+	fprintf (outf, "(");
+	write_attr_return_type (outf, attr);
+	fprintf (outf, ")");
+
+	fprintf (outf, "get_attr_%s", attr2->name);
+	if (attr->type == AT_CONST)
+	  gcc_unreachable ();
+	else if (attr->type == AT_CODE_ALT)
+	  fprintf (outf, " (icode, which_alternative)\n");
+	else
+	  fprintf (outf, " (insn)\n");
       }
       break;
 
@@ -4623,11 +4658,10 @@  find_attr (const char **name_p, int create)
     return NULL;
 
   attr = oballoc (struct attr_desc);
+  memset (attr, 0, sizeof (*attr));
   attr->name = DEF_ATTR_STRING (name);
-  attr->enum_name = 0;
-  attr->first_value = attr->default_val = NULL;
-  attr->is_numeric = attr->is_const = attr->is_special = 0;
   attr->next = attrs[index];
+  attr->type = AT_INSN;
   attrs[index] = attr;
 
   *name_p = attr->name;
@@ -4646,7 +4680,7 @@  make_internal_attr (const char *name, rtx value, int special)
   gcc_assert (!attr->default_val);
 
   attr->is_numeric = 1;
-  attr->is_const = 0;
+  attr->type = AT_INSN;
   attr->is_special = (special & ATTR_SPECIAL) != 0;
   attr->default_val = get_attr_value (file_location ("<internal>", 0),
 				      value, attr, -2);
@@ -4770,6 +4804,7 @@  gen_insn_reserv (md_rtx_info *info)
   memset (&attr, 0, sizeof (attr));
   attr.name = DEF_ATTR_STRING (XSTR (def, 0));
   attr.loc = info->loc;
+  attr.type = AT_INSN;
 
   decl->name            = DEF_ATTR_STRING (XSTR (def, 0));
   decl->default_latency = XINT (def, 1);
@@ -4904,7 +4939,7 @@  find_tune_attr (rtx exp)
       attr = find_attr (&XSTR (exp, 0), 0);
       gcc_assert (attr);
 
-      if (attr->is_const && !attr->is_special)
+      if (attr->is_const () && !attr->is_special)
 	{
 	  struct insn_reserv *decl;
 
@@ -4942,7 +4977,7 @@  make_automaton_attrs (void)
       struct attr_value *val;
       bool first = true;
 
-      gcc_assert (tune_attr->is_const
+      gcc_assert (tune_attr->is_const ()
 		  && !tune_attr->is_special
 		  && !tune_attr->is_numeric);
 
@@ -5335,7 +5370,7 @@  main (int argc, char **argv)
 	  outf = attr_file;
 #undef IS_ATTR_GROUP
 
-	if (! attr->is_special && ! attr->is_const)
+	if (! attr->is_special && !attr->is_const ())
 	  write_attr_get (outf, attr);
       }
 
diff --git a/gcc/gensupport.c b/gcc/gensupport.c
index 484ead2..7f737a2 100644
--- a/gcc/gensupport.c
+++ b/gcc/gensupport.c
@@ -434,10 +434,10 @@  remove_from_queue (struct queue_elem *elem, struct queue_elem **queue)
     *queue = elem->next;
 }
 
-/* Build a define_attr for an binary attribute with name NAME and
-   possible values "yes" and "no", and queue it.  */
+/* Build a define_attr for a binary attribute with name NAME, type TYPE,
+   and possible values "yes" and "no", then queue it.  */
 static void
-add_define_attr (const char *name)
+add_define_attr (const char *name, const char *type)
 {
   struct queue_elem *e = XNEW (struct queue_elem);
   rtx t1 = rtx_alloc (DEFINE_ATTR);
@@ -445,6 +445,7 @@  add_define_attr (const char *name)
   XSTR (t1, 1) = "no,yes";
   XEXP (t1, 2) = rtx_alloc (CONST_STRING);
   XSTR (XEXP (t1, 2), 0) = "yes";
+  XSTR (t1, 3) = type;
   e->data = t1;
   e->loc = file_location ("built-in", -1);
   e->next = define_attr_queue;
@@ -1372,15 +1373,21 @@  alter_attrs_for_insn (rtx insn)
       struct queue_elem *elem;
 
       global_changes_made = true;
-      add_define_attr ("ce_enabled");
-      add_define_attr ("nonce_enabled");
 
       for (elem = define_attr_queue; elem ; elem = elem->next)
 	if (strcmp (XSTR (elem->data, 0), "enabled") == 0)
 	  {
+	    add_define_attr ("ce_enabled", XSTR (elem->data, 3));
+	    add_define_attr ("nonce_enabled", XSTR (elem->data, 3));
 	    XEXP (elem->data, 2)
 	      = modify_attr_enabled_ce (XEXP (elem->data, 2));
+	    break;
 	  }
+      if (!elem)
+	{
+	  add_define_attr ("ce_enabled", "code,alternative");
+	  add_define_attr ("nonce_enabled", "code,alternative");
+	}
     }
   if (enabled_idx == -1)
     return;
@@ -3061,3 +3068,44 @@  needs_barrier_p (rtx x)
 	  && GET_CODE (SET_DEST (x)) == PC
 	  && GET_CODE (SET_SRC (x)) == LABEL_REF);
 }
+
+/* Parse the attribute type for DEFINE_ATTR or DEFINE_ENUM_ATTR definition
+   DEF, which was found at location LOC.  */
+
+attr_type
+parse_attr_type (file_location loc, rtx def)
+{
+  const char *type = XSTR (def, 3);
+  if (GET_CODE (XEXP (def, 2)) == CONST)
+    {
+      if (type[0])
+	error_at (loc, "constant attributes cannot have type `%s'", type);
+      return AT_CONST;
+    }
+
+  if (type[0] == 0 || strcmp (type, "insn") == 0)
+    return AT_INSN;
+
+  if (strcmp (type, "code,alternative") == 0)
+    return AT_CODE_ALT;
+
+  error_at (loc, "unknown attribute type `%s'", type);
+  return AT_INSN;
+}
+
+/* Return an English description of TYPE, for error messages.  */
+
+const char *
+get_attr_type_name (attr_type type)
+{
+  switch (type)
+    {
+    case AT_CONST:
+      return "constant";
+    case AT_CODE_ALT:
+      return "code-and-alternative";
+    case AT_INSN:
+      return "instruction";
+    }
+  gcc_unreachable ();
+}
diff --git a/gcc/gensupport.h b/gcc/gensupport.h
index 0199e39..b330a0d 100644
--- a/gcc/gensupport.h
+++ b/gcc/gensupport.h
@@ -25,6 +25,24 @@  along with GCC; see the file COPYING3.  If not see
 struct obstack;
 extern struct obstack *rtl_obstack;
 
+/* Classifies the type of a DEFINE_ATTR or DEFINE_ENUM_ATTR.
+   The enum is in increasing order of generality, so that attributes
+   with a higher type can refer to attributes with a lower type, but not
+   vice versa.  */
+enum attr_type {
+  /* The attribute depends only on global state; it is not a property
+     of a specific instruction.  These attributes have traditionally
+     been called "constant" and are associated with a (const ...) rtx.  */
+  AT_CONST,
+
+  /* The attribute depends on an instruction code and alternative number.  */
+  AT_CODE_ALT,
+
+  /* The attribute depends on an rtx instruction, and may examine the
+     operands of that instruction.  */
+  AT_INSN
+};
+
 /* Information about an .md define_* rtx.  */
 struct md_rtx_info {
   /* The rtx itself.  */
@@ -135,5 +153,7 @@  extern void compute_test_codes (rtx, file_location, char *);
 extern file_location get_file_location (rtx);
 extern const char *get_emit_function (rtx);
 extern bool needs_barrier_p (rtx);
+attr_type parse_attr_type (file_location, rtx);
+const char *get_attr_type_name (attr_type);
 
 #endif /* GCC_GENSUPPORT_H */
diff --git a/gcc/read-rtl.c b/gcc/read-rtl.c
index 36e42cd..2b5e7a2 100644
--- a/gcc/read-rtl.c
+++ b/gcc/read-rtl.c
@@ -944,6 +944,7 @@  add_define_attr_for_define_subst (const char *attr_name, vec<rtx> *queue)
   XSTR (return_rtx, 0) = xstrdup (attr_name);
   XSTR (return_rtx, 1) = xstrdup ("no,yes");
   XEXP (return_rtx, 2) = const_str;
+  XSTR (return_rtx, 3) = "code,alternative";
 
   queue->safe_push (return_rtx);
 }
diff --git a/gcc/rtl.def b/gcc/rtl.def
index cca469d..5a46829 100644
--- a/gcc/rtl.def
+++ b/gcc/rtl.def
@@ -1246,14 +1246,16 @@  DEF_RTL_EXPR(DEFINE_INSN_RESERVATION, "define_insn_reservation", "sies", RTX_EXT
 /* Definition of an insn attribute.
    1st operand: name of the attribute
    2nd operand: comma-separated list of possible attribute values
-   3rd operand: expression for the default value of the attribute.  */
-DEF_RTL_EXPR(DEFINE_ATTR, "define_attr", "sse", RTX_EXTRA)
+   3rd operand: expression for the default value of the attribute.
+   4th operand: optional type.  */
+DEF_RTL_EXPR(DEFINE_ATTR, "define_attr", "sses", RTX_EXTRA)
 
 /* Definition of an insn attribute that uses an existing enumerated type.
    1st operand: name of the attribute
    2nd operand: the name of the enumerated type
-   3rd operand: expression for the default value of the attribute.  */
-DEF_RTL_EXPR(DEFINE_ENUM_ATTR, "define_enum_attr", "sse", RTX_EXTRA)
+   3rd operand: expression for the default value of the attribute.
+   4th operand: optional type.  */
+DEF_RTL_EXPR(DEFINE_ENUM_ATTR, "define_enum_attr", "sses", RTX_EXTRA)
 
 /* Marker for the name of an attribute.  */
 DEF_RTL_EXPR(ATTR, "attr", "s", RTX_EXTRA)