#include "errors.h"
#include "read-md.h"
#include "gensupport.h"
+#include "vecprim.h"
/* Flags for make_internal_attr's `special' parameter. */
#define ATTR_NONE 0
static struct attr_value *find_most_used (struct attr_desc *);
static void write_attr_set (struct attr_desc *, int, rtx,
const char *, const char *, rtx,
- int, int);
+ int, int, unsigned int);
static void write_attr_case (struct attr_desc *, struct attr_value *,
int, const char *, const char *, int, rtx);
static void write_attr_value (struct attr_desc *, rtx);
delays = delay;
}
-/* Given a piece of RTX, print a C expression to test its truth value.
- We use AND and IOR both for logical and bit-wise operations, so
- interpret them as logical unless they are inside a comparison expression.
- The first bit of FLAGS will be nonzero in that case.
+/* Names of attributes that could be possibly cached. */
+static const char *cached_attrs[32];
+/* Number of such attributes. */
+static int cached_attr_count;
+/* Bitmasks of possibly cached attributes. */
+static unsigned int attrs_seen_once, attrs_seen_more_than_once;
+static unsigned int attrs_to_cache;
+static unsigned int attrs_cached_inside, attrs_cached_after;
- Set the second bit of FLAGS to make references to attribute values use
- a cached local variable instead of calling a function. */
+/* Finds non-const attributes that could be possibly cached.
+ When create is TRUE, fills in cached_attrs array.
+ Computes ATTRS_SEEN_ONCE and ATTRS_SEEN_MORE_THAN_ONCE
+ bitmasks. */
static void
-write_test_expr (rtx exp, int flags)
+find_attrs_to_cache (rtx exp, bool create)
+{
+ int i;
+ const char *name;
+ struct attr_desc *attr;
+
+ if (exp == NULL)
+ return;
+
+ switch (GET_CODE (exp))
+ {
+ case NOT:
+ if (GET_CODE (XEXP (exp, 0)) == EQ_ATTR)
+ find_attrs_to_cache (XEXP (exp, 0), create);
+ return;
+
+ case EQ_ATTR:
+ name = XSTR (exp, 0);
+ if (name == alternative_name)
+ return;
+ for (i = 0; i < cached_attr_count; i++)
+ if (name == cached_attrs[i])
+ {
+ if ((attrs_seen_once & (1U << i)) != 0)
+ attrs_seen_more_than_once |= (1U << i);
+ else
+ attrs_seen_once |= (1U << i);
+ return;
+ }
+ if (!create)
+ return;
+ attr = find_attr (&name, 0);
+ gcc_assert (attr);
+ if (attr->is_const)
+ return;
+ if (cached_attr_count == 32)
+ return;
+ cached_attrs[cached_attr_count] = XSTR (exp, 0);
+ attrs_seen_once |= (1U << cached_attr_count);
+ cached_attr_count++;
+ return;
+
+ case AND:
+ case IOR:
+ find_attrs_to_cache (XEXP (exp, 0), create);
+ find_attrs_to_cache (XEXP (exp, 1), create);
+ return;
+
+ case COND:
+ for (i = 0; i < XVECLEN (exp, 0); i += 2)
+ find_attrs_to_cache (XVECEXP (exp, 0, i), create);
+ return;
+
+ default:
+ return;
+ }
+}
+
+/* Given a piece of RTX, print a C expression to test its truth value.
+ We use AND and IOR both for logical and bit-wise operations, so
+ interpret them as logical unless they are inside a comparison expression. */
+
+/* Interpret AND/IOR as bit-wise operations instead of logical. */
+#define FLG_BITWISE 1
+/* Set if cached attribute will be known initialized in else block after
+ this condition. This is true for LHS of toplevel && and || and
+ even for RHS of ||, but not for RHS of &&. */
+#define FLG_AFTER 2
+/* Set if cached attribute will be known initialized in then block after
+ this condition. This is true for LHS of toplevel && and || and
+ even for RHS of &&, but not for RHS of ||. */
+#define FLG_INSIDE 4
+/* Cleared when an operand of &&. */
+#define FLG_OUTSIDE_AND 8
+
+static unsigned int
+write_test_expr (rtx exp, unsigned int attrs_cached, int flags)
{
int comparison_operator = 0;
RTX_CODE code;
case EQ: case NE:
case GE: case GT:
case LE: case LT:
- comparison_operator = 1;
+ comparison_operator = FLG_BITWISE;
case PLUS: case MINUS: case MULT: case DIV: case MOD:
case AND: case IOR: case XOR:
case ASHIFT: case LSHIFTRT: case ASHIFTRT:
- write_test_expr (XEXP (exp, 0), flags | comparison_operator);
+ if ((code != AND && code != IOR) || (flags & FLG_BITWISE))
+ {
+ flags &= ~(FLG_AFTER | FLG_INSIDE | FLG_OUTSIDE_AND);
+ write_test_expr (XEXP (exp, 0), attrs_cached,
+ flags | comparison_operator);
+ }
+ else
+ {
+ if (code == AND)
+ flags &= ~FLG_OUTSIDE_AND;
+ if (GET_CODE (XEXP (exp, 0)) == code
+ || GET_CODE (XEXP (exp, 0)) == EQ_ATTR
+ || (GET_CODE (XEXP (exp, 0)) == NOT
+ && GET_CODE (XEXP (XEXP (exp, 0), 0)) == EQ_ATTR))
+ attrs_cached
+ = write_test_expr (XEXP (exp, 0), attrs_cached, flags);
+ else
+ write_test_expr (XEXP (exp, 0), attrs_cached, flags);
+ }
switch (code)
{
case EQ:
printf (" %% ");
break;
case AND:
- if (flags & 1)
+ if (flags & FLG_BITWISE)
printf (" & ");
else
printf (" && ");
break;
case IOR:
- if (flags & 1)
+ if (flags & FLG_BITWISE)
printf (" | ");
else
printf (" || ");
gcc_unreachable ();
}
- write_test_expr (XEXP (exp, 1), flags | comparison_operator);
+ if (code == AND)
+ {
+ /* For if (something && (cached_x = get_attr_x (insn)) == X)
+ cached_x is only known to be initialized in then block. */
+ flags &= ~FLG_AFTER;
+ }
+ else if (code == IOR)
+ {
+ if (flags & FLG_OUTSIDE_AND)
+ /* For if (something || (cached_x = get_attr_x (insn)) == X)
+ cached_x is only known to be initialized in else block
+ and else if conditions. */
+ flags &= ~FLG_INSIDE;
+ else
+ /* For if ((something || (cached_x = get_attr_x (insn)) == X)
+ && something_else)
+ cached_x is not know to be initialized anywhere. */
+ flags &= ~(FLG_AFTER | FLG_INSIDE);
+ }
+ if ((code == AND || code == IOR)
+ && (GET_CODE (XEXP (exp, 1)) == code
+ || GET_CODE (XEXP (exp, 1)) == EQ_ATTR
+ || (GET_CODE (XEXP (exp, 1)) == NOT
+ && GET_CODE (XEXP (XEXP (exp, 1), 0)) == EQ_ATTR)))
+ attrs_cached
+ = write_test_expr (XEXP (exp, 1), attrs_cached, flags);
+ else
+ write_test_expr (XEXP (exp, 1), attrs_cached,
+ flags | comparison_operator);
break;
case NOT:
/* Special-case (not (eq_attrq "alternative" "x")) */
- if (! (flags & 1) && GET_CODE (XEXP (exp, 0)) == EQ_ATTR
- && XSTR (XEXP (exp, 0), 0) == alternative_name)
+ if (! (flags & FLG_BITWISE) && GET_CODE (XEXP (exp, 0)) == EQ_ATTR)
{
- printf ("which_alternative != %s", XSTR (XEXP (exp, 0), 1));
+ if (XSTR (XEXP (exp, 0), 0) == alternative_name)
+ {
+ printf ("which_alternative != %s", XSTR (XEXP (exp, 0), 1));
+ break;
+ }
+
+ printf ("! ");
+ attrs_cached = write_test_expr (XEXP (exp, 0), attrs_cached, flags);
break;
}
switch (code)
{
case NOT:
- if (flags & 1)
+ if (flags & FLG_BITWISE)
printf ("~ ");
else
printf ("! ");
gcc_unreachable ();
}
- write_test_expr (XEXP (exp, 0), flags);
+ flags &= ~(FLG_AFTER | FLG_INSIDE | FLG_OUTSIDE_AND);
+ write_test_expr (XEXP (exp, 0), attrs_cached, flags);
break;
case EQ_ATTR_ALT:
{
int set = XINT (exp, 0), bit = 0;
- if (flags & 1)
+ if (flags & FLG_BITWISE)
fatal ("EQ_ATTR_ALT not valid inside comparison");
if (!set)
have been removed by optimization. Handle "alternative"
specially and give error if EQ_ATTR present inside a comparison. */
case EQ_ATTR:
- if (flags & 1)
+ if (flags & FLG_BITWISE)
fatal ("EQ_ATTR not valid inside comparison");
if (XSTR (exp, 0) == alternative_name)
{
write_test_expr (evaluate_eq_attr (exp, attr,
attr->default_val->value, -2, -2),
- flags);
+ attrs_cached, 0);
}
else
{
- if (flags & 2)
- printf ("attr_%s", attr->name);
+ int i;
+ for (i = 0; i < cached_attr_count; i++)
+ if (attr->name == cached_attrs[i])
+ break;
+ if (i < cached_attr_count && (attrs_cached & (1U << i)) != 0)
+ printf ("cached_%s", attr->name);
+ else if (i < cached_attr_count && (attrs_to_cache & (1U << i)) != 0)
+ {
+ printf ("(cached_%s = get_attr_%s (insn))",
+ attr->name, attr->name);
+ if (flags & FLG_AFTER)
+ attrs_cached_after |= (1U << i);
+ if (flags & FLG_INSIDE)
+ attrs_cached_inside |= (1U << i);
+ attrs_cached |= (1U << i);
+ }
else
printf ("get_attr_%s (insn)", attr->name);
printf (" == ");
/* Comparison test of flags for define_delays. */
case ATTR_FLAG:
- if (flags & 1)
+ if (flags & FLG_BITWISE)
fatal ("ATTR_FLAG not valid inside comparison");
printf ("(flags & ATTR_FLAG_%s) != 0", XSTR (exp, 0));
break;
break;
case IF_THEN_ELSE:
- write_test_expr (XEXP (exp, 0), flags & 2);
+ write_test_expr (XEXP (exp, 0), attrs_cached, 0);
printf (" ? ");
- write_test_expr (XEXP (exp, 1), flags | 1);
+ write_test_expr (XEXP (exp, 1), attrs_cached, FLG_BITWISE);
printf (" : ");
- write_test_expr (XEXP (exp, 2), flags | 1);
+ write_test_expr (XEXP (exp, 2), attrs_cached, FLG_BITWISE);
break;
default:
}
printf (")");
+ return attrs_cached;
}
/* Given an attribute value, return the maximum CONST_STRING argument
write_attr_get (struct attr_desc *attr)
{
struct attr_value *av, *common_av;
+ int i, j;
/* Find the most used attribute value. Handle that as the `default' of the
switch we will generate. */
if (av->num_insns == 1)
write_attr_set (attr, 2, av->value, "return", ";",
true_rtx, av->first_insn->def->insn_code,
- av->first_insn->def->insn_index);
+ av->first_insn->def->insn_index, 0);
else if (av->num_insns != 0)
write_attr_set (attr, 2, av->value, "return", ";",
- true_rtx, -2, 0);
+ true_rtx, -2, 0, 0);
printf ("}\n\n");
return;
}
printf ("{\n");
+
+ /* Find attributes that are worth caching in the conditions. */
+ cached_attr_count = 0;
+ attrs_seen_more_than_once = 0;
+ for (av = attr->first_value; av; av = av->next)
+ {
+ attrs_seen_once = 0;
+ find_attrs_to_cache (av->value, true);
+ }
+ /* Remove those that aren't worth caching from the array. */
+ for (i = 0, j = 0; i < cached_attr_count; i++)
+ if ((attrs_seen_more_than_once & (1U << i)) != 0)
+ {
+ const char *name = cached_attrs[i];
+ struct attr_desc *cached_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)
+ printf (" enum %s", cached_attr->enum_name);
+ else if (!cached_attr->is_numeric)
+ printf (" enum attr_%s", cached_attr->name);
+ else
+ printf (" int");
+ printf (" cached_%s ATTRIBUTE_UNUSED;\n", name);
+ j++;
+ }
+ cached_attr_count = j;
+ if (cached_attr_count)
+ printf ("\n");
+
printf (" switch (recog_memoized (insn))\n");
printf (" {\n");
write_attr_case (attr, common_av, 0, "return", ";", 4, true_rtx);
printf (" }\n}\n\n");
+ cached_attr_count = 0;
}
/* Given an AND tree of known true terms (because we are inside an `if' with
static void
write_attr_set (struct attr_desc *attr, int indent, rtx value,
const char *prefix, const char *suffix, rtx known_true,
- int insn_code, int insn_index)
+ int insn_code, int insn_index, unsigned int attrs_cached)
{
if (GET_CODE (value) == COND)
{
int first_if = 1;
int i;
+ if (cached_attr_count)
+ {
+ attrs_seen_once = 0;
+ attrs_seen_more_than_once = 0;
+ for (i = 0; i < XVECLEN (value, 0); i += 2)
+ find_attrs_to_cache (XVECEXP (value, 0, i), false);
+ attrs_to_cache |= attrs_seen_more_than_once;
+ }
+
for (i = 0; i < XVECLEN (value, 0); i += 2)
{
rtx testexp;
if (inner_true == false_rtx)
continue;
+ attrs_cached_inside = attrs_cached;
+ attrs_cached_after = attrs_cached;
write_indent (indent);
printf ("%sif ", first_if ? "" : "else ");
first_if = 0;
- write_test_expr (testexp, 0);
+ write_test_expr (testexp, attrs_cached,
+ (FLG_AFTER | FLG_INSIDE | FLG_OUTSIDE_AND));
+ attrs_cached = attrs_cached_after;
printf ("\n");
write_indent (indent + 2);
printf ("{\n");
write_attr_set (attr, indent + 4,
XVECEXP (value, 0, i + 1), prefix, suffix,
- inner_true, insn_code, insn_index);
+ inner_true, insn_code, insn_index,
+ attrs_cached_inside);
write_indent (indent + 2);
printf ("}\n");
our_known_true = newexp;
}
write_attr_set (attr, first_if ? indent : indent + 4, default_val,
- prefix, suffix, our_known_true, insn_code, insn_index);
+ prefix, suffix, our_known_true, insn_code, insn_index,
+ attrs_cached);
if (! first_if)
{
printf ("extract_insn_cached (insn);\n");
}
+ attrs_to_cache = 0;
if (av->num_insns == 1)
write_attr_set (attr, indent + 2, av->value, prefix, suffix,
known_true, av->first_insn->def->insn_code,
- av->first_insn->def->insn_index);
+ av->first_insn->def->insn_index, 0);
else
write_attr_set (attr, indent + 2, av->value, prefix, suffix,
- known_true, -2, 0);
+ known_true, -2, 0, 0);
if (strncmp (prefix, "return", 6))
{
}
else
printf (" else if (");
- write_test_expr (test, 0);
+ write_test_expr (test, 0, 0);
printf (")\n");
printf (" {\n");
printf (" internal_dfa_insn_code\n");