aboutsummaryrefslogtreecommitdiff
path: root/gcc/recog.c
diff options
context:
space:
mode:
authorRichard Sandiford <richard.sandiford@arm.com>2020-12-17 00:15:07 +0000
committerRichard Sandiford <richard.sandiford@arm.com>2020-12-17 00:15:07 +0000
commit0d74260a1f6704da869b87d163f4be31fd0f2b41 (patch)
treeecb8e15bf6da3cd0a8a96cc22819679f05ec439c /gcc/recog.c
parenteb74135dd35213f800f434df92b2835ce49f8ea6 (diff)
downloadgcc-0d74260a1f6704da869b87d163f4be31fd0f2b41.zip
gcc-0d74260a1f6704da869b87d163f4be31fd0f2b41.tar.gz
gcc-0d74260a1f6704da869b87d163f4be31fd0f2b41.tar.bz2
recog: Add a class for propagating into insns
This patch adds yet another way of propagating into an instruction and simplifying the result. (The net effect of the series is to keep the total number of propagation approaches the same though, since a later patch removes the fwprop.c routines.) One of the drawbacks of the validate_replace_* routines is that they only do simple simplifications, mostly canonicalisations: /* Do changes needed to keep rtx consistent. Don't do any other simplifications, as it is not our job. */ if (simplify) simplify_while_replacing (loc, to, object, op0_mode); But substituting can often lead to real simplification opportunities. simplify-rtx.c:simplify_replace_rtx does fully simplify the result, but it only operates on specific rvalues rather than full instruction patterns. It is also nondestructive, which means that it returns a new rtx whenever a substitution or simplification was possible. This can create quite a bit of garbage rtl in the context of a speculative recog, where changing the contents of a pointer is often enough. The new routines are therefore supposed to provide simplify_replace_rtx- style substitution in recog. They go to some effort to prevent garbage rtl from being created. At the moment, the new routines fail if the pattern would still refer to the old "from" value in some way. That might be unnecessary in some contexts; if so, it could be put behind a configuration parameter. gcc/ * recog.h (insn_propagation): New class. * recog.c (insn_propagation::apply_to_mem_1): New function. (insn_propagation::apply_to_rvalue_1): Likewise. (insn_propagation::apply_to_lvalue_1): Likewise. (insn_propagation::apply_to_pattern_1): Likewise. (insn_propagation::apply_to_pattern): Likewise. (insn_propagation::apply_to_rvalue): Likewise.
Diffstat (limited to 'gcc/recog.c')
-rw-r--r--gcc/recog.c380
1 files changed, 380 insertions, 0 deletions
diff --git a/gcc/recog.c b/gcc/recog.c
index cee481f..e9aa1ba 100644
--- a/gcc/recog.c
+++ b/gcc/recog.c
@@ -997,6 +997,386 @@ validate_simplify_insn (rtx_insn *insn)
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