This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
Redone rtl invariant checking function
- To: rth at cygnus dot com, patches at x86-64 dot org, gcc-patches at gcc dot gnu dot org
- Subject: Redone rtl invariant checking function
- From: Jan Hubicka <jh at suse dot cz>
- Date: Fri, 15 Dec 2000 15:30:59 +0100
Hi
This is my second attempt to implement function that verifies rtl invariants.
This one is cleaner and more extensible. It has already caught many gcc
problems, so I think it is good idea to get it in before 3.0
Originally my plan was to fix all problems noticed by the verify_rtx and then
submit the patch, but since the problems are numberous and many of the patches
was never reviewed, I've decided rather document the problems in the comments
and disable checkes for the moment.
In case such function will be accepted, I will try to fix all the problem
mentioned.
Currently, gcc is able to bootstrap on i386 vith verify_rtx called from
recog_memoized with the fixes I've sent today and yesterday.
Comments are welcome
Honza
Fri Dec 15 15:24:36 MET 2000 Jan Hubicka <jh@suse.cz>
* rtlanal.c (verify_rtx_1, VERIFY, verify_rtx): New.
Index: egcs/gcc//rtlanal.c
===================================================================
RCS file: /cvs/gcc/egcs/gcc/rtlanal.c,v
retrieving revision 1.75
diff -c -3 -p -r1.75 rtlanal.c
*** rtlanal.c 2000/12/12 21:23:04 1.75
--- rtlanal.c 2000/12/15 14:13:25
*************** static int rtx_addr_can_trap_p PARAMS ((
*** 29,34 ****
--- 29,36 ----
static void reg_set_p_1 PARAMS ((rtx, rtx, void *));
static void insn_dependent_p_1 PARAMS ((rtx, rtx, void *));
static void reg_set_last_1 PARAMS ((rtx, rtx, void *));
+ static void verify_rtx_1 PARAMS ((rtx, rtx, enum machine_mode,
+ enum rtx_code, int));
/* Forward declarations */
*************** loc_mentioned_in_p (loc, in)
*** 2536,2538 ****
--- 2538,2971 ----
}
return 0;
}
+
+ #define VERIFY(condition, text) \
+ { \
+ if (!(condition)) \
+ { \
+ error_str=text; \
+ goto error; \
+ } \
+ }
+ /* ??? Is it possible somehow to determine maximal number of argument for
+ RTX code? */
+ #define LENGTH_MAX 20
+ static void
+ verify_rtx_1 (insn, x, expected_mode, outer_code, check_current_mode)
+ rtx insn, x;
+ enum machine_mode expected_mode;
+ enum rtx_code outer_code;
+ int check_current_mode;
+ {
+ enum rtx_code code;
+ enum machine_mode mode;
+ int length;
+ int i;
+ const char *fmt;
+ const char *error_str;
+ int modes[LENGTH_MAX];
+ int recurse[LENGTH_MAX];
+ int check_mode[LENGTH_MAX];
+
+ /* Quick checking for NIL. */
+ if (!x)
+ {
+ VERIFY (outer_code == INSN_LIST || outer_code == EXPR_LIST
+ || outer_code == INSN || outer_code == JUMP_INSN
+ || outer_code == CALL_INSN,
+ "NIL not expected place");
+ return;
+ }
+
+ code = GET_CODE (x);
+ mode = GET_MODE (x);
+ length = GET_RTX_LENGTH (code);
+ fmt = GET_RTX_FORMAT (code);
+
+ for (i = 0; i < length; i++)
+ modes[i] = mode, recurse[i] = 1, check_mode[i] = 1;
+ if (check_current_mode)
+ {
+ VERIFY (mode == expected_mode
+ || (mode == VOIDmode
+ && (code == CONST_INT || code == CALL || code == EXPR_LIST
+ || code == PC || code == STRICT_LOW_PART))
+ /* ??? CSE attempts to substitue register for EXPR_LIST
+ when hitting libcalls. */
+ || (code == EXPR_LIST && mode == VOIDmode)
+ /* ??? i386 produces UNSPECs and CONSTs in VOIDmode,
+ should be SImode. */
+ || (code == UNSPEC && mode == VOIDmode)
+ || (code == CONST && mode == VOIDmode)
+ /* memory clobber in asm operands is represented as VOIDmode
+ scratch. */
+ || (code == SCRATCH && mode == VOIDmode
+ && expected_mode == Pmode)
+ /* ??? We probably should require LABEL_REF to be Pmode in the
+ expressions, but current documentation require them to be
+ VOIDmode. */
+ || (code == LABEL_REF && mode == VOIDmode
+ && expected_mode == Pmode)
+ || (code == CONST_DOUBLE && mode == VOIDmode &&
+ INTEGRAL_MODE_P (expected_mode)),
+ "Wrong subexpression mode");
+ }
+ switch (code)
+ {
+ case CONST_INT:
+ VERIFY (mode == VOIDmode, "Mode is not VOIDmode");
+ break;
+ case CONST_DOUBLE:
+ VERIFY (mode == VOIDmode || GET_MODE_CLASS (mode) == MODE_FLOAT,
+ "Wrong mode for CONST_DOUBLE");
+ VERIFY (CONST_DOUBLE_MEM (x) == const0_rtx ||
+ CONST_DOUBLE_MEM (x) == cc0_rtx ||
+ GET_CODE (CONST_DOUBLE_MEM (x)) == MEM,
+ "Wrong ADDR field (should be memory or const0_rtx or cc0_rtx");
+ if (GET_CODE (CONST_DOUBLE_MEM (x)) != MEM)
+ recurse[0] = 0;
+ break;
+ case SYMBOL_REF:
+ VERIFY (mode == Pmode,
+ "SYMBOL_REF is Pmode");
+ break;
+ case LABEL_REF:
+ /* ??? LABEL_REF is documented as allways VOIDmode, but some code
+ emit it as Pmode. It makes sense to make it Pmode inside non-jump
+ expressions in order to avoid problematic VOIDmodes in the rtl
+ stream. */
+ VERIFY (mode == Pmode || mode == VOIDmode,
+ "Wrong mode of LABEL_REF");
+ VERIFY (!check_current_mode ||
+ expected_mode == Pmode || expected_mode == VOIDmode,
+ "LABEL_REF should be only in Pmode expressions");
+ recurse[0] = 0;
+ break;
+ case HIGH:
+ VERIFY (mode == Pmode,
+ "HIGH is Pmode");
+ break;
+ case REG:
+ VERIFY (mode != BLKmode && mode != VOIDmode,
+ "Wrong mode for REG");
+ /* ??? Some parts of compiler generate register with REGNO set to 10000
+ just to compute the costs. Accept it. */
+ VERIFY (REGNO (x) <= (unsigned)max_reg_num ()
+ || REGNO (x) == 10000,
+ "Invalid REGNO for REG");
+ break;
+ case SUBREG:
+ /* ??? too weak! */
+ VERIFY (mode != BLKmode && mode != VOIDmode
+ && mode != GET_MODE (SUBREG_REG (x)),
+ "Wrong mode of SUBREG");
+ VERIFY (GET_MODE (SUBREG_REG (x)) != VOIDmode,
+ "Wrong mode of SUBREG_REG");
+ VERIFY (SUBREG_WORD (x) * BITS_PER_WORD
+ <= GET_MODE_BITSIZE (GET_MODE (SUBREG_REG (x))),
+ "SUBREG_WORD is out of range");
+ check_mode[0] = 0;
+ break;
+ case PC:
+ VERIFY (x == pc_rtx, "Non-unique pc_rtx");
+ VERIFY (!check_current_mode ||
+ expected_mode == Pmode || expected_mode == VOIDmode,
+ "PC should be only in Pmode expressions");
+ break;
+ case RETURN:
+ /* ??? Why RETURN is not unique? */
+ VERIFY (mode == VOIDmode, "RETURN is VOIDmode");
+ break;
+ case MEM:
+ VERIFY (mode != VOIDmode, "MEM is VOIDmode");
+ modes[0] = Pmode;
+ break;
+ case CC0:
+ VERIFY (x == cc0_rtx, "Non-unique cc0_rtx");
+ break;
+ case CONST:
+ /* ??? CONST should be always Pmode, but isn't on i386. */
+ case SCRATCH:
+ case COND:
+ case CONST_STRING:
+ break;
+ case ASHIFT:
+ case ASHIFTRT:
+ case LSHIFTRT:
+ VERIFY (GET_MODE (XEXP (x, 0)) == VOIDmode
+ || INTEGRAL_MODE_P (GET_MODE (XEXP (x, 0))),
+ "Wrong shifted value");
+ /* ??? Is there some way to get shift counter mode used by the
+ machine? */
+ VERIFY (GET_MODE (XEXP (x, 1)) == VOIDmode
+ || INTEGRAL_MODE_P (GET_MODE (XEXP (x, 1))),
+ "Wrong shift counter");
+ check_mode [1] = 0;
+ break;
+ /* The stup for arithmetics is correct - we check all arguments to have
+ same mode. */
+ case PLUS:
+ case ABS:
+ case SQRT:
+ case MULT:
+ case DIV:
+ case UDIV:
+ case MOD:
+ case UMOD:
+ case SMIN:
+ case SMAX:
+ case UMIN:
+ case UMAX:
+ case LO_SUM:
+ case IOR:
+ case XOR:
+ case AND:
+ case NEG:
+ case MINUS:
+ VERIFY (INTEGRAL_MODE_P (mode) || GET_MODE_CLASS (mode) == MODE_FLOAT,
+ "Wrong mode for arithmetics.");
+ break;
+ case NOT:
+ case PRE_DEC:
+ case PRE_INC:
+ case POST_DEC:
+ case POST_INC:
+ VERIFY (INTEGRAL_MODE_P (mode),
+ "Wrong mode for arithmetics.");
+ break;
+ case POST_MODIFY:
+ case PRE_MODIFY:
+ VERIFY (INTEGRAL_MODE_P (mode),
+ "Wrong mode for arithmetics.");
+ VERIFY (GET_CODE (XEXP (x, 1)) == PLUS || GET_CODE (XEXP (x, 1)) == MINUS,
+ "Second operand of *_MODIFY is PLUS or MINUS");
+ VERIFY (XEXP (XEXP (x, 1), 0) == XEXP (x, 0),
+ "Modified operand is not equivalent to operand of PLUS/MINUS expression.");
+ break;
+ case FFS:
+ VERIFY (INTEGRAL_MODE_P (mode)
+ && INTEGRAL_MODE_P (GET_MODE (XEXP (x, 0))),
+ "Wrong mode for FFS.");
+ check_mode[0] = 0;
+ break;
+ case COMPARE:
+ VERIFY (mode == VOIDmode || GET_MODE_CLASS (mode) == MODE_CC,
+ "Wrong mode for compare.");
+ relational:
+ {
+ enum machine_mode compare_mode = GET_MODE (XEXP (x, 0));
+ if (compare_mode == VOIDmode)
+ compare_mode = GET_MODE (XEXP (x, 1));
+ modes[0] = modes[1] = compare_mode;
+ /*??? compares with both VOIDmode operands are not well
+ defined and should be prohibited.
+ Also lots of code expect first operand to be non-VOIDmode.
+ Disable this for now, since this rule is broken most of time. */
+ #if 0
+ VERIFY (GET_MODE (XEXP (x, 0)) != VOIDmode,
+ "First operand should be NON-VOIDmode.");
+ #endif
+ }
+ break;
+ case EQ:
+ case NE:
+ case GT:
+ case GTU:
+ case LT:
+ case LTU:
+ case GE:
+ case GEU:
+ case LE:
+ case LEU:
+ VERIFY ((mode == VOIDmode && outer_code == IF_THEN_ELSE)
+ || INTEGRAL_MODE_P (mode),
+ "Wrong mode of relational operation.");
+ goto relational;
+ case IF_THEN_ELSE:
+ modes [0] = VOIDmode;
+ break;
+ case SIGN_EXTRACT:
+ case ZERO_EXTRACT:
+ VERIFY (INTEGRAL_MODE_P (mode),
+ "Wrong mode of extraction.");
+ /* ??? Manual specifies the mode of MEM to be always QImode, but
+ this does not hold after reload. */
+ VERIFY (reload_completed || reload_in_progress
+ || GET_CODE (XEXP (x, 0)) != MEM
+ || GET_MODE (XEXP (x, 0)) == QImode,
+ "Memory operand of *_EXTRACT should be QImode.");
+
+ VERIFY (GET_CODE (XEXP (x, 1)) != CONST_INT
+ || INTVAL (XEXP (x, 1)) > 0,
+ "Incorect size for *_EXTRACT");
+ if (GET_CODE (XEXP (x, 0)) != MEM)
+ {
+ VERIFY (GET_CODE (XEXP (x, 2)) != CONST_INT
+ || (INTVAL (XEXP (x, 2)) >= 0
+ && (INTVAL (XEXP (x, 2))
+ < (int)GET_MODE_BITSIZE (GET_MODE (XEXP (x, 0))))),
+ "Incorect pos for *_EXTRACT");
+ VERIFY (GET_CODE (XEXP (x, 1)) != CONST_INT
+ || (INTVAL (XEXP (x, 1))
+ <= (int)GET_MODE_BITSIZE (GET_MODE (XEXP (x, 0)))),
+ "Incorect size for *_EXTRACT");
+ VERIFY (GET_CODE (XEXP (x, 1)) != CONST_INT
+ || GET_CODE (XEXP (x, 1)) != CONST_INT
+ || (INTVAL (XEXP (x, 1)) + INTVAL (XEXP (x, 2))
+ <= (int)GET_MODE_BITSIZE (GET_MODE (XEXP (x, 0)))),
+ "Incorect field possition for *_EXTRACT");
+ }
+ check_mode [0] = 0;
+ /* ??? implement checking of operand modes. */
+ check_mode [1] = 0;
+ check_mode [2] = 0;
+ break;
+ case SIGN_EXTEND:
+ case ZERO_EXTEND:
+ VERIFY (INTEGRAL_MODE_P (mode)
+ && (INTEGRAL_MODE_P (GET_MODE (XEXP (x, 0)))
+ /* ??? zero_extends of VOIDmode are not well defined and should
+ be prohibited. Unforutnately integrate.c is generating them
+ often. */
+ || GET_MODE (XEXP (x, 0)) == VOIDmode)
+ && (GET_MODE_BITSIZE (mode)
+ > GET_MODE_BITSIZE (GET_MODE ((XEXP (x, 0))))),
+ "Wrong mode for extension.");
+ check_mode [0] = 0;
+ break;
+ case FLOAT_EXTEND:
+ VERIFY (GET_MODE_CLASS (mode) == MODE_FLOAT
+ && GET_MODE_CLASS (GET_MODE (XEXP (x, 0))) == MODE_FLOAT
+ && (GET_MODE_BITSIZE (mode)
+ > GET_MODE_BITSIZE (GET_MODE ((XEXP (x, 0))))),
+ "Wrong mode for extension.");
+ check_mode [0] = 0;
+ break;
+ case TRUNCATE:
+ VERIFY (INTEGRAL_MODE_P (mode)
+ && INTEGRAL_MODE_P (GET_MODE (XEXP (x, 0)))
+ && (GET_MODE_BITSIZE (mode)
+ < GET_MODE_BITSIZE (GET_MODE ((XEXP (x, 0))))),
+ "Wrong mode for truncation.");
+ check_mode [0] = 0;
+ break;
+ case FLOAT_TRUNCATE:
+ VERIFY (GET_MODE_CLASS (mode) == MODE_FLOAT
+ && GET_MODE_CLASS (GET_MODE (XEXP (x, 0))) == MODE_FLOAT
+ && (GET_MODE_BITSIZE (mode)
+ < GET_MODE_BITSIZE (GET_MODE ((XEXP (x, 0))))),
+ "Wrong mode for truncation.");
+ check_mode [0] = 0;
+ break;
+ case FLOAT:
+ case UNSIGNED_FLOAT:
+ VERIFY (GET_MODE_CLASS (mode) == MODE_FLOAT
+ && (INTEGRAL_MODE_P (GET_MODE (XEXP (x, 0)))
+ || GET_MODE (XEXP (x, 0)) == VOIDmode),
+ "Wrong mode for FLOAT.");
+ check_mode [0] = 0;
+ break;
+ case FIX:
+ case UNSIGNED_FIX:
+ VERIFY (INTEGRAL_MODE_P (mode)
+ && GET_MODE_CLASS (GET_MODE (XEXP (x, 0))) == MODE_FLOAT,
+ "Wrong mode for FIX.");
+ check_mode [0] = 0;
+ break;
+ case STRICT_LOW_PART:
+ VERIFY (mode == VOIDmode, "STRICT_LOW_PART is VOIDmode.");
+ VERIFY (INTEGRAL_MODE_P (GET_MODE (XEXP (x, 0))),
+ "Wrong mode of STRICT_LOW_PART");
+ /* ??? Some code (such as validate_replace_rtx) breaks this rule
+ by attempting to simplify SUBREG. */
+ #if 0
+ VERIFY (GET_CODE (XEXP (x, 0)) == SUBREG,
+ "STRICT_LOW_PART must contain SUBREG.");
+ #endif
+ check_mode [0] = 0;
+ break;
+ case SET:
+ {
+ enum machine_mode set_mode = GET_MODE (SET_DEST (x));
+ if (GET_CODE (SET_DEST (x)) == STRICT_LOW_PART)
+ set_mode = GET_MODE (XEXP (SET_DEST (x), 0));
+ /* ??? Indirect jumps does have Pmode register as SET_DEST,
+ while most of other jumps are VOIDmode. */
+ if (GET_CODE (SET_DEST (x)) == PC)
+ set_mode = GET_MODE (SET_SRC (x));
+ VERIFY (mode == VOIDmode, "SET is VOIDmode");
+ modes[0] = modes[1] = set_mode;
+ }
+ break;
+ case CALL:
+ VERIFY (mode == VOIDmode, "CALL is VOIDmode");
+ modes[0] = FUNCTION_MODE;
+ check_mode[1] = 0;
+ break;
+ case CLOBBER:
+ case USE:
+ VERIFY (mode == VOIDmode, "CLOBBER and USE is VOIDmode");
+ check_mode [0] = 0;
+ break;
+ case PARALLEL:
+ modes[0] = VOIDmode;
+ break;
+ case ADDR_VEC:
+ case ADDR_DIFF_VEC:
+ VERIFY (mode == Pmode, "ADDR_VEC is Pmode");
+ break;
+ case ASM_INPUT:
+ if (outer_code == ASM_OPERANDS)
+ {
+ VERIFY (mode != VOIDmode, "ASM_INPUT is VOIDmode");
+ }
+ else
+ VERIFY (mode == VOIDmode, "ASM_INPUT is non VOIDmode");
+ break;
+ case INSN:
+ case NOTE:
+ case JUMP_INSN:
+ case CALL_INSN:
+ case CODE_LABEL:
+ case BARRIER:
+ recurse[0] = 0;
+ recurse[1] = 0;
+ recurse[2] = 0;
+ check_mode[3] = VOIDmode;
+ for (i = 4; i < length; i++)
+ check_mode[i] = 0;
+ break;
+ case UNSPEC:
+ case UNSPEC_VOLATILE:
+ case SEQUENCE:
+ case EXPR_LIST:
+ case INSN_LIST:
+ default:
+ for (i = 0; i < length; i++)
+ modes[i] = VOIDmode, recurse[i] = 1, check_mode[i] = 0;
+ break;
+ }
+ for (i = length - 1; i >= 0; i--)
+ {
+ int j;
+ if (!recurse [i])
+ continue;
+ if (fmt[i] == 'e')
+ verify_rtx_1 (insn, XEXP (x, i), modes[i], code, check_mode[i]);
+ else if (fmt[i] == 'E')
+ for (j = XVECLEN (x, i) - 1; j >= 0; j--)
+ verify_rtx_1 (insn, XVECEXP (x, i, j), modes[i],
+ code, check_mode[i]);
+ }
+ return;
+ error:
+ debug_rtx (x);
+ fatal_insn (error_str, insn);
+ }
+ void
+ verify_rtx (x)
+ rtx x;
+ {
+ verify_rtx_1 (x, x, VOIDmode, NIL, 0);
+ }
+ #undef VERIFY