return ((num_changes_pending () > 0) && (apply_change_group () > 0));
}
+/* Try to process the address of memory expression MEM. Return true on
+ success; leave the caller to clean up on failure. */
+
+bool
+insn_propagation::apply_to_mem_1 (rtx mem)
+{
+ auto old_num_changes = num_validated_changes ();
+ mem_depth += 1;
+ bool res = apply_to_rvalue_1 (&XEXP (mem, 0));
+ mem_depth -= 1;
+ if (!res)
+ return false;
+
+ if (old_num_changes != num_validated_changes ()
+ && should_check_mems
+ && !check_mem (old_num_changes, mem))
+ return false;
+
+ return true;
+}
+
+/* Try to process the rvalue expression at *LOC. Return true on success;
+ leave the caller to clean up on failure. */
+
+bool
+insn_propagation::apply_to_rvalue_1 (rtx *loc)
+{
+ rtx x = *loc;
+ enum rtx_code code = GET_CODE (x);
+ machine_mode mode = GET_MODE (x);
+
+ auto old_num_changes = num_validated_changes ();
+ if (from && GET_CODE (x) == GET_CODE (from) && rtx_equal_p (x, from))
+ {
+ /* Don't replace register asms in asm statements; we mustn't
+ change the user's register allocation. */
+ if (REG_P (x)
+ && HARD_REGISTER_P (x)
+ && register_asm_p (x)
+ && asm_noperands (PATTERN (insn)) > 0)
+ return false;
+
+ if (should_unshare)
+ validate_unshare_change (insn, loc, to, 1);
+ else
+ validate_change (insn, loc, to, 1);
+ if (mem_depth && !REG_P (to) && !CONSTANT_P (to))
+ {
+ /* We're substituting into an address, but TO will have the
+ form expected outside an address. Canonicalize it if
+ necessary. */
+ insn_propagation subprop (insn);
+ subprop.mem_depth += 1;
+ if (!subprop.apply_to_rvalue (loc))
+ gcc_unreachable ();
+ if (should_unshare
+ && num_validated_changes () != old_num_changes + 1)
+ {
+ /* TO is owned by someone else, so create a copy and
+ return TO to its original form. */
+ rtx to = copy_rtx (*loc);
+ cancel_changes (old_num_changes);
+ validate_change (insn, loc, to, 1);
+ }
+ }
+ num_replacements += 1;
+ should_unshare = true;
+ result_flags |= UNSIMPLIFIED;
+ return true;
+ }
+
+ /* Recursively apply the substitution and see if we can simplify
+ the result. This specifically shouldn't use simplify_gen_* for
+ speculative simplifications, since we want to avoid generating new
+ expressions where possible. */
+ auto old_result_flags = result_flags;
+ rtx newx = NULL_RTX;
+ bool recurse_p = false;
+ switch (GET_RTX_CLASS (code))
+ {
+ case RTX_UNARY:
+ {
+ machine_mode op0_mode = GET_MODE (XEXP (x, 0));
+ if (!apply_to_rvalue_1 (&XEXP (x, 0)))
+ return false;
+ if (from && old_num_changes == num_validated_changes ())
+ return true;
+
+ newx = simplify_unary_operation (code, mode, XEXP (x, 0), op0_mode);
+ break;
+ }
+
+ case RTX_BIN_ARITH:
+ case RTX_COMM_ARITH:
+ {
+ if (!apply_to_rvalue_1 (&XEXP (x, 0))
+ || !apply_to_rvalue_1 (&XEXP (x, 1)))
+ return false;
+ if (from && old_num_changes == num_validated_changes ())
+ return true;
+
+ if (GET_RTX_CLASS (code) == RTX_COMM_ARITH
+ && swap_commutative_operands_p (XEXP (x, 0), XEXP (x, 1)))
+ newx = simplify_gen_binary (code, mode, XEXP (x, 1), XEXP (x, 0));
+ else
+ newx = simplify_binary_operation (code, mode,
+ XEXP (x, 0), XEXP (x, 1));
+ break;
+ }
+
+ case RTX_COMPARE:
+ case RTX_COMM_COMPARE:
+ {
+ machine_mode op_mode = (GET_MODE (XEXP (x, 0)) != VOIDmode
+ ? GET_MODE (XEXP (x, 0))
+ : GET_MODE (XEXP (x, 1)));
+ if (!apply_to_rvalue_1 (&XEXP (x, 0))
+ || !apply_to_rvalue_1 (&XEXP (x, 1)))
+ return false;
+ if (from && old_num_changes == num_validated_changes ())
+ return true;
+
+ newx = simplify_relational_operation (code, mode, op_mode,
+ XEXP (x, 0), XEXP (x, 1));
+ break;
+ }
+
+ case RTX_TERNARY:
+ case RTX_BITFIELD_OPS:
+ {
+ machine_mode op0_mode = GET_MODE (XEXP (x, 0));
+ if (!apply_to_rvalue_1 (&XEXP (x, 0))
+ || !apply_to_rvalue_1 (&XEXP (x, 1))
+ || !apply_to_rvalue_1 (&XEXP (x, 2)))
+ return false;
+ if (from && old_num_changes == num_validated_changes ())
+ return true;
+
+ newx = simplify_ternary_operation (code, mode, op0_mode,
+ XEXP (x, 0), XEXP (x, 1),
+ XEXP (x, 2));
+ break;
+ }
+
+ case RTX_EXTRA:
+ if (code == SUBREG)
+ {
+ machine_mode inner_mode = GET_MODE (SUBREG_REG (x));
+ if (!apply_to_rvalue_1 (&SUBREG_REG (x)))
+ return false;
+ if (from && old_num_changes == num_validated_changes ())
+ return true;
+
+ rtx inner = SUBREG_REG (x);
+ newx = simplify_subreg (mode, inner, inner_mode, SUBREG_BYTE (x));
+ /* Reject the same cases that simplify_gen_subreg would. */
+ if (!newx
+ && (GET_CODE (inner) == SUBREG
+ || GET_CODE (inner) == CONCAT
+ || GET_MODE (inner) == VOIDmode
+ || !validate_subreg (mode, inner_mode,
+ inner, SUBREG_BYTE (x))))
+ {
+ failure_reason = "would create an invalid subreg";
+ return false;
+ }
+ break;
+ }
+ else
+ recurse_p = true;
+ break;
+
+ case RTX_OBJ:
+ if (code == LO_SUM)
+ {
+ if (!apply_to_rvalue_1 (&XEXP (x, 0))
+ || !apply_to_rvalue_1 (&XEXP (x, 1)))
+ return false;
+ if (from && old_num_changes == num_validated_changes ())
+ return true;
+
+ /* (lo_sum (high x) y) -> y where x and y have the same base. */
+ rtx op0 = XEXP (x, 0);
+ rtx op1 = XEXP (x, 1);
+ if (GET_CODE (op0) == HIGH)
+ {
+ rtx base0, base1, offset0, offset1;
+ split_const (XEXP (op0, 0), &base0, &offset0);
+ split_const (op1, &base1, &offset1);
+ if (rtx_equal_p (base0, base1))
+ newx = op1;
+ }
+ }
+ else if (code == REG)
+ {
+ if (from && REG_P (from) && reg_overlap_mentioned_p (x, from))
+ {
+ failure_reason = "inexact register overlap";
+ return false;
+ }
+ }
+ else if (code == MEM)
+ return apply_to_mem_1 (x);
+ else
+ recurse_p = true;
+ break;
+
+ case RTX_CONST_OBJ:
+ break;
+
+ case RTX_AUTOINC:
+ if (from && reg_overlap_mentioned_p (XEXP (x, 0), from))
+ {
+ failure_reason = "is subject to autoinc";
+ return false;
+ }
+ recurse_p = true;
+ break;
+
+ case RTX_MATCH:
+ case RTX_INSN:
+ gcc_unreachable ();
+ }
+
+ if (recurse_p)
+ {
+ const char *fmt = GET_RTX_FORMAT (code);
+ for (int i = 0; fmt[i]; i++)
+ switch (fmt[i])
+ {
+ case 'E':
+ for (int j = 0; j < XVECLEN (x, i); j++)
+ if (!apply_to_rvalue_1 (&XVECEXP (x, i, j)))
+ return false;
+ break;
+
+ case 'e':
+ if (XEXP (x, i) && !apply_to_rvalue_1 (&XEXP (x, i)))
+ return false;
+ break;
+ }
+ }
+ else if (newx && !rtx_equal_p (x, newx))
+ {
+ /* All substitutions made by OLD_NUM_CHANGES onwards have been
+ simplified. */
+ result_flags = ((result_flags & ~UNSIMPLIFIED)
+ | (old_result_flags & UNSIMPLIFIED));
+
+ if (should_note_simplifications)
+ note_simplification (old_num_changes, old_result_flags, x, newx);
+
+ /* There's no longer any point unsharing the substitutions made
+ for subexpressions, since we'll just copy this one instead. */
+ bool unshare = false;
+ for (int i = old_num_changes; i < num_changes; ++i)
+ {
+ unshare |= changes[i].unshare;
+ changes[i].unshare = false;
+ }
+ if (unshare)
+ validate_unshare_change (insn, loc, newx, 1);
+ else
+ validate_change (insn, loc, newx, 1);
+ }
+
+ return true;
+}
+
+/* Try to process the lvalue expression at *LOC. Return true on success;
+ leave the caller to clean up on failure. */
+
+bool
+insn_propagation::apply_to_lvalue_1 (rtx dest)
+{
+ rtx old_dest = dest;
+ while (GET_CODE (dest) == SUBREG
+ || GET_CODE (dest) == ZERO_EXTRACT
+ || GET_CODE (dest) == STRICT_LOW_PART)
+ {
+ if (GET_CODE (dest) == ZERO_EXTRACT
+ && (!apply_to_rvalue_1 (&XEXP (dest, 1))
+ || !apply_to_rvalue_1 (&XEXP (dest, 2))))
+ return false;
+ dest = XEXP (dest, 0);
+ }
+
+ if (MEM_P (dest))
+ return apply_to_mem_1 (dest);
+
+ /* Check whether the substitution is safe in the presence of this lvalue. */
+ if (!from
+ || dest == old_dest
+ || !REG_P (dest)
+ || !reg_overlap_mentioned_p (dest, from))
+ return true;
+
+ if (SUBREG_P (old_dest)
+ && SUBREG_REG (old_dest) == dest
+ && !read_modify_subreg_p (old_dest))
+ return true;
+
+ failure_reason = "is part of a read-write destination";
+ return false;
+}
+
+/* Try to process the instruction pattern at *LOC. Return true on success;
+ leave the caller to clean up on failure. */
+
+bool
+insn_propagation::apply_to_pattern_1 (rtx *loc)
+{
+ rtx body = *loc;
+ switch (GET_CODE (body))
+ {
+ case COND_EXEC:
+ return (apply_to_rvalue_1 (&COND_EXEC_TEST (body))
+ && apply_to_pattern_1 (&COND_EXEC_CODE (body)));
+
+ case PARALLEL:
+ {
+ int last = XVECLEN (body, 0) - 1;
+ for (int i = 0; i < last; ++i)
+ if (!apply_to_pattern_1 (&XVECEXP (body, 0, i)))
+ return false;
+ return apply_to_pattern_1 (&XVECEXP (body, 0, last));
+ }
+
+ case ASM_OPERANDS:
+ for (int i = 0, len = ASM_OPERANDS_INPUT_LENGTH (body); i < len; ++i)
+ if (!apply_to_rvalue_1 (&ASM_OPERANDS_INPUT (body, i)))
+ return false;
+ return true;
+
+ case CLOBBER:
+ return apply_to_lvalue_1 (XEXP (body, 0));
+
+ case SET:
+ return (apply_to_lvalue_1 (SET_DEST (body))
+ && apply_to_rvalue_1 (&SET_SRC (body)));
+
+ default:
+ /* All the other possibilities never store and can use a normal
+ rtx walk. This includes:
+
+ - USE
+ - TRAP_IF
+ - PREFETCH
+ - UNSPEC
+ - UNSPEC_VOLATILE. */
+ return apply_to_rvalue_1 (loc);
+ }
+}
+
+/* Apply this insn_propagation object's simplification or substitution
+ to the instruction pattern at LOC. */
+
+bool
+insn_propagation::apply_to_pattern (rtx *loc)
+{
+ unsigned int num_changes = num_validated_changes ();
+ bool res = apply_to_pattern_1 (loc);
+ if (!res)
+ cancel_changes (num_changes);
+ return res;
+}
+
+/* Apply this insn_propagation object's simplification or substitution
+ to the rvalue expression at LOC. */
+
+bool
+insn_propagation::apply_to_rvalue (rtx *loc)
+{
+ unsigned int num_changes = num_validated_changes ();
+ bool res = apply_to_rvalue_1 (loc);
+ if (!res)
+ cancel_changes (num_changes);
+ return res;
+}
+
/* Check whether INSN matches a specific alternative of an .md pattern. */
bool
return alt[i].matches >= 0 ? alt[alt[i].matches].cl : alt[i].cl;
}
+/* A class for substituting one rtx for another within an instruction,
+ or for recursively simplifying the instruction as-is. Derived classes
+ can record or filter certain decisions. */
+
+class insn_propagation : public simplify_context
+{
+public:
+ /* Assignments for RESULT_FLAGS.
+
+ UNSIMPLIFIED is true if a substitution has been made inside an rtx
+ X and if neither X nor its parent expressions could be simplified.
+
+ FIRST_SPARE_RESULT is the first flag available for derived classes. */
+ static const uint16_t UNSIMPLIFIED = 1U << 0;
+ static const uint16_t FIRST_SPARE_RESULT = 1U << 1;
+
+ insn_propagation (rtx_insn *);
+ insn_propagation (rtx_insn *, rtx, rtx, bool = true);
+ bool apply_to_pattern (rtx *);
+ bool apply_to_rvalue (rtx *);
+
+ /* Return true if we should accept a substitution into the address of
+ memory expression MEM. Undoing changes OLD_NUM_CHANGES and up restores
+ MEM's original address. */
+ virtual bool check_mem (int /*old_num_changes*/,
+ rtx /*mem*/) { return true; }
+
+ /* Note that we've simplified OLD_RTX into NEW_RTX. When substituting,
+ this only happens if a substitution occured within OLD_RTX.
+ Undoing OLD_NUM_CHANGES and up will restore the old form of OLD_RTX.
+ OLD_RESULT_FLAGS is the value that RESULT_FLAGS had before processing
+ OLD_RTX. */
+ virtual void note_simplification (int /*old_num_changes*/,
+ uint16_t /*old_result_flags*/,
+ rtx /*old_rtx*/, rtx /*new_rtx*/) {}
+
+private:
+ bool apply_to_mem_1 (rtx);
+ bool apply_to_lvalue_1 (rtx);
+ bool apply_to_rvalue_1 (rtx *);
+ bool apply_to_pattern_1 (rtx *);
+
+public:
+ /* The instruction that we are simplifying or propagating into. */
+ rtx_insn *insn;
+
+ /* If FROM is nonnull, we're replacing FROM with TO, otherwise we're
+ just doing a recursive simplification. */
+ rtx from;
+ rtx to;
+
+ /* The number of times that we have replaced FROM with TO. */
+ unsigned int num_replacements;
+
+ /* A bitmask of flags that describe the result of the simplificiation;
+ see above for details. */
+ uint16_t result_flags : 16;
+
+ /* True if we should unshare TO when making the next substitution,
+ false if we can use TO itself. */
+ uint16_t should_unshare : 1;
+
+ /* True if we should call check_mem after substituting into a memory. */
+ uint16_t should_check_mems : 1;
+
+ /* True if we should call note_simplification after each simplification. */
+ uint16_t should_note_simplifications : 1;
+
+ /* For future expansion. */
+ uint16_t spare : 13;
+
+ /* Gives the reason that a substitution failed, for debug purposes. */
+ const char *failure_reason;
+};
+
+/* Try to replace FROM with TO in INSN. SHARED_P is true if TO is shared
+ with other instructions, false if INSN can use TO directly. */
+
+inline insn_propagation::insn_propagation (rtx_insn *insn, rtx from, rtx to,
+ bool shared_p)
+ : insn (insn),
+ from (from),
+ to (to),
+ num_replacements (0),
+ result_flags (0),
+ should_unshare (shared_p),
+ should_check_mems (false),
+ should_note_simplifications (false),
+ spare (0),
+ failure_reason (nullptr)
+{
+}
+
+/* Try to simplify INSN without performing a substitution. */
+
+inline insn_propagation::insn_propagation (rtx_insn *insn)
+ : insn_propagation (insn, NULL_RTX, NULL_RTX)
+{
+}
+
extern void init_recog (void);
extern void init_recog_no_volatile (void);
extern int check_asm_operands (rtx);