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:
Richard Henderson 2001-10-11 00:07:30 -07:00 committed by Richard Henderson
parent 592188a538
commit 84b7230235
13 changed files with 414 additions and 149 deletions

View File

@ -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.

View File

@ -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:

View File

@ -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

View File

@ -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:

View File

@ -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,

View File

@ -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

View File

@ -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

View File

@ -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. */

View File

@ -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;

View File

@ -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':

View File

@ -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':

View File

@ -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.

View File

@ -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. */