#include "hconfig.h"
#include "system.h"
#include "rtl.h"
#include "obstack.h"
#define OUTPUT_LABEL(INDENT_STRING, LABEL_NUMBER) \
printf("%sL%d: ATTRIBUTE_UNUSED_LABEL\n", (INDENT_STRING), (LABEL_NUMBER))
static struct obstack obstack;
struct obstack *rtl_obstack = &obstack;
#define obstack_chunk_alloc xmalloc
#define obstack_chunk_free free
char **insn_name_ptr = 0;
int insn_name_ptr_size = 0;
struct decision_head { struct decision *first, *last; };
struct decision
{
int number;
char *position;
RTX_CODE code;
char ignore_code;
char ignore_mode;
int veclen;
enum machine_mode mode;
char enforce_mode;
char retest_code, retest_mode;
int test_elt_zero_int;
int elt_zero_int;
int test_elt_one_int;
int elt_one_int;
int test_elt_zero_wide;
HOST_WIDE_INT elt_zero_wide;
const char *tests;
int pred;
char *c_test;
struct decision_head success;
int insn_code_number;
int num_clobbers_to_add;
struct decision *next;
struct decision *prev;
struct decision *afterward;
int opno;
int dupno;
int label_needed;
int subroutine_number;
};
#define SUBROUTINE_THRESHOLD 50
static int next_subroutine_number;
enum routine_type {RECOG, SPLIT};
static int next_number;
static int next_insn_code;
static int next_index;
static int max_depth;
static struct pred_table
{
const char *name;
RTX_CODE codes[NUM_RTX_CODE];
} preds[]
= {{"general_operand", {CONST_INT, CONST_DOUBLE, CONST, SYMBOL_REF,
LABEL_REF, SUBREG, REG, MEM}},
#ifdef PREDICATE_CODES
PREDICATE_CODES
#endif
{"address_operand", {CONST_INT, CONST_DOUBLE, CONST, SYMBOL_REF,
LABEL_REF, SUBREG, REG, MEM, PLUS, MINUS, MULT}},
{"register_operand", {SUBREG, REG}},
{"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}},
{"nonmemory_operand", {CONST_INT, CONST_DOUBLE, CONST, SYMBOL_REF,
LABEL_REF, SUBREG, REG}},
{"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}},
{"mode_independent_operand", {CONST_INT, CONST_DOUBLE, CONST, SYMBOL_REF,
LABEL_REF, SUBREG, REG, MEM}}};
#define NUM_KNOWN_PREDS (sizeof preds / sizeof preds[0])
static struct decision_head make_insn_sequence PROTO((rtx, enum routine_type));
static struct decision *add_to_sequence PROTO((rtx, struct decision_head *,
const char *));
static int not_both_true PROTO((struct decision *, struct decision *,
int));
static int position_merit PROTO((struct decision *, enum machine_mode,
enum rtx_code));
static struct decision_head merge_trees PROTO((struct decision_head,
struct decision_head));
static int break_out_subroutines PROTO((struct decision_head,
enum routine_type, int));
static void write_subroutine PROTO((struct decision *, enum routine_type));
static void write_tree_1 PROTO((struct decision *, const char *,
struct decision *, enum routine_type));
static void print_code PROTO((enum rtx_code));
static int same_codes PROTO((struct decision *, enum rtx_code));
static void clear_codes PROTO((struct decision *));
static int same_modes PROTO((struct decision *, enum machine_mode));
static void clear_modes PROTO((struct decision *));
static void write_tree PROTO((struct decision *, const char *,
struct decision *, int,
enum routine_type));
static void change_state PROTO((const char *, const char *, int));
void fatal PVPROTO((const char *, ...))
ATTRIBUTE_PRINTF_1 ATTRIBUTE_NORETURN;
void fancy_abort PROTO((void)) ATTRIBUTE_NORETURN;
static struct decision_head
make_insn_sequence (insn, type)
rtx insn;
enum routine_type type;
{
rtx x;
char *c_test = XSTR (insn, type == RECOG ? 2 : 1);
struct decision *last;
struct decision_head head;
{
static char *last_real_name = "insn";
static int last_real_code = 0;
char *name;
if (insn_name_ptr_size <= next_insn_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);
bzero ((PTR)(insn_name_ptr + insn_name_ptr_size),
sizeof(char *) * (new_size - insn_name_ptr_size));
insn_name_ptr_size = new_size;
}
name = XSTR (insn, 0);
if (!name || name[0] == '\0')
{
name = xmalloc (strlen (last_real_name) + 10);
sprintf (name, "%s+%d", last_real_name,
next_insn_code - last_real_code);
}
else
{
last_real_name = name;
last_real_code = next_insn_code;
}
insn_name_ptr[next_insn_code] = name;
}
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);
}
last = add_to_sequence (x, &head, "");
if (c_test[0])
last->c_test = c_test;
last->insn_code_number = next_insn_code;
last->num_clobbers_to_add = 0;
if (type == RECOG && GET_CODE (x) == PARALLEL)
{
int i;
for (i = XVECLEN (x, 0); i > 0; i--)
if (GET_CODE (XVECEXP (x, 0, i - 1)) != CLOBBER
|| (GET_CODE (XEXP (XVECEXP (x, 0, i - 1), 0)) != REG
&& GET_CODE (XEXP (XVECEXP (x, 0, i - 1), 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);
}
last = add_to_sequence (new, &clobber_head, "");
if (c_test[0])
last->c_test = c_test;
last->insn_code_number = next_insn_code;
last->num_clobbers_to_add = XVECLEN (x, 0) - i;
head = merge_trees (head, clobber_head);
}
}
next_insn_code++;
if (type == SPLIT)
printf ("extern rtx gen_split_%d ();\n", last->insn_code_number);
return head;
}
static struct decision *
add_to_sequence (pattern, last, position)
rtx pattern;
struct decision_head *last;
const char *position;
{
register RTX_CODE code;
register struct decision *new
= (struct decision *) xmalloc (sizeof (struct decision));
struct decision *this;
char *newpos;
register char *fmt;
register size_t i;
int depth = strlen (position);
int len;
if (depth > max_depth)
max_depth = depth;
new->number = next_number++;
new->position = xstrdup (position);
new->ignore_code = 0;
new->ignore_mode = 0;
new->enforce_mode = 1;
new->retest_code = new->retest_mode = 0;
new->veclen = 0;
new->test_elt_zero_int = 0;
new->test_elt_one_int = 0;
new->test_elt_zero_wide = 0;
new->elt_zero_int = 0;
new->elt_one_int = 0;
new->elt_zero_wide = 0;
new->tests = 0;
new->pred = -1;
new->c_test = 0;
new->success.first = new->success.last = 0;
new->insn_code_number = -1;
new->num_clobbers_to_add = 0;
new->next = 0;
new->prev = 0;
new->afterward = 0;
new->opno = -1;
new->dupno = -1;
new->label_needed = 0;
new->subroutine_number = 0;
this = new;
last->first = last->last = new;
newpos = (char *) alloca (depth + 2);
strcpy (newpos, position);
newpos[depth + 1] = 0;
restart:
new->mode = GET_MODE (pattern);
new->code = code = GET_CODE (pattern);
switch (code)
{
case MATCH_OPERAND:
case MATCH_SCRATCH:
case MATCH_OPERATOR:
case MATCH_PARALLEL:
case MATCH_INSN2:
new->opno = XINT (pattern, 0);
new->code = (code == MATCH_PARALLEL ? PARALLEL : UNKNOWN);
new->enforce_mode = 0;
if (code == MATCH_SCRATCH)
new->tests = "scratch_operand";
else
new->tests = XSTR (pattern, 1);
if (*new->tests == 0)
new->tests = 0;
if (new->tests)
{
for (i = 0; i < NUM_KNOWN_PREDS; i++)
if (! strcmp (preds[i].name, new->tests))
{
int j;
int allows_const_int = 0;
new->pred = i;
if (preds[i].codes[1] == 0 && new->code == UNKNOWN)
{
new->code = preds[i].codes[0];
if (! strcmp ("const_int_operand", new->tests))
new->tests = 0, new->pred = -1;
}
for (j = 0; j < NUM_RTX_CODE && preds[i].codes[j] != 0; j++)
if (preds[i].codes[j] == CONST_INT)
allows_const_int = 1;
if (! allows_const_int)
new->enforce_mode = new->ignore_mode= 1;
break;
}
#ifdef PREDICATE_CODES
if (i == NUM_KNOWN_PREDS)
fprintf (stderr, "Warning: `%s' not in PREDICATE_CODES\n",
new->tests);
#endif
}
if (code == MATCH_OPERATOR || code == MATCH_PARALLEL)
{
for (i = 0; i < (size_t) XVECLEN (pattern, 2); i++)
{
newpos[depth] = i + (code == MATCH_OPERATOR ? '0': 'a');
new = add_to_sequence (XVECEXP (pattern, 2, i),
&new->success, newpos);
}
}
return new;
case MATCH_OP_DUP:
new->opno = XINT (pattern, 0);
new->dupno = XINT (pattern, 0);
new->code = UNKNOWN;
new->tests = 0;
for (i = 0; i < (size_t) XVECLEN (pattern, 1); i++)
{
newpos[depth] = i + '0';
new = add_to_sequence (XVECEXP (pattern, 1, i),
&new->success, newpos);
}
return new;
case MATCH_DUP:
case MATCH_PAR_DUP:
new->dupno = XINT (pattern, 0);
new->code = UNKNOWN;
new->enforce_mode = 0;
return new;
case ADDRESS:
pattern = XEXP (pattern, 0);
goto restart;
case SET:
if (GET_MODE (SET_SRC (pattern)) != VOIDmode
&& GET_MODE (SET_DEST (pattern)) != VOIDmode
&& GET_MODE (SET_SRC (pattern)) != GET_MODE (SET_DEST (pattern))
&& ! (GET_CODE (SET_SRC (pattern)) == MATCH_OPERAND
&& ! strcmp (XSTR (SET_SRC (pattern), 1), "address_operand")))
{
print_rtl (stderr, pattern);
fputc ('\n', stderr);
fatal ("mode mismatch in SET");
}
newpos[depth] = '0';
new = add_to_sequence (SET_DEST (pattern), &new->success, newpos);
this->success.first->enforce_mode = 1;
newpos[depth] = '1';
new = add_to_sequence (SET_SRC (pattern), &new->success, newpos);
if (GET_CODE (SET_DEST (pattern)) == CC0
&& GET_CODE (SET_SRC (pattern)) != COMPARE)
this->success.first->enforce_mode = 1;
return new;
case SIGN_EXTEND:
case ZERO_EXTEND:
case STRICT_LOW_PART:
newpos[depth] = '0';
new = add_to_sequence (XEXP (pattern, 0), &new->success, newpos);
this->success.first->enforce_mode = 1;
return new;
case SUBREG:
this->test_elt_one_int = 1;
this->elt_one_int = XINT (pattern, 1);
newpos[depth] = '0';
new = add_to_sequence (XEXP (pattern, 0), &new->success, newpos);
this->success.first->enforce_mode = 1;
return new;
case ZERO_EXTRACT:
case SIGN_EXTRACT:
newpos[depth] = '0';
new = add_to_sequence (XEXP (pattern, 0), &new->success, newpos);
this->success.first->enforce_mode = 1;
newpos[depth] = '1';
new = add_to_sequence (XEXP (pattern, 1), &new->success, newpos);
newpos[depth] = '2';
new = add_to_sequence (XEXP (pattern, 2), &new->success, newpos);
return new;
case EQ: case NE: case LE: case LT: case GE: case GT:
case LEU: case LTU: case GEU: case GTU:
if (GET_CODE (XEXP (pattern, 0)) == CC0)
break;
case COMPARE:
newpos[depth] = '0';
new = add_to_sequence (XEXP (pattern, 0), &new->success, newpos);
this->success.first->enforce_mode = 1;
newpos[depth] = '1';
new = add_to_sequence (XEXP (pattern, 1), &new->success, newpos);
return new;
default:
break;
}
fmt = GET_RTX_FORMAT (code);
len = GET_RTX_LENGTH (code);
for (i = 0; i < (size_t) len; i++)
{
newpos[depth] = '0' + i;
if (fmt[i] == 'e' || fmt[i] == 'u')
new = add_to_sequence (XEXP (pattern, i), &new->success, newpos);
else if (fmt[i] == 'i' && i == 0)
{
this->test_elt_zero_int = 1;
this->elt_zero_int = XINT (pattern, i);
}
else if (fmt[i] == 'i' && i == 1)
{
this->test_elt_one_int = 1;
this->elt_one_int = XINT (pattern, i);
}
else if (fmt[i] == 'w' && i == 0)
{
this->test_elt_zero_wide = 1;
this->elt_zero_wide = XWINT (pattern, i);
}
else if (fmt[i] == 'E')
{
register int j;
if (i != 0)
abort ();
this->veclen = XVECLEN (pattern, i);
for (j = 0; j < XVECLEN (pattern, i); j++)
{
newpos[depth] = 'a' + j;
new = add_to_sequence (XVECEXP (pattern, i, j),
&new->success, newpos);
}
}
else if (fmt[i] != '0')
abort ();
}
return new;
}
static int
not_both_true (d1, d2, toplevel)
struct decision *d1, *d2;
int toplevel;
{
struct decision *p1, *p2;
if ((d1->enforce_mode && d2->enforce_mode
&& d1->mode != VOIDmode && d2->mode != VOIDmode && d1->mode != d2->mode)
|| (d1->code != UNKNOWN && d2->code != UNKNOWN && d1->code != d2->code)
|| (d1->test_elt_zero_int && d2->test_elt_zero_int
&& d1->elt_zero_int != d2->elt_zero_int)
|| (d1->test_elt_one_int && d2->test_elt_one_int
&& d1->elt_one_int != d2->elt_one_int)
|| (d1->test_elt_zero_wide && d2->test_elt_zero_wide
&& d1->elt_zero_wide != d2->elt_zero_wide)
|| (d1->veclen && d2->veclen && d1->veclen != d2->veclen))
return 1;
if ((d1->tests == 0 && d1->code == UNKNOWN)
|| (d2->tests == 0 && d2->code == UNKNOWN))
return 0;
if (d1->pred >= 0 || d2->pred >= 0)
{
int i, j;
if (d2->pred >= 0)
p1 = d1, d1 = d2, d2 = p1;
if (d2->code != UNKNOWN)
{
for (i = 0; i < NUM_RTX_CODE && preds[d1->pred].codes[i] != 0; i++)
if (preds[d1->pred].codes[i] == d2->code)
break;
if (preds[d1->pred].codes[i] == 0)
return 1;
}
else if (d2->pred >= 0)
{
for (i = 0; i < NUM_RTX_CODE && preds[d1->pred].codes[i] != 0; i++)
{
for (j = 0; j < NUM_RTX_CODE; j++)
if (preds[d2->pred].codes[j] == 0
|| preds[d2->pred].codes[j] == preds[d1->pred].codes[i])
break;
if (preds[d2->pred].codes[j] != 0)
break;
}
if (preds[d1->pred].codes[i] == 0)
return 1;
}
}
if (toplevel || d1->success.first == 0 || d2->success.first == 0)
return 0;
for (p1 = d1->success.first; p1; p1 = p1->next)
for (p2 = d2->success.first; p2; p2 = p2->next)
if (! not_both_true (p1, p2, 0))
return 0;
return 1;
}
static int
position_merit (p, mode, code)
struct decision *p;
enum machine_mode mode;
enum rtx_code code;
{
enum machine_mode p_mode;
if (p == 0)
return mode == VOIDmode ? 3 : 2;
p_mode = p->enforce_mode ? p->mode : VOIDmode;
if (p_mode == mode && p->code== code)
return 0;
if ((code != UNKNOWN
&& ((p_mode == mode && p->code != UNKNOWN)
|| (p_mode != mode && p->next
&& (p->next->enforce_mode ? p->next->mode : VOIDmode) == mode
&& (p->next->code == UNKNOWN))))
|| (code == UNKNOWN && p_mode == mode
&& (p->next == 0
|| (p->next->enforce_mode ? p->next->mode : VOIDmode) != mode)))
return 1;
if (p_mode != mode
&& ((mode != VOIDmode && p_mode != VOIDmode)
|| (mode == VOIDmode && p->next == 0)))
return 2;
return 3;
}
static struct decision_head
merge_trees (oldh, addh)
register struct decision_head oldh, addh;
{
struct decision *add, *next;
if (oldh.first == 0)
return addh;
if (addh.first == 0)
return oldh;
if (strcmp (oldh.first->position, addh.first->position))
abort ();
for (add = addh.first; add; add = next)
{
enum machine_mode add_mode = add->enforce_mode ? add->mode : VOIDmode;
struct decision *best_position = 0;
int best_merit = 4;
struct decision *old;
next = add->next;
for (old = oldh.last; old; old = old->prev)
{
int our_merit;
if (old->tests == 0 && old->test_elt_zero_int == 0
&& old->test_elt_one_int == 0 && old->veclen == 0
&& old->test_elt_zero_wide == 0
&& old->dupno == -1 && old->mode == VOIDmode
&& old->code == UNKNOWN
&& (old->c_test != 0 || add->c_test != 0))
;
else if ((old->tests == add->tests
|| (old->pred >= 0 && old->pred == add->pred)
|| (old->tests && add->tests
&& !strcmp (old->tests, add->tests)))
&& old->test_elt_zero_int == add->test_elt_zero_int
&& old->elt_zero_int == add->elt_zero_int
&& old->test_elt_one_int == add->test_elt_one_int
&& old->elt_one_int == add->elt_one_int
&& old->test_elt_zero_wide == add->test_elt_zero_wide
&& old->elt_zero_wide == add->elt_zero_wide
&& old->veclen == add->veclen
&& old->dupno == add->dupno
&& old->opno == add->opno
&& old->code == add->code
&& old->enforce_mode == add->enforce_mode
&& old->mode == add->mode)
{
if (old->c_test != add->c_test
&& ! (old->c_test && add->c_test
&& !strcmp (old->c_test, add->c_test)))
{
if (old->insn_code_number >= 0 || old->opno >= 0)
{
struct decision *split
= (struct decision *) xmalloc (sizeof (struct decision));
memcpy (split, old, sizeof (struct decision));
old->success.first = old->success.last = split;
old->c_test = 0;
old->opno = -1;
old->insn_code_number = -1;
old->num_clobbers_to_add = 0;
split->number = next_number++;
split->next = split->prev = 0;
split->mode = VOIDmode;
split->code = UNKNOWN;
split->veclen = 0;
split->test_elt_zero_int = 0;
split->test_elt_one_int = 0;
split->test_elt_zero_wide = 0;
split->tests = 0;
split->pred = -1;
split->dupno = -1;
}
if (add->insn_code_number >= 0 || add->opno >= 0)
{
struct decision *split
= (struct decision *) xmalloc (sizeof (struct decision));
memcpy (split, add, sizeof (struct decision));
add->success.first = add->success.last = split;
add->c_test = 0;
add->opno = -1;
add->insn_code_number = -1;
add->num_clobbers_to_add = 0;
split->number = next_number++;
split->next = split->prev = 0;
split->mode = VOIDmode;
split->code = UNKNOWN;
split->veclen = 0;
split->test_elt_zero_int = 0;
split->test_elt_one_int = 0;
split->test_elt_zero_wide = 0;
split->tests = 0;
split->pred = -1;
split->dupno = -1;
}
}
if (old->insn_code_number >= 0 && add->insn_code_number >= 0)
{
if (old->num_clobbers_to_add == 0
&& add->num_clobbers_to_add > 0)
;
else if (old->num_clobbers_to_add > 0
&& add->num_clobbers_to_add == 0)
{
old->insn_code_number = add->insn_code_number;
old->num_clobbers_to_add = 0;
}
else
fatal ("Two actions at one point in tree for insns \"%s\" (%d) and \"%s\" (%d)",
insn_name_ptr[old->insn_code_number],
old->insn_code_number,
insn_name_ptr[add->insn_code_number],
add->insn_code_number);
}
if (old->insn_code_number == -1)
old->insn_code_number = add->insn_code_number;
old->success = merge_trees (old->success, add->success);
add = 0;
break;
}
if (best_merit != 0
&& ((our_merit = position_merit (old, add_mode, add->code))
< best_merit))
best_merit = our_merit, best_position = old;
if (! not_both_true (old, add, 0))
break;
}
if (add == 0)
continue;
if (best_position == 0)
abort ();
if (old == 0
&& position_merit (NULL_PTR, add_mode, add->code) < best_merit)
{
add->prev = 0;
add->next = oldh.first;
oldh.first->prev = add;
oldh.first = add;
}
else
{
add->prev = best_position;
add->next = best_position->next;
best_position->next = add;
if (best_position == oldh.last)
oldh.last = add;
else
add->next->prev = add;
}
}
return oldh;
}
static int
break_out_subroutines (head, type, initial)
struct decision_head head;
enum routine_type type;
int initial;
{
int size = 0;
struct decision *sub;
for (sub = head.first; sub; sub = sub->next)
size += 1 + break_out_subroutines (sub->success, type, 0);
if (size > SUBROUTINE_THRESHOLD && ! initial)
{
head.first->subroutine_number = ++next_subroutine_number;
write_subroutine (head.first, type);
size = 1;
}
return size;
}
static void
write_subroutine (tree, type)
struct decision *tree;
enum routine_type type;
{
int i;
if (type == SPLIT)
printf ("rtx\nsplit");
else
printf ("int\nrecog");
if (tree != 0 && tree->subroutine_number > 0)
printf ("_%d", tree->subroutine_number);
else if (type == SPLIT)
printf ("_insns");
printf (" (x0, insn");
if (type == RECOG)
printf (", pnum_clobbers");
printf (")\n");
printf (" register rtx x0;\n rtx insn ATTRIBUTE_UNUSED;\n");
if (type == RECOG)
printf (" int *pnum_clobbers ATTRIBUTE_UNUSED;\n");
printf ("{\n");
printf (" register rtx *ro = &recog_operand[0];\n");
printf (" register rtx ");
for (i = 1; i < max_depth; i++)
printf ("x%d ATTRIBUTE_UNUSED, ", i);
printf ("x%d ATTRIBUTE_UNUSED;\n", max_depth);
printf (" %s tem ATTRIBUTE_UNUSED;\n", type == SPLIT ? "rtx" : "int");
write_tree (tree, "", NULL_PTR, 1, type);
printf (" ret0: return %d;\n}\n\n", type == SPLIT ? 0 : -1);
}
static const char *indents[]
= {" ", " ", " ", " ", " ", " ", " ", " ",
"\t", "\t ", "\t ", "\t ", "\t ", "\t ", "\t ",
"\t\t", "\t\t ", "\t\t ", "\t\t ", "\t\t ", "\t\t "};
static void
write_tree_1 (tree, prevpos, afterward, type)
struct decision *tree;
const char *prevpos;
struct decision *afterward;
enum routine_type type;
{
register struct decision *p, *p1;
register int depth = tree ? strlen (tree->position) : 0;
enum machine_mode switch_mode = VOIDmode;
RTX_CODE switch_code = UNKNOWN;
int uncond = 0;
char modemap[NUM_MACHINE_MODES];
char codemap[NUM_RTX_CODE];
int indent = 2;
int i;
printf ("\n");
if (tree && tree->subroutine_number == 0)
{
OUTPUT_LABEL (" ", tree->number);
tree->label_needed = 0;
}
if (tree)
{
change_state (prevpos, tree->position, 2);
prevpos = tree->position;
}
for (p = tree; p; p = p->next)
{
enum machine_mode mode = p->enforce_mode ? p->mode : VOIDmode;
int need_bracket;
int wrote_bracket = 0;
int inner_indent;
if (p->success.first == 0 && p->insn_code_number < 0)
abort ();
for (p1 = p->next; p1 && not_both_true (p, p1, 1); p1 = p1->next)
;
p->afterward = p1;
if (p1)
{
if (mode == VOIDmode && p1->enforce_mode && p1->mode != VOIDmode)
p1->retest_mode = 1;
if (p->code == UNKNOWN && p1->code != UNKNOWN)
p1->retest_code = 1;
p1->label_needed = 1;
}
if (switch_code != UNKNOWN
&& (switch_code != p->code || switch_mode != mode
|| (p->label_needed && (p->retest_mode || p->retest_code))))
{
enum rtx_code code = p->code;
if (p->pred >= 0)
{
for (i = 0; i < NUM_RTX_CODE && preds[p->pred].codes[i] != 0; i++)
if (codemap[(int) preds[p->pred].codes[i]])
break;
if (preds[p->pred].codes[i] == 0)
code = MATCH_OPERAND;
}
if (code == UNKNOWN || codemap[(int) code]
|| switch_mode != mode
|| (p->label_needed && (p->retest_mode || p->retest_code)))
{
printf ("%s}\n", indents[indent - 2]);
switch_code = UNKNOWN;
indent -= 4;
}
else
{
if (! uncond)
printf ("%sbreak;\n", indents[indent]);
if (code == MATCH_OPERAND)
{
for (i = 0; i < NUM_RTX_CODE && preds[p->pred].codes[i] != 0; i++)
{
printf ("%scase ", indents[indent - 2]);
print_code (preds[p->pred].codes[i]);
printf (":\n");
codemap[(int) preds[p->pred].codes[i]] = 1;
}
}
else
{
printf ("%scase ", indents[indent - 2]);
print_code (code);
printf (":\n");
codemap[(int) p->code] = 1;
}
switch_code = code;
}
uncond = 0;
}
if (switch_mode != VOIDmode
&& (switch_mode != mode || (p->label_needed && p->retest_mode)))
{
if (mode == VOIDmode || modemap[(int) mode]
|| (p->label_needed && p->retest_mode))
{
printf ("%s}\n", indents[indent - 2]);
switch_mode = VOIDmode;
indent -= 4;
}
else
{
if (! uncond)
printf (" break;\n");
printf (" case %smode:\n", GET_MODE_NAME (mode));
switch_mode = mode;
modemap[(int) mode] = 1;
}
uncond = 0;
}
if (! p->label_needed && uncond)
abort ();
if (p->label_needed && (p->retest_mode || p->retest_code))
{
OUTPUT_LABEL (indents[indent - 2], p->number);
p->label_needed = 0;
}
uncond = 0;
if (switch_mode == VOIDmode && switch_code == UNKNOWN)
{
if (mode != VOIDmode && p->next && same_modes (p, mode))
{
printf (" if (GET_MODE (x%d) != %smode)\n",
depth, GET_MODE_NAME (p->mode));
if (afterward)
{
printf (" {\n");
change_state (p->position, afterward->position, 6);
printf (" goto L%d;\n }\n", afterward->number);
}
else
printf (" goto ret0;\n");
clear_modes (p);
mode = VOIDmode;
}
if (p->code != UNKNOWN && p->next && same_codes (p, p->code))
{
printf (" if (GET_CODE (x%d) != ", depth);
print_code (p->code);
printf (")\n");
if (afterward)
{
printf (" {\n");
change_state (p->position, afterward->position, indent + 4);
printf (" goto L%d;\n }\n", afterward->number);
}
else
printf (" goto ret0;\n");
clear_codes (p);
}
}
if (switch_mode == VOIDmode && mode != VOIDmode && p->next != 0
&& p->next->enforce_mode && p->next->mode != VOIDmode)
{
memset (modemap, 0, sizeof modemap);
printf ("%sswitch (GET_MODE (x%d))\n", indents[indent], depth);
printf ("%s{\n", indents[indent + 2]);
indent += 4;
printf ("%sdefault:\n%sbreak;\n", indents[indent - 2],
indents[indent]);
printf ("%scase %smode:\n", indents[indent - 2],
GET_MODE_NAME (mode));
modemap[(int) mode] = 1;
switch_mode = mode;
}
if (switch_code == UNKNOWN && p->code != UNKNOWN && ! p->ignore_code
&& p->next != 0 && p->next->code != UNKNOWN)
{
memset (codemap, 0, sizeof codemap);
printf ("%sswitch (GET_CODE (x%d))\n", indents[indent], depth);
printf ("%s{\n", indents[indent + 2]);
indent += 4;
printf ("%sdefault:\n%sbreak;\n", indents[indent - 2],
indents[indent]);
printf ("%scase ", indents[indent - 2]);
print_code (p->code);
printf (":\n");
codemap[(int) p->code] = 1;
switch_code = p->code;
}
if (p->label_needed)
OUTPUT_LABEL (indents[indent - 2], p->number);
inner_indent = indent;
if ((mode != switch_mode && ! p->ignore_mode)
|| (p->code != switch_code && p->code != UNKNOWN && ! p->ignore_code)
|| p->test_elt_zero_int || p->test_elt_one_int
|| p->test_elt_zero_wide || p->veclen
|| p->dupno >= 0 || p->tests || p->num_clobbers_to_add)
{
printf ("%sif (", indents[indent]);
if (mode != switch_mode && ! p->ignore_mode)
printf ("GET_MODE (x%d) == %smode && ",
depth, GET_MODE_NAME (mode));
if (p->code != switch_code && p->code != UNKNOWN && ! p->ignore_code)
{
printf ("GET_CODE (x%d) == ", depth);
print_code (p->code);
printf (" && ");
}
if (p->test_elt_zero_int)
printf ("XINT (x%d, 0) == %d && ", depth, p->elt_zero_int);
if (p->test_elt_one_int)
printf ("XINT (x%d, 1) == %d && ", depth, p->elt_one_int);
if (p->test_elt_zero_wide)
{
HOST_WIDE_INT offset = p->elt_zero_wide == -2147483647 - 1;
printf ("XWINT (x%d, 0) == ", depth);
printf (HOST_WIDE_INT_PRINT_DEC, p->elt_zero_wide + offset);
printf ("%s && ", offset ? "-1" : "");
}
if (p->veclen)
printf ("XVECLEN (x%d, 0) == %d && ", depth, p->veclen);
if (p->dupno >= 0)
printf ("rtx_equal_p (x%d, ro[%d]) && ", depth, p->dupno);
if (p->num_clobbers_to_add)
printf ("pnum_clobbers != 0 && ");
if (p->tests)
printf ("%s (x%d, %smode)", p->tests, depth,
GET_MODE_NAME (p->mode));
else
printf ("1");
printf (")\n");
inner_indent += 2;
}
else
uncond = 1;
need_bracket = ! uncond;
if (p->opno >= 0)
{
if (need_bracket)
{
printf ("%s{\n", indents[inner_indent]);
inner_indent += 2;
wrote_bracket = 1;
need_bracket = 0;
}
printf ("%sro[%d] = x%d;\n", indents[inner_indent], p->opno, depth);
}
if (p->c_test)
{
printf ("%sif (%s)\n", indents[inner_indent], p->c_test);
inner_indent += 2;
uncond = 0;
need_bracket = 1;
}
if (p->insn_code_number >= 0)
{
if (type == SPLIT)
printf ("%sreturn gen_split_%d (operands);\n",
indents[inner_indent], p->insn_code_number);
else
{
if (p->num_clobbers_to_add)
{
if (need_bracket)
{
printf ("%s{\n", indents[inner_indent]);
inner_indent += 2;
}
printf ("%s*pnum_clobbers = %d;\n",
indents[inner_indent], p->num_clobbers_to_add);
printf ("%sreturn %d;\n",
indents[inner_indent], p->insn_code_number);
if (need_bracket)
{
inner_indent -= 2;
printf ("%s}\n", indents[inner_indent]);
}
}
else
printf ("%sreturn %d;\n",
indents[inner_indent], p->insn_code_number);
}
}
else
printf ("%sgoto L%d;\n", indents[inner_indent],
p->success.first->number);
if (wrote_bracket)
printf ("%s}\n", indents[inner_indent - 2]);
}
if (switch_code != UNKNOWN)
{
printf ("%s}\n", indents[indent - 2]);
indent -= 4;
uncond = 0;
}
if (switch_mode != VOIDmode)
{
printf ("%s}\n", indents[indent - 2]);
indent -= 4;
uncond = 0;
}
if (indent != 2)
abort ();
if (uncond)
return;
if (afterward)
{
change_state (prevpos, afterward->position, 2);
printf (" goto L%d;\n", afterward->number);
}
else
printf (" goto ret0;\n");
}
static void
print_code (code)
enum rtx_code code;
{
register char *p1;
for (p1 = GET_RTX_NAME (code); *p1; p1++)
{
if (*p1 >= 'a' && *p1 <= 'z')
putchar (*p1 + 'A' - 'a');
else
putchar (*p1);
}
}
static int
same_codes (p, code)
register struct decision *p;
register enum rtx_code code;
{
for (; p; p = p->next)
if (p->code != code)
return 0;
return 1;
}
static void
clear_codes (p)
register struct decision *p;
{
for (; p; p = p->next)
p->ignore_code = 1;
}
static int
same_modes (p, mode)
register struct decision *p;
register enum machine_mode mode;
{
for (; p; p = p->next)
if ((p->enforce_mode ? p->mode : VOIDmode) != mode)
return 0;
return 1;
}
static void
clear_modes (p)
register struct decision *p;
{
for (; p; p = p->next)
p->enforce_mode = 0;
}
static void
write_tree (tree, prevpos, afterward, initial, type)
struct decision *tree;
const char *prevpos;
struct decision *afterward;
int initial;
enum routine_type type;
{
register struct decision *p;
const char *name_prefix = (type == SPLIT ? "split" : "recog");
const char *call_suffix = (type == SPLIT ? "" : ", pnum_clobbers");
if (! initial && tree->subroutine_number > 0)
{
OUTPUT_LABEL (" ", tree->number);
if (afterward)
{
printf (" tem = %s_%d (x0, insn%s);\n",
name_prefix, tree->subroutine_number, call_suffix);
if (type == SPLIT)
printf (" if (tem != 0) return tem;\n");
else
printf (" if (tem >= 0) return tem;\n");
change_state (tree->position, afterward->position, 2);
printf (" goto L%d;\n", afterward->number);
}
else
printf (" return %s_%d (x0, insn%s);\n",
name_prefix, tree->subroutine_number, call_suffix);
return;
}
write_tree_1 (tree, prevpos, afterward, type);
for (p = tree; p; p = p->next)
if (p->success.first)
write_tree (p->success.first, p->position,
p->afterward ? p->afterward : afterward, 0, type);
}
static void
change_state (oldpos, newpos, indent)
const char *oldpos;
const char *newpos;
int indent;
{
int odepth = strlen (oldpos);
int depth = odepth;
int ndepth = strlen (newpos);
while (strncmp (oldpos, newpos, depth))
--depth;
while (depth < ndepth)
{
if (newpos[depth] >= 'a' && newpos[depth] <= 'z')
printf ("%sx%d = XVECEXP (x%d, 0, %d);\n",
indents[indent], depth + 1, depth, newpos[depth] - 'a');
else
printf ("%sx%d = XEXP (x%d, %c);\n",
indents[indent], depth + 1, depth, newpos[depth]);
++depth;
}
}
char *
xstrdup (input)
const char *input;
{
register size_t len = strlen (input) + 1;
register char *output = xmalloc (len);
memcpy (output, input, len);
return output;
}
PTR
xrealloc (old, size)
PTR old;
size_t size;
{
register PTR ptr;
if (old)
ptr = (PTR) realloc (old, size);
else
ptr = (PTR) malloc (size);
if (!ptr)
fatal ("virtual memory exhausted");
return ptr;
}
PTR
xmalloc (size)
size_t size;
{
register PTR val = (PTR) malloc (size);
if (val == 0)
fatal ("virtual memory exhausted");
return val;
}
void
fatal VPROTO ((const char *format, ...))
{
#ifndef ANSI_PROTOTYPES
const char *format;
#endif
va_list ap;
VA_START (ap, format);
#ifndef ANSI_PROTOTYPES
format = va_arg (ap, const char *);
#endif
fprintf (stderr, "genrecog: ");
vfprintf (stderr, format, ap);
va_end (ap);
fprintf (stderr, "\n");
fprintf (stderr, "after %d definitions\n", next_index);
exit (FATAL_EXIT_CODE);
}
void
fancy_abort ()
{
fatal ("Internal gcc abort.");
}
int
main (argc, argv)
int argc;
char **argv;
{
rtx desc;
struct decision_head recog_tree;
struct decision_head split_tree;
FILE *infile;
register int c;
obstack_init (rtl_obstack);
recog_tree.first = recog_tree.last = split_tree.first = split_tree.last = 0;
if (argc <= 1)
fatal ("No input file name.");
infile = fopen (argv[1], "r");
if (infile == 0)
{
perror (argv[1]);
exit (FATAL_EXIT_CODE);
}
init_rtl ();
next_insn_code = 0;
next_index = 0;
printf ("/* Generated automatically by the program `genrecog'\n\
from the machine description file `md'. */\n\n");
printf ("#include \"config.h\"\n");
printf ("#include \"system.h\"\n");
printf ("#include \"rtl.h\"\n");
printf ("#include \"insn-config.h\"\n");
printf ("#include \"recog.h\"\n");
printf ("#include \"real.h\"\n");
printf ("#include \"output.h\"\n");
printf ("#include \"flags.h\"\n");
printf ("\n");
while (1)
{
c = read_skip_spaces (infile);
if (c == EOF)
break;
ungetc (c, infile);
desc = read_rtx (infile);
if (GET_CODE (desc) == DEFINE_INSN)
recog_tree = merge_trees (recog_tree,
make_insn_sequence (desc, RECOG));
else if (GET_CODE (desc) == DEFINE_SPLIT)
split_tree = merge_trees (split_tree,
make_insn_sequence (desc, SPLIT));
if (GET_CODE (desc) == DEFINE_PEEPHOLE
|| GET_CODE (desc) == DEFINE_EXPAND)
next_insn_code++;
next_index++;
}
printf ("\n\
/* `recog' contains a decision tree\n\
that recognizes whether the rtx X0 is a valid instruction.\n\
\n\
recog returns -1 if the rtx is not valid.\n\
If the rtx is valid, recog returns a nonnegative number\n\
which is the insn code number for the pattern that matched.\n");
printf (" This is the same as the order in the machine description of\n\
the entry that matched. This number can be used as an index into various\n\
insn_* tables, such as insn_templates, insn_outfun, and insn_n_operands\n\
(found in insn-output.c).\n\n");
printf (" The third argument to recog is an optional pointer to an int.\n\
If present, recog will accept a pattern if it matches except for\n\
missing CLOBBER expressions at the end. In that case, the value\n\
pointed to by the optional pointer will be set to the number of\n\
CLOBBERs that need to be added (it should be initialized to zero by\n\
the caller). If it is set nonzero, the caller should allocate a\n\
PARALLEL of the appropriate size, copy the initial entries, and call\n\
add_clobbers (found in insn-emit.c) to fill in the CLOBBERs.");
if (split_tree.first)
printf ("\n\n The function split_insns returns 0 if the rtl could not\n\
be split or the split rtl in a SEQUENCE if it can be.");
printf ("*/\n\n");
printf ("#define operands recog_operand\n\n");
next_subroutine_number = 0;
break_out_subroutines (recog_tree, RECOG, 1);
write_subroutine (recog_tree.first, RECOG);
next_subroutine_number = 0;
break_out_subroutines (split_tree, SPLIT, 1);
write_subroutine (split_tree.first, SPLIT);
fflush (stdout);
exit (ferror (stdout) != 0 ? FATAL_EXIT_CODE : SUCCESS_EXIT_CODE);
return 0;
}