stmt.c (parse_output_constraint): New function, split out from ...

* stmt.c (parse_output_constraint): New function, split out
	from ...
	(expand_asm_operands): ... here.  Use parse_output_constraint.
	* tree.h (parse_output_constraint): Declare it.

	* semantics.c (finish_asm_stmt): Mark the output operands
	to an asm addressable, if necessary.

From-SVN: r43941
This commit is contained in:
Mark Mitchell 2001-07-11 17:35:56 +00:00 committed by Mark Mitchell
parent a615c28a76
commit 40b18c0a37
6 changed files with 256 additions and 135 deletions

View File

@ -1,3 +1,10 @@
2001-07-11 Mark Mitchell <mark@codesourcery.com>
* stmt.c (parse_output_constraint): New function, split out
from ...
(expand_asm_operands): ... here. Use parse_output_constraint.
* tree.h (parse_output_constraint): Declare it.
2001-07-11 Richard Henderson <rth@redhat.com>
* bitmap.c: Comment some functions; fiddle whitespace.

View File

@ -1,3 +1,8 @@
2001-07-11 Mark Mitchell <mark@codesourcery.com>
* semantics.c (finish_asm_stmt): Mark the output operands
to an asm addressable, if necessary.
2001-07-11 Ben Elliston <bje@redhat.com>
* Revert this change -- there is a subtle bug.

View File

@ -894,23 +894,65 @@ finish_asm_stmt (cv_qualifier, string, output_operands,
}
if (!processing_template_decl)
for (t = input_operands; t; t = TREE_CHAIN (t))
{
tree converted_operand
= decay_conversion (TREE_VALUE (t));
{
int i;
int ninputs;
int noutputs;
/* If the type of the operand hasn't been determined (e.g.,
because it involves an overloaded function), then issue an
error message. There's no context available to resolve the
overloading. */
if (TREE_TYPE (converted_operand) == unknown_type_node)
{
cp_error ("type of asm operand `%E' could not be determined",
TREE_VALUE (t));
converted_operand = error_mark_node;
}
TREE_VALUE (t) = converted_operand;
}
for (t = input_operands; t; t = TREE_CHAIN (t))
{
tree converted_operand
= decay_conversion (TREE_VALUE (t));
/* If the type of the operand hasn't been determined (e.g.,
because it involves an overloaded function), then issue
an error message. There's no context available to
resolve the overloading. */
if (TREE_TYPE (converted_operand) == unknown_type_node)
{
cp_error ("type of asm operand `%E' could not be determined",
TREE_VALUE (t));
converted_operand = error_mark_node;
}
TREE_VALUE (t) = converted_operand;
}
ninputs = list_length (input_operands);
noutputs = list_length (output_operands);
for (i = 0, t = output_operands; t; t = TREE_CHAIN (t), ++i)
{
bool allows_mem;
bool allows_reg;
bool is_inout;
const char *constraint;
tree operand;
constraint = TREE_STRING_POINTER (TREE_PURPOSE (t));
operand = TREE_VALUE (output_operands);
if (!parse_output_constraint (&constraint,
i, ninputs, noutputs,
&allows_mem,
&allows_reg,
&is_inout))
{
/* By marking the type as erroneous, we will not try to
process this operand again in expand_asm_operands. */
TREE_TYPE (operand) = error_mark_node;
continue;
}
/* If the operand is a DECL that is going to end up in
memory, assume it is addressable. This is a bit more
conservative than it would ideally be; the exact test is
buried deep in expand_asm_operands and depends on the
DECL_RTL for the OPERAND -- which we don't have at this
point. */
if (!allows_reg && DECL_P (operand))
mark_addressable (operand);
}
}
r = build_stmt (ASM_STMT, cv_qualifier, string,
output_operands, input_operands,

View File

@ -1292,6 +1292,164 @@ expand_asm (body)
last_expr_type = 0;
}
/* Parse the output constraint pointed to by *CONSTRAINT_P. It is the
OPERAND_NUMth output operand, indexed from zero. There are NINPUTS
inputs and NOUTPUTS outputs to this extended-asm. Upon return,
*ALLOWS_MEM will be TRUE iff the constraint allows the use of a
memory operand. Similarly, *ALLOWS_REG will be TRUE iff the
constraint allows the use of a register operand. And, *IS_INOUT
will be true if the operand is read-write, i.e., if it is used as
an input as well as an output. If *CONSTRAINT_P is not in
canonical form, it will be made canonical. (Note that `+' will be
rpelaced with `=' as part of this process.)
Returns TRUE if all went well; FALSE if an error occurred. */
bool
parse_output_constraint (constraint_p,
operand_num,
ninputs,
noutputs,
allows_mem,
allows_reg,
is_inout)
const char **constraint_p;
int operand_num;
int ninputs;
int noutputs;
bool *allows_mem;
bool *allows_reg;
bool *is_inout;
{
const char *constraint = *constraint_p;
const char *p;
/* Assume the constraint doesn't allow the use of either a register
or memory. */
*allows_mem = false;
*allows_reg = false;
/* Allow the `=' or `+' to not be at the beginning of the string,
since it wasn't explicitly documented that way, and there is a
large body of code that puts it last. Swap the character to
the front, so as not to uglify any place else. */
p = strchr (constraint, '=');
if (!p)
p = strchr (constraint, '+');
/* If the string doesn't contain an `=', issue an error
message. */
if (!p)
{
error ("output operand constraint lacks `='");
return false;
}
/* If the constraint begins with `+', then the operand is both read
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)
{
char *buf;
size_t c_len = strlen (constraint);
if (p != constraint)
warning ("output constraint `%c' for operand %d is not at the beginning",
*p, operand_num);
/* Make a copy of the constraint. */
buf = alloca (c_len + 1);
strcpy (buf, constraint);
/* Swap the first character and the `=' or `+'. */
buf[p - constraint] = buf[0];
/* Make sure the first character is an `='. (Until we do this,
it might be a `+'.) */
buf[0] = '=';
/* Replace the constraint with the canonicalized string. */
*constraint_p = ggc_alloc_string (buf, c_len);
constraint = *constraint_p;
}
/* Loop through the constraint string. */
for (p = constraint + 1; *p; ++p)
switch (*p)
{
case '+':
case '=':
error ("operand constraint contains '+' or '=' at illegal position.");
return false;
case '%':
if (operand_num + 1 == ninputs + noutputs)
{
error ("`%%' constraint used with last operand");
return false;
}
break;
case 'V': case 'm': case 'o':
*allows_mem = true;
break;
case '?': case '!': case '*': case '&': case '#':
case 'E': case 'F': case 'G': case 'H':
case 's': case 'i': case 'n':
case 'I': case 'J': case 'K': case 'L': case 'M':
case 'N': case 'O': case 'P': case ',':
break;
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
error ("matching constraint not valid in output operand");
return false;
case '<': case '>':
/* ??? Before flow, auto inc/dec insns are not supposed to exist,
excepting those that expand_call created. So match memory
and hope. */
*allows_mem = true;
break;
case 'g': case 'X':
*allows_reg = true;
*allows_mem = true;
break;
case 'p': case 'r':
*allows_reg = true;
break;
default:
if (!ISALPHA (*p))
break;
if (REG_CLASS_FROM_LETTER (*p) != NO_REGS)
*allows_reg = true;
#ifdef EXTRA_CONSTRAINT
else
{
/* Otherwise we can't assume anything about the nature of
the constraint except that it isn't purely registers.
Treat it like "g" and hope for the best. */
*allows_reg = true;
*allows_mem = true;
}
#endif
break;
}
return true;
}
/* Generate RTL for an asm statement with arguments.
STRING is the instruction template.
OUTPUTS is a list of output arguments (lvalues); INPUTS a list of inputs.
@ -1411,15 +1569,12 @@ expand_asm_operands (string, outputs, inputs, clobbers, vol, filename, line)
tree val = TREE_VALUE (tail);
tree type = TREE_TYPE (val);
const char *constraint;
char *p;
int c_len;
int j;
int is_inout = 0;
int allows_reg = 0;
int allows_mem = 0;
bool is_inout;
bool allows_reg;
bool allows_mem;
/* If there's an erroneous arg, emit no insn. */
if (TREE_TYPE (val) == error_mark_node)
if (type == error_mark_node)
return;
/* Make sure constraint has `=' and does not have `+'. Also, see
@ -1429,119 +1584,17 @@ expand_asm_operands (string, outputs, inputs, clobbers, vol, filename, line)
constraint = TREE_STRING_POINTER (TREE_PURPOSE (tail));
output_constraints[i] = constraint;
c_len = strlen (constraint);
/* Allow the `=' or `+' to not be at the beginning of the string,
since it wasn't explicitly documented that way, and there is a
large body of code that puts it last. Swap the character to
the front, so as not to uglify any place else. */
switch (c_len)
{
default:
if ((p = strchr (constraint, '=')) != NULL)
break;
if ((p = strchr (constraint, '+')) != NULL)
break;
case 0:
error ("output operand constraint lacks `='");
return;
}
j = p - constraint;
is_inout = *p == '+';
if (j || is_inout)
{
/* Have to throw away this constraint string and get a new one. */
char *buf = alloca (c_len + 1);
buf[0] = '=';
if (j)
memcpy (buf + 1, constraint, j);
memcpy (buf + 1 + j, p + 1, c_len - j); /* not -j-1 - copy null */
constraint = ggc_alloc_string (buf, c_len);
output_constraints[i] = constraint;
if (j)
warning (
"output constraint `%c' for operand %d is not at the beginning",
*p, i);
}
/* Make sure we can specify the matching operand. */
if (is_inout && i > 9)
{
error ("output operand constraint %d contains `+'", i);
return;
}
for (j = 1; j < c_len; j++)
switch (constraint[j])
{
case '+':
case '=':
error ("operand constraint contains '+' or '=' at illegal position.");
return;
case '%':
if (i + 1 == ninputs + noutputs)
{
error ("`%%' constraint used with last operand");
return;
}
break;
case '?': case '!': case '*': case '&': case '#':
case 'E': case 'F': case 'G': case 'H':
case 's': case 'i': case 'n':
case 'I': case 'J': case 'K': case 'L': case 'M':
case 'N': case 'O': case 'P': case ',':
break;
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
error ("matching constraint not valid in output operand");
break;
case 'V': case 'm': case 'o':
allows_mem = 1;
break;
case '<': case '>':
/* ??? Before flow, auto inc/dec insns are not supposed to exist,
excepting those that expand_call created. So match memory
and hope. */
allows_mem = 1;
break;
case 'g': case 'X':
allows_reg = 1;
allows_mem = 1;
break;
case 'p': case 'r':
allows_reg = 1;
break;
default:
if (! ISALPHA (constraint[j]))
{
error ("invalid punctuation `%c' in constraint",
constraint[j]);
return;
}
if (REG_CLASS_FROM_LETTER (constraint[j]) != NO_REGS)
allows_reg = 1;
#ifdef EXTRA_CONSTRAINT
else
{
/* Otherwise we can't assume anything about the nature of
the constraint except that it isn't purely registers.
Treat it like "g" and hope for the best. */
allows_reg = 1;
allows_mem = 1;
}
#endif
break;
}
/* Try to parse the output constraint. If that fails, there's
no point in going further. */
if (!parse_output_constraint (&output_constraints[i],
i,
ninputs,
noutputs,
&allows_mem,
&allows_reg,
&is_inout))
return;
/* If an output operand is not a decl or indirect ref and our constraint
allows a register, make a temporary to act as an intermediate.

View File

@ -0,0 +1,11 @@
// Build don't link:
// Skip if not target: i?86-*-*
// Special g++ Options: -O2
typedef unsigned long long uint64;
uint64 fstps(void)
{
uint64 ret;
asm volatile("fstps %0" : "=m" (ret));
return ret;
}

View File

@ -2851,6 +2851,9 @@ extern int div_and_round_double PARAMS ((enum tree_code, int,
/* In stmt.c */
extern void emit_nop PARAMS ((void));
extern void expand_computed_goto PARAMS ((tree));
extern bool parse_output_constraint PARAMS ((const char **,
int, int, int,
bool *, bool *, bool *));
extern void expand_asm_operands PARAMS ((tree, tree, tree, tree, int,
const char *, int));
extern int any_pending_cleanups PARAMS ((int));