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
@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
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
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;
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':
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':
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));
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)
{
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;
const char *filename;
int line;
{
- rtvec argvec, constraints;
+ rtvec argvec, constraintvec;
rtx body;
int ninputs = list_length (inputs);
int noutputs = list_length (outputs);
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;
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 --
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;
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;
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,
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;
/* 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;
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 '&'. */
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;
- }
-
- /* 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);
+ {
+ char *end;
+ unsigned long match;
- 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':
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);
/* ??? 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;
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
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));
}
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;
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;
+}
\f
/* Generate RTL to evaluate the expression EXP
and remember it in case this is the VALUE in a ({... VALUE; }) constr. */