This is the mail archive of the
gcc@gcc.gnu.org
mailing list for the GCC project.
RFC/RFH Drastic simplifications to generic operand predicates
- From: Zack Weinberg <zack at codesourcery dot com>
- To: gcc at gcc dot gnu dot org
- Date: Thu, 05 Aug 2004 20:40:04 -0700
- Subject: RFC/RFH Drastic simplifications to generic operand predicates
Yes, I'm still theoretically working on ia64 floating-point arithmetic.
This particular side track has taken me straight into a cacotopic
stain¹, so I'm going to abandon it, but I want to write down how far I
got in case someone cares to pick it up.
If you look at all the operand predicates in recog.c, you notice that
there's a lot of almost-duplicated code, that it's rather poorly
organized, and finally that some of the things that are allowed are
Just Plain Weird. For instance, (subreg (arithmetic expression)) and
(subreg (mem)) are allowed by all of register_operand, nonmemory_operand,
memory_operand, and general_operand -- under the appropriate
conditions, which are subtly different for each one.
I decided to clean up the organization, and I decided that it would be
sensible to nuke all the weird stuff, on the theory that the machine-
independent compiler already has to be capable of not generating the
weird stuff, in case it encounters target-specific operand predicates
that don't allow the weird stuff. This worked fairly well. I did
have to modify one other place -- reload will turn (subreg (reg)) into
(subreg (mem)) if the reg in question gets dumped to the stack; it's
cleanup_subreg_operands' job to fix this, but it currently does that
by looping over recog_data.operands, and if the predicates disallow
(subreg (mem)), extract_insn will abort before c_s_o gets a chance to
do anything.
And then I ran into the cacotopic stain, as I said, because it turns
out that -- contrary to my expectation -- many targets have this
habit of doing really weird things with SUBREG. For instance, i386.md:
(define_insn "*cmpqi_ext_1"
[(set (reg 17)
(compare
(match_operand:QI 0 "general_operand" "Qm")
(subreg:QI
(zero_extract:SI
(match_operand 1 "ext_register_operand" "Q")
(const_int 8)
(const_int 8)) 0)))]
"!TARGET_64BIT && ix86_match_ccmode (insn, CCmode)"
"cmp{b}\t{%h1, %0|%0, %h1}"
[(set_attr "type" "icmp")
(set_attr "mode" "QI")])
Here the SUBREG can, I think, safely be replaced with a TRUNCATE.
A nastier case, again from i386.md:
(define_insn "*lea_1_zext"
[(set (match_operand:DI 0 "register_operand" "=r")
(zero_extend:DI
(subreg:SI (match_operand:DI 1 "no_seg_address_operand" "p") 0)))]
"TARGET_64BIT"
"lea{l}\t{%a1, %k0|%k0, %a1}"
[(set_attr "type" "lea")
(set_attr "mode" "SI")])
(define_insn_and_split "*lea_general_2_zext"
[(set (match_operand:DI 0 "register_operand" "=r")
(zero_extend:DI
(plus:SI (mult:SI (match_operand:SI 1 "index_register_operand" "r")
(match_operand:SI 2 "const248_operand" "n"))
(match_operand:SI 3 "nonmemory_operand" "ri"))))]
"TARGET_64BIT"
"#"
"&& reload_completed"
[(set (match_dup 0)
(zero_extend:DI (subreg:SI (plus:DI (mult:DI (match_dup 1)
(match_dup 2))
(match_dup 3)) 0)))]
{
operands[1] = gen_lowpart (Pmode, operands[1]);
operands[3] = gen_lowpart (Pmode, operands[3]);
}
[(set_attr "type" "lea")
(set_attr "mode" "SI")])
no_seg_address_operand, see, requires an expression in Pmode (which is
perfectly reasonable for a real address instead of a clever way of
doing arithmetic). Again I think this is fixable, it's just
nontrivial. Possibly one might move the zero_extends inside the
arithmetic, change the subreg to a truncate, and change the i386
definition of GO_IF_LEGITIMATE_ADDRESS to match. Now multiply by two
dozen odd machine descriptions -- not all of them have this sort of
thing, but we'd have to go through and check them all.
I still think this is a good idea; it's just bigger and hairier than I
can handle right now. The diff below is as far as I got -- the
recog.c part may be hard to read as I rearranged the order of
functions in the file as well as making content changes.
zw
¹ yes, I did just finish reading _Iron Council_.
===================================================================
Index: final.c
*** final.c 27 Jul 2004 19:09:30 -0000 1.325
--- final.c 6 Aug 2004 03:38:22 -0000
*************** notice_source_line (rtx insn)
*** 2575,2612 ****
return false;
}
/* For each operand in INSN, simplify (subreg (reg)) so that it refers
directly to the desired hard register. */
void
cleanup_subreg_operands (rtx insn)
{
! int i;
extract_insn_cached (insn);
- for (i = 0; i < recog_data.n_operands; i++)
- {
- /* The following test cannot use recog_data.operand when testing
- for a SUBREG: the underlying object might have been changed
- already if we are inside a match_operator expression that
- matches the else clause. Instead we test the underlying
- expression directly. */
- if (GET_CODE (*recog_data.operand_loc[i]) == SUBREG)
- recog_data.operand[i] = alter_subreg (recog_data.operand_loc[i]);
- else if (GET_CODE (recog_data.operand[i]) == PLUS
- || GET_CODE (recog_data.operand[i]) == MULT
- || MEM_P (recog_data.operand[i]))
- recog_data.operand[i] = walk_alter_subreg (recog_data.operand_loc[i]);
- }
-
- for (i = 0; i < recog_data.n_dups; i++)
- {
- if (GET_CODE (*recog_data.dup_loc[i]) == SUBREG)
- *recog_data.dup_loc[i] = alter_subreg (recog_data.dup_loc[i]);
- else if (GET_CODE (*recog_data.dup_loc[i]) == PLUS
- || GET_CODE (*recog_data.dup_loc[i]) == MULT
- || MEM_P (*recog_data.dup_loc[i]))
- *recog_data.dup_loc[i] = walk_alter_subreg (recog_data.dup_loc[i]);
- }
}
/* If X is a SUBREG, replace it with a REG or a MEM,
--- 2575,2604 ----
return false;
}
+ /* Subroutine of cleanup_subreg_operands; called via for_each_rtx;
+ sole purpose in life is to call alter_subreg on SUBREGs it
+ encounters. */
+ static int
+ cleanup_subreg_operands_1 (rtx *x, void *data ATTRIBUTE_UNUSED)
+ {
+ if (GET_CODE (*x) == SUBREG)
+ {
+ *x = alter_subreg (x);
+ return -1;
+ }
+ else
+ return 0;
+ }
+
/* For each operand in INSN, simplify (subreg (reg)) so that it refers
directly to the desired hard register. */
void
cleanup_subreg_operands (rtx insn)
{
! for_each_rtx (&PATTERN (insn), cleanup_subreg_operands_1, 0);
! /* validate changes */
extract_insn_cached (insn);
}
/* If X is a SUBREG, replace it with a REG or a MEM,
===================================================================
Index: recog.c
*** recog.c 3 Aug 2004 23:37:32 -0000 1.208
--- recog.c 6 Aug 2004 03:38:22 -0000
*************** find_single_use (rtx dest, rtx insn, rtx
*** 882,1205 ****
return 0;
}
! /* Return 1 if OP is a valid general operand for machine mode MODE.
! This is either a register reference, a memory reference,
! or a constant. In the case of a memory reference, the address
! is checked for general validity for the target machine.
!
! Register and memory references must have mode MODE in order to be valid,
! but some constants have no machine mode and are valid for any mode.
! If MODE is VOIDmode, OP is checked for validity for whatever mode
! it has.
! The main use of this function is as a predicate in match_operand
! expressions in the machine description.
! For an explanation of this function's behavior for registers of
! class NO_REGS, see the comment for `register_operand'. */
!
! int
! general_operand (rtx op, enum machine_mode mode)
{
! enum rtx_code code = GET_CODE (op);
!
! if (mode == VOIDmode)
! mode = GET_MODE (op);
!
! /* Don't accept CONST_INT or anything similar
! if the caller wants something floating. */
! if (GET_MODE (op) == VOIDmode && mode != VOIDmode
! && GET_MODE_CLASS (mode) != MODE_INT
! && GET_MODE_CLASS (mode) != MODE_PARTIAL_INT)
! return 0;
!
! if (GET_CODE (op) == CONST_INT
! && mode != VOIDmode
! && trunc_int_for_mode (INTVAL (op), mode) != INTVAL (op))
! return 0;
!
! if (CONSTANT_P (op))
! return ((GET_MODE (op) == VOIDmode || GET_MODE (op) == mode
! || mode == VOIDmode)
! && (! flag_pic || LEGITIMATE_PIC_OPERAND_P (op))
! && LEGITIMATE_CONSTANT_P (op));
!
! /* Except for certain constants with VOIDmode, already checked for,
! OP's mode must match MODE if MODE specifies a mode. */
! if (GET_MODE (op) != mode)
! return 0;
! if (code == SUBREG)
{
! rtx sub = SUBREG_REG (op);
!
! #ifdef INSN_SCHEDULING
! /* On machines that have insn scheduling, we want all memory
! reference to be explicit, so outlaw paradoxical SUBREGs. */
! if (MEM_P (sub)
! && GET_MODE_SIZE (mode) > GET_MODE_SIZE (GET_MODE (sub)))
! return 0;
! #endif
! /* Avoid memories with nonzero SUBREG_BYTE, as offsetting the memory
! may result in incorrect reference. We should simplify all valid
! subregs of MEM anyway. But allow this after reload because we
! might be called from cleanup_subreg_operands.
!
! ??? This is a kludge. */
! if (!reload_completed && SUBREG_BYTE (op) != 0
! && MEM_P (sub))
! return 0;
! /* FLOAT_MODE subregs can't be paradoxical. Combine will occasionally
! create such rtl, and we must reject it. */
! if (GET_MODE_CLASS (GET_MODE (op)) == MODE_FLOAT
! && GET_MODE_SIZE (GET_MODE (op)) > GET_MODE_SIZE (GET_MODE (sub)))
! return 0;
! op = sub;
! code = GET_CODE (op);
}
! if (code == REG)
! /* A register whose class is NO_REGS is not a general operand. */
! return (REGNO (op) >= FIRST_PSEUDO_REGISTER
! || REGNO_REG_CLASS (REGNO (op)) != NO_REGS);
! if (code == MEM)
! {
! rtx y = XEXP (op, 0);
! if (! volatile_ok && MEM_VOLATILE_P (op))
! return 0;
! /* Use the mem's mode, since it will be reloaded thus. */
! mode = GET_MODE (op);
! GO_IF_LEGITIMATE_ADDRESS (mode, y, win);
! }
! return 0;
! win:
! return 1;
}
-
- /* Return 1 if OP is a valid memory address for a memory reference
- of mode MODE.
! The main use of this function is as a predicate in match_operand
! expressions in the machine description. */
! int
! address_operand (rtx op, enum machine_mode mode)
{
! return memory_address_p (mode, op);
! }
! /* Return 1 if OP is a register reference of mode MODE.
! If MODE is VOIDmode, accept a register in any mode.
! The main use of this function is as a predicate in match_operand
! expressions in the machine description.
! As a special exception, registers whose class is NO_REGS are
! not accepted by `register_operand'. The reason for this change
! is to allow the representation of special architecture artifacts
! (such as a condition code register) without extending the rtl
! definitions. Since registers of class NO_REGS cannot be used
! as registers in any case where register classes are examined,
! it is most consistent to keep this function from accepting them. */
! int
register_operand (rtx op, enum machine_mode mode)
{
if (GET_MODE (op) != mode && mode != VOIDmode)
! return 0;
if (GET_CODE (op) == SUBREG)
{
rtx sub = SUBREG_REG (op);
! /* Before reload, we can allow (SUBREG (MEM...)) as a register operand
! because it is guaranteed to be reloaded into one.
! Just make sure the MEM is valid in itself.
! (Ideally, (SUBREG (MEM)...) should not exist after reload,
! but currently it does result from (SUBREG (REG)...) where the
! reg went on the stack.) */
! if (! reload_completed && MEM_P (sub))
! return general_operand (op, mode);
!
#ifdef CANNOT_CHANGE_MODE_CLASS
if (REG_P (sub)
&& REGNO (sub) < FIRST_PSEUDO_REGISTER
! && REG_CANNOT_CHANGE_MODE_P (REGNO (sub), GET_MODE (sub), mode)
! && GET_MODE_CLASS (GET_MODE (sub)) != MODE_COMPLEX_INT
! && GET_MODE_CLASS (GET_MODE (sub)) != MODE_COMPLEX_FLOAT)
! return 0;
#endif
/* FLOAT_MODE subregs can't be paradoxical. Combine will occasionally
create such rtl, and we must reject it. */
if (GET_MODE_CLASS (GET_MODE (op)) == MODE_FLOAT
&& GET_MODE_SIZE (GET_MODE (op)) > GET_MODE_SIZE (GET_MODE (sub)))
! return 0;
op = sub;
}
! /* We don't consider registers whose class is NO_REGS
! to be a register operand. */
return (REG_P (op)
&& (REGNO (op) >= FIRST_PSEUDO_REGISTER
|| REGNO_REG_CLASS (REGNO (op)) != NO_REGS));
}
! /* Return 1 for a register in Pmode; ignore the tested mode. */
! int
pmode_register_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
{
return register_operand (op, Pmode);
}
! /* Return 1 if OP should match a MATCH_SCRATCH, i.e., if it is a SCRATCH
! or a hard register. */
! int
scratch_operand (rtx op, enum machine_mode mode)
{
if (GET_MODE (op) != mode && mode != VOIDmode)
! return 0;
return (GET_CODE (op) == SCRATCH
|| (REG_P (op)
! && REGNO (op) < FIRST_PSEUDO_REGISTER));
}
! /* Return 1 if OP is a valid immediate operand for mode MODE.
! The main use of this function is as a predicate in match_operand
! expressions in the machine description. */
! int
! immediate_operand (rtx op, enum machine_mode mode)
{
! /* Don't accept CONST_INT or anything similar
! if the caller wants something floating. */
! if (GET_MODE (op) == VOIDmode && mode != VOIDmode
! && GET_MODE_CLASS (mode) != MODE_INT
! && GET_MODE_CLASS (mode) != MODE_PARTIAL_INT)
! return 0;
!
! if (GET_CODE (op) == CONST_INT
! && mode != VOIDmode
! && trunc_int_for_mode (INTVAL (op), mode) != INTVAL (op))
! return 0;
! return (CONSTANT_P (op)
! && (GET_MODE (op) == mode || mode == VOIDmode
! || GET_MODE (op) == VOIDmode)
! && (! flag_pic || LEGITIMATE_PIC_OPERAND_P (op))
! && LEGITIMATE_CONSTANT_P (op));
}
! /* Returns 1 if OP is an operand that is a CONST_INT. */
! int
! const_int_operand (rtx op, enum machine_mode mode)
{
! if (GET_CODE (op) != CONST_INT)
! return 0;
!
! if (mode != VOIDmode
! && trunc_int_for_mode (INTVAL (op), mode) != INTVAL (op))
! return 0;
!
! return 1;
}
! /* Returns 1 if OP is an operand that is a constant integer or constant
! floating-point number. */
! int
! const_double_operand (rtx op, enum machine_mode mode)
{
! /* Don't accept CONST_INT or anything similar
! if the caller wants something floating. */
! if (GET_MODE (op) == VOIDmode && mode != VOIDmode
! && GET_MODE_CLASS (mode) != MODE_INT
! && GET_MODE_CLASS (mode) != MODE_PARTIAL_INT)
! return 0;
! return ((GET_CODE (op) == CONST_DOUBLE || GET_CODE (op) == CONST_INT)
! && (mode == VOIDmode || GET_MODE (op) == mode
! || GET_MODE (op) == VOIDmode));
! }
! /* Return 1 if OP is a general operand that is not an immediate operand. */
! int
! nonimmediate_operand (rtx op, enum machine_mode mode)
! {
! return (general_operand (op, mode) && ! CONSTANT_P (op));
}
! /* Return 1 if OP is a register reference or immediate value of mode MODE. */
!
! int
! nonmemory_operand (rtx op, enum machine_mode mode)
! {
! if (CONSTANT_P (op))
! {
! /* Don't accept CONST_INT or anything similar
! if the caller wants something floating. */
! if (GET_MODE (op) == VOIDmode && mode != VOIDmode
! && GET_MODE_CLASS (mode) != MODE_INT
! && GET_MODE_CLASS (mode) != MODE_PARTIAL_INT)
! return 0;
!
! if (GET_CODE (op) == CONST_INT
! && mode != VOIDmode
! && trunc_int_for_mode (INTVAL (op), mode) != INTVAL (op))
! return 0;
!
! return ((GET_MODE (op) == VOIDmode || GET_MODE (op) == mode
! || mode == VOIDmode)
! && (! flag_pic || LEGITIMATE_PIC_OPERAND_P (op))
! && LEGITIMATE_CONSTANT_P (op));
! }
! if (GET_MODE (op) != mode && mode != VOIDmode)
! return 0;
! if (GET_CODE (op) == SUBREG)
! {
! /* Before reload, we can allow (SUBREG (MEM...)) as a register operand
! because it is guaranteed to be reloaded into one.
! Just make sure the MEM is valid in itself.
! (Ideally, (SUBREG (MEM)...) should not exist after reload,
! but currently it does result from (SUBREG (REG)...) where the
! reg went on the stack.) */
! if (! reload_completed && MEM_P (SUBREG_REG (op)))
! return general_operand (op, mode);
! op = SUBREG_REG (op);
! }
!
! /* We don't consider registers whose class is NO_REGS
! to be a register operand. */
! return (REG_P (op)
! && (REGNO (op) >= FIRST_PSEUDO_REGISTER
! || REGNO_REG_CLASS (REGNO (op)) != NO_REGS));
}
! /* Return 1 if OP is a valid operand that stands for pushing a
! value of mode MODE onto the stack.
! The main use of this function is as a predicate in match_operand
! expressions in the machine description. */
! int
push_operand (rtx op, enum machine_mode mode)
{
unsigned int rounded_size = GET_MODE_SIZE (mode);
--- 882,1120 ----
return 0;
}
! /* Generic operand predicates. These are primarily used in
! match_operand expressions in the machine description.
! The first category of operand predicates is those dealing
! exclusively with immediate (constant) operands. Because
! CONST_INTs and integer CONST_DOUBLEs have VOIDmode, they
! treat modes specially. */
! /* Helper function: Verify that OP (which is guaranteed to be a
! CONST_INT) fits in MODE. */
! static inline bool
! validate_const_int_for_mode (rtx op, enum machine_mode mode)
{
! return (mode == VOIDmode
! || trunc_int_for_mode (INTVAL (op), mode) == INTVAL (op));
! }
! /* Helper function: OP may be any kind of constant; do machine-independent
! tests which verify that it is suitable for MODE. */
! static inline bool
! validate_constant_for_mode (rtx op, enum machine_mode mode)
! {
! if (mode != VOIDmode)
{
! /* Don't accept an integer CONST_INT or CONST_DOUBLE if the caller
! wants something floating. */
! if (!SCALAR_INT_MODE_P (mode) && GET_MODE (op) == VOIDmode)
! return false;
! /* ??? Do this check for CONST_DOUBLEs too? But how? */
! if (GET_CODE (op) == CONST_INT
! && !validate_const_int_for_mode (op, mode))
! return false;
! /* Except for CONST_INTs and integer CONST_DOUBLEs, the modes
! must match. */
! if (GET_MODE (op) != VOIDmode && GET_MODE (op) != mode)
! return false;
}
! return true;
! }
! /* Return true if OP is a CONST_INT that fits in MODE. */
! bool
! const_int_operand (rtx op, enum machine_mode mode)
! {
! if (GET_CODE (op) != CONST_INT)
! return false;
! return validate_const_int_for_mode (op, mode);
! }
! /* Return true if OP is a CONST_INT or CONST_DOUBLE that fits in MODE. */
! bool
! const_double_operand (rtx op, enum machine_mode mode)
! {
! if (GET_CODE (op) != CONST_INT && GET_CODE (op) != CONST_DOUBLE)
! return false;
!
! return validate_constant_for_mode (op, mode);
}
! /* Return true if OP is a valid immediate operand for mode MODE.
! Unlike const_int_operand and const_double_operand, this function
! calls LEGITIMATE_PIC_OPERAND_P and LEGITIMATE_CONSTANT_P. */
! bool
! immediate_operand (rtx op, enum machine_mode mode)
{
! if (!CONSTANT_P (op))
! return false;
! if (!validate_constant_for_mode (op, mode))
! return false;
!
! return ((! flag_pic || LEGITIMATE_PIC_OPERAND_P (op))
! && LEGITIMATE_CONSTANT_P (op));
! }
! /* The second category of operand predicates deal with quantities which
! are, or will become, machine registers. */
! /* Return true if OP is a valid register operand - (reg) or
! (subreg (reg)) - for MODE. */
! bool
register_operand (rtx op, enum machine_mode mode)
{
if (GET_MODE (op) != mode && mode != VOIDmode)
! return false;
if (GET_CODE (op) == SUBREG)
{
rtx sub = SUBREG_REG (op);
! /* (subreg:X (reg:Y r)), where r is a hard register, must be
! disallowed allowed when the register can't change its mode
! without a conversion instruction. */
#ifdef CANNOT_CHANGE_MODE_CLASS
if (REG_P (sub)
&& REGNO (sub) < FIRST_PSEUDO_REGISTER
! && REG_CANNOT_CHANGE_MODE_P (REGNO (sub), GET_MODE (sub), mode))
! return false;
#endif
/* FLOAT_MODE subregs can't be paradoxical. Combine will occasionally
create such rtl, and we must reject it. */
if (GET_MODE_CLASS (GET_MODE (op)) == MODE_FLOAT
&& GET_MODE_SIZE (GET_MODE (op)) > GET_MODE_SIZE (GET_MODE (sub)))
! return false;
op = sub;
}
! /* We don't consider registers whose class is NO_REGS to be register
! operands. This allows special architecture artifacts (such as
! condition code registers) to be represented with REG rtl, without
! risking reload trying to store general values in them. This is
! consistent with other treatment of register classes: wherever
! register classes are examined, registers of class NO_REGS are not
! considered available. */
return (REG_P (op)
&& (REGNO (op) >= FIRST_PSEUDO_REGISTER
|| REGNO_REG_CLASS (REGNO (op)) != NO_REGS));
}
! /* This predicate is to be used whenever one would like to write
! (match_operand:P n "register_operand") but can't because read-rtl.c
! doesn't recognize Pmode. This is hard to fix, because Pmode isn't
! a normal mode, it's a #define, and it might not be #defined to a
! constant. It would have to be special-cased in the generator programs. */
! bool
pmode_register_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
{
return register_operand (op, Pmode);
}
! /* Return true if OP is a SCRATCH expression or a a hard register
! valid for MODE. This is used internally by (match_scratch)
! patterns; it should not be called directly. As with
! register_operand, registers whose class is NO_REGS are not allowed. */
! bool
scratch_operand (rtx op, enum machine_mode mode)
{
if (GET_MODE (op) != mode && mode != VOIDmode)
! return false;
return (GET_CODE (op) == SCRATCH
|| (REG_P (op)
! && REGNO (op) < FIRST_PSEUDO_REGISTER
! && REGNO_REG_CLASS (REGNO (op)) != NO_REGS));
}
! /* The third category of operand predicates deal with quantities which
! are required to be in memory. */
! /* Helper function: Return true if ADDR is a valid address for a
! memory reference in mode MODE, according to the weak form of
! GO_IF_LEGITIMATE_ADDRESS. This is also used elsewhere. */
! bool
! memory_address_p (enum machine_mode mode ATTRIBUTE_UNUSED, rtx addr)
{
! GO_IF_LEGITIMATE_ADDRESS (mode, addr, win);
! return false;
! win:
! return true;
}
! /* Return true if OP is a valid address for a memory reference in mode
! MODE. This is exactly the same as memory_address_p, but the
! arguments are in the appropriate order for an operand predicate.
!
! Note that unlike most other operand predicates, the mode of OP is
! unlikely to be equal to MODE, since OP is a pointer to an object,
! not the object itself. */
! bool
! address_operand (rtx op, enum machine_mode mode)
{
! return memory_address_p (mode, op);
}
! /* Return true if OP is a valid memory reference with mode MODE,
! including a valid address. */
! bool
! memory_operand (rtx op, enum machine_mode mode)
{
! if (!MEM_P (op))
! return false;
! if (mode != VOIDmode && GET_MODE (op) != mode)
! return false;
! if (!volatile_ok && MEM_VOLATILE_P (op))
! return false;
! /* Use the mem's mode, since it will be reloaded thus. */
! return memory_address_p (GET_MODE (op), XEXP (op, 0));
}
! /* Return true if OP is a valid indirect memory reference with mode
! MODE; that is, a memory reference whose address is a general_operand.
! ??? When does this differ from memory_operand? It cannot be true
! when memory_operand is false, so it must be that some ports have
! valid (per memory_address_p) addresses that fail general_operand,
! but that seems awfully implausible. */
! bool
! indirect_operand (rtx op, enum machine_mode mode)
! {
! return (memory_operand (op, mode)
! && general_operand (XEXP (op, 0), Pmode));
}
! /* Return true if OP is a valid operand that stands for pushing a
! value of mode MODE onto the stack. This will be a MEM with some
! kind of auto-modify annotation; STACK_PUSH_CODE says which.
! ??? Why insist on a PRE_MODIFY in the rounded-size case? And why
! is this asymmetric with pop_operand? */
! bool
push_operand (rtx op, enum machine_mode mode)
{
unsigned int rounded_size = GET_MODE_SIZE (mode);
*************** push_operand (rtx op, enum machine_mode
*** 1209,1225 ****
#endif
if (!MEM_P (op))
! return 0;
if (mode != VOIDmode && GET_MODE (op) != mode)
! return 0;
op = XEXP (op, 0);
if (rounded_size == GET_MODE_SIZE (mode))
{
if (GET_CODE (op) != STACK_PUSH_CODE)
! return 0;
}
else
{
--- 1124,1140 ----
#endif
if (!MEM_P (op))
! return false;
if (mode != VOIDmode && GET_MODE (op) != mode)
! return false;
op = XEXP (op, 0);
if (rounded_size == GET_MODE_SIZE (mode))
{
if (GET_CODE (op) != STACK_PUSH_CODE)
! return false;
}
else
{
*************** push_operand (rtx op, enum machine_mode
*** 1233,1342 ****
|| INTVAL (XEXP (XEXP (op, 1), 1)) != (int) rounded_size
#endif
)
! return 0;
}
return XEXP (op, 0) == stack_pointer_rtx;
}
! /* Return 1 if OP is a valid operand that stands for popping a
value of mode MODE off the stack.
! The main use of this function is as a predicate in match_operand
! expressions in the machine description. */
! int
pop_operand (rtx op, enum machine_mode mode)
{
if (!MEM_P (op))
! return 0;
if (mode != VOIDmode && GET_MODE (op) != mode)
! return 0;
op = XEXP (op, 0);
if (GET_CODE (op) != STACK_POP_CODE)
! return 0;
return XEXP (op, 0) == stack_pointer_rtx;
}
! /* Return 1 if ADDR is a valid memory address for mode MODE. */
! int
! memory_address_p (enum machine_mode mode ATTRIBUTE_UNUSED, rtx addr)
! {
! GO_IF_LEGITIMATE_ADDRESS (mode, addr, win);
! return 0;
! win:
! return 1;
}
! /* Return 1 if OP is a valid memory reference with mode MODE,
! including a valid address.
! The main use of this function is as a predicate in match_operand
! expressions in the machine description. */
!
! int
! memory_operand (rtx op, enum machine_mode mode)
{
! rtx inner;
!
! if (! reload_completed)
! /* Note that no SUBREG is a memory operand before end of reload pass,
! because (SUBREG (MEM...)) forces reloading into a register. */
! return MEM_P (op) && general_operand (op, mode);
!
! if (mode != VOIDmode && GET_MODE (op) != mode)
! return 0;
!
! inner = op;
! if (GET_CODE (inner) == SUBREG)
! inner = SUBREG_REG (inner);
!
! return (MEM_P (inner) && general_operand (op, mode));
}
! /* Return 1 if OP is a valid indirect memory reference with mode MODE;
! that is, a memory reference whose address is a general_operand. */
! int
! indirect_operand (rtx op, enum machine_mode mode)
{
! /* Before reload, a SUBREG isn't in memory (see memory_operand, above). */
! if (! reload_completed
! && GET_CODE (op) == SUBREG && MEM_P (SUBREG_REG (op)))
! {
! int offset = SUBREG_BYTE (op);
! rtx inner = SUBREG_REG (op);
!
! if (mode != VOIDmode && GET_MODE (op) != mode)
! return 0;
!
! /* The only way that we can have a general_operand as the resulting
! address is if OFFSET is zero and the address already is an operand
! or if the address is (plus Y (const_int -OFFSET)) and Y is an
! operand. */
!
! return ((offset == 0 && general_operand (XEXP (inner, 0), Pmode))
! || (GET_CODE (XEXP (inner, 0)) == PLUS
! && GET_CODE (XEXP (XEXP (inner, 0), 1)) == CONST_INT
! && INTVAL (XEXP (XEXP (inner, 0), 1)) == -offset
! && general_operand (XEXP (XEXP (inner, 0), 0), Pmode)));
! }
!
! return (MEM_P (op)
! && memory_operand (op, mode)
! && general_operand (XEXP (op, 0), Pmode));
}
! /* Return 1 if this is a comparison operator. This allows the use of
! MATCH_OPERATOR to recognize all the branch insns. */
! int
comparison_operator (rtx op, enum machine_mode mode)
{
return ((mode == VOIDmode || GET_MODE (op) == mode)
--- 1148,1220 ----
|| INTVAL (XEXP (XEXP (op, 1), 1)) != (int) rounded_size
#endif
)
! return false;
}
return XEXP (op, 0) == stack_pointer_rtx;
}
! /* Return true if OP is a valid operand that stands for popping a
value of mode MODE off the stack.
! ??? Why asymmetric with push_operand? */
! bool
pop_operand (rtx op, enum machine_mode mode)
{
if (!MEM_P (op))
! return false;
if (mode != VOIDmode && GET_MODE (op) != mode)
! return false;
op = XEXP (op, 0);
if (GET_CODE (op) != STACK_POP_CODE)
! return false;
return XEXP (op, 0) == stack_pointer_rtx;
}
! /* Finally we have combined predicates, that accept more than one of the
! above categories. There is no nonregister_operand because it hasn't
! been found generally useful. */
! /* Return true if OP is an acceptable register or memory operand,
! in mode MODE. */
! bool
! nonimmediate_operand (rtx op, enum machine_mode mode)
! {
! return register_operand (op, mode) || memory_operand (op, mode);
}
! /* Return true of OP is an acceptable register or immediate operand,
! in mode MODE. */
! bool
! nonmemory_operand (rtx op, enum machine_mode mode)
{
! return immediate_operand (op, mode) || register_operand (op, mode);
}
! /* Return true if OP is an acceptable general operand: immediate,
! register, or memory, in mode MODE. */
! bool
! general_operand (rtx op, enum machine_mode mode)
{
! return (immediate_operand (op, mode)
! || register_operand (op, mode)
! || memory_operand (op, mode));
}
! /* Operator predicates are just like operand predicates, but they are
! used with (match_operator) expressions. */
! /* Return true if OP is a comparison operation in mode MODE. */
!
! bool
comparison_operator (rtx op, enum machine_mode mode)
{
return ((mode == VOIDmode || GET_MODE (op) == mode)
*************** reg_fits_class_p (rtx operand, enum reg_
*** 2643,2649 ****
static rtx
split_insn (rtx insn)
{
- /* Split insns here to get max fine-grain parallelism. */
rtx first = PREV_INSN (insn);
rtx last = try_split (PATTERN (insn), insn, 1);
--- 2521,2526 ----
*************** split_insn (rtx insn)
*** 2653,2672 ****
/* try_split returns the NOTE that INSN became. */
SET_INSN_DELETED (insn);
- /* ??? Coddle to md files that generate subregs in post-reload
- splitters instead of computing the proper hard register. */
- if (reload_completed && first != last)
- {
- first = NEXT_INSN (first);
- for (;;)
- {
- if (INSN_P (first))
- cleanup_subreg_operands (first);
- if (first == last)
- break;
- first = NEXT_INSN (first);
- }
- }
return last;
}
--- 2530,2535 ----
===================================================================
Index: recog.h
*** recog.h 25 Jul 2004 17:57:23 -0000 1.50
--- recog.h 6 Aug 2004 03:38:22 -0000
*************** extern int num_validated_changes (void);
*** 83,89 ****
extern void cancel_changes (int);
extern int constrain_operands (int);
extern int constrain_operands_cached (int);
! extern int memory_address_p (enum machine_mode, rtx);
extern int strict_memory_address_p (enum machine_mode, rtx);
extern int validate_replace_rtx_subexp (rtx, rtx, rtx, rtx *);
extern int validate_replace_rtx (rtx, rtx, rtx);
--- 83,89 ----
extern void cancel_changes (int);
extern int constrain_operands (int);
extern int constrain_operands_cached (int);
! extern bool memory_address_p (enum machine_mode, rtx);
extern int strict_memory_address_p (enum machine_mode, rtx);
extern int validate_replace_rtx_subexp (rtx, rtx, rtx, rtx *);
extern int validate_replace_rtx (rtx, rtx, rtx);
*************** extern int next_insn_tests_no_inequality
*** 96,116 ****
extern int reg_fits_class_p (rtx, enum reg_class, int, enum machine_mode);
extern rtx *find_single_use (rtx, rtx, rtx *);
! extern int general_operand (rtx, enum machine_mode);
! extern int address_operand (rtx, enum machine_mode);
! extern int register_operand (rtx, enum machine_mode);
! extern int pmode_register_operand (rtx, enum machine_mode);
! extern int scratch_operand (rtx, enum machine_mode);
! extern int immediate_operand (rtx, enum machine_mode);
! extern int const_int_operand (rtx, enum machine_mode);
! extern int const_double_operand (rtx, enum machine_mode);
! extern int nonimmediate_operand (rtx, enum machine_mode);
! extern int nonmemory_operand (rtx, enum machine_mode);
! extern int push_operand (rtx, enum machine_mode);
! extern int pop_operand (rtx, enum machine_mode);
! extern int memory_operand (rtx, enum machine_mode);
! extern int indirect_operand (rtx, enum machine_mode);
! extern int comparison_operator (rtx, enum machine_mode);
extern int offsettable_memref_p (rtx);
extern int offsettable_nonstrict_memref_p (rtx);
--- 96,116 ----
extern int reg_fits_class_p (rtx, enum reg_class, int, enum machine_mode);
extern rtx *find_single_use (rtx, rtx, rtx *);
! extern bool general_operand (rtx, enum machine_mode);
! extern bool address_operand (rtx, enum machine_mode);
! extern bool register_operand (rtx, enum machine_mode);
! extern bool pmode_register_operand (rtx, enum machine_mode);
! extern bool scratch_operand (rtx, enum machine_mode);
! extern bool immediate_operand (rtx, enum machine_mode);
! extern bool const_int_operand (rtx, enum machine_mode);
! extern bool const_double_operand (rtx, enum machine_mode);
! extern bool nonimmediate_operand (rtx, enum machine_mode);
! extern bool nonmemory_operand (rtx, enum machine_mode);
! extern bool push_operand (rtx, enum machine_mode);
! extern bool pop_operand (rtx, enum machine_mode);
! extern bool memory_operand (rtx, enum machine_mode);
! extern bool indirect_operand (rtx, enum machine_mode);
! extern bool comparison_operator (rtx, enum machine_mode);
extern int offsettable_memref_p (rtx);
extern int offsettable_nonstrict_memref_p (rtx);