Commit 40b18c0a by Mark Mitchell Committed by Mark Mitchell

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
parent a615c28a
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> 2001-07-11 Richard Henderson <rth@redhat.com>
* bitmap.c: Comment some functions; fiddle whitespace. * bitmap.c: Comment some functions; fiddle whitespace.
......
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> 2001-07-11 Ben Elliston <bje@redhat.com>
* Revert this change -- there is a subtle bug. * Revert this change -- there is a subtle bug.
......
...@@ -894,15 +894,20 @@ finish_asm_stmt (cv_qualifier, string, output_operands, ...@@ -894,15 +894,20 @@ finish_asm_stmt (cv_qualifier, string, output_operands,
} }
if (!processing_template_decl) if (!processing_template_decl)
{
int i;
int ninputs;
int noutputs;
for (t = input_operands; t; t = TREE_CHAIN (t)) for (t = input_operands; t; t = TREE_CHAIN (t))
{ {
tree converted_operand tree converted_operand
= decay_conversion (TREE_VALUE (t)); = decay_conversion (TREE_VALUE (t));
/* If the type of the operand hasn't been determined (e.g., /* If the type of the operand hasn't been determined (e.g.,
because it involves an overloaded function), then issue an because it involves an overloaded function), then issue
error message. There's no context available to resolve the an error message. There's no context available to
overloading. */ resolve the overloading. */
if (TREE_TYPE (converted_operand) == unknown_type_node) if (TREE_TYPE (converted_operand) == unknown_type_node)
{ {
cp_error ("type of asm operand `%E' could not be determined", cp_error ("type of asm operand `%E' could not be determined",
...@@ -912,6 +917,43 @@ finish_asm_stmt (cv_qualifier, string, output_operands, ...@@ -912,6 +917,43 @@ finish_asm_stmt (cv_qualifier, string, output_operands,
TREE_VALUE (t) = converted_operand; 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, r = build_stmt (ASM_STMT, cv_qualifier, string,
output_operands, input_operands, output_operands, input_operands,
clobbers); clobbers);
......
...@@ -1292,6 +1292,164 @@ expand_asm (body) ...@@ -1292,6 +1292,164 @@ expand_asm (body)
last_expr_type = 0; 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. /* Generate RTL for an asm statement with arguments.
STRING is the instruction template. STRING is the instruction template.
OUTPUTS is a list of output arguments (lvalues); INPUTS a list of inputs. 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) ...@@ -1411,15 +1569,12 @@ expand_asm_operands (string, outputs, inputs, clobbers, vol, filename, line)
tree val = TREE_VALUE (tail); tree val = TREE_VALUE (tail);
tree type = TREE_TYPE (val); tree type = TREE_TYPE (val);
const char *constraint; const char *constraint;
char *p; bool is_inout;
int c_len; bool allows_reg;
int j; bool allows_mem;
int is_inout = 0;
int allows_reg = 0;
int allows_mem = 0;
/* If there's an erroneous arg, emit no insn. */ /* If there's an erroneous arg, emit no insn. */
if (TREE_TYPE (val) == error_mark_node) if (type == error_mark_node)
return; return;
/* Make sure constraint has `=' and does not have `+'. Also, see /* Make sure constraint has `=' and does not have `+'. Also, see
...@@ -1429,119 +1584,17 @@ expand_asm_operands (string, outputs, inputs, clobbers, vol, filename, line) ...@@ -1429,119 +1584,17 @@ expand_asm_operands (string, outputs, inputs, clobbers, vol, filename, line)
constraint = TREE_STRING_POINTER (TREE_PURPOSE (tail)); constraint = TREE_STRING_POINTER (TREE_PURPOSE (tail));
output_constraints[i] = constraint; output_constraints[i] = constraint;
c_len = strlen (constraint);
/* Allow the `=' or `+' to not be at the beginning of the string, /* Try to parse the output constraint. If that fails, there's
since it wasn't explicitly documented that way, and there is a no point in going further. */
large body of code that puts it last. Swap the character to if (!parse_output_constraint (&output_constraints[i],
the front, so as not to uglify any place else. */ i,
switch (c_len) ninputs,
{ noutputs,
default: &allows_mem,
if ((p = strchr (constraint, '=')) != NULL) &allows_reg,
break; &is_inout))
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; 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;
}
/* If an output operand is not a decl or indirect ref and our constraint /* 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. allows a register, make a temporary to act as an intermediate.
......
// 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;
}
...@@ -2851,6 +2851,9 @@ extern int div_and_round_double PARAMS ((enum tree_code, int, ...@@ -2851,6 +2851,9 @@ extern int div_and_round_double PARAMS ((enum tree_code, int,
/* In stmt.c */ /* In stmt.c */
extern void emit_nop PARAMS ((void)); extern void emit_nop PARAMS ((void));
extern void expand_computed_goto PARAMS ((tree)); 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, extern void expand_asm_operands PARAMS ((tree, tree, tree, tree, int,
const char *, int)); const char *, int));
extern int any_pending_cleanups PARAMS ((int)); extern int any_pending_cleanups PARAMS ((int));
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment