c-parse.in (asm_operand): Allow named operands.
* c-parse.in (asm_operand): Allow named operands. * genconfig.c (max_recog_operands): Set to 29. * local-alloc.c (requires_inout): Skip multiple digits. * recog.c (asm_operand_ok): Likewise. (preprocess_constraints): Use strtoul for matching constraints. (constrain_operands): Likewise. * regmove.c (find_matches): Likewise. * reload.c (find_reloads): Likewise. * stmt.c (parse_output_constraint): Don't reject in-out constraint on operands > 9. Reject '[' in constraint. (expand_asm_operands): Handle named operands. Use strtoul for matching constraints. (check_operand_nalternatives): Split out from expand_asm_operands. (check_unique_operand_names): New. (resolve_operand_names, resolve_operand_name_1): New. * doc/extend.texi (Extended Asm): Document named operands. * doc/md.texi (Simple Constraints): Document matching constraints on operands > 9. * parse.y (asm_operand): Allow named operands. * semantics.c (finish_asm_stmt): Tweek for changed location of the operand constrant. From-SVN: r46179
This commit is contained in:
parent
592188a538
commit
84b7230235
@ -1,3 +1,25 @@
|
||||
2001-10-10 Richard Henderson <rth@redhat.com>
|
||||
|
||||
* c-parse.in (asm_operand): Allow named operands.
|
||||
* genconfig.c (max_recog_operands): Set to 29.
|
||||
* local-alloc.c (requires_inout): Skip multiple digits.
|
||||
* recog.c (asm_operand_ok): Likewise.
|
||||
(preprocess_constraints): Use strtoul for matching constraints.
|
||||
(constrain_operands): Likewise.
|
||||
* regmove.c (find_matches): Likewise.
|
||||
* reload.c (find_reloads): Likewise.
|
||||
* stmt.c (parse_output_constraint): Don't reject in-out
|
||||
constraint on operands > 9. Reject '[' in constraint.
|
||||
(expand_asm_operands): Handle named operands. Use strtoul
|
||||
for matching constraints.
|
||||
(check_operand_nalternatives): Split out from expand_asm_operands.
|
||||
(check_unique_operand_names): New.
|
||||
(resolve_operand_names, resolve_operand_name_1): New.
|
||||
|
||||
* doc/extend.texi (Extended Asm): Document named operands.
|
||||
* doc/md.texi (Simple Constraints): Document matching constraints
|
||||
on operands > 9.
|
||||
|
||||
2001-10-10 Richard Henderson <rth@redhat.com>
|
||||
|
||||
* combine.c (try_combine): Handle a SEQUENCE of one insn.
|
||||
|
@ -2454,7 +2454,9 @@ nonnull_asm_operands:
|
||||
|
||||
asm_operand:
|
||||
STRING '(' expr ')'
|
||||
{ $$ = build_tree_list ($1, $3); }
|
||||
{ $$ = build_tree_list (build_tree_list (NULL_TREE, $1), $3); }
|
||||
| '[' identifier ']' STRING '(' expr ')'
|
||||
{ $$ = build_tree_list (build_tree_list ($2, $4), $6); }
|
||||
;
|
||||
|
||||
asm_clobbers:
|
||||
|
@ -1,3 +1,9 @@
|
||||
2001-10-10 Richard Henderson <rth@redhat.com>
|
||||
|
||||
* parse.y (asm_operand): Allow named operands.
|
||||
* semantics.c (finish_asm_stmt): Tweek for changed location
|
||||
of the operand constrant.
|
||||
|
||||
2001-10-09 Jason Merrill <jason_merrill@redhat.com>
|
||||
|
||||
* call.c (standard_conversion): Add bad conversion between
|
||||
|
@ -3607,7 +3607,9 @@ nonnull_asm_operands:
|
||||
|
||||
asm_operand:
|
||||
STRING '(' expr ')'
|
||||
{ $$ = build_tree_list ($$, $3); }
|
||||
{ $$ = build_tree_list (build_tree_list (NULL_TREE, $1), $3); }
|
||||
| '[' identifier ']' STRING '(' expr ')'
|
||||
{ $$ = build_tree_list (build_tree_list ($2, $4), $6); }
|
||||
;
|
||||
|
||||
asm_clobbers:
|
||||
|
@ -929,7 +929,7 @@ finish_asm_stmt (cv_qualifier, string, output_operands,
|
||||
const char *constraint;
|
||||
tree operand;
|
||||
|
||||
constraint = TREE_STRING_POINTER (TREE_PURPOSE (t));
|
||||
constraint = TREE_STRING_POINTER (TREE_VALUE (TREE_PURPOSE (t)));
|
||||
operand = TREE_VALUE (output_operands);
|
||||
|
||||
if (!parse_output_constraint (&constraint,
|
||||
|
@ -3382,14 +3382,34 @@ Each operand is described by an operand-constraint string followed by
|
||||
the C expression in parentheses. A colon separates the assembler
|
||||
template from the first output operand and another separates the last
|
||||
output operand from the first input, if any. Commas separate the
|
||||
operands within each group. The total number of operands is limited to
|
||||
ten or to the maximum number of operands in any instruction pattern in
|
||||
the machine description, whichever is greater.
|
||||
operands within each group. The total number of operands is currently
|
||||
limited to 30; this limitation may be lifted in some future version of
|
||||
GCC.
|
||||
|
||||
If there are no output operands but there are input operands, you must
|
||||
place two consecutive colons surrounding the place where the output
|
||||
operands would go.
|
||||
|
||||
As of GCC version 3.1, it is also possible to specify input and output
|
||||
operands using symbolic names which can be referenced within the
|
||||
assembler code. These names are specified inside square brackets
|
||||
preceding the constraint string, and can be referenced inside the
|
||||
assembler code using @code{%[@var{name}]} instead of a percentage sign
|
||||
followed by the operand number. Using named operands the above example
|
||||
could look like:
|
||||
|
||||
@example
|
||||
asm ("fsinx %[angle],%[output]"
|
||||
: [output] "=f" (result)
|
||||
: [angle] "f" (angle));
|
||||
@end example
|
||||
|
||||
@noindent
|
||||
Note that the symbolic operand names have no relation whatsoever to
|
||||
other C identifiers. You may use any name you like, even those of
|
||||
existing C symbols, but must ensure that no two operands within the same
|
||||
assembler construct use the same symbolic name.
|
||||
|
||||
Output operand expressions must be lvalues; the compiler can check this.
|
||||
The input operands need not be lvalues. The compiler cannot check
|
||||
whether the operands have data types that are reasonable for the
|
||||
@ -3425,10 +3445,10 @@ asm ("combine %2,%0" : "=r" (foo) : "0" (foo), "g" (bar));
|
||||
|
||||
@noindent
|
||||
The constraint @samp{"0"} for operand 1 says that it must occupy the
|
||||
same location as operand 0. A digit in constraint is allowed only in an
|
||||
input operand and it must refer to an output operand.
|
||||
same location as operand 0. A number in constraint is allowed only in
|
||||
an input operand and it must refer to an output operand.
|
||||
|
||||
Only a digit in the constraint can guarantee that one operand will be in
|
||||
Only a number in the constraint can guarantee that one operand will be in
|
||||
the same place as another. The mere fact that @code{foo} is the value
|
||||
of both operands is not enough to guarantee that they will be in the
|
||||
same place in the generated assembler code. The following would not
|
||||
@ -3446,6 +3466,15 @@ register (copying it afterward to @code{foo}'s own address). Of course,
|
||||
since the register for operand 1 is not even mentioned in the assembler
|
||||
code, the result will not work, but GCC can't tell that.
|
||||
|
||||
As of GCC version 3.1, one may write @code{[@var{name}]} instead of
|
||||
the operand number for a matching constraint. For example:
|
||||
|
||||
@example
|
||||
asm ("cmoveq %1,%2,%[result]"
|
||||
: [result] "=r"(result)
|
||||
: "r" (test), "r"(new), "[result]"(old));
|
||||
@end example
|
||||
|
||||
Some instructions clobber specific hard registers. To describe this,
|
||||
write a third colon after the input operands, followed by the names of
|
||||
the clobbered hard registers (given as strings). Here is a realistic
|
||||
|
@ -894,6 +894,13 @@ An operand that matches the specified operand number is allowed. If a
|
||||
digit is used together with letters within the same alternative, the
|
||||
digit should come last.
|
||||
|
||||
This number is allowed to be more than a single digit. If multiple
|
||||
digits are encountered consecutavely, they are interpreted as a single
|
||||
decimal integer. There is scant chance for ambiguity, since to-date
|
||||
it has never been desirable that @samp{10} be interpreted as matching
|
||||
either operand 1 @emph{or} operand 0. Should this be desired, one
|
||||
can use multiple alternatives instead.
|
||||
|
||||
@cindex matching constraint
|
||||
@cindex constraint, matching
|
||||
This is called a @dfn{matching constraint} and what it really means is
|
||||
|
@ -285,8 +285,10 @@ main (argc, argv)
|
||||
puts ("#ifndef GCC_INSN_CONFIG_H");
|
||||
puts ("#define GCC_INSN_CONFIG_H\n");
|
||||
|
||||
/* Allow at least 10 operands for the sake of asm constructs. */
|
||||
max_recog_operands = 9; /* We will add 1 later. */
|
||||
/* Allow at least 30 operands for the sake of asm constructs. */
|
||||
/* ??? We *really* ought to reorganize things such that there
|
||||
is no fixed upper bound. */
|
||||
max_recog_operands = 29; /* We will add 1 later. */
|
||||
max_dup_operands = 1;
|
||||
|
||||
/* Read the machine description. */
|
||||
|
@ -1302,7 +1302,7 @@ block_alloc (b)
|
||||
for (i = 1; i < recog_data.n_operands; i++)
|
||||
{
|
||||
const char *p = recog_data.constraints[i];
|
||||
int this_match = (requires_inout (p));
|
||||
int this_match = requires_inout (p);
|
||||
|
||||
n_matching_alts += this_match;
|
||||
if (this_match == recog_data.n_alternatives)
|
||||
@ -2409,8 +2409,6 @@ requires_inout (p)
|
||||
case '=': case '+': case '?':
|
||||
case '#': case '&': case '!':
|
||||
case '*': case '%':
|
||||
case '1': case '2': case '3': case '4': case '5':
|
||||
case '6': case '7': case '8': case '9':
|
||||
case 'm': case '<': case '>': case 'V': case 'o':
|
||||
case 'E': case 'F': case 'G': case 'H':
|
||||
case 's': case 'i': case 'n':
|
||||
@ -2431,6 +2429,13 @@ requires_inout (p)
|
||||
found_zero = 1;
|
||||
break;
|
||||
|
||||
case '1': case '2': case '3': case '4': case '5':
|
||||
case '6': case '7': case '8': case '9':
|
||||
/* Skip the balance of the matching constraint. */
|
||||
while (*p >= '0' && *p <= '9')
|
||||
p++;
|
||||
break;
|
||||
|
||||
default:
|
||||
if (REG_CLASS_FROM_LETTER (c) == NO_REGS)
|
||||
break;
|
||||
|
83
gcc/recog.c
83
gcc/recog.c
@ -1657,6 +1657,8 @@ asm_operand_ok (op, constraint)
|
||||
proper matching constraint, but we can't actually fail
|
||||
the check if they didn't. Indicate that results are
|
||||
inconclusive. */
|
||||
while (*constraint >= '0' && *constraint <= '9')
|
||||
constraint++;
|
||||
result = -1;
|
||||
break;
|
||||
|
||||
@ -2211,8 +2213,12 @@ preprocess_constraints ()
|
||||
|
||||
case '0': case '1': case '2': case '3': case '4':
|
||||
case '5': case '6': case '7': case '8': case '9':
|
||||
op_alt[j].matches = c - '0';
|
||||
recog_op_alt[op_alt[j].matches][j].matched = i;
|
||||
{
|
||||
char *end;
|
||||
op_alt[j].matches = strtoul (p - 1, &end, 10);
|
||||
recog_op_alt[op_alt[j].matches][j].matched = i;
|
||||
p = end;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'm':
|
||||
@ -2364,45 +2370,54 @@ constrain_operands (strict)
|
||||
|
||||
case '0': case '1': case '2': case '3': case '4':
|
||||
case '5': case '6': case '7': case '8': case '9':
|
||||
{
|
||||
/* This operand must be the same as a previous one.
|
||||
This kind of constraint is used for instructions such
|
||||
as add when they take only two operands.
|
||||
|
||||
/* This operand must be the same as a previous one.
|
||||
This kind of constraint is used for instructions such
|
||||
as add when they take only two operands.
|
||||
Note that the lower-numbered operand is passed first.
|
||||
|
||||
Note that the lower-numbered operand is passed first.
|
||||
If we are not testing strictly, assume that this
|
||||
constraint will be satisfied. */
|
||||
|
||||
If we are not testing strictly, assume that this constraint
|
||||
will be satisfied. */
|
||||
if (strict < 0)
|
||||
val = 1;
|
||||
else
|
||||
{
|
||||
rtx op1 = recog_data.operand[c - '0'];
|
||||
rtx op2 = recog_data.operand[opno];
|
||||
char *end;
|
||||
int match;
|
||||
|
||||
/* A unary operator may be accepted by the predicate,
|
||||
but it is irrelevant for matching constraints. */
|
||||
if (GET_RTX_CLASS (GET_CODE (op1)) == '1')
|
||||
op1 = XEXP (op1, 0);
|
||||
if (GET_RTX_CLASS (GET_CODE (op2)) == '1')
|
||||
op2 = XEXP (op2, 0);
|
||||
match = strtoul (p - 1, &end, 10);
|
||||
p = end;
|
||||
|
||||
val = operands_match_p (op1, op2);
|
||||
}
|
||||
if (strict < 0)
|
||||
val = 1;
|
||||
else
|
||||
{
|
||||
rtx op1 = recog_data.operand[match];
|
||||
rtx op2 = recog_data.operand[opno];
|
||||
|
||||
matching_operands[opno] = c - '0';
|
||||
matching_operands[c - '0'] = opno;
|
||||
/* A unary operator may be accepted by the predicate,
|
||||
but it is irrelevant for matching constraints. */
|
||||
if (GET_RTX_CLASS (GET_CODE (op1)) == '1')
|
||||
op1 = XEXP (op1, 0);
|
||||
if (GET_RTX_CLASS (GET_CODE (op2)) == '1')
|
||||
op2 = XEXP (op2, 0);
|
||||
|
||||
if (val != 0)
|
||||
win = 1;
|
||||
/* If output is *x and input is *--x,
|
||||
arrange later to change the output to *--x as well,
|
||||
since the output op is the one that will be printed. */
|
||||
if (val == 2 && strict > 0)
|
||||
{
|
||||
funny_match[funny_match_index].this = opno;
|
||||
funny_match[funny_match_index++].other = c - '0';
|
||||
}
|
||||
val = operands_match_p (op1, op2);
|
||||
}
|
||||
|
||||
matching_operands[opno] = match;
|
||||
matching_operands[match] = opno;
|
||||
|
||||
if (val != 0)
|
||||
win = 1;
|
||||
|
||||
/* If output is *x and input is *--x, arrange later
|
||||
to change the output to *--x as well, since the
|
||||
output op is the one that will be printed. */
|
||||
if (val == 2 && strict > 0)
|
||||
{
|
||||
funny_match[funny_match_index].this = opno;
|
||||
funny_match[funny_match_index++].other = match;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 'p':
|
||||
|
@ -1576,16 +1576,23 @@ find_matches (insn, matchp)
|
||||
matchp->commutative[op_no] = op_no + 1;
|
||||
matchp->commutative[op_no + 1] = op_no;
|
||||
break;
|
||||
|
||||
case '0': case '1': case '2': case '3': case '4':
|
||||
case '5': case '6': case '7': case '8': case '9':
|
||||
c -= '0';
|
||||
if (c < op_no && likely_spilled[(unsigned char) c])
|
||||
break;
|
||||
matchp->with[op_no] = c;
|
||||
any_matches = 1;
|
||||
if (matchp->commutative[op_no] >= 0)
|
||||
matchp->with[matchp->commutative[op_no]] = c;
|
||||
{
|
||||
char *end;
|
||||
unsigned long match = strtoul (p - 1, &end, 10);
|
||||
p = end;
|
||||
|
||||
if (match < op_no && likely_spilled[match])
|
||||
break;
|
||||
matchp->with[op_no] = match;
|
||||
any_matches = 1;
|
||||
if (matchp->commutative[op_no] >= 0)
|
||||
matchp->with[matchp->commutative[op_no]] = match;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'h':
|
||||
case 'j': case 'k': case 'l': case 'p': case 'q': case 't': case 'u':
|
||||
case 'v': case 'w': case 'x': case 'y': case 'z': case 'A': case 'B':
|
||||
|
@ -2551,7 +2551,8 @@ find_reloads (insn, replace, ind_levels, live_known, reload_reg_p)
|
||||
}
|
||||
else if (c >= '0' && c <= '9')
|
||||
{
|
||||
c -= '0';
|
||||
c = strtoul (p - 1, &p, 10);
|
||||
|
||||
operands_match[c][i]
|
||||
= operands_match_p (recog_data.operand[c],
|
||||
recog_data.operand[i]);
|
||||
@ -2939,8 +2940,8 @@ find_reloads (insn, replace, ind_levels, live_known, reload_reg_p)
|
||||
|
||||
case '0': case '1': case '2': case '3': case '4':
|
||||
case '5': case '6': case '7': case '8': case '9':
|
||||
c = strtoul (p - 1, &p, 10);
|
||||
|
||||
c -= '0';
|
||||
this_alternative_matches[i] = c;
|
||||
/* We are supposed to match a previous operand.
|
||||
If we do, we win if that one did.
|
||||
|
351
gcc/stmt.c
351
gcc/stmt.c
@ -404,6 +404,11 @@ static void expand_nl_goto_receiver PARAMS ((void));
|
||||
static void expand_nl_goto_receivers PARAMS ((struct nesting *));
|
||||
static void fixup_gotos PARAMS ((struct nesting *, rtx, tree,
|
||||
rtx, int));
|
||||
static bool check_operand_nalternatives PARAMS ((tree, tree));
|
||||
static bool check_unique_operand_names PARAMS ((tree, tree));
|
||||
static tree resolve_operand_names PARAMS ((tree, tree, tree,
|
||||
const char **));
|
||||
static char *resolve_operand_name_1 PARAMS ((char *, tree, tree));
|
||||
static void expand_null_return_1 PARAMS ((rtx));
|
||||
static void expand_value_return PARAMS ((rtx));
|
||||
static int tail_recursion_args PARAMS ((tree, tree));
|
||||
@ -1355,14 +1360,6 @@ parse_output_constraint (constraint_p,
|
||||
from and written to. */
|
||||
*is_inout = (*p == '+');
|
||||
|
||||
/* Make sure we can specify the matching operand. */
|
||||
if (*is_inout && operand_num > 9)
|
||||
{
|
||||
error ("output operand constraint %d contains `+'",
|
||||
operand_num);
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Canonicalize the output constraint so that it begins with `='. */
|
||||
if (p != constraint || is_inout)
|
||||
{
|
||||
@ -1416,6 +1413,7 @@ parse_output_constraint (constraint_p,
|
||||
|
||||
case '0': case '1': case '2': case '3': case '4':
|
||||
case '5': case '6': case '7': case '8': case '9':
|
||||
case '[':
|
||||
error ("matching constraint not valid in output operand");
|
||||
return false;
|
||||
|
||||
@ -1478,7 +1476,7 @@ expand_asm_operands (string, outputs, inputs, clobbers, vol, filename, line)
|
||||
const char *filename;
|
||||
int line;
|
||||
{
|
||||
rtvec argvec, constraints;
|
||||
rtvec argvec, constraintvec;
|
||||
rtx body;
|
||||
int ninputs = list_length (inputs);
|
||||
int noutputs = list_length (outputs);
|
||||
@ -1492,8 +1490,8 @@ expand_asm_operands (string, outputs, inputs, clobbers, vol, filename, line)
|
||||
rtx *real_output_rtx = (rtx *) alloca (noutputs * sizeof (rtx));
|
||||
enum machine_mode *inout_mode
|
||||
= (enum machine_mode *) alloca (noutputs * sizeof (enum machine_mode));
|
||||
const char **output_constraints
|
||||
= alloca (noutputs * sizeof (const char *));
|
||||
const char **constraints
|
||||
= (const char **) alloca ((noutputs + ninputs) * sizeof (const char *));
|
||||
/* The insn we have emitted. */
|
||||
rtx insn;
|
||||
int old_generating_concat_p = generating_concat_p;
|
||||
@ -1504,10 +1502,18 @@ expand_asm_operands (string, outputs, inputs, clobbers, vol, filename, line)
|
||||
|
||||
if (current_function_check_memory_usage)
|
||||
{
|
||||
error ("`asm' cannot be used with `-fcheck-memory-usage'");
|
||||
error ("`asm' cannot be used in function where memory usage is checked");
|
||||
return;
|
||||
}
|
||||
|
||||
if (! check_operand_nalternatives (outputs, inputs))
|
||||
return;
|
||||
|
||||
if (! check_unique_operand_names (outputs, inputs))
|
||||
return;
|
||||
|
||||
string = resolve_operand_names (string, outputs, inputs, constraints);
|
||||
|
||||
#ifdef MD_ASM_CLOBBERS
|
||||
/* Sometimes we wish to automatically clobber registers across an asm.
|
||||
Case in point is when the i386 backend moved from cc0 to a hard reg --
|
||||
@ -1516,12 +1522,6 @@ expand_asm_operands (string, outputs, inputs, clobbers, vol, filename, line)
|
||||
MD_ASM_CLOBBERS (clobbers);
|
||||
#endif
|
||||
|
||||
if (current_function_check_memory_usage)
|
||||
{
|
||||
error ("`asm' cannot be used in function where memory usage is checked");
|
||||
return;
|
||||
}
|
||||
|
||||
/* Count the number of meaningful clobbered registers, ignoring what
|
||||
we would ignore later. */
|
||||
nclobbers = 0;
|
||||
@ -1538,43 +1538,10 @@ expand_asm_operands (string, outputs, inputs, clobbers, vol, filename, line)
|
||||
|
||||
last_expr_type = 0;
|
||||
|
||||
/* Check that the number of alternatives is constant across all
|
||||
operands. */
|
||||
if (outputs || inputs)
|
||||
{
|
||||
tree tmp = TREE_PURPOSE (outputs ? outputs : inputs);
|
||||
int nalternatives = n_occurrences (',', TREE_STRING_POINTER (tmp));
|
||||
tree next = inputs;
|
||||
|
||||
if (nalternatives + 1 > MAX_RECOG_ALTERNATIVES)
|
||||
{
|
||||
error ("too many alternatives in `asm'");
|
||||
return;
|
||||
}
|
||||
|
||||
tmp = outputs;
|
||||
while (tmp)
|
||||
{
|
||||
const char *constraint = TREE_STRING_POINTER (TREE_PURPOSE (tmp));
|
||||
|
||||
if (n_occurrences (',', constraint) != nalternatives)
|
||||
{
|
||||
error ("operand constraints for `asm' differ in number of alternatives");
|
||||
return;
|
||||
}
|
||||
|
||||
if (TREE_CHAIN (tmp))
|
||||
tmp = TREE_CHAIN (tmp);
|
||||
else
|
||||
tmp = next, next = 0;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0, tail = outputs; tail; tail = TREE_CHAIN (tail), i++)
|
||||
{
|
||||
tree val = TREE_VALUE (tail);
|
||||
tree type = TREE_TYPE (val);
|
||||
const char *constraint;
|
||||
bool is_inout;
|
||||
bool allows_reg;
|
||||
bool allows_mem;
|
||||
@ -1588,12 +1555,9 @@ expand_asm_operands (string, outputs, inputs, clobbers, vol, filename, line)
|
||||
the worst that happens if we get it wrong is we issue an error
|
||||
message. */
|
||||
|
||||
constraint = TREE_STRING_POINTER (TREE_PURPOSE (tail));
|
||||
output_constraints[i] = constraint;
|
||||
|
||||
/* Try to parse the output constraint. If that fails, there's
|
||||
no point in going further. */
|
||||
if (!parse_output_constraint (&output_constraints[i],
|
||||
if (!parse_output_constraint (&constraints[i],
|
||||
i,
|
||||
ninputs,
|
||||
noutputs,
|
||||
@ -1659,15 +1623,16 @@ expand_asm_operands (string, outputs, inputs, clobbers, vol, filename, line)
|
||||
return;
|
||||
}
|
||||
|
||||
/* Make vectors for the expression-rtx and constraint strings. */
|
||||
/* Make vectors for the expression-rtx, constraint strings,
|
||||
and named operands. */
|
||||
|
||||
argvec = rtvec_alloc (ninputs);
|
||||
constraints = rtvec_alloc (ninputs);
|
||||
constraintvec = rtvec_alloc (ninputs);
|
||||
|
||||
body = gen_rtx_ASM_OPERANDS ((noutputs == 0 ? VOIDmode
|
||||
: GET_MODE (output_rtx[0])),
|
||||
TREE_STRING_POINTER (string),
|
||||
empty_string, 0, argvec, constraints,
|
||||
empty_string, 0, argvec, constraintvec,
|
||||
filename, line);
|
||||
|
||||
MEM_VOLATILE_P (body) = vol;
|
||||
@ -1675,8 +1640,7 @@ expand_asm_operands (string, outputs, inputs, clobbers, vol, filename, line)
|
||||
/* Eval the inputs and put them into ARGVEC.
|
||||
Put their constraints into ASM_INPUTs and store in CONSTRAINTS. */
|
||||
|
||||
i = 0;
|
||||
for (tail = inputs; tail; tail = TREE_CHAIN (tail))
|
||||
for (i = 0, tail = inputs; tail; tail = TREE_CHAIN (tail), ++i)
|
||||
{
|
||||
int j;
|
||||
int allows_reg = 0, allows_mem = 0;
|
||||
@ -1698,9 +1662,8 @@ expand_asm_operands (string, outputs, inputs, clobbers, vol, filename, line)
|
||||
return;
|
||||
}
|
||||
|
||||
constraint = TREE_STRING_POINTER (TREE_PURPOSE (tail));
|
||||
orig_constraint = constraint = constraints[i + noutputs];
|
||||
c_len = strlen (constraint);
|
||||
orig_constraint = constraint;
|
||||
|
||||
/* Make sure constraint has neither `=', `+', nor '&'. */
|
||||
|
||||
@ -1744,28 +1707,30 @@ expand_asm_operands (string, outputs, inputs, clobbers, vol, filename, line)
|
||||
operands to memory. */
|
||||
case '0': case '1': case '2': case '3': case '4':
|
||||
case '5': case '6': case '7': case '8': case '9':
|
||||
if (constraint[j] >= '0' + noutputs)
|
||||
{
|
||||
error
|
||||
("matching constraint references invalid operand number");
|
||||
return;
|
||||
}
|
||||
{
|
||||
char *end;
|
||||
unsigned long match;
|
||||
|
||||
/* Try and find the real constraint for this dup. */
|
||||
if ((j == 0 && c_len == 1)
|
||||
|| (j == 1 && c_len == 2 && constraint[0] == '%'))
|
||||
{
|
||||
tree o = outputs;
|
||||
|
||||
for (j = constraint[j] - '0'; j > 0; --j)
|
||||
o = TREE_CHAIN (o);
|
||||
|
||||
constraint = TREE_STRING_POINTER (TREE_PURPOSE (o));
|
||||
c_len = strlen (constraint);
|
||||
j = 0;
|
||||
break;
|
||||
}
|
||||
match = strtoul (constraint + j, &end, 10);
|
||||
if (match >= (unsigned long) noutputs)
|
||||
{
|
||||
error ("matching constraint references invalid operand number");
|
||||
return;
|
||||
}
|
||||
|
||||
/* Try and find the real constraint for this dup. Only do
|
||||
this if the matching constraint is the only alternative. */
|
||||
if (*end == '\0'
|
||||
&& (j == 0 || (j == 1 && constraint[0] == '%')))
|
||||
{
|
||||
constraint = constraints[match];
|
||||
c_len = strlen (constraint);
|
||||
j = 0;
|
||||
break;
|
||||
}
|
||||
else
|
||||
j = end - constraint;
|
||||
}
|
||||
/* Fall through. */
|
||||
|
||||
case 'p': case 'r':
|
||||
@ -1814,7 +1779,8 @@ expand_asm_operands (string, outputs, inputs, clobbers, vol, filename, line)
|
||||
if (allows_reg)
|
||||
op = force_reg (TYPE_MODE (TREE_TYPE (TREE_VALUE (tail))), op);
|
||||
else if (!allows_mem)
|
||||
warning ("asm operand %d probably doesn't match constraints", i);
|
||||
warning ("asm operand %d probably doesn't match constraints",
|
||||
i + noutputs);
|
||||
else if (CONSTANT_P (op))
|
||||
op = force_const_mem (TYPE_MODE (TREE_TYPE (TREE_VALUE (tail))),
|
||||
op);
|
||||
@ -1843,7 +1809,8 @@ expand_asm_operands (string, outputs, inputs, clobbers, vol, filename, line)
|
||||
/* ??? Leave this only until we have experience with what
|
||||
happens in combine and elsewhere when constraints are
|
||||
not satisfied. */
|
||||
warning ("asm operand %d probably doesn't match constraints", i);
|
||||
warning ("asm operand %d probably doesn't match constraints",
|
||||
i + noutputs);
|
||||
}
|
||||
generating_concat_p = old_generating_concat_p;
|
||||
ASM_OPERANDS_INPUT (body, i) = op;
|
||||
@ -1851,7 +1818,6 @@ expand_asm_operands (string, outputs, inputs, clobbers, vol, filename, line)
|
||||
ASM_OPERANDS_INPUT_CONSTRAINT_EXP (body, i)
|
||||
= gen_rtx_ASM_INPUT (TYPE_MODE (TREE_TYPE (TREE_VALUE (tail))),
|
||||
orig_constraint);
|
||||
i++;
|
||||
}
|
||||
|
||||
/* Protect all the operands from the queue now that they have all been
|
||||
@ -1870,24 +1836,26 @@ expand_asm_operands (string, outputs, inputs, clobbers, vol, filename, line)
|
||||
for (i = 0; i < ninout; i++)
|
||||
{
|
||||
int j = inout_opnum[i];
|
||||
char buffer[16];
|
||||
|
||||
ASM_OPERANDS_INPUT (body, ninputs - ninout + i)
|
||||
= output_rtx[j];
|
||||
|
||||
sprintf (buffer, "%d", j);
|
||||
ASM_OPERANDS_INPUT_CONSTRAINT_EXP (body, ninputs - ninout + i)
|
||||
= gen_rtx_ASM_INPUT (inout_mode[i], digit_string (j));
|
||||
= gen_rtx_ASM_INPUT (inout_mode[i], ggc_alloc_string (buffer, -1));
|
||||
}
|
||||
|
||||
generating_concat_p = old_generating_concat_p;
|
||||
|
||||
/* Now, for each output, construct an rtx
|
||||
(set OUTPUT (asm_operands INSN OUTPUTNUMBER OUTPUTCONSTRAINT
|
||||
ARGVEC CONSTRAINTS))
|
||||
(set OUTPUT (asm_operands INSN OUTPUTCONSTRAINT OUTPUTNUMBER
|
||||
ARGVEC CONSTRAINTS OPNAMES))
|
||||
If there is more than one, put them inside a PARALLEL. */
|
||||
|
||||
if (noutputs == 1 && nclobbers == 0)
|
||||
{
|
||||
ASM_OPERANDS_OUTPUT_CONSTRAINT (body)
|
||||
= output_constraints[0];
|
||||
ASM_OPERANDS_OUTPUT_CONSTRAINT (body) = constraints[0];
|
||||
insn = emit_insn (gen_rtx_SET (VOIDmode, output_rtx[0], body));
|
||||
}
|
||||
|
||||
@ -1916,8 +1884,7 @@ expand_asm_operands (string, outputs, inputs, clobbers, vol, filename, line)
|
||||
gen_rtx_ASM_OPERANDS
|
||||
(GET_MODE (output_rtx[i]),
|
||||
TREE_STRING_POINTER (string),
|
||||
output_constraints[i],
|
||||
i, argvec, constraints,
|
||||
constraints[i], i, argvec, constraintvec,
|
||||
filename, line));
|
||||
|
||||
MEM_VOLATILE_P (SET_SRC (XVECEXP (body, 0, i))) = vol;
|
||||
@ -1971,6 +1938,206 @@ expand_asm_operands (string, outputs, inputs, clobbers, vol, filename, line)
|
||||
|
||||
free_temp_slots ();
|
||||
}
|
||||
|
||||
/* A subroutine of expand_asm_operands. Check that all operands have
|
||||
the same number of alternatives. Return true if so. */
|
||||
|
||||
static bool
|
||||
check_operand_nalternatives (outputs, inputs)
|
||||
tree outputs, inputs;
|
||||
{
|
||||
if (outputs || inputs)
|
||||
{
|
||||
tree tmp = TREE_PURPOSE (outputs ? outputs : inputs);
|
||||
int nalternatives
|
||||
= n_occurrences (',', TREE_STRING_POINTER (TREE_VALUE (tmp)));
|
||||
tree next = inputs;
|
||||
|
||||
if (nalternatives + 1 > MAX_RECOG_ALTERNATIVES)
|
||||
{
|
||||
error ("too many alternatives in `asm'");
|
||||
return false;
|
||||
}
|
||||
|
||||
tmp = outputs;
|
||||
while (tmp)
|
||||
{
|
||||
const char *constraint
|
||||
= TREE_STRING_POINTER (TREE_VALUE (TREE_PURPOSE (tmp)));
|
||||
|
||||
if (n_occurrences (',', constraint) != nalternatives)
|
||||
{
|
||||
error ("operand constraints for `asm' differ in number of alternatives");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (TREE_CHAIN (tmp))
|
||||
tmp = TREE_CHAIN (tmp);
|
||||
else
|
||||
tmp = next, next = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* A subroutine of expand_asm_operands. Check that all operand names
|
||||
are unique. Return true if so. We rely on the fact that these names
|
||||
are identifiers, and so have been canonicalized by get_identifier,
|
||||
so all we need are pointer comparisons. */
|
||||
|
||||
static bool
|
||||
check_unique_operand_names (outputs, inputs)
|
||||
tree outputs, inputs;
|
||||
{
|
||||
tree i, j;
|
||||
|
||||
for (i = outputs; i ; i = TREE_CHAIN (i))
|
||||
{
|
||||
tree i_name = TREE_PURPOSE (TREE_PURPOSE (i));
|
||||
if (! i_name)
|
||||
continue;
|
||||
|
||||
for (j = TREE_CHAIN (i); j ; j = TREE_CHAIN (j))
|
||||
if (i_name == TREE_PURPOSE (TREE_PURPOSE (j)))
|
||||
goto failure;
|
||||
}
|
||||
|
||||
for (i = inputs; i ; i = TREE_CHAIN (i))
|
||||
{
|
||||
tree i_name = TREE_PURPOSE (TREE_PURPOSE (i));
|
||||
if (! i_name)
|
||||
continue;
|
||||
|
||||
for (j = TREE_CHAIN (i); j ; j = TREE_CHAIN (j))
|
||||
if (i_name == TREE_PURPOSE (TREE_PURPOSE (j)))
|
||||
goto failure;
|
||||
for (j = outputs; j ; j = TREE_CHAIN (j))
|
||||
if (i_name == TREE_PURPOSE (TREE_PURPOSE (j)))
|
||||
goto failure;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
failure:
|
||||
error ("duplicate asm operand name '%s'",
|
||||
IDENTIFIER_POINTER (TREE_PURPOSE (TREE_PURPOSE (i))));
|
||||
return false;
|
||||
}
|
||||
|
||||
/* A subroutine of expand_asm_operands. Resolve the names of the operands
|
||||
in *POUTPUTS and *PINPUTS to numbers, and replace the name expansions in
|
||||
STRING and in the constraints to those numbers. */
|
||||
|
||||
static tree
|
||||
resolve_operand_names (string, outputs, inputs, pconstraints)
|
||||
tree string;
|
||||
tree outputs, inputs;
|
||||
const char **pconstraints;
|
||||
{
|
||||
char *buffer = xstrdup (TREE_STRING_POINTER (string));
|
||||
char *p;
|
||||
tree t;
|
||||
|
||||
/* Assume that we will not need extra space to perform the substitution.
|
||||
This because we get to remove '[' and ']', which means we cannot have
|
||||
a problem until we have more than 999 operands. */
|
||||
|
||||
p = buffer;
|
||||
while ((p = strchr (p, '%')) != NULL)
|
||||
{
|
||||
if (*++p != '[')
|
||||
continue;
|
||||
p = resolve_operand_name_1 (p, outputs, inputs);
|
||||
}
|
||||
|
||||
string = build_string (strlen (buffer), buffer);
|
||||
free (buffer);
|
||||
|
||||
/* Collect output constraints here because it's convenient.
|
||||
There should be no named operands here; this is verified
|
||||
in expand_asm_operand. */
|
||||
for (t = outputs; t ; t = TREE_CHAIN (t), pconstraints++)
|
||||
*pconstraints = TREE_STRING_POINTER (TREE_VALUE (TREE_PURPOSE (t)));
|
||||
|
||||
/* Substitute [<name>] in input constraint strings. */
|
||||
for (t = inputs; t ; t = TREE_CHAIN (t), pconstraints++)
|
||||
{
|
||||
const char *c = TREE_STRING_POINTER (TREE_VALUE (TREE_PURPOSE (t)));
|
||||
if (strchr (c, '[') == NULL)
|
||||
*pconstraints = c;
|
||||
else
|
||||
{
|
||||
p = buffer = xstrdup (c);
|
||||
while ((p = strchr (p, '[')) != NULL)
|
||||
p = resolve_operand_name_1 (p, outputs, inputs);
|
||||
|
||||
*pconstraints = ggc_alloc_string (buffer, -1);
|
||||
free (buffer);
|
||||
}
|
||||
}
|
||||
|
||||
return string;
|
||||
}
|
||||
|
||||
/* A subroutine of resolve_operand_names. P points to the '[' for a
|
||||
potential named operand of the form [<name>]. In place, replace
|
||||
the name and brackets with a number. Return a pointer to the
|
||||
balance of the string after substitution. */
|
||||
|
||||
static char *
|
||||
resolve_operand_name_1 (p, outputs, inputs)
|
||||
char *p;
|
||||
tree outputs, inputs;
|
||||
{
|
||||
char *q;
|
||||
int op;
|
||||
tree t;
|
||||
size_t len;
|
||||
|
||||
/* Collect the operand name. */
|
||||
q = strchr (p, ']');
|
||||
if (!q)
|
||||
{
|
||||
error ("missing close brace for named operand");
|
||||
return strchr (p, '\0');
|
||||
}
|
||||
len = q - p - 1;
|
||||
|
||||
/* Resolve the name to a number. */
|
||||
for (op = 0, t = outputs; t ; t = TREE_CHAIN (t), op++)
|
||||
{
|
||||
const char *c = IDENTIFIER_POINTER (TREE_PURPOSE (TREE_PURPOSE (t)));
|
||||
if (strncmp (c, p + 1, len) == 0 && c[len] == '\0')
|
||||
goto found;
|
||||
}
|
||||
for (t = inputs; t ; t = TREE_CHAIN (t), op++)
|
||||
{
|
||||
const char *c = IDENTIFIER_POINTER (TREE_PURPOSE (TREE_PURPOSE (t)));
|
||||
if (strncmp (c, p + 1, len) == 0 && c[len] == '\0')
|
||||
goto found;
|
||||
}
|
||||
|
||||
*q = '\0';
|
||||
error ("undefined named operand '%s'", p + 1);
|
||||
op = 0;
|
||||
found:
|
||||
|
||||
/* Replace the name with the number. Unfortunately, not all libraries
|
||||
get the return value of sprintf correct, so search for the end of the
|
||||
generated string by hand. */
|
||||
sprintf (p, "%d", op);
|
||||
p = strchr (p, '\0');
|
||||
|
||||
/* Verify the no extra buffer space assumption. */
|
||||
if (p > q)
|
||||
abort ();
|
||||
|
||||
/* Shift the rest of the buffer down to fill the gap. */
|
||||
memmove (p, q + 1, strlen (q + 1) + 1);
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
/* Generate RTL to evaluate the expression EXP
|
||||
and remember it in case this is the VALUE in a ({... VALUE; }) constr. */
|
||||
|
Loading…
Reference in New Issue
Block a user