diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 5e491e99f68..b322551d11a 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,25 @@ +2001-10-10 Richard Henderson + + * 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 * combine.c (try_combine): Handle a SEQUENCE of one insn. diff --git a/gcc/c-parse.in b/gcc/c-parse.in index 28ac2b5a550..8096d1cef27 100644 --- a/gcc/c-parse.in +++ b/gcc/c-parse.in @@ -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: diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog index 49fdb783d03..8d867bbd579 100644 --- a/gcc/cp/ChangeLog +++ b/gcc/cp/ChangeLog @@ -1,3 +1,9 @@ +2001-10-10 Richard Henderson + + * 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 * call.c (standard_conversion): Add bad conversion between diff --git a/gcc/cp/parse.y b/gcc/cp/parse.y index a7651b57468..f3e85ffe849 100644 --- a/gcc/cp/parse.y +++ b/gcc/cp/parse.y @@ -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: diff --git a/gcc/cp/semantics.c b/gcc/cp/semantics.c index ec7b968f046..41e3bcf4b46 100644 --- a/gcc/cp/semantics.c +++ b/gcc/cp/semantics.c @@ -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, diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi index 027647d09b2..132ccbad14f 100644 --- a/gcc/doc/extend.texi +++ b/gcc/doc/extend.texi @@ -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 diff --git a/gcc/doc/md.texi b/gcc/doc/md.texi index 685f5150630..7a62eaf25c0 100644 --- a/gcc/doc/md.texi +++ b/gcc/doc/md.texi @@ -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 diff --git a/gcc/genconfig.c b/gcc/genconfig.c index f9ae6d5e3ce..27000be7d93 100644 --- a/gcc/genconfig.c +++ b/gcc/genconfig.c @@ -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. */ diff --git a/gcc/local-alloc.c b/gcc/local-alloc.c index 2637b7e78a1..22d7d48b528 100644 --- a/gcc/local-alloc.c +++ b/gcc/local-alloc.c @@ -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; diff --git a/gcc/recog.c b/gcc/recog.c index e240ebbe2cc..15048705763 100644 --- a/gcc/recog.c +++ b/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': diff --git a/gcc/regmove.c b/gcc/regmove.c index ce62cce72a5..89a55ca44e1 100644 --- a/gcc/regmove.c +++ b/gcc/regmove.c @@ -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': diff --git a/gcc/reload.c b/gcc/reload.c index 613d6c7e2c7..8b01ce25403 100644 --- a/gcc/reload.c +++ b/gcc/reload.c @@ -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. diff --git a/gcc/stmt.c b/gcc/stmt.c index b7ced6a8eca..1f91823bdc8 100644 --- a/gcc/stmt.c +++ b/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 [] 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 []. 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. */