#include "bconfig.h"
#include "system.h"
#include "coretypes.h"
#include "tm.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))
struct decision_head
{
struct decision *first;
struct decision *last;
};
struct decision_test
{
struct decision_test *next;
enum decision_type
{
DT_num_insns,
DT_mode, DT_code, DT_veclen,
DT_elt_zero_int, DT_elt_one_int, DT_elt_zero_wide, DT_elt_zero_wide_safe,
DT_const_int,
DT_veclen_ge, DT_dup, DT_pred, DT_c_test,
DT_accept_op, DT_accept_insn
} type;
union
{
int num_insns;
enum machine_mode mode;
RTX_CODE code;
struct
{
const char *name;
const struct pred_data *data;
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 max_depth;
static int pattern_lineno;
static int error_count;
#define N 0
#define Y 1
#define I 2
#define TRISTATE_AND(a,b) \
((a) == I ? ((b) == N ? N : I) : \
(b) == I ? ((a) == N ? N : I) : \
(a) && (b))
#define TRISTATE_OR(a,b) \
((a) == I ? ((b) == Y ? Y : I) : \
(b) == I ? ((a) == Y ? Y : I) : \
(a) || (b))
#define TRISTATE_NOT(a) \
((a) == I ? I : !(a))
static char did_you_mean_codes[NUM_RTX_CODE];
static void
compute_predicate_codes (rtx exp, char codes[NUM_RTX_CODE])
{
char op0_codes[NUM_RTX_CODE];
char op1_codes[NUM_RTX_CODE];
char op2_codes[NUM_RTX_CODE];
int i;
switch (GET_CODE (exp))
{
case AND:
compute_predicate_codes (XEXP (exp, 0), op0_codes);
compute_predicate_codes (XEXP (exp, 1), op1_codes);
for (i = 0; i < NUM_RTX_CODE; i++)
codes[i] = TRISTATE_AND (op0_codes[i], op1_codes[i]);
break;
case IOR:
compute_predicate_codes (XEXP (exp, 0), op0_codes);
compute_predicate_codes (XEXP (exp, 1), op1_codes);
for (i = 0; i < NUM_RTX_CODE; i++)
codes[i] = TRISTATE_OR (op0_codes[i], op1_codes[i]);
break;
case NOT:
compute_predicate_codes (XEXP (exp, 0), op0_codes);
for (i = 0; i < NUM_RTX_CODE; i++)
codes[i] = TRISTATE_NOT (op0_codes[i]);
break;
case IF_THEN_ELSE:
compute_predicate_codes (XEXP (exp, 0), op0_codes);
compute_predicate_codes (XEXP (exp, 1), op1_codes);
compute_predicate_codes (XEXP (exp, 2), op2_codes);
for (i = 0; i < NUM_RTX_CODE; i++)
codes[i] = TRISTATE_OR (TRISTATE_AND (op0_codes[i], op1_codes[i]),
TRISTATE_AND (TRISTATE_NOT (op0_codes[i]),
op2_codes[i]));
break;
case MATCH_CODE:
if (XSTR (exp, 1)[0] != '\0')
{
memset (codes, Y, NUM_RTX_CODE);
break;
}
memset (codes, N, NUM_RTX_CODE);
{
const char *next_code = XSTR (exp, 0);
const char *code;
if (*next_code == '\0')
{
message_with_line (pattern_lineno, "empty match_code expression");
error_count++;
break;
}
while ((code = scan_comma_elt (&next_code)) != 0)
{
size_t n = next_code - code;
int found_it = 0;
for (i = 0; i < NUM_RTX_CODE; i++)
if (!strncmp (code, GET_RTX_NAME (i), n)
&& GET_RTX_NAME (i)[n] == '\0')
{
codes[i] = Y;
found_it = 1;
break;
}
if (!found_it)
{
message_with_line (pattern_lineno, "match_code \"%.*s\" matches nothing",
(int) n, code);
error_count ++;
for (i = 0; i < NUM_RTX_CODE; i++)
if (!strncasecmp (code, GET_RTX_NAME (i), n)
&& GET_RTX_NAME (i)[n] == '\0'
&& !did_you_mean_codes[i])
{
did_you_mean_codes[i] = 1;
message_with_line (pattern_lineno, "(did you mean \"%s\"?)", GET_RTX_NAME (i));
}
}
}
}
break;
case MATCH_OPERAND:
{
struct pred_data *p = lookup_predicate (XSTR (exp, 1));
if (!p)
{
message_with_line (pattern_lineno,
"reference to unknown predicate '%s'",
XSTR (exp, 1));
error_count++;
break;
}
for (i = 0; i < NUM_RTX_CODE; i++)
codes[i] = p->codes[i] ? I : N;
}
break;
case MATCH_TEST:
memset (codes, I, NUM_RTX_CODE);
break;
default:
message_with_line (pattern_lineno,
"'%s' cannot be used in a define_predicate expression",
GET_RTX_NAME (GET_CODE (exp)));
error_count++;
memset (codes, I, NUM_RTX_CODE);
break;
}
}
#undef TRISTATE_OR
#undef TRISTATE_AND
#undef TRISTATE_NOT
static void
process_define_predicate (rtx desc)
{
struct pred_data *pred = xcalloc (sizeof (struct pred_data), 1);
char codes[NUM_RTX_CODE];
bool seen_one = false;
int i;
pred->name = XSTR (desc, 0);
if (GET_CODE (desc) == DEFINE_SPECIAL_PREDICATE)
pred->special = 1;
compute_predicate_codes (XEXP (desc, 1), codes);
for (i = 0; i < NUM_RTX_CODE; i++)
if (codes[i] != N)
{
pred->codes[i] = true;
if (GET_RTX_CLASS (i) != RTX_CONST_OBJ)
pred->allows_non_const = true;
if (i != REG
&& i != SUBREG
&& i != MEM
&& i != CONCAT
&& i != PARALLEL
&& i != STRICT_LOW_PART)
pred->allows_non_lvalue = true;
if (seen_one)
pred->singleton = UNKNOWN;
else
{
pred->singleton = i;
seen_one = true;
}
}
add_predicate (pred);
}
#undef I
#undef N
#undef Y
static struct decision *new_decision
(const char *, struct decision_head *);
static struct decision_test *new_decision_test
(enum decision_type, struct decision_test ***);
static rtx find_operand
(rtx, int, rtx);
static rtx find_matching_operand
(rtx, int);
static void validate_pattern
(rtx, rtx, rtx, int);
static struct decision *add_to_sequence
(rtx, struct decision_head *, const char *, enum routine_type, int);
static int maybe_both_true_2
(struct decision_test *, struct decision_test *);
static int maybe_both_true_1
(struct decision_test *, struct decision_test *);
static int maybe_both_true
(struct decision *, struct decision *, int);
static int nodes_identical_1
(struct decision_test *, struct decision_test *);
static int nodes_identical
(struct decision *, struct decision *);
static void merge_accept_insn
(struct decision *, struct decision *);
static void merge_trees
(struct decision_head *, struct decision_head *);
static void factor_tests
(struct decision_head *);
static void simplify_tests
(struct decision_head *);
static int break_out_subroutines
(struct decision_head *, int);
static void find_afterward
(struct decision_head *, struct decision *);
static void change_state
(const char *, const char *, const char *);
static void print_code
(enum rtx_code);
static void write_afterward
(struct decision *, struct decision *, const char *);
static struct decision *write_switch
(struct decision *, int);
static void write_cond
(struct decision_test *, int, enum routine_type);
static void write_action
(struct decision *, struct decision_test *, int, int,
struct decision *, enum routine_type);
static int is_unconditional
(struct decision_test *, enum routine_type);
static int write_node
(struct decision *, int, enum routine_type);
static void write_tree_1
(struct decision_head *, int, enum routine_type);
static void write_tree
(struct decision_head *, const char *, enum routine_type, int);
static void write_subroutine
(struct decision_head *, enum routine_type);
static void write_subroutines
(struct decision_head *, enum routine_type);
static void write_header
(void);
static struct decision_head make_insn_sequence
(rtx, enum routine_type);
static void process_tree
(struct decision_head *, enum routine_type);
static void debug_decision_0
(struct decision *, int, int);
static void debug_decision_1
(struct decision *, int);
static void debug_decision_2
(struct decision_test *);
extern void debug_decision
(struct decision *);
extern void debug_decision_list
(struct decision *);
static struct decision *
new_decision (const char *position, struct decision_head *last)
{
struct decision *new = xcalloc (1, sizeof (struct decision));
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 (enum decision_type type, struct decision_test ***pplace)
{
struct decision_test **place = *pplace;
struct decision_test *test;
test = XNEW (struct decision_test);
test->next = *place;
test->type = type;
*place = test;
place = &test->next;
*pplace = place;
return test;
}
static rtx
find_operand (rtx pattern, int n, rtx stop)
{
const char *fmt;
RTX_CODE code;
int i, j, len;
rtx r;
if (pattern == stop)
return stop;
code = GET_CODE (pattern);
if ((code == MATCH_SCRATCH
|| 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, stop)) != 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, stop))
!= NULL_RTX)
return r;
break;
case 'i': case 'w': case '0': case 's':
break;
default:
gcc_unreachable ();
}
}
return NULL;
}
static rtx
find_matching_operand (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:
gcc_unreachable ();
}
}
return NULL;
}
static void
validate_pattern (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_DUP:
case MATCH_OP_DUP:
case MATCH_PAR_DUP:
if (find_operand (insn, XINT (pattern, 0), pattern) == pattern)
{
message_with_line (pattern_lineno,
"operand %i duplicated before defined",
XINT (pattern, 0));
error_count++;
}
break;
case MATCH_OPERAND:
case MATCH_OPERATOR:
{
const char *pred_name = XSTR (pattern, 1);
const struct pred_data *pred;
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)
{
pred = lookup_predicate (pred_name);
if (!pred)
message_with_line (pattern_lineno,
"warning: unknown predicate '%s'",
pred_name);
}
else
pred = 0;
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 && pred->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
&& pred
&& !pred->special
&& pred->allows_non_const
&& 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), NULL);
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), NULL);
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:
gcc_unreachable ();
}
}
}
static struct decision *
add_to_sequence (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 = 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)
{
int num_insns;
num_insns = XVECLEN (pattern, 0);
if (num_insns > 1)
{
test = new_decision_test (DT_num_insns, &place);
test->u.num_insns = num_insns;
last = &sub->success;
}
else
{
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:
{
RTX_CODE was_code = code;
const char *pred_name;
bool allows_const_int = true;
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)
{
const struct pred_data *pred;
test = new_decision_test (DT_pred, &place);
test->u.pred.name = pred_name;
test->u.pred.mode = mode;
pred = lookup_predicate (pred_name);
if (pred)
{
test->u.pred.data = pred;
allows_const_int = pred->codes[CONST_INT];
if (was_code == MATCH_PARALLEL
&& pred->singleton != PARALLEL)
message_with_line (pattern_lineno,
"predicate '%s' used in match_parallel "
"does not allow only PARALLEL", pred->name);
else
code = pred->singleton;
}
else
message_with_line (pattern_lineno,
"warning: unknown predicate '%s' in '%s' expression",
pred_name, GET_RTX_NAME (was_code));
}
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')
{
gcc_assert (i < 2);
if (!i)
{
test = new_decision_test (DT_elt_zero_int, &place);
test->u.intval = XINT (pattern, i);
}
else
{
test = new_decision_test (DT_elt_one_int, &place);
test->u.intval = XINT (pattern, i);
}
}
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;
gcc_assert (!i);
test = new_decision_test (type, &place);
test->u.intval = XWINT (pattern, i);
}
else if (fmt[i] == 'E')
{
gcc_assert (!i);
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:
gcc_unreachable ();
}
}
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;
}
gcc_assert (this->tests);
ret:
free (subpos);
return sub;
}
static int
maybe_both_true_2 (struct decision_test *d1, struct decision_test *d2)
{
if (d1->type == d2->type)
{
switch (d1->type)
{
case DT_num_insns:
if (d1->u.num_insns == d2->u.num_insns)
return 1;
else
return -1;
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.data)
{
if (d2->type == DT_code)
{
if (!d1->u.pred.data->codes[d2->u.code])
return 0;
}
else if (d2->type == DT_pred && d2->u.pred.data)
{
bool common = false;
enum rtx_code c;
for (c = 0; c < NUM_RTX_CODE; c++)
if (d1->u.pred.data->codes[c] && d2->u.pred.data->codes[c])
{
common = true;
break;
}
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 (struct decision_test *d1, struct decision_test *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 (struct decision *d1, struct decision *d2,
int toplevel)
{
struct decision *p1, *p2;
int cmp;
cmp = strcmp (d1->position, d2->position);
if (cmp != 0)
{
gcc_assert (!toplevel);
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 (struct decision_test *d1, struct decision_test *d2)
{
switch (d1->type)
{
case DT_num_insns:
return d1->u.num_insns == d2->u.num_insns;
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:
gcc_unreachable ();
}
}
static int
nodes_identical (struct decision *d1, struct decision *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 (struct decision *oldd, struct decision *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 (struct decision_head *oldh, struct decision_head *addh)
{
struct decision *next, *add;
if (addh->first == 0)
return;
if (oldh->first == 0)
{
*oldh = *addh;
return;
}
gcc_assert (!strcmp (oldh->first->position, addh->first->position));
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 (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 (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 (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 (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 (const char *oldpos, const char *newpos, 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]))
{
printf ("%stem = peep2_next_insn (%d);\n",
indent, newpos[depth] - 'A');
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 (enum rtx_code code)
{
const char *p;
for (p = GET_RTX_NAME (code); *p; p++)
putchar (TOUPPER (*p));
}
static void
write_afterward (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, indent);
printf ("%sgoto L%d;\n", indent, afterward->number);
}
}
static void
print_host_wide_int (HOST_WIDE_INT val)
{
HOST_WIDE_INT min = (unsigned HOST_WIDE_INT)1 << (HOST_BITS_PER_WIDE_INT-1);
if (val == min)
printf ("(" HOST_WIDE_INT_PRINT_DEC_C "-1)", val + 1);
else
printf (HOST_WIDE_INT_PRINT_DEC_C, val);
}
static struct decision *
write_switch (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.data)
{
const struct pred_data *data = p->tests->u.pred.data;
RTX_CODE c;
for (c = 0; c < NUM_RTX_CODE; c++)
if (codemap[c] && data->codes[c])
goto pred_done;
for (c = 0; c < NUM_RTX_CODE; c++)
if (data->codes[c])
{
fputs (" case ", stdout);
print_code (c);
fputs (":\n", stdout);
codemap[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:
gcc_unreachable ();
}
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:
print_host_wide_int (p->tests->u.intval);
break;
default:
gcc_unreachable ();
}
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 (struct decision_test *p, int depth,
enum routine_type subroutine_type)
{
switch (p->type)
{
case DT_num_insns:
printf ("peep2_current_count >= %d", p->u.num_insns);
break;
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);
print_host_wide_int (p->u.intval);
break;
case DT_const_int:
printf ("x%d == const_int_rtx[MAX_SAVED_CONST_INT + (%d)]",
depth, (int) 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:
print_c_condition (p->u.c_test);
break;
case DT_accept_insn:
gcc_assert (subroutine_type == RECOG);
gcc_assert (p->u.insn.num_clobbers_to_add);
printf ("pnum_clobbers != NULL");
break;
default:
gcc_unreachable ();
}
}
static void
write_action (struct decision *p, struct decision_test *test,
int depth, int 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;
gcc_assert (test->type == DT_accept_insn);
}
}
gcc_assert (!test->next);
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; /* %s */\n", indent,
test->u.insn.code_number,
get_insn_name (test->u.insn.code_number));
break;
case SPLIT:
printf ("%sreturn gen_split_%d (insn, 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:
gcc_unreachable ();
}
}
else
{
printf("%sgoto L%d;\n", indent, success->number);
success->need_label = 1;
}
if (want_close)
fputs (" }\n", stdout);
}
static int
is_unconditional (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:
gcc_unreachable ();
}
}
return 0;
}
static int
write_node (struct decision *p, int depth,
enum routine_type subroutine_type)
{
struct decision_test *test, *last_test;
int uncond;
for (test = p->tests; test; test = test->next)
{
if (test->type == DT_code
&& test->u.code == CONST_INT
&& test->next
&& test->next->type == DT_elt_zero_wide_safe
&& -MAX_SAVED_CONST_INT <= test->next->u.intval
&& test->next->u.intval <= MAX_SAVED_CONST_INT)
{
test->type = DT_const_int;
test->u.intval = test->next->u.intval;
test->next = test->next->next;
}
}
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)
{
last_test = test;
if (is_unconditional (test, subroutine_type))
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 (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 (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, " ");
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, " ");
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 (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\n\
recog%s (rtx x0 ATTRIBUTE_UNUSED,\n\trtx insn ATTRIBUTE_UNUSED,\n\tint *pnum_clobbers ATTRIBUTE_UNUSED)\n", s_or_e, extension);
break;
case SPLIT:
printf ("%srtx\n\
split%s (rtx x0 ATTRIBUTE_UNUSED, rtx insn ATTRIBUTE_UNUSED)\n",
s_or_e, extension);
break;
case PEEPHOLE2:
printf ("%srtx\n\
peephole2%s (rtx x0 ATTRIBUTE_UNUSED,\n\trtx insn ATTRIBUTE_UNUSED,\n\tint *_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 (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 (void)
{
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 \"coretypes.h\"\n\
#include \"tm.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\
#include \"tm-constrs.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 (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];
gcc_assert (truth);
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
|| (!REG_P (XEXP (y, 0))
&& 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 (rtx, rtx *);\n", next_insn_code);
break;
case PEEPHOLE2:
printf ("extern rtx gen_peephole2_%d (rtx, rtx *);\n",
next_insn_code);
break;
}
return head;
}
static void
process_tree (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 (int, char **);
int
main (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 (init_md_reader_args (argc, argv) != SUCCESS_EXIT_CODE)
return (FATAL_EXIT_CODE);
next_insn_code = 0;
write_header ();
while (1)
{
desc = read_md_rtx (&pattern_lineno, &next_insn_code);
if (desc == NULL)
break;
switch (GET_CODE (desc))
{
case DEFINE_PREDICATE:
case DEFINE_SPECIAL_PREDICATE:
process_define_predicate (desc);
break;
case DEFINE_INSN:
h = make_insn_sequence (desc, RECOG);
merge_trees (&recog_tree, &h);
break;
case DEFINE_SPLIT:
h = make_insn_sequence (desc, SPLIT);
merge_trees (&split_tree, &h);
break;
case DEFINE_PEEPHOLE2:
h = make_insn_sequence (desc, PEEPHOLE2);
merge_trees (&peephole2_tree, &h);
default:
;
}
}
if (error_count || have_error)
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);
}
static void
debug_decision_2 (struct decision_test *test)
{
switch (test->type)
{
case DT_num_insns:
fprintf (stderr, "num_insns=%d", test->u.num_insns);
break;
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=" HOST_WIDE_INT_PRINT_DEC, test->u.intval);
break;
case DT_elt_zero_wide_safe:
fprintf (stderr, "elt0_ws=" 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:
gcc_unreachable ();
}
}
static void
debug_decision_1 (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 (struct decision *d, int indent, int 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 (struct decision *d)
{
debug_decision_0 (d, 0, 1000000);
}
void
debug_decision_list (struct decision *d)
{
while (d)
{
debug_decision_0 (d, 0, 0);
d = d->next;
}
}