#include "hconfig.h"
#include "system.h"
#include "rtl.h"
#include "errors.h"
#include "gensupport.h"
#define OUTPUT_LABEL(INDENT_STRING, LABEL_NUMBER) \
printf("%sL%d: ATTRIBUTE_UNUSED_LABEL\n", (INDENT_STRING), (LABEL_NUMBER))
static char **insn_name_ptr = 0;
static int insn_name_ptr_size = 0;
struct decision_head
{
struct decision *first;
struct decision *last;
};
struct decision_test
{
struct decision_test *next;
enum decision_type
{
DT_mode, DT_code, DT_veclen,
DT_elt_zero_int, DT_elt_one_int, DT_elt_zero_wide, DT_elt_zero_wide_safe,
DT_veclen_ge, DT_dup, DT_pred, DT_c_test,
DT_accept_op, DT_accept_insn
} type;
union
{
enum machine_mode mode;
RTX_CODE code;
struct
{
const char *name;
int index;
enum machine_mode mode;
} pred;
const char *c_test;
int veclen;
int dup;
HOST_WIDE_INT intval;
int opno;
struct {
int code_number;
int lineno;
int num_clobbers_to_add;
} insn;
} u;
};
struct decision
{
struct decision_head success;
struct decision *next;
struct decision *prev;
struct decision *afterward;
const char *position;
struct decision_test *tests;
int number;
int subroutine_number;
int need_label;
};
#define SUBROUTINE_THRESHOLD 100
static int next_subroutine_number;
enum routine_type {
RECOG, SPLIT, PEEPHOLE2
};
#define IS_SPLIT(X) ((X) != RECOG)
static int next_number;
static int next_insn_code;
static int next_index;
static int max_depth;
static int pattern_lineno;
static int error_count;
static const struct pred_table
{
const char *const name;
const RTX_CODE codes[NUM_RTX_CODE];
} preds[] = {
{"general_operand", {CONST_INT, CONST_DOUBLE, CONST, SYMBOL_REF,
LABEL_REF, SUBREG, REG, MEM, ADDRESSOF}},
#ifdef PREDICATE_CODES
PREDICATE_CODES
#endif
{"address_operand", {CONST_INT, CONST_DOUBLE, CONST, SYMBOL_REF,
LABEL_REF, SUBREG, REG, MEM, ADDRESSOF,
PLUS, MINUS, MULT}},
{"register_operand", {SUBREG, REG, ADDRESSOF}},
{"pmode_register_operand", {SUBREG, REG, ADDRESSOF}},
{"scratch_operand", {SCRATCH, REG}},
{"immediate_operand", {CONST_INT, CONST_DOUBLE, CONST, SYMBOL_REF,
LABEL_REF}},
{"const_int_operand", {CONST_INT}},
{"const_double_operand", {CONST_INT, CONST_DOUBLE}},
{"nonimmediate_operand", {SUBREG, REG, MEM, ADDRESSOF}},
{"nonmemory_operand", {CONST_INT, CONST_DOUBLE, CONST, SYMBOL_REF,
LABEL_REF, SUBREG, REG, ADDRESSOF}},
{"push_operand", {MEM}},
{"pop_operand", {MEM}},
{"memory_operand", {SUBREG, MEM}},
{"indirect_operand", {SUBREG, MEM}},
{"comparison_operator", {EQ, NE, LE, LT, GE, GT, LEU, LTU, GEU, GTU,
UNORDERED, ORDERED, UNEQ, UNGE, UNGT, UNLE,
UNLT, LTGT}},
{"mode_independent_operand", {CONST_INT, CONST_DOUBLE, CONST, SYMBOL_REF,
LABEL_REF, SUBREG, REG, MEM, ADDRESSOF}}
};
#define NUM_KNOWN_PREDS ARRAY_SIZE (preds)
static const char *const special_mode_pred_table[] = {
#ifdef SPECIAL_MODE_PREDICATES
SPECIAL_MODE_PREDICATES
#endif
"pmode_register_operand"
};
#define NUM_SPECIAL_MODE_PREDS ARRAY_SIZE (special_mode_pred_table)
static struct decision *new_decision
PARAMS ((const char *, struct decision_head *));
static struct decision_test *new_decision_test
PARAMS ((enum decision_type, struct decision_test ***));
static rtx find_operand
PARAMS ((rtx, int));
static rtx find_matching_operand
PARAMS ((rtx, int));
static void validate_pattern
PARAMS ((rtx, rtx, rtx, int));
static struct decision *add_to_sequence
PARAMS ((rtx, struct decision_head *, const char *, enum routine_type, int));
static int maybe_both_true_2
PARAMS ((struct decision_test *, struct decision_test *));
static int maybe_both_true_1
PARAMS ((struct decision_test *, struct decision_test *));
static int maybe_both_true
PARAMS ((struct decision *, struct decision *, int));
static int nodes_identical_1
PARAMS ((struct decision_test *, struct decision_test *));
static int nodes_identical
PARAMS ((struct decision *, struct decision *));
static void merge_accept_insn
PARAMS ((struct decision *, struct decision *));
static void merge_trees
PARAMS ((struct decision_head *, struct decision_head *));
static void factor_tests
PARAMS ((struct decision_head *));
static void simplify_tests
PARAMS ((struct decision_head *));
static int break_out_subroutines
PARAMS ((struct decision_head *, int));
static void find_afterward
PARAMS ((struct decision_head *, struct decision *));
static void change_state
PARAMS ((const char *, const char *, struct decision *, const char *));
static void print_code
PARAMS ((enum rtx_code));
static void write_afterward
PARAMS ((struct decision *, struct decision *, const char *));
static struct decision *write_switch
PARAMS ((struct decision *, int));
static void write_cond
PARAMS ((struct decision_test *, int, enum routine_type));
static void write_action
PARAMS ((struct decision *, struct decision_test *, int, int,
struct decision *, enum routine_type));
static int is_unconditional
PARAMS ((struct decision_test *, enum routine_type));
static int write_node
PARAMS ((struct decision *, int, enum routine_type));
static void write_tree_1
PARAMS ((struct decision_head *, int, enum routine_type));
static void write_tree
PARAMS ((struct decision_head *, const char *, enum routine_type, int));
static void write_subroutine
PARAMS ((struct decision_head *, enum routine_type));
static void write_subroutines
PARAMS ((struct decision_head *, enum routine_type));
static void write_header
PARAMS ((void));
static struct decision_head make_insn_sequence
PARAMS ((rtx, enum routine_type));
static void process_tree
PARAMS ((struct decision_head *, enum routine_type));
static void record_insn_name
PARAMS ((int, const char *));
static void debug_decision_0
PARAMS ((struct decision *, int, int));
static void debug_decision_1
PARAMS ((struct decision *, int));
static void debug_decision_2
PARAMS ((struct decision_test *));
extern void debug_decision
PARAMS ((struct decision *));
extern void debug_decision_list
PARAMS ((struct decision *));
static struct decision *
new_decision (position, last)
const char *position;
struct decision_head *last;
{
struct decision *new
= (struct decision *) xmalloc (sizeof (struct decision));
memset (new, 0, sizeof (*new));
new->success = *last;
new->position = xstrdup (position);
new->number = next_number++;
last->first = last->last = new;
return new;
}
static struct decision_test *
new_decision_test (type, pplace)
enum decision_type type;
struct decision_test ***pplace;
{
struct decision_test **place = *pplace;
struct decision_test *test;
test = (struct decision_test *) xmalloc (sizeof (*test));
test->next = *place;
test->type = type;
*place = test;
place = &test->next;
*pplace = place;
return test;
}
static rtx
find_operand (pattern, n)
rtx pattern;
int n;
{
const char *fmt;
RTX_CODE code;
int i, j, len;
rtx r;
code = GET_CODE (pattern);
if ((code == MATCH_SCRATCH
|| code == MATCH_INSN
|| code == MATCH_OPERAND
|| code == MATCH_OPERATOR
|| code == MATCH_PARALLEL)
&& XINT (pattern, 0) == n)
return pattern;
fmt = GET_RTX_FORMAT (code);
len = GET_RTX_LENGTH (code);
for (i = 0; i < len; i++)
{
switch (fmt[i])
{
case 'e': case 'u':
if ((r = find_operand (XEXP (pattern, i), n)) != NULL_RTX)
return r;
break;
case 'V':
if (! XVEC (pattern, i))
break;
case 'E':
for (j = 0; j < XVECLEN (pattern, i); j++)
if ((r = find_operand (XVECEXP (pattern, i, j), n)) != NULL_RTX)
return r;
break;
case 'i': case 'w': case '0': case 's':
break;
default:
abort ();
}
}
return NULL;
}
static rtx
find_matching_operand (pattern, n)
rtx pattern;
int n;
{
const char *fmt;
RTX_CODE code;
int i, j, len;
rtx r;
code = GET_CODE (pattern);
if (code == MATCH_OPERAND
&& (XSTR (pattern, 2)[0] == '0' + n
|| (XSTR (pattern, 2)[0] == '%'
&& XSTR (pattern, 2)[1] == '0' + n)))
return pattern;
fmt = GET_RTX_FORMAT (code);
len = GET_RTX_LENGTH (code);
for (i = 0; i < len; i++)
{
switch (fmt[i])
{
case 'e': case 'u':
if ((r = find_matching_operand (XEXP (pattern, i), n)))
return r;
break;
case 'V':
if (! XVEC (pattern, i))
break;
case 'E':
for (j = 0; j < XVECLEN (pattern, i); j++)
if ((r = find_matching_operand (XVECEXP (pattern, i, j), n)))
return r;
break;
case 'i': case 'w': case '0': case 's':
break;
default:
abort ();
}
}
return NULL;
}
static void
validate_pattern (pattern, insn, set, set_code)
rtx pattern;
rtx insn;
rtx set;
int set_code;
{
const char *fmt;
RTX_CODE code;
size_t i, len;
int j;
code = GET_CODE (pattern);
switch (code)
{
case MATCH_SCRATCH:
return;
case MATCH_INSN:
case MATCH_OPERAND:
case MATCH_OPERATOR:
{
const char *pred_name = XSTR (pattern, 1);
int allows_non_lvalue = 1, allows_non_const = 1;
int special_mode_pred = 0;
const char *c_test;
if (GET_CODE (insn) == DEFINE_INSN)
c_test = XSTR (insn, 2);
else
c_test = XSTR (insn, 1);
if (pred_name[0] != 0)
{
for (i = 0; i < NUM_KNOWN_PREDS; i++)
if (! strcmp (preds[i].name, pred_name))
break;
if (i < NUM_KNOWN_PREDS)
{
int j;
allows_non_lvalue = allows_non_const = 0;
for (j = 0; preds[i].codes[j] != 0; j++)
{
RTX_CODE c = preds[i].codes[j];
if (c != LABEL_REF
&& c != SYMBOL_REF
&& c != CONST_INT
&& c != CONST_DOUBLE
&& c != CONST
&& c != HIGH
&& c != CONSTANT_P_RTX)
allows_non_const = 1;
if (c != REG
&& c != SUBREG
&& c != MEM
&& c != ADDRESSOF
&& c != CONCAT
&& c != PARALLEL
&& c != STRICT_LOW_PART)
allows_non_lvalue = 1;
}
}
else
{
#ifdef PREDICATE_CODES
message_with_line (pattern_lineno,
"warning: `%s' not in PREDICATE_CODES",
pred_name);
#endif
}
for (i = 0; i < NUM_SPECIAL_MODE_PREDS; ++i)
if (strcmp (pred_name, special_mode_pred_table[i]) == 0)
{
special_mode_pred = 1;
break;
}
}
if (code == MATCH_OPERAND)
{
const char constraints0 = XSTR (pattern, 2)[0];
if (GET_CODE (insn) == DEFINE_EXPAND
|| GET_CODE (insn) == DEFINE_SPLIT
|| GET_CODE (insn) == DEFINE_PEEPHOLE2)
{
if (constraints0)
message_with_line (pattern_lineno,
"warning: constraints not supported in %s",
rtx_name[GET_CODE (insn)]);
}
else if (set && constraints0)
{
if (set_code == '+')
{
if (constraints0 == '+')
;
else if (constraints0 == '='
&& find_matching_operand (insn, XINT (pattern, 0)))
;
else
{
message_with_line (pattern_lineno,
"operand %d missing in-out reload",
XINT (pattern, 0));
error_count++;
}
}
else if (constraints0 != '=' && constraints0 != '+')
{
message_with_line (pattern_lineno,
"operand %d missing output reload",
XINT (pattern, 0));
error_count++;
}
}
}
if (set
&& pred_name[0] != '\0'
&& allows_non_lvalue)
{
message_with_line (pattern_lineno,
"warning: destination operand %d allows non-lvalue",
XINT (pattern, 0));
}
if (GET_MODE (pattern) == VOIDmode
&& code == MATCH_OPERAND
&& GET_CODE (insn) == DEFINE_INSN
&& allows_non_const
&& ! special_mode_pred
&& pred_name[0] != '\0'
&& strcmp (pred_name, "address_operand") != 0
&& strstr (c_test, "operands") == NULL
&& ! (set
&& GET_CODE (set) == SET
&& GET_CODE (SET_SRC (set)) == CALL))
{
message_with_line (pattern_lineno,
"warning: operand %d missing mode?",
XINT (pattern, 0));
}
return;
}
case SET:
{
enum machine_mode dmode, smode;
rtx dest, src;
dest = SET_DEST (pattern);
src = SET_SRC (pattern);
if (GET_CODE (dest) == STRICT_LOW_PART)
dest = XEXP (dest, 0);
if (GET_CODE (dest) == MATCH_DUP
|| GET_CODE (dest) == MATCH_OP_DUP
|| GET_CODE (dest) == MATCH_PAR_DUP)
dest = find_operand (insn, XINT (dest, 0));
if (GET_CODE (src) == MATCH_DUP
|| GET_CODE (src) == MATCH_OP_DUP
|| GET_CODE (src) == MATCH_PAR_DUP)
src = find_operand (insn, XINT (src, 0));
dmode = GET_MODE (dest);
smode = GET_MODE (src);
if (GET_CODE (src) == MATCH_OPERAND
&& ! strcmp (XSTR (src, 1), "address_operand"))
;
else if (dmode != VOIDmode && smode != VOIDmode && dmode != smode)
{
message_with_line (pattern_lineno,
"mode mismatch in set: %smode vs %smode",
GET_MODE_NAME (dmode), GET_MODE_NAME (smode));
error_count++;
}
else if (dmode != smode
&& GET_CODE (dest) != PC
&& GET_CODE (dest) != CC0
&& GET_CODE (src) != PC
&& GET_CODE (src) != CC0
&& GET_CODE (src) != CONST_INT)
{
const char *which;
which = (dmode == VOIDmode ? "destination" : "source");
message_with_line (pattern_lineno,
"warning: %s missing a mode?", which);
}
if (dest != SET_DEST (pattern))
validate_pattern (dest, insn, pattern, '=');
validate_pattern (SET_DEST (pattern), insn, pattern, '=');
validate_pattern (SET_SRC (pattern), insn, NULL_RTX, 0);
return;
}
case CLOBBER:
validate_pattern (SET_DEST (pattern), insn, pattern, '=');
return;
case ZERO_EXTRACT:
validate_pattern (XEXP (pattern, 0), insn, set, set ? '+' : 0);
validate_pattern (XEXP (pattern, 1), insn, NULL_RTX, 0);
validate_pattern (XEXP (pattern, 2), insn, NULL_RTX, 0);
return;
case STRICT_LOW_PART:
validate_pattern (XEXP (pattern, 0), insn, set, set ? '+' : 0);
return;
case LABEL_REF:
if (GET_MODE (XEXP (pattern, 0)) != VOIDmode)
{
message_with_line (pattern_lineno,
"operand to label_ref %smode not VOIDmode",
GET_MODE_NAME (GET_MODE (XEXP (pattern, 0))));
error_count++;
}
break;
default:
break;
}
fmt = GET_RTX_FORMAT (code);
len = GET_RTX_LENGTH (code);
for (i = 0; i < len; i++)
{
switch (fmt[i])
{
case 'e': case 'u':
validate_pattern (XEXP (pattern, i), insn, NULL_RTX, 0);
break;
case 'E':
for (j = 0; j < XVECLEN (pattern, i); j++)
validate_pattern (XVECEXP (pattern, i, j), insn, NULL_RTX, 0);
break;
case 'i': case 'w': case '0': case 's':
break;
default:
abort ();
}
}
}
static struct decision *
add_to_sequence (pattern, last, position, insn_type, top)
rtx pattern;
struct decision_head *last;
const char *position;
enum routine_type insn_type;
int top;
{
RTX_CODE code;
struct decision *this, *sub;
struct decision_test *test;
struct decision_test **place;
char *subpos;
size_t i;
const char *fmt;
int depth = strlen (position);
int len;
enum machine_mode mode;
if (depth > max_depth)
max_depth = depth;
subpos = (char *) xmalloc (depth + 2);
strcpy (subpos, position);
subpos[depth + 1] = 0;
sub = this = new_decision (position, last);
place = &this->tests;
restart:
mode = GET_MODE (pattern);
code = GET_CODE (pattern);
switch (code)
{
case PARALLEL:
if (insn_type == PEEPHOLE2 && top)
{
last->first = last->last = NULL;
for (i = 0; i < (size_t) XVECLEN (pattern, 0); i++)
{
subpos[depth] = (i > 0 ? 'A' + i : 0);
sub = add_to_sequence (XVECEXP (pattern, 0, i),
last, subpos, insn_type, 0);
last = &sub->success;
}
goto ret;
}
break;
case MATCH_PARALLEL:
test = new_decision_test (DT_veclen_ge, &place);
test->u.veclen = XVECLEN (pattern, 2);
case MATCH_OPERAND:
case MATCH_SCRATCH:
case MATCH_OPERATOR:
case MATCH_INSN:
{
const char *pred_name;
RTX_CODE was_code = code;
int allows_const_int = 1;
if (code == MATCH_SCRATCH)
{
pred_name = "scratch_operand";
code = UNKNOWN;
}
else
{
pred_name = XSTR (pattern, 1);
if (code == MATCH_PARALLEL)
code = PARALLEL;
else
code = UNKNOWN;
}
if (pred_name[0] != 0)
{
test = new_decision_test (DT_pred, &place);
test->u.pred.name = pred_name;
test->u.pred.mode = mode;
for (i = 0; i < NUM_KNOWN_PREDS; i++)
if (! strcmp (preds[i].name, pred_name))
break;
if (i < NUM_KNOWN_PREDS)
{
int j;
test->u.pred.index = i;
if (preds[i].codes[1] == 0 && code == UNKNOWN)
code = preds[i].codes[0];
allows_const_int = 0;
for (j = 0; preds[i].codes[j] != 0; j++)
if (preds[i].codes[j] == CONST_INT)
{
allows_const_int = 1;
break;
}
}
else
test->u.pred.index = -1;
}
if (allows_const_int)
mode = VOIDmode;
test = new_decision_test (DT_accept_op, &place);
test->u.opno = XINT (pattern, 0);
if (was_code == MATCH_OPERATOR || was_code == MATCH_PARALLEL)
{
char base = (was_code == MATCH_OPERATOR ? '0' : 'a');
for (i = 0; i < (size_t) XVECLEN (pattern, 2); i++)
{
subpos[depth] = i + base;
sub = add_to_sequence (XVECEXP (pattern, 2, i),
&sub->success, subpos, insn_type, 0);
}
}
goto fini;
}
case MATCH_OP_DUP:
code = UNKNOWN;
test = new_decision_test (DT_dup, &place);
test->u.dup = XINT (pattern, 0);
test = new_decision_test (DT_accept_op, &place);
test->u.opno = XINT (pattern, 0);
for (i = 0; i < (size_t) XVECLEN (pattern, 1); i++)
{
subpos[depth] = i + '0';
sub = add_to_sequence (XVECEXP (pattern, 1, i),
&sub->success, subpos, insn_type, 0);
}
goto fini;
case MATCH_DUP:
case MATCH_PAR_DUP:
code = UNKNOWN;
test = new_decision_test (DT_dup, &place);
test->u.dup = XINT (pattern, 0);
goto fini;
case ADDRESS:
pattern = XEXP (pattern, 0);
goto restart;
default:
break;
}
fmt = GET_RTX_FORMAT (code);
len = GET_RTX_LENGTH (code);
for (i = 0; i < (size_t) len; i++)
{
if (fmt[i] == 'i')
{
if (i == 0)
{
test = new_decision_test (DT_elt_zero_int, &place);
test->u.intval = XINT (pattern, i);
}
else if (i == 1)
{
test = new_decision_test (DT_elt_one_int, &place);
test->u.intval = XINT (pattern, i);
}
else
abort ();
}
else if (fmt[i] == 'w')
{
enum decision_type type
= ((int) XWINT (pattern, i) == XWINT (pattern, i))
? DT_elt_zero_wide_safe : DT_elt_zero_wide;
if (i != 0)
abort ();
test = new_decision_test (type, &place);
test->u.intval = XWINT (pattern, i);
}
else if (fmt[i] == 'E')
{
if (i != 0)
abort ();
test = new_decision_test (DT_veclen, &place);
test->u.veclen = XVECLEN (pattern, i);
}
}
for (i = 0; i < (size_t) len; i++)
{
switch (fmt[i])
{
case 'e': case 'u':
subpos[depth] = '0' + i;
sub = add_to_sequence (XEXP (pattern, i), &sub->success,
subpos, insn_type, 0);
break;
case 'E':
{
int j;
for (j = 0; j < XVECLEN (pattern, i); j++)
{
subpos[depth] = 'a' + j;
sub = add_to_sequence (XVECEXP (pattern, i, j),
&sub->success, subpos, insn_type, 0);
}
break;
}
case 'i': case 'w':
break;
case '0':
break;
default:
abort ();
}
}
fini:
if (code != UNKNOWN)
{
place = &this->tests;
test = new_decision_test (DT_code, &place);
test->u.code = code;
}
if (mode != VOIDmode)
{
place = &this->tests;
test = new_decision_test (DT_mode, &place);
test->u.mode = mode;
}
if (this->tests == NULL)
abort ();
ret:
free (subpos);
return sub;
}
static int
maybe_both_true_2 (d1, d2)
struct decision_test *d1, *d2;
{
if (d1->type == d2->type)
{
switch (d1->type)
{
case DT_mode:
return d1->u.mode == d2->u.mode;
case DT_code:
return d1->u.code == d2->u.code;
case DT_veclen:
return d1->u.veclen == d2->u.veclen;
case DT_elt_zero_int:
case DT_elt_one_int:
case DT_elt_zero_wide:
case DT_elt_zero_wide_safe:
return d1->u.intval == d2->u.intval;
default:
break;
}
}
if (d1->type == DT_pred || d2->type == DT_pred)
{
if (d2->type == DT_pred)
{
struct decision_test *tmp;
tmp = d1, d1 = d2, d2 = tmp;
}
if (d1->u.pred.mode != VOIDmode)
{
if (d2->type == DT_mode)
{
if (d1->u.pred.mode != d2->u.mode
&& strcmp (d1->u.pred.name, "address_operand") != 0)
return 0;
}
}
if (d1->u.pred.index >= 0)
{
if (d2->type == DT_code)
{
const RTX_CODE *c = &preds[d1->u.pred.index].codes[0];
while (*c != 0)
{
if (*c == d2->u.code)
break;
++c;
}
if (*c == 0)
return 0;
}
else if (d2->type == DT_pred && d2->u.pred.index >= 0)
{
const RTX_CODE *c1 = &preds[d1->u.pred.index].codes[0];
int common = 0;
while (*c1 != 0 && !common)
{
const RTX_CODE *c2 = &preds[d2->u.pred.index].codes[0];
while (*c2 != 0 && !common)
{
common = (*c1 == *c2);
++c2;
}
++c1;
}
if (!common)
return 0;
}
}
}
if (d1->type == DT_veclen && d2->type == DT_veclen_ge)
return d1->u.veclen >= d2->u.veclen;
if (d1->type == DT_veclen_ge && d2->type == DT_veclen)
return d2->u.veclen >= d1->u.veclen;
return -1;
}
static int
maybe_both_true_1 (d1, d2)
struct decision_test *d1, *d2;
{
struct decision_test *t1, *t2;
if (d1->type == DT_accept_op || d2->type == DT_accept_op)
return 1;
while (d1 && d2 && d1->type == d2->type)
{
if (maybe_both_true_2 (d1, d2) == 0)
return 0;
d1 = d1->next, d2 = d2->next;
}
for (t1 = d1; t1 ; t1 = t1->next)
for (t2 = d2; t2 ; t2 = t2->next)
if (maybe_both_true_2 (t1, t2) == 0)
return 0;
return -1;
}
static int
maybe_both_true (d1, d2, toplevel)
struct decision *d1, *d2;
int toplevel;
{
struct decision *p1, *p2;
int cmp;
cmp = strcmp (d1->position, d2->position);
if (cmp != 0)
{
if (toplevel)
abort ();
if (cmp > 0)
p1 = d1, d1 = d2, d2 = p1;
if (d1->success.first == 0)
return 1;
for (p1 = d1->success.first; p1; p1 = p1->next)
if (maybe_both_true (p1, d2, 0))
return 1;
return 0;
}
cmp = maybe_both_true_1 (d1->tests, d2->tests);
if (cmp >= 0)
return cmp;
if (toplevel || d1->success.first == 0 || d2->success.first == 0)
return 1;
for (p1 = d1->success.first; p1; p1 = p1->next)
for (p2 = d2->success.first; p2; p2 = p2->next)
if (maybe_both_true (p1, p2, 0))
return 1;
return 0;
}
static int
nodes_identical_1 (d1, d2)
struct decision_test *d1, *d2;
{
switch (d1->type)
{
case DT_mode:
return d1->u.mode == d2->u.mode;
case DT_code:
return d1->u.code == d2->u.code;
case DT_pred:
return (d1->u.pred.mode == d2->u.pred.mode
&& strcmp (d1->u.pred.name, d2->u.pred.name) == 0);
case DT_c_test:
return strcmp (d1->u.c_test, d2->u.c_test) == 0;
case DT_veclen:
case DT_veclen_ge:
return d1->u.veclen == d2->u.veclen;
case DT_dup:
return d1->u.dup == d2->u.dup;
case DT_elt_zero_int:
case DT_elt_one_int:
case DT_elt_zero_wide:
case DT_elt_zero_wide_safe:
return d1->u.intval == d2->u.intval;
case DT_accept_op:
return d1->u.opno == d2->u.opno;
case DT_accept_insn:
return 1;
default:
abort ();
}
}
static int
nodes_identical (d1, d2)
struct decision *d1, *d2;
{
struct decision_test *t1, *t2;
for (t1 = d1->tests, t2 = d2->tests; t1 && t2; t1 = t1->next, t2 = t2->next)
{
if (t1->type != t2->type)
return 0;
if (! nodes_identical_1 (t1, t2))
return 0;
}
if (t1 != t2)
return 0;
if (d1->success.first
&& d2->success.first
&& strcmp (d1->success.first->position, d2->success.first->position))
return 0;
return 1;
}
static void
merge_accept_insn (oldd, addd)
struct decision *oldd, *addd;
{
struct decision_test *old, *add;
for (old = oldd->tests; old; old = old->next)
if (old->type == DT_accept_insn)
break;
if (old == NULL)
return;
for (add = addd->tests; add; add = add->next)
if (add->type == DT_accept_insn)
break;
if (add == NULL)
return;
if (old->u.insn.num_clobbers_to_add == 0
&& add->u.insn.num_clobbers_to_add > 0)
{
}
else if (old->u.insn.num_clobbers_to_add > 0
&& add->u.insn.num_clobbers_to_add == 0)
{
old->u.insn = add->u.insn;
}
else
{
message_with_line (add->u.insn.lineno, "`%s' matches `%s'",
get_insn_name (add->u.insn.code_number),
get_insn_name (old->u.insn.code_number));
message_with_line (old->u.insn.lineno, "previous definition of `%s'",
get_insn_name (old->u.insn.code_number));
error_count++;
}
}
static void
merge_trees (oldh, addh)
struct decision_head *oldh, *addh;
{
struct decision *next, *add;
if (addh->first == 0)
return;
if (oldh->first == 0)
{
*oldh = *addh;
return;
}
if (strcmp (oldh->first->position, addh->first->position))
abort ();
for (add = addh->first; add ; add = next)
{
struct decision *old, *insert_before = NULL;
next = add->next;
for (old = oldh->last; old; old = old->prev)
{
if (nodes_identical (old, add))
{
merge_accept_insn (old, add);
merge_trees (&old->success, &add->success);
goto merged_nodes;
}
if (maybe_both_true (old, add, 0))
break;
if ((int) add->tests->type < (int) old->tests->type)
insert_before = old;
}
if (insert_before == NULL)
{
add->next = NULL;
add->prev = oldh->last;
oldh->last->next = add;
oldh->last = add;
}
else
{
if ((add->prev = insert_before->prev) != NULL)
add->prev->next = add;
else
oldh->first = add;
add->next = insert_before;
insert_before->prev = add;
}
merged_nodes:;
}
}
static void
factor_tests (head)
struct decision_head *head;
{
struct decision *first, *next;
for (first = head->first; first && first->next; first = next)
{
enum decision_type type;
struct decision *new, *old_last;
type = first->tests->type;
next = first->next;
if (next->tests->type != type)
continue;
if (type != DT_mode
&& type != DT_code
&& type != DT_veclen
&& type != DT_elt_zero_int
&& type != DT_elt_one_int
&& type != DT_elt_zero_wide_safe)
continue;
if (first->tests->next != NULL)
{
new = new_decision (first->position, &first->success);
new->tests = first->tests->next;
first->tests->next = NULL;
}
first->next = NULL;
old_last = head->last;
head->last = first;
do
{
struct decision_head h;
if (next->tests->next != NULL)
{
new = new_decision (next->position, &next->success);
new->tests = next->tests->next;
next->tests->next = NULL;
}
new = next;
next = next->next;
new->next = NULL;
h.first = h.last = new;
merge_trees (head, &h);
}
while (next && next->tests->type == type);
if (next)
{
next->prev = head->last;
head->last->next = next;
head->last = old_last;
}
}
for (first = head->first; first; first = first->next)
factor_tests (&first->success);
}
static void
simplify_tests (head)
struct decision_head *head;
{
struct decision *tree;
for (tree = head->first; tree; tree = tree->next)
{
struct decision_test *a, *b;
a = tree->tests;
b = a->next;
if (b == NULL)
continue;
while (b && b->type != DT_pred)
b = b->next;
if (b)
{
while (a->type == DT_mode || a->type == DT_code)
a = a->next;
tree->tests = a;
}
}
for (tree = head->first; tree; tree = tree->next)
simplify_tests (&tree->success);
}
static int
break_out_subroutines (head, initial)
struct decision_head *head;
int initial;
{
int size = 0;
struct decision *sub;
for (sub = head->first; sub; sub = sub->next)
size += 1 + break_out_subroutines (&sub->success, 0);
if (size > SUBROUTINE_THRESHOLD && ! initial)
{
head->first->subroutine_number = ++next_subroutine_number;
size = 1;
}
return size;
}
static void
find_afterward (head, real_afterward)
struct decision_head *head;
struct decision *real_afterward;
{
struct decision *p, *q, *afterward;
p = head->first;
afterward = (p->subroutine_number > 0 ? NULL : real_afterward);
for ( ; p ; p = p->next)
{
for (q = p->next; q ; q = q->next)
if (maybe_both_true (p, q, 1))
break;
if (!q)
q = afterward;
p->afterward = q;
if (q)
q->need_label = 1;
}
for (p = head->first; p ; p = p->next)
if (p->success.first)
find_afterward (&p->success, p->afterward);
p = head->first;
if (p->subroutine_number > 0)
p->afterward = real_afterward;
}
static void
change_state (oldpos, newpos, afterward, indent)
const char *oldpos;
const char *newpos;
struct decision *afterward;
const char *indent;
{
int odepth = strlen (oldpos);
int ndepth = strlen (newpos);
int depth;
int old_has_insn, new_has_insn;
for (depth = odepth; strncmp (oldpos, newpos, depth) != 0; --depth)
continue;
for (old_has_insn = odepth - 1; old_has_insn >= 0; --old_has_insn)
if (ISUPPER (oldpos[old_has_insn]))
break;
for (new_has_insn = ndepth - 1; new_has_insn >= 0; --new_has_insn)
if (ISUPPER (newpos[new_has_insn]))
break;
while (depth < ndepth)
{
if (ISUPPER (newpos[depth]))
{
if (old_has_insn >= 0 && oldpos[old_has_insn] >= newpos[depth])
{
printf ("%stem = peep2_next_insn (%d);\n",
indent, newpos[depth] - 'A');
}
else
{
printf ("%stem = peep2_next_insn (%d);\n",
indent, newpos[depth] - 'A');
printf ("%sif (tem == NULL_RTX)\n", indent);
if (afterward)
printf ("%s goto L%d;\n", indent, afterward->number);
else
printf ("%s goto ret0;\n", indent);
}
printf ("%sx%d = PATTERN (tem);\n", indent, depth + 1);
}
else if (ISLOWER (newpos[depth]))
printf ("%sx%d = XVECEXP (x%d, 0, %d);\n",
indent, depth + 1, depth, newpos[depth] - 'a');
else
printf ("%sx%d = XEXP (x%d, %c);\n",
indent, depth + 1, depth, newpos[depth]);
++depth;
}
}
static void
print_code (code)
enum rtx_code code;
{
const char *p;
for (p = GET_RTX_NAME (code); *p; p++)
putchar (TOUPPER (*p));
}
static void
write_afterward (start, afterward, indent)
struct decision *start;
struct decision *afterward;
const char *indent;
{
if (!afterward || start->subroutine_number > 0)
printf("%sgoto ret0;\n", indent);
else
{
change_state (start->position, afterward->position, NULL, indent);
printf ("%sgoto L%d;\n", indent, afterward->number);
}
}
static struct decision *
write_switch (start, depth)
struct decision *start;
int depth;
{
struct decision *p = start;
enum decision_type type = p->tests->type;
struct decision *needs_label = NULL;
if (!p->next
|| p->tests->next
|| p->next->tests->type != type
|| p->next->tests->next
|| nodes_identical_1 (p->tests, p->next->tests))
return p;
if (type == DT_code)
{
char codemap[NUM_RTX_CODE];
struct decision *ret;
RTX_CODE code;
memset (codemap, 0, sizeof(codemap));
printf (" switch (GET_CODE (x%d))\n {\n", depth);
code = p->tests->u.code;
do
{
if (p != start && p->need_label && needs_label == NULL)
needs_label = p;
printf (" case ");
print_code (code);
printf (":\n goto L%d;\n", p->success.first->number);
p->success.first->need_label = 1;
codemap[code] = 1;
p = p->next;
}
while (p
&& ! p->tests->next
&& p->tests->type == DT_code
&& ! codemap[code = p->tests->u.code]);
if (needs_label != NULL)
ret = needs_label;
else
ret = p;
while (p && p->tests->type == DT_pred
&& p->tests->u.pred.index >= 0)
{
const RTX_CODE *c;
for (c = &preds[p->tests->u.pred.index].codes[0]; *c ; ++c)
if (codemap[(int) *c] != 0)
goto pred_done;
for (c = &preds[p->tests->u.pred.index].codes[0]; *c ; ++c)
{
printf (" case ");
print_code (*c);
printf (":\n");
codemap[(int) *c] = 1;
}
printf (" goto L%d;\n", p->number);
p->need_label = 1;
p = p->next;
}
pred_done:
printf (" default:\n");
if (p != ret)
{
if (p)
{
printf (" goto L%d;\n", p->number);
p->need_label = 1;
}
else
write_afterward (start, start->afterward, " ");
}
else
printf (" break;\n");
printf (" }\n");
return ret;
}
else if (type == DT_mode
|| type == DT_veclen
|| type == DT_elt_zero_int
|| type == DT_elt_one_int
|| type == DT_elt_zero_wide_safe)
{
const char *indent = "";
if (type == DT_elt_zero_wide_safe)
{
indent = " ";
printf(" if ((int) XWINT (x%d, 0) == XWINT (x%d, 0))\n", depth, depth);
}
printf ("%s switch (", indent);
switch (type)
{
case DT_mode:
printf ("GET_MODE (x%d)", depth);
break;
case DT_veclen:
printf ("XVECLEN (x%d, 0)", depth);
break;
case DT_elt_zero_int:
printf ("XINT (x%d, 0)", depth);
break;
case DT_elt_one_int:
printf ("XINT (x%d, 1)", depth);
break;
case DT_elt_zero_wide_safe:
printf ("(int) XWINT (x%d, 0)", depth);
break;
default:
abort ();
}
printf (")\n%s {\n", indent);
do
{
struct decision *q;
for (q = start; q != p; q = q->next)
if (nodes_identical_1 (p->tests, q->tests))
goto case_done;
if (p != start && p->need_label && needs_label == NULL)
needs_label = p;
printf ("%s case ", indent);
switch (type)
{
case DT_mode:
printf ("%smode", GET_MODE_NAME (p->tests->u.mode));
break;
case DT_veclen:
printf ("%d", p->tests->u.veclen);
break;
case DT_elt_zero_int:
case DT_elt_one_int:
case DT_elt_zero_wide:
case DT_elt_zero_wide_safe:
printf (HOST_WIDE_INT_PRINT_DEC_C, p->tests->u.intval);
break;
default:
abort ();
}
printf (":\n%s goto L%d;\n", indent, p->success.first->number);
p->success.first->need_label = 1;
p = p->next;
}
while (p && p->tests->type == type && !p->tests->next);
case_done:
printf ("%s default:\n%s break;\n%s }\n",
indent, indent, indent);
return needs_label != NULL ? needs_label : p;
}
else
{
return p;
}
}
static void
write_cond (p, depth, subroutine_type)
struct decision_test *p;
int depth;
enum routine_type subroutine_type;
{
switch (p->type)
{
case DT_mode:
printf ("GET_MODE (x%d) == %smode", depth, GET_MODE_NAME (p->u.mode));
break;
case DT_code:
printf ("GET_CODE (x%d) == ", depth);
print_code (p->u.code);
break;
case DT_veclen:
printf ("XVECLEN (x%d, 0) == %d", depth, p->u.veclen);
break;
case DT_elt_zero_int:
printf ("XINT (x%d, 0) == %d", depth, (int) p->u.intval);
break;
case DT_elt_one_int:
printf ("XINT (x%d, 1) == %d", depth, (int) p->u.intval);
break;
case DT_elt_zero_wide:
case DT_elt_zero_wide_safe:
printf ("XWINT (x%d, 0) == ", depth);
printf (HOST_WIDE_INT_PRINT_DEC_C, p->u.intval);
break;
case DT_veclen_ge:
printf ("XVECLEN (x%d, 0) >= %d", depth, p->u.veclen);
break;
case DT_dup:
printf ("rtx_equal_p (x%d, operands[%d])", depth, p->u.dup);
break;
case DT_pred:
printf ("%s (x%d, %smode)", p->u.pred.name, depth,
GET_MODE_NAME (p->u.pred.mode));
break;
case DT_c_test:
printf ("(%s)", p->u.c_test);
break;
case DT_accept_insn:
switch (subroutine_type)
{
case RECOG:
if (p->u.insn.num_clobbers_to_add == 0)
abort ();
printf ("pnum_clobbers != NULL");
break;
default:
abort ();
}
break;
default:
abort ();
}
}
static void
write_action (p, test, depth, uncond, success, subroutine_type)
struct decision *p;
struct decision_test *test;
int depth, uncond;
struct decision *success;
enum routine_type subroutine_type;
{
const char *indent;
int want_close = 0;
if (uncond)
indent = " ";
else if (test->type == DT_accept_op || test->type == DT_accept_insn)
{
fputs (" {\n", stdout);
indent = " ";
want_close = 1;
}
else
indent = " ";
if (test->type == DT_accept_op)
{
printf("%soperands[%d] = x%d;\n", indent, test->u.opno, depth);
if (test->next)
{
test = test->next;
if (test->type != DT_accept_insn)
abort ();
}
}
if (test->next)
abort ();
if (test->type == DT_accept_insn)
{
switch (subroutine_type)
{
case RECOG:
if (test->u.insn.num_clobbers_to_add != 0)
printf ("%s*pnum_clobbers = %d;\n",
indent, test->u.insn.num_clobbers_to_add);
printf ("%sreturn %d;\n", indent, test->u.insn.code_number);
break;
case SPLIT:
printf ("%sreturn gen_split_%d (operands);\n",
indent, test->u.insn.code_number);
break;
case PEEPHOLE2:
{
int match_len = 0, i;
for (i = strlen (p->position) - 1; i >= 0; --i)
if (ISUPPER (p->position[i]))
{
match_len = p->position[i] - 'A';
break;
}
printf ("%s*_pmatch_len = %d;\n", indent, match_len);
printf ("%stem = gen_peephole2_%d (insn, operands);\n",
indent, test->u.insn.code_number);
printf ("%sif (tem != 0)\n%s return tem;\n", indent, indent);
}
break;
default:
abort ();
}
}
else
{
printf("%sgoto L%d;\n", indent, success->number);
success->need_label = 1;
}
if (want_close)
fputs (" }\n", stdout);
}
static int
is_unconditional (t, subroutine_type)
struct decision_test *t;
enum routine_type subroutine_type;
{
if (t->type == DT_accept_op)
return 1;
if (t->type == DT_accept_insn)
{
switch (subroutine_type)
{
case RECOG:
return (t->u.insn.num_clobbers_to_add == 0);
case SPLIT:
return 1;
case PEEPHOLE2:
return -1;
default:
abort ();
}
}
return 0;
}
static int
write_node (p, depth, subroutine_type)
struct decision *p;
int depth;
enum routine_type subroutine_type;
{
struct decision_test *test, *last_test;
int uncond;
last_test = test = p->tests;
uncond = is_unconditional (test, subroutine_type);
if (uncond == 0)
{
printf (" if (");
write_cond (test, depth, subroutine_type);
while ((test = test->next) != NULL)
{
int uncond2;
last_test = test;
uncond2 = is_unconditional (test, subroutine_type);
if (uncond2 != 0)
break;
printf ("\n && ");
write_cond (test, depth, subroutine_type);
}
printf (")\n");
}
write_action (p, last_test, depth, uncond, p->success.first, subroutine_type);
return uncond > 0;
}
static void
write_tree_1 (head, depth, subroutine_type)
struct decision_head *head;
int depth;
enum routine_type subroutine_type;
{
struct decision *p, *next;
int uncond = 0;
for (p = head->first; p ; p = next)
{
if (p != head->first && p->need_label)
OUTPUT_LABEL (" ", p->number);
next = write_switch (p, depth);
if (p != next)
uncond = 0;
else
{
uncond = write_node (p, depth, subroutine_type);
next = p->next;
}
}
if (! uncond)
write_afterward (head->last, head->last->afterward, " ");
}
static void
write_tree (head, prevpos, type, initial)
struct decision_head *head;
const char *prevpos;
enum routine_type type;
int initial;
{
struct decision *p = head->first;
putchar ('\n');
if (p->need_label)
OUTPUT_LABEL (" ", p->number);
if (! initial && p->subroutine_number > 0)
{
static const char * const name_prefix[] = {
"recog", "split", "peephole2"
};
static const char * const call_suffix[] = {
", pnum_clobbers", "", ", _pmatch_len"
};
if (p->afterward)
{
printf (" tem = %s_%d (x0, insn%s);\n",
name_prefix[type], p->subroutine_number, call_suffix[type]);
if (IS_SPLIT (type))
printf (" if (tem != 0)\n return tem;\n");
else
printf (" if (tem >= 0)\n return tem;\n");
change_state (p->position, p->afterward->position, NULL, " ");
printf (" goto L%d;\n", p->afterward->number);
}
else
{
printf (" return %s_%d (x0, insn%s);\n",
name_prefix[type], p->subroutine_number, call_suffix[type]);
}
}
else
{
int depth = strlen (p->position);
change_state (prevpos, p->position, head->last->afterward, " ");
write_tree_1 (head, depth, type);
for (p = head->first; p; p = p->next)
if (p->success.first)
write_tree (&p->success, p->position, type, 0);
}
}
static void
write_subroutine (head, type)
struct decision_head *head;
enum routine_type type;
{
int subfunction = head->first ? head->first->subroutine_number : 0;
const char *s_or_e;
char extension[32];
int i;
s_or_e = subfunction ? "static " : "";
if (subfunction)
sprintf (extension, "_%d", subfunction);
else if (type == RECOG)
extension[0] = '\0';
else
strcpy (extension, "_insns");
switch (type)
{
case RECOG:
printf ("%sint recog%s PARAMS ((rtx, rtx, int *));\n", s_or_e, extension);
printf ("%sint\n\
recog%s (x0, insn, pnum_clobbers)\n\
rtx x0 ATTRIBUTE_UNUSED;\n\
rtx insn ATTRIBUTE_UNUSED;\n\
int *pnum_clobbers ATTRIBUTE_UNUSED;\n", s_or_e, extension);
break;
case SPLIT:
printf ("%srtx split%s PARAMS ((rtx, rtx));\n", s_or_e, extension);
printf ("%srtx\n\
split%s (x0, insn)\n\
rtx x0 ATTRIBUTE_UNUSED;\n\
rtx insn ATTRIBUTE_UNUSED;\n", s_or_e, extension);
break;
case PEEPHOLE2:
printf ("%srtx peephole2%s PARAMS ((rtx, rtx, int *));\n",
s_or_e, extension);
printf ("%srtx\n\
peephole2%s (x0, insn, _pmatch_len)\n\
rtx x0 ATTRIBUTE_UNUSED;\n\
rtx insn ATTRIBUTE_UNUSED;\n\
int *_pmatch_len ATTRIBUTE_UNUSED;\n", s_or_e, extension);
break;
}
printf ("{\n rtx * const operands ATTRIBUTE_UNUSED = &recog_data.operand[0];\n");
for (i = 1; i <= max_depth; i++)
printf (" rtx x%d ATTRIBUTE_UNUSED;\n", i);
printf (" %s tem ATTRIBUTE_UNUSED;\n", IS_SPLIT (type) ? "rtx" : "int");
if (!subfunction)
printf (" recog_data.insn = NULL_RTX;\n");
if (head->first)
write_tree (head, "", type, 1);
else
printf (" goto ret0;\n");
printf (" ret0:\n return %d;\n}\n\n", IS_SPLIT (type) ? 0 : -1);
}
static void
write_subroutines (head, type)
struct decision_head *head;
enum routine_type type;
{
struct decision *p;
for (p = head->first; p ; p = p->next)
if (p->success.first)
write_subroutines (&p->success, type);
if (head->first->subroutine_number > 0)
write_subroutine (head, type);
}
static void
write_header ()
{
puts ("\
/* Generated automatically by the program `genrecog' from the target\n\
machine description file. */\n\
\n\
#include \"config.h\"\n\
#include \"system.h\"\n\
#include \"rtl.h\"\n\
#include \"tm_p.h\"\n\
#include \"function.h\"\n\
#include \"insn-config.h\"\n\
#include \"recog.h\"\n\
#include \"real.h\"\n\
#include \"output.h\"\n\
#include \"flags.h\"\n\
#include \"hard-reg-set.h\"\n\
#include \"resource.h\"\n\
#include \"toplev.h\"\n\
#include \"reload.h\"\n\
\n");
puts ("\n\
/* `recog' contains a decision tree that recognizes whether the rtx\n\
X0 is a valid instruction.\n\
\n\
recog returns -1 if the rtx is not valid. If the rtx is valid, recog\n\
returns a nonnegative number which is the insn code number for the\n\
pattern that matched. This is the same as the order in the machine\n\
description of the entry that matched. This number can be used as an\n\
index into `insn_data' and other tables.\n");
puts ("\
The third argument to recog is an optional pointer to an int. If\n\
present, recog will accept a pattern if it matches except for missing\n\
CLOBBER expressions at the end. In that case, the value pointed to by\n\
the optional pointer will be set to the number of CLOBBERs that need\n\
to be added (it should be initialized to zero by the caller). If it");
puts ("\
is set nonzero, the caller should allocate a PARALLEL of the\n\
appropriate size, copy the initial entries, and call add_clobbers\n\
(found in insn-emit.c) to fill in the CLOBBERs.\n\
");
puts ("\n\
The function split_insns returns 0 if the rtl could not\n\
be split or the split rtl as an INSN list if it can be.\n\
\n\
The function peephole2_insns returns 0 if the rtl could not\n\
be matched. If there was a match, the new rtl is returned in an INSN list,\n\
and LAST_INSN will point to the last recognized insn in the old sequence.\n\
*/\n\n");
}
static struct decision_head
make_insn_sequence (insn, type)
rtx insn;
enum routine_type type;
{
rtx x;
const char *c_test = XSTR (insn, type == RECOG ? 2 : 1);
int truth = maybe_eval_c_test (c_test);
struct decision *last;
struct decision_test *test, **place;
struct decision_head head;
char c_test_pos[2];
if (truth == 0)
abort ();
record_insn_name (next_insn_code, (type == RECOG ? XSTR (insn, 0) : NULL));
c_test_pos[0] = '\0';
if (type == PEEPHOLE2)
{
int i, j;
x = rtx_alloc (PARALLEL);
PUT_MODE (x, VOIDmode);
XVEC (x, 0) = rtvec_alloc (XVECLEN (insn, 0));
for (i = j = 0; i < XVECLEN (insn, 0); i++)
{
rtx tmp = XVECEXP (insn, 0, i);
if (GET_CODE (tmp) != MATCH_SCRATCH && GET_CODE (tmp) != MATCH_DUP)
{
XVECEXP (x, 0, j) = tmp;
j++;
}
}
XVECLEN (x, 0) = j;
c_test_pos[0] = 'A' + j - 1;
c_test_pos[1] = '\0';
}
else if (XVECLEN (insn, type == RECOG) == 1)
x = XVECEXP (insn, type == RECOG, 0);
else
{
x = rtx_alloc (PARALLEL);
XVEC (x, 0) = XVEC (insn, type == RECOG);
PUT_MODE (x, VOIDmode);
}
validate_pattern (x, insn, NULL_RTX, 0);
memset(&head, 0, sizeof(head));
last = add_to_sequence (x, &head, "", type, 1);
for (test = last->tests; test->next; test = test->next)
continue;
place = &test->next;
if (truth == -1)
{
if (test->type == DT_accept_op)
{
last = new_decision (c_test_pos, &last->success);
place = &last->tests;
}
test = new_decision_test (DT_c_test, &place);
test->u.c_test = c_test;
}
test = new_decision_test (DT_accept_insn, &place);
test->u.insn.code_number = next_insn_code;
test->u.insn.lineno = pattern_lineno;
test->u.insn.num_clobbers_to_add = 0;
switch (type)
{
case RECOG:
if (GET_CODE (x) == PARALLEL)
{
int i;
for (i = XVECLEN (x, 0); i > 0; i--)
{
rtx y = XVECEXP (x, 0, i - 1);
if (GET_CODE (y) != CLOBBER
|| (GET_CODE (XEXP (y, 0)) != REG
&& GET_CODE (XEXP (y, 0)) != MATCH_SCRATCH))
break;
}
if (i != XVECLEN (x, 0))
{
rtx new;
struct decision_head clobber_head;
if (i == 1)
new = XVECEXP (x, 0, 0);
else
{
int j;
new = rtx_alloc (PARALLEL);
XVEC (new, 0) = rtvec_alloc (i);
for (j = i - 1; j >= 0; j--)
XVECEXP (new, 0, j) = XVECEXP (x, 0, j);
}
memset (&clobber_head, 0, sizeof(clobber_head));
last = add_to_sequence (new, &clobber_head, "", type, 1);
for (test = last->tests; test->next; test = test->next)
continue;
place = &test->next;
if (test->type == DT_accept_op)
{
last = new_decision ("", &last->success);
place = &last->tests;
}
if (truth == -1)
{
test = new_decision_test (DT_c_test, &place);
test->u.c_test = c_test;
}
test = new_decision_test (DT_accept_insn, &place);
test->u.insn.code_number = next_insn_code;
test->u.insn.lineno = pattern_lineno;
test->u.insn.num_clobbers_to_add = XVECLEN (x, 0) - i;
merge_trees (&head, &clobber_head);
}
}
break;
case SPLIT:
printf ("extern rtx gen_split_%d PARAMS ((rtx *));\n", next_insn_code);
break;
case PEEPHOLE2:
printf ("extern rtx gen_peephole2_%d PARAMS ((rtx, rtx *));\n",
next_insn_code);
break;
}
return head;
}
static void
process_tree (head, subroutine_type)
struct decision_head *head;
enum routine_type subroutine_type;
{
if (head->first == NULL)
{
if (subroutine_type == PEEPHOLE2)
return;
}
else
{
factor_tests (head);
next_subroutine_number = 0;
break_out_subroutines (head, 1);
find_afterward (head, NULL);
simplify_tests(head);
write_subroutines (head, subroutine_type);
}
write_subroutine (head, subroutine_type);
}
extern int main PARAMS ((int, char **));
int
main (argc, argv)
int argc;
char **argv;
{
rtx desc;
struct decision_head recog_tree, split_tree, peephole2_tree, h;
progname = "genrecog";
memset (&recog_tree, 0, sizeof recog_tree);
memset (&split_tree, 0, sizeof split_tree);
memset (&peephole2_tree, 0, sizeof peephole2_tree);
if (argc <= 1)
fatal ("no input file name");
if (init_md_reader_args (argc, argv) != SUCCESS_EXIT_CODE)
return (FATAL_EXIT_CODE);
next_insn_code = 0;
next_index = 0;
write_header ();
while (1)
{
desc = read_md_rtx (&pattern_lineno, &next_insn_code);
if (desc == NULL)
break;
if (GET_CODE (desc) == DEFINE_INSN)
{
h = make_insn_sequence (desc, RECOG);
merge_trees (&recog_tree, &h);
}
else if (GET_CODE (desc) == DEFINE_SPLIT)
{
h = make_insn_sequence (desc, SPLIT);
merge_trees (&split_tree, &h);
}
else if (GET_CODE (desc) == DEFINE_PEEPHOLE2)
{
h = make_insn_sequence (desc, PEEPHOLE2);
merge_trees (&peephole2_tree, &h);
}
next_index++;
}
if (error_count)
return FATAL_EXIT_CODE;
puts ("\n\n");
process_tree (&recog_tree, RECOG);
process_tree (&split_tree, SPLIT);
process_tree (&peephole2_tree, PEEPHOLE2);
fflush (stdout);
return (ferror (stdout) != 0 ? FATAL_EXIT_CODE : SUCCESS_EXIT_CODE);
}
const char *
get_insn_name (code)
int code;
{
if (code < insn_name_ptr_size)
return insn_name_ptr[code];
else
return NULL;
}
static void
record_insn_name (code, name)
int code;
const char *name;
{
static const char *last_real_name = "insn";
static int last_real_code = 0;
char *new;
if (insn_name_ptr_size <= code)
{
int new_size;
new_size = (insn_name_ptr_size ? insn_name_ptr_size * 2 : 512);
insn_name_ptr =
(char **) xrealloc (insn_name_ptr, sizeof(char *) * new_size);
memset (insn_name_ptr + insn_name_ptr_size, 0,
sizeof(char *) * (new_size - insn_name_ptr_size));
insn_name_ptr_size = new_size;
}
if (!name || name[0] == '\0')
{
new = xmalloc (strlen (last_real_name) + 10);
sprintf (new, "%s+%d", last_real_name, code - last_real_code);
}
else
{
last_real_name = new = xstrdup (name);
last_real_code = code;
}
insn_name_ptr[code] = new;
}
static void
debug_decision_2 (test)
struct decision_test *test;
{
switch (test->type)
{
case DT_mode:
fprintf (stderr, "mode=%s", GET_MODE_NAME (test->u.mode));
break;
case DT_code:
fprintf (stderr, "code=%s", GET_RTX_NAME (test->u.code));
break;
case DT_veclen:
fprintf (stderr, "veclen=%d", test->u.veclen);
break;
case DT_elt_zero_int:
fprintf (stderr, "elt0_i=%d", (int) test->u.intval);
break;
case DT_elt_one_int:
fprintf (stderr, "elt1_i=%d", (int) test->u.intval);
break;
case DT_elt_zero_wide:
fprintf (stderr, "elt0_w=");
fprintf (stderr, HOST_WIDE_INT_PRINT_DEC, test->u.intval);
break;
case DT_elt_zero_wide_safe:
fprintf (stderr, "elt0_ws=");
fprintf (stderr, HOST_WIDE_INT_PRINT_DEC, test->u.intval);
break;
case DT_veclen_ge:
fprintf (stderr, "veclen>=%d", test->u.veclen);
break;
case DT_dup:
fprintf (stderr, "dup=%d", test->u.dup);
break;
case DT_pred:
fprintf (stderr, "pred=(%s,%s)",
test->u.pred.name, GET_MODE_NAME(test->u.pred.mode));
break;
case DT_c_test:
{
char sub[16+4];
strncpy (sub, test->u.c_test, sizeof(sub));
memcpy (sub+16, "...", 4);
fprintf (stderr, "c_test=\"%s\"", sub);
}
break;
case DT_accept_op:
fprintf (stderr, "A_op=%d", test->u.opno);
break;
case DT_accept_insn:
fprintf (stderr, "A_insn=(%d,%d)",
test->u.insn.code_number, test->u.insn.num_clobbers_to_add);
break;
default:
abort ();
}
}
static void
debug_decision_1 (d, indent)
struct decision *d;
int indent;
{
int i;
struct decision_test *test;
if (d == NULL)
{
for (i = 0; i < indent; ++i)
putc (' ', stderr);
fputs ("(nil)\n", stderr);
return;
}
for (i = 0; i < indent; ++i)
putc (' ', stderr);
putc ('{', stderr);
test = d->tests;
if (test)
{
debug_decision_2 (test);
while ((test = test->next) != NULL)
{
fputs (" + ", stderr);
debug_decision_2 (test);
}
}
fprintf (stderr, "} %d n %d a %d\n", d->number,
(d->next ? d->next->number : -1),
(d->afterward ? d->afterward->number : -1));
}
static void
debug_decision_0 (d, indent, maxdepth)
struct decision *d;
int indent, maxdepth;
{
struct decision *n;
int i;
if (maxdepth < 0)
return;
if (d == NULL)
{
for (i = 0; i < indent; ++i)
putc (' ', stderr);
fputs ("(nil)\n", stderr);
return;
}
debug_decision_1 (d, indent);
for (n = d->success.first; n ; n = n->next)
debug_decision_0 (n, indent + 2, maxdepth - 1);
}
void
debug_decision (d)
struct decision *d;
{
debug_decision_0 (d, 0, 1000000);
}
void
debug_decision_list (d)
struct decision *d;
{
while (d)
{
debug_decision_0 (d, 0, 0);
d = d->next;
}
}