From 40b18c0a378124dca8b0f29f798fb47de9516e15 Mon Sep 17 00:00:00 2001 From: Mark Mitchell Date: Wed, 11 Jul 2001 17:35:56 +0000 Subject: [PATCH] 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 --- gcc/ChangeLog | 7 + gcc/cp/ChangeLog | 5 + gcc/cp/semantics.c | 76 +++-- gcc/stmt.c | 291 ++++++++++++-------- gcc/testsuite/g++.old-deja/g++.other/asm3.C | 11 + gcc/tree.h | 3 + 6 files changed, 257 insertions(+), 136 deletions(-) create mode 100644 gcc/testsuite/g++.old-deja/g++.other/asm3.C diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 82cce17c0a0..7093a5c0488 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,10 @@ +2001-07-11 Mark Mitchell + + * 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 * bitmap.c: Comment some functions; fiddle whitespace. diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog index 03d03d3fb75..486533b44cf 100644 --- a/gcc/cp/ChangeLog +++ b/gcc/cp/ChangeLog @@ -1,3 +1,8 @@ +2001-07-11 Mark Mitchell + + * semantics.c (finish_asm_stmt): Mark the output operands + to an asm addressable, if necessary. + 2001-07-11 Ben Elliston * Revert this change -- there is a subtle bug. diff --git a/gcc/cp/semantics.c b/gcc/cp/semantics.c index c82d2911381..2bb4051affc 100644 --- a/gcc/cp/semantics.c +++ b/gcc/cp/semantics.c @@ -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)); - - /* 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; - } + { + int i; + int ninputs; + int noutputs; + + 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, diff --git a/gcc/stmt.c b/gcc/stmt.c index ea736473ee5..425c18541a1 100644 --- a/gcc/stmt.c +++ b/gcc/stmt.c @@ -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. diff --git a/gcc/testsuite/g++.old-deja/g++.other/asm3.C b/gcc/testsuite/g++.old-deja/g++.other/asm3.C new file mode 100644 index 00000000000..d37de69329a --- /dev/null +++ b/gcc/testsuite/g++.old-deja/g++.other/asm3.C @@ -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; +} diff --git a/gcc/tree.h b/gcc/tree.h index 4becd00ab82..edb344f6991 100644 --- a/gcc/tree.h +++ b/gcc/tree.h @@ -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)); -- 2.30.2