diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 045b9d4c953..199a8d42c21 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,14 @@ +Fri Jan 15 18:42:12 1999 Richard Henderson + + * expr.c (queued_subexp_p): Make public. + * expr.h (queued_subexp_p): Declare it. + * recog.c (asm_operand_ok): New function. + (check_asm_operands): Use it. After reload, use constrain_operands + instead. + * recog.h (asm_operand_ok): Declare it. + * stmt.c (expand_asm_operands): Use it to try harder to make + asms initially satisfy their constraints. + Fri Jan 15 17:43:59 1999 Jeffrey A. Law * sparc.h (LEGITIMIZE_RELOAD_ADDRESS): Do not create diff --git a/gcc/expr.c b/gcc/expr.c index 12e0cd01005..1d2a399bb49 100644 --- a/gcc/expr.c +++ b/gcc/expr.c @@ -149,7 +149,6 @@ extern rtx arg_pointer_save_area; static rtx get_push_address PROTO ((int)); static rtx enqueue_insn PROTO((rtx, rtx)); -static int queued_subexp_p PROTO((rtx)); static void init_queue PROTO((void)); static int move_by_pieces_ninsns PROTO((unsigned int, int)); static void move_by_pieces_1 PROTO((rtx (*) (rtx, ...), enum machine_mode, @@ -478,7 +477,7 @@ protect_from_queue (x, modify) We handle only combinations of MEM, PLUS, MINUS and MULT operators since memory addresses generally contain only those. */ -static int +int queued_subexp_p (x) rtx x; { diff --git a/gcc/expr.h b/gcc/expr.h index cc2fb3079e2..5b329376516 100644 --- a/gcc/expr.h +++ b/gcc/expr.h @@ -716,6 +716,9 @@ extern rtx protect_from_queue PROTO((rtx, int)); /* Perform all the pending incrementations. */ extern void emit_queue PROTO((void)); +/* Tell if something has a queued subexpression. */ +extern int queued_subexp_p PROTO((rtx)); + /* Emit some rtl insns to move data between rtx's, converting machine modes. Both modes must be floating or both fixed. */ extern void convert_move PROTO((rtx, rtx, int)); diff --git a/gcc/recog.c b/gcc/recog.c index fa8c069b46e..0d87386cdb7 100644 --- a/gcc/recog.c +++ b/gcc/recog.c @@ -1,5 +1,5 @@ /* Subroutines used by or related to instruction recognition. - Copyright (C) 1987, 1988, 91-97, 1998 Free Software Foundation, Inc. + Copyright (C) 1987, 1988, 91-98, 1999 Free Software Foundation, Inc. This file is part of GNU CC. @@ -152,21 +152,40 @@ int check_asm_operands (x) rtx x; { - int noperands = asm_noperands (x); + int noperands; rtx *operands; + char **constraints; int i; + /* Post-reload, be more strict with things. */ + if (reload_completed) + { + /* ??? Doh! We've not got the wrapping insn. Cook one up. */ + extract_insn (make_insn_raw (x)); + constrain_operands (1); + return which_alternative >= 0; + } + + noperands = asm_noperands (x); if (noperands < 0) return 0; if (noperands == 0) return 1; operands = (rtx *) alloca (noperands * sizeof (rtx)); - decode_asm_operands (x, operands, NULL_PTR, NULL_PTR, NULL_PTR); + constraints = (char **) alloca (noperands * sizeof (char *)); + + decode_asm_operands (x, operands, NULL_PTR, constraints, NULL_PTR); for (i = 0; i < noperands; i++) - if (!general_operand (operands[i], VOIDmode)) - return 0; + { + char *c = constraints[i]; + if (ISDIGIT ((unsigned char)c[0])) + c = constraints[c[0] - '0']; + + if (! asm_operand_ok (operands[i], c)) + return 0; + } return 1; } @@ -1493,6 +1512,204 @@ decode_asm_operands (body, operands, operand_locs, constraints, modes) return template; } + +/* Check if an asm_operand matches it's constraints. */ + +int +asm_operand_ok (op, constraint) + rtx op; + const char *constraint; +{ + /* Use constrain_operands after reload. */ + if (reload_completed) + abort (); + + while (*constraint) + { + switch (*constraint++) + { + case '=': + case '+': + case '*': + case '%': + case '?': + case '!': + case '#': + case '&': + case ',': + break; + + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + /* Our caller is supposed to have given us the proper + matching constraint. */ + /* abort (); */ + break; + + case 'p': + if (address_operand (op, VOIDmode)) + return 1; + break; + + case 'm': + case 'V': /* non-offsettable */ + if (memory_operand (op, VOIDmode)) + return 1; + break; + + case 'o': /* offsettable */ + if (offsettable_nonstrict_memref_p (op)) + return 1; + break; + + case '<': + if (GET_CODE (op) == MEM + && (GET_CODE (XEXP (op, 0)) == PRE_DEC + || GET_CODE (XEXP (op, 0)) == POST_DEC)) + return 1; + break; + + case '>': + if (GET_CODE (op) == MEM + && (GET_CODE (XEXP (op, 0)) == PRE_INC + || GET_CODE (XEXP (op, 0)) == POST_INC)) + return 1; + break; + + case 'E': +#ifndef REAL_ARITHMETIC + /* Match any floating double constant, but only if + we can examine the bits of it reliably. */ + if ((HOST_FLOAT_FORMAT != TARGET_FLOAT_FORMAT + || HOST_BITS_PER_WIDE_INT != BITS_PER_WORD) + && GET_MODE (op) != VOIDmode && ! flag_pretend_float) + break; +#endif + /* FALLTHRU */ + + case 'F': + if (GET_CODE (op) == CONST_DOUBLE) + return 1; + break; + + case 'G': + if (GET_CODE (op) == CONST_DOUBLE + && CONST_DOUBLE_OK_FOR_LETTER_P (op, 'G')) + return 1; + break; + case 'H': + if (GET_CODE (op) == CONST_DOUBLE + && CONST_DOUBLE_OK_FOR_LETTER_P (op, 'H')) + return 1; + break; + + case 's': + if (GET_CODE (op) == CONST_INT + || (GET_CODE (op) == CONST_DOUBLE + && GET_MODE (op) == VOIDmode)) + break; + /* FALLTHRU */ + + case 'i': + if (CONSTANT_P (op) +#ifdef LEGITIMATE_PIC_OPERAND_P + && (! flag_pic || LEGITIMATE_PIC_OPERAND_P (op)) +#endif + ) + return 1; + break; + + case 'n': + if (GET_CODE (op) == CONST_INT + || (GET_CODE (op) == CONST_DOUBLE + && GET_MODE (op) == VOIDmode)) + return 1; + break; + + case 'I': + if (GET_CODE (op) == CONST_INT + && CONST_OK_FOR_LETTER_P (INTVAL (op), 'I')) + return 1; + break; + case 'J': + if (GET_CODE (op) == CONST_INT + && CONST_OK_FOR_LETTER_P (INTVAL (op), 'J')) + return 1; + break; + case 'K': + if (GET_CODE (op) == CONST_INT + && CONST_OK_FOR_LETTER_P (INTVAL (op), 'K')) + return 1; + break; + case 'L': + if (GET_CODE (op) == CONST_INT + && CONST_OK_FOR_LETTER_P (INTVAL (op), 'L')) + return 1; + break; + case 'M': + if (GET_CODE (op) == CONST_INT + && CONST_OK_FOR_LETTER_P (INTVAL (op), 'M')) + return 1; + break; + case 'N': + if (GET_CODE (op) == CONST_INT + && CONST_OK_FOR_LETTER_P (INTVAL (op), 'N')) + return 1; + break; + case 'O': + if (GET_CODE (op) == CONST_INT + && CONST_OK_FOR_LETTER_P (INTVAL (op), 'O')) + return 1; + break; + case 'P': + if (GET_CODE (op) == CONST_INT + && CONST_OK_FOR_LETTER_P (INTVAL (op), 'P')) + return 1; + break; + + case 'X': + return 1; + + case 'g': + if (general_operand (op, VOIDmode)) + return 1; + break; + +#ifdef EXTRA_CONSTRAINT + case 'Q': + if (EXTRA_CONSTRAINT (op, 'Q')) + return 1; + break; + case 'R': + if (EXTRA_CONSTRAINT (op, 'R')) + return 1; + break; + case 'S': + if (EXTRA_CONSTRAINT (op, 'S')) + return 1; + break; + case 'T': + if (EXTRA_CONSTRAINT (op, 'T')) + return 1; + break; + case 'U': + if (EXTRA_CONSTRAINT (op, 'U')) + return 1; + break; +#endif + + case 'r': + default: + if (GET_MODE (op) == BLKmode) + break; + if (register_operand (op, VOIDmode)) + return 1; + break; + } + } + + return 0; +} /* Given an rtx *P, if it is a sum containing an integer constant term, return the location (type rtx *) of the pointer to that constant term. diff --git a/gcc/recog.h b/gcc/recog.h index ded5667f72a..d85cc0f71e1 100644 --- a/gcc/recog.h +++ b/gcc/recog.h @@ -70,6 +70,7 @@ extern void init_recog PROTO((void)); extern void init_recog_no_volatile PROTO((void)); extern int recog_memoized PROTO((rtx)); extern int check_asm_operands PROTO((rtx)); +extern int asm_operand_ok PROTO((rtx, const char *)); extern int validate_change PROTO((rtx, rtx *, rtx, int)); extern int apply_change_group PROTO((void)); extern int num_validated_changes PROTO((void)); diff --git a/gcc/stmt.c b/gcc/stmt.c index 4f2e911dfbc..4e972e94606 100644 --- a/gcc/stmt.c +++ b/gcc/stmt.c @@ -1397,9 +1397,10 @@ expand_asm_operands (string, outputs, inputs, clobbers, vol, filename, line) for (tail = inputs; tail; tail = TREE_CHAIN (tail)) { int j; - int allows_reg = 0; - char *constraint; + int allows_reg = 0, allows_mem = 0; + char *constraint, *orig_constraint; int c_len; + rtx op; /* If there's an erroneous arg, emit no insn, because the ASM_INPUT would get VOIDmode @@ -1417,6 +1418,7 @@ expand_asm_operands (string, outputs, inputs, clobbers, vol, filename, line) c_len = TREE_STRING_LENGTH (TREE_PURPOSE (tail)) - 1; constraint = TREE_STRING_POINTER (TREE_PURPOSE (tail)); + orig_constraint = constraint; /* Make sure constraint has neither `=', `+', nor '&'. */ @@ -1424,19 +1426,28 @@ expand_asm_operands (string, outputs, inputs, clobbers, vol, filename, line) switch (constraint[j]) { case '+': case '=': case '&': - error ("input operand constraint contains `%c'", constraint[j]); - return; + if (constraint == orig_constraint) + { + error ("input operand constraint contains `%c'", constraint[j]); + return; + } + break; case '%': - if (i + 1 == ninputs - ninout) + if (constraint == orig_constraint + && i + 1 == ninputs - ninout) { error ("`%%' constraint used with last operand"); return; } break; + case 'V': case 'm': case 'o': + allows_mem = 1; + break; + + case '<': case '>': case '?': case '!': case '*': - case 'V': case 'm': case 'o': case '<': case '>': case 'E': case 'F': case 'G': case 'H': case 'X': case 's': case 'i': case 'n': case 'I': case 'J': case 'K': case 'L': case 'M': @@ -1460,48 +1471,73 @@ expand_asm_operands (string, outputs, inputs, clobbers, vol, filename, line) return; } + /* Try and find the real constraint for this dup. */ + if (j == 0 && c_len == 1) + { + tree o = outputs; + for (j = constraint[j] - '0'; j > 0; --j) + o = TREE_CHAIN (o); + + c_len = TREE_STRING_LENGTH (TREE_PURPOSE (o)) - 1; + constraint = TREE_STRING_POINTER (TREE_PURPOSE (o)); + j = 0; + break; + } + /* ... fall through ... */ - case 'p': case 'g': case 'r': + case 'p': case 'r': default: allows_reg = 1; break; + + case 'g': + allows_reg = 1; + allows_mem = 1; + break; } - if (! allows_reg) + if (! allows_reg && allows_mem) mark_addressable (TREE_VALUE (tail)); - XVECEXP (body, 3, i) /* argvec */ - = expand_expr (TREE_VALUE (tail), NULL_RTX, VOIDmode, 0); - if (CONSTANT_P (XVECEXP (body, 3, i)) - && ! general_operand (XVECEXP (body, 3, i), - TYPE_MODE (TREE_TYPE (TREE_VALUE (tail))))) + op = expand_expr (TREE_VALUE (tail), NULL_RTX, VOIDmode, 0); + + if (! asm_operand_ok (op, constraint)) { if (allows_reg) - XVECEXP (body, 3, i) - = force_reg (TYPE_MODE (TREE_TYPE (TREE_VALUE (tail))), - XVECEXP (body, 3, i)); + 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); + else if (CONSTANT_P (op)) + op = force_const_mem (TYPE_MODE (TREE_TYPE (TREE_VALUE (tail))), + op); + else if (GET_CODE (op) == REG + || GET_CODE (op) == SUBREG + || GET_CODE (op) == CONCAT) + { + tree type = TREE_TYPE (TREE_VALUE (tail)); + rtx memloc = assign_temp (type, 1, 1, 1); + + emit_move_insn (memloc, op); + op = memloc; + } + else if (GET_CODE (op) == MEM && MEM_VOLATILE_P (op)) + /* We won't recognize volatile memory as available a + memory_operand at this point. Ignore it. */ + ; + else if (queued_subexp_p (op)) + ; else - XVECEXP (body, 3, i) - = force_const_mem (TYPE_MODE (TREE_TYPE (TREE_VALUE (tail))), - XVECEXP (body, 3, i)); - } - - if (! allows_reg - && (GET_CODE (XVECEXP (body, 3, i)) == REG - || GET_CODE (XVECEXP (body, 3, i)) == SUBREG - || GET_CODE (XVECEXP (body, 3, i)) == CONCAT)) - { - tree type = TREE_TYPE (TREE_VALUE (tail)); - rtx memloc = assign_temp (type, 1, 1, 1); - - emit_move_insn (memloc, XVECEXP (body, 3, i)); - XVECEXP (body, 3, i) = memloc; + /* ??? 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); } + XVECEXP (body, 3, i) = op; XVECEXP (body, 4, i) /* constraints */ = gen_rtx_ASM_INPUT (TYPE_MODE (TREE_TYPE (TREE_VALUE (tail))), - constraint); + orig_constraint); i++; }