From f4fde1b378ad68fb2dec6719ed26c1b901488e03 Mon Sep 17 00:00:00 2001 From: Richard Sandiford Date: Fri, 31 May 2019 16:27:49 +0000 Subject: [PATCH] New .md construct: define_insn_and_rewrite Several SVE patterns need define_insn_and_splits that generate the same insn_code, but with different operands. That's probably a niche requirement, but it's cropping up often enough on the ACLE branch that I think it would be good to have a syntactic sugar for it. This patch therefore adds a new construct called define_insn_and_rewrite. It's basically a define_insn_and_split with an implicit split pattern, obtained by copying the insn pattern and replacing match_operands with match_dups and match_operators with match_op_dups. 2019-05-31 Richard Sandiford gcc/ * doc/md.texi: Document define_insn_and_rewrite. * rtl.def (DEFINE_INSN_AND_REWRITE): New rtx code. * gensupport.c (queue_elem): Update comment. (replace_operands_with_dups): New function. (gen_rewrite_sequence): Likewise. (process_rtx): Handle DEFINE_INSN_AND_REWRITE. * read-rtl.c (apply_subst_iterator): Likewise. (add_condition_to_rtx, named_rtx_p): Likewise. (rtx_reader::read_rtx_operand): Likewise. * config/aarch64/aarch64-sve.md (while_ult_cc): Rename to... (*while_ult_cc): ...this and use define_insn_and_rewrite. (*cond__any): Turn into define_insn_and_rewrites. Remove separate define_split. From-SVN: r271815 --- gcc/ChangeLog | 18 +++++ gcc/config/aarch64/aarch64-sve.md | 87 +++++++++-------------- gcc/doc/md.texi | 113 ++++++++++++++++++++++++++++++ gcc/gensupport.c | 74 +++++++++++++++++-- gcc/read-rtl.c | 13 ++-- gcc/rtl.def | 6 ++ 6 files changed, 249 insertions(+), 62 deletions(-) diff --git a/gcc/ChangeLog b/gcc/ChangeLog index b04b38b5b9f..0ab40754384 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,21 @@ +2019-05-31 Richard Sandiford + + * doc/md.texi: Document define_insn_and_rewrite. + * rtl.def (DEFINE_INSN_AND_REWRITE): New rtx code. + * gensupport.c (queue_elem): Update comment. + (replace_operands_with_dups): New function. + (gen_rewrite_sequence): Likewise. + (process_rtx): Handle DEFINE_INSN_AND_REWRITE. + * read-rtl.c (apply_subst_iterator): Likewise. + (add_condition_to_rtx, named_rtx_p): Likewise. + (rtx_reader::read_rtx_operand): Likewise. + * config/aarch64/aarch64-sve.md + (while_ult_cc): Rename to... + (*while_ult_cc): ...this and use + define_insn_and_rewrite. + (*cond__any): Turn into define_insn_and_rewrites. + Remove separate define_split. + 2019-05-31 Jan Hubicka * tree-ssa-alias.c (type_has_components_p): New function. diff --git a/gcc/config/aarch64/aarch64-sve.md b/gcc/config/aarch64/aarch64-sve.md index 255058650f9..038762e19b8 100644 --- a/gcc/config/aarch64/aarch64-sve.md +++ b/gcc/config/aarch64/aarch64-sve.md @@ -1345,7 +1345,7 @@ ;; WHILELO sets the flags in the same way as a PTEST with a PTRUE GP. ;; Handle the case in which both results are useful. The GP operand ;; to the PTEST isn't needed, so we allow it to be anything. -(define_insn_and_split "while_ult_cc" +(define_insn_and_rewrite "*while_ult_cc" [(set (reg:CC CC_REGNUM) (compare:CC (unspec:SI [(match_operand:PRED_ALL 1) @@ -1364,12 +1364,8 @@ ;; Force the compiler to drop the unused predicate operand, so that we ;; don't have an unnecessary PTRUE. "&& !CONSTANT_P (operands[1])" - [(const_int 0)] { - emit_insn (gen_while_ult_cc - (operands[0], CONSTM1_RTX (mode), - operands[2], operands[3])); - DONE; + operands[1] = CONSTM1_RTX (mode); } ) @@ -2003,7 +1999,7 @@ ) ;; Synthetic predications with select unmatched. -(define_insn "*cond__any" +(define_insn_and_rewrite "*cond__any" [(set (match_operand:SVE_I 0 "register_operand" "=&w") (unspec:SVE_I [(match_operand: 1 "register_operand" "Upl") @@ -2012,11 +2008,20 @@ (match_operand:SVE_I 3 "register_operand" "w")) (match_operand:SVE_I 4 "register_operand" "w")] UNSPEC_SEL))] - "TARGET_SVE" + "TARGET_SVE + && !(rtx_equal_p (operands[0], operands[4]) + || rtx_equal_p (operands[2], operands[4]) + || rtx_equal_p (operands[3], operands[4]))" "#" + "&& reload_completed" + { + emit_insn (gen_vcond_mask_ (operands[0], operands[2], + operands[4], operands[1])); + operands[4] = operands[2] = operands[0]; + } ) -(define_insn "*cond__any" +(define_insn_and_rewrite "*cond__any" [(set (match_operand:SVE_SDI 0 "register_operand" "=&w") (unspec:SVE_SDI [(match_operand: 1 "register_operand" "Upl") @@ -2025,33 +2030,17 @@ (match_operand:SVE_SDI 3 "register_operand" "w")) (match_operand:SVE_SDI 4 "register_operand" "w")] UNSPEC_SEL))] - "TARGET_SVE" - "#" -) - -(define_split - [(set (match_operand:SVE_I 0 "register_operand") - (unspec:SVE_I - [(match_operand: 1 "register_operand") - (match_operator:SVE_I 5 "aarch64_sve_any_binary_operator" - [(match_operand:SVE_I 2 "register_operand") - (match_operand:SVE_I 3 "register_operand")]) - (match_operand:SVE_I 4 "register_operand")] - UNSPEC_SEL))] - "TARGET_SVE && reload_completed + "TARGET_SVE && !(rtx_equal_p (operands[0], operands[4]) || rtx_equal_p (operands[2], operands[4]) || rtx_equal_p (operands[3], operands[4]))" - ; Not matchable by any one insn or movprfx insn. We need a separate select. - [(set (match_dup 0) - (unspec:SVE_I [(match_dup 1) (match_dup 2) (match_dup 4)] - UNSPEC_SEL)) - (set (match_dup 0) - (unspec:SVE_I - [(match_dup 1) - (match_op_dup 5 [(match_dup 0) (match_dup 3)]) - (match_dup 0)] - UNSPEC_SEL))] + "#" + "&& reload_completed" + { + emit_insn (gen_vcond_mask_ (operands[0], operands[2], + operands[4], operands[1])); + operands[4] = operands[2] = operands[0]; + } ) ;; Set operand 0 to the last active element in operand 3, or to tied @@ -2957,7 +2946,7 @@ ) ;; Synthetic predication of floating-point operations with select unmatched. -(define_insn_and_split "*cond__any" +(define_insn_and_rewrite "*cond__any" [(set (match_operand:SVE_F 0 "register_operand" "=&w") (unspec:SVE_F [(match_operand: 1 "register_operand" "Upl") @@ -2974,14 +2963,11 @@ || rtx_equal_p (operands[2], operands[4]) || rtx_equal_p (operands[3], operands[4]))" ; Not matchable by any one insn or movprfx insn. We need a separate select. - [(set (match_dup 0) - (unspec:SVE_F [(match_dup 1) (match_dup 2) (match_dup 4)] UNSPEC_SEL)) - (set (match_dup 0) - (unspec:SVE_F - [(match_dup 1) - (unspec:SVE_F [(match_dup 0) (match_dup 3)] SVE_COND_FP_BINARY) - (match_dup 0)] - UNSPEC_SEL))] + { + emit_insn (gen_vcond_mask_ (operands[0], operands[2], + operands[4], operands[1])); + operands[4] = operands[2] = operands[0]; + } ) ;; Predicated floating-point ternary operations with select. @@ -3044,7 +3030,7 @@ ;; Predicated floating-point ternary operations in which the value for ;; inactive lanes is distinct from the other inputs. -(define_insn_and_split "*cond__any" +(define_insn_and_rewrite "*cond__any" [(set (match_operand:SVE_F 0 "register_operand" "=&w, &w, ?&w") (unspec:SVE_F [(match_operand: 1 "register_operand" "Upl, Upl, Upl") @@ -3066,16 +3052,11 @@ "&& reload_completed && !CONSTANT_P (operands[5]) && !rtx_equal_p (operands[0], operands[5])" - [(set (match_dup 0) - (unspec:SVE_F [(match_dup 1) (match_dup 4) (match_dup 5)] UNSPEC_SEL)) - (set (match_dup 0) - (unspec:SVE_F - [(match_dup 1) - (unspec:SVE_F [(match_dup 2) (match_dup 3) (match_dup 0)] - SVE_COND_FP_TERNARY) - (match_dup 0)] - UNSPEC_SEL))] - "" + { + emit_insn (gen_vcond_mask_ (operands[0], operands[4], + operands[5], operands[1])); + operands[5] = operands[4] = operands[0]; + } [(set_attr "movprfx" "yes")] ) diff --git a/gcc/doc/md.texi b/gcc/doc/md.texi index 775b8f5b715..4bc57d680af 100644 --- a/gcc/doc/md.texi +++ b/gcc/doc/md.texi @@ -8498,6 +8498,119 @@ functionality as two separate @code{define_insn} and @code{define_split} patterns. It exists for compactness, and as a maintenance tool to prevent having to ensure the two patterns' templates match. +@findex define_insn_and_rewrite +It is sometimes useful to have a @code{define_insn_and_split} +that replaces specific operands of an instruction but leaves the +rest of the instruction pattern unchanged. You can do this directly +with a @code{define_insn_and_split}, but it requires a +@var{new-insn-pattern-1} that repeats most of the original @var{insn-pattern}. +There is also the complication that an implicit @code{parallel} in +@var{insn-pattern} must become an explicit @code{parallel} in +@var{new-insn-pattern-1}, which is easy to overlook. +A simpler alternative is to use @code{define_insn_and_rewrite}, which +is a form of @code{define_insn_and_split} that automatically generates +@var{new-insn-pattern-1} by replacing each @code{match_operand} +in @var{insn-pattern} with a corresponding @code{match_dup}, and each +@code{match_operator} in the pattern with a corresponding @code{match_op_dup}. +The arguments are otherwise identical to @code{define_insn_and_split}: + +@smallexample +(define_insn_and_rewrite + [@var{insn-pattern}] + "@var{condition}" + "@var{output-template}" + "@var{split-condition}" + "@var{preparation-statements}" + [@var{insn-attributes}]) +@end smallexample + +The @code{match_dup}s and @code{match_op_dup}s in the new +instruction pattern use any new operand values that the +@var{preparation-statements} store in the @code{operands} array, +as for a normal @code{define_insn_and_split}. @var{preparation-statements} +can also emit additional instructions before the new instruction. +They can even emit an entirely different sequence of instructions and +use @code{DONE} to avoid emitting a new form of the original +instruction. + +The split in a @code{define_insn_and_rewrite} is only intended +to apply to existing instructions that match @var{insn-pattern}. +@var{split-condition} must therefore start with @code{&&}, +so that the split condition applies on top of @var{condition}. + +Here is an example from the AArch64 SVE port, in which operand 1 is +known to be equivalent to an all-true constant and isn't used by the +output template: + +@smallexample +(define_insn_and_rewrite "*while_ult_cc" + [(set (reg:CC CC_REGNUM) + (compare:CC + (unspec:SI [(match_operand:PRED_ALL 1) + (unspec:PRED_ALL + [(match_operand:GPI 2 "aarch64_reg_or_zero" "rZ") + (match_operand:GPI 3 "aarch64_reg_or_zero" "rZ")] + UNSPEC_WHILE_LO)] + UNSPEC_PTEST_PTRUE) + (const_int 0))) + (set (match_operand:PRED_ALL 0 "register_operand" "=Upa") + (unspec:PRED_ALL [(match_dup 2) + (match_dup 3)] + UNSPEC_WHILE_LO))] + "TARGET_SVE" + "whilelo\t%0., %2, %3" + ;; Force the compiler to drop the unused predicate operand, so that we + ;; don't have an unnecessary PTRUE. + "&& !CONSTANT_P (operands[1])" + @{ + operands[1] = CONSTM1_RTX (mode); + @} +) +@end smallexample + +The splitter in this case simply replaces operand 1 with the constant +value that it is known to have. The equivalent @code{define_insn_and_split} +would be: + +@smallexample +(define_insn_and_split "*while_ult_cc" + [(set (reg:CC CC_REGNUM) + (compare:CC + (unspec:SI [(match_operand:PRED_ALL 1) + (unspec:PRED_ALL + [(match_operand:GPI 2 "aarch64_reg_or_zero" "rZ") + (match_operand:GPI 3 "aarch64_reg_or_zero" "rZ")] + UNSPEC_WHILE_LO)] + UNSPEC_PTEST_PTRUE) + (const_int 0))) + (set (match_operand:PRED_ALL 0 "register_operand" "=Upa") + (unspec:PRED_ALL [(match_dup 2) + (match_dup 3)] + UNSPEC_WHILE_LO))] + "TARGET_SVE" + "whilelo\t%0., %2, %3" + ;; Force the compiler to drop the unused predicate operand, so that we + ;; don't have an unnecessary PTRUE. + "&& !CONSTANT_P (operands[1])" + [(parallel + [(set (reg:CC CC_REGNUM) + (compare:CC + (unspec:SI [(match_dup 1) + (unspec:PRED_ALL [(match_dup 2) + (match_dup 3)] + UNSPEC_WHILE_LO)] + UNSPEC_PTEST_PTRUE) + (const_int 0))) + (set (match_dup 0) + (unspec:PRED_ALL [(match_dup 2) + (match_dup 3)] + UNSPEC_WHILE_LO))])] + @{ + operands[1] = CONSTM1_RTX (mode); + @} +) +@end smallexample + @end ifset @ifset INTERNALS @node Including Patterns diff --git a/gcc/gensupport.c b/gcc/gensupport.c index 31a67d5ad39..0150346d409 100644 --- a/gcc/gensupport.c +++ b/gcc/gensupport.c @@ -70,8 +70,8 @@ struct queue_elem rtx data; file_location loc; struct queue_elem *next; - /* In a DEFINE_INSN that came from a DEFINE_INSN_AND_SPLIT, SPLIT - points to the generated DEFINE_SPLIT. */ + /* In a DEFINE_INSN that came from a DEFINE_INSN_AND_SPLIT or + DEFINE_INSN_AND_REWRITE, SPLIT points to the generated DEFINE_SPLIT. */ struct queue_elem *split; }; @@ -485,6 +485,63 @@ remove_constraints (rtx part) } } +/* Recursively replace MATCH_OPERANDs with MATCH_DUPs and MATCH_OPERATORs + with MATCH_OP_DUPs in X. */ + +static rtx +replace_operands_with_dups (rtx x) +{ + if (x == 0) + return x; + + rtx newx; + if (GET_CODE (x) == MATCH_OPERAND) + { + newx = rtx_alloc (MATCH_DUP); + XINT (newx, 0) = XINT (x, 0); + } + else if (GET_CODE (x) == MATCH_OPERATOR) + { + newx = rtx_alloc (MATCH_OP_DUP); + XINT (newx, 0) = XINT (x, 0); + XVEC (newx, 1) = XVEC (x, 2); + } + else + newx = shallow_copy_rtx (x); + + const char *format_ptr = GET_RTX_FORMAT (GET_CODE (x)); + for (int i = 0; i < GET_RTX_LENGTH (GET_CODE (x)); i++) + switch (*format_ptr++) + { + case 'e': + case 'u': + XEXP (newx, i) = replace_operands_with_dups (XEXP (x, i)); + break; + case 'E': + if (XVEC (x, i) != NULL) + { + XVEC (newx, i) = rtvec_alloc (XVECLEN (x, i)); + for (int j = 0; j < XVECLEN (x, i); j++) + XVECEXP (newx, i, j) + = replace_operands_with_dups (XVECEXP (x, i, j)); + } + break; + } + return newx; +} + +/* Convert matching pattern VEC from a DEFINE_INSN_AND_REWRITE into + a sequence that should be generated by the splitter. */ + +static rtvec +gen_rewrite_sequence (rtvec vec) +{ + rtvec new_vec = rtvec_alloc (1); + rtx x = add_implicit_parallel (vec); + RTVEC_ELT (new_vec, 0) = replace_operands_with_dups (x); + return new_vec; +} + /* Process a top level rtx in some way, queuing as appropriate. */ static void @@ -527,6 +584,7 @@ process_rtx (rtx desc, file_location loc) break; case DEFINE_INSN_AND_SPLIT: + case DEFINE_INSN_AND_REWRITE: { const char *split_cond; rtx split; @@ -534,6 +592,7 @@ process_rtx (rtx desc, file_location loc) int i; struct queue_elem *insn_elem; struct queue_elem *split_elem; + int split_code = (GET_CODE (desc) == DEFINE_INSN_AND_REWRITE ? 5 : 6); /* Create a split with values from the insn_and_split. */ split = rtx_alloc (DEFINE_SPLIT); @@ -555,12 +614,17 @@ process_rtx (rtx desc, file_location loc) split_cond = rtx_reader_ptr->join_c_conditions (XSTR (desc, 2), split_cond + 2); } + else if (GET_CODE (desc) == DEFINE_INSN_AND_REWRITE) + error_at (loc, "the rewrite condition must start with `&&'"); XSTR (split, 1) = split_cond; - XVEC (split, 2) = XVEC (desc, 5); - XSTR (split, 3) = XSTR (desc, 6); + if (GET_CODE (desc) == DEFINE_INSN_AND_REWRITE) + XVEC (split, 2) = gen_rewrite_sequence (XVEC (desc, 1)); + else + XVEC (split, 2) = XVEC (desc, 5); + XSTR (split, 3) = XSTR (desc, split_code); /* Fix up the DEFINE_INSN. */ - attr = XVEC (desc, 7); + attr = XVEC (desc, split_code + 1); PUT_CODE (desc, DEFINE_INSN); XVEC (desc, 4) = attr; diff --git a/gcc/read-rtl.c b/gcc/read-rtl.c index f72b2c35c7d..f37708f6967 100644 --- a/gcc/read-rtl.c +++ b/gcc/read-rtl.c @@ -286,9 +286,11 @@ apply_subst_iterator (rtx rt, unsigned int, int value) return; gcc_assert (GET_CODE (rt) == DEFINE_INSN || GET_CODE (rt) == DEFINE_INSN_AND_SPLIT + || GET_CODE (rt) == DEFINE_INSN_AND_REWRITE || GET_CODE (rt) == DEFINE_EXPAND); - int attrs = GET_CODE (rt) == DEFINE_INSN_AND_SPLIT ? 7 : 4; + int attrs = (GET_CODE (rt) == DEFINE_INSN_AND_SPLIT ? 7 + : GET_CODE (rt) == DEFINE_INSN_AND_REWRITE ? 6 : 4); attrs_vec = XVEC (rt, attrs); /* If we've already added attribute 'current_iterator_name', then we @@ -549,6 +551,7 @@ add_condition_to_rtx (rtx x, const char *extra) break; case DEFINE_INSN_AND_SPLIT: + case DEFINE_INSN_AND_REWRITE: XSTR (x, 2) = add_condition_to_string (XSTR (x, 2), extra); XSTR (x, 4) = add_condition_to_string (XSTR (x, 4), extra); break; @@ -632,6 +635,7 @@ named_rtx_p (rtx x) case DEFINE_EXPAND: case DEFINE_INSN: case DEFINE_INSN_AND_SPLIT: + case DEFINE_INSN_AND_REWRITE: return true; default: @@ -1837,8 +1841,8 @@ rtx_reader::read_rtx_operand (rtx return_rtx, int idx) break; } - /* The output template slot of a DEFINE_INSN, - DEFINE_INSN_AND_SPLIT, or DEFINE_PEEPHOLE automatically + /* The output template slot of a DEFINE_INSN, DEFINE_INSN_AND_SPLIT, + DEFINE_INSN_AND_REWRITE or DEFINE_PEEPHOLE automatically gets a star inserted as its first character, if it is written with a brace block instead of a string constant. */ star_if_braced = (format_ptr[idx] == 'T'); @@ -1855,7 +1859,8 @@ rtx_reader::read_rtx_operand (rtx return_rtx, int idx) if (*stringbuf == '\0' && idx == 0 && (GET_CODE (return_rtx) == DEFINE_INSN - || GET_CODE (return_rtx) == DEFINE_INSN_AND_SPLIT)) + || GET_CODE (return_rtx) == DEFINE_INSN_AND_SPLIT + || GET_CODE (return_rtx) == DEFINE_INSN_AND_REWRITE)) { struct obstack *string_obstack = get_string_obstack (); char line_name[20]; diff --git a/gcc/rtl.def b/gcc/rtl.def index f4c9d946cb5..63b09b3d1c4 100644 --- a/gcc/rtl.def +++ b/gcc/rtl.def @@ -936,6 +936,12 @@ DEF_RTL_EXPR(DEFINE_SPLIT, "define_split", "EsES", RTX_EXTRA) 7: optionally, a vector of attributes for this insn. */ DEF_RTL_EXPR(DEFINE_INSN_AND_SPLIT, "define_insn_and_split", "sEsTsESV", RTX_EXTRA) +/* A form of define_insn_and_split in which the split insn pattern (operand 5) + is determined automatically by replacing match_operands with match_dups + and match_operators with match_op_dups. The operands are the same as + define_insn_and_split but with operand 5 removed. */ +DEF_RTL_EXPR(DEFINE_INSN_AND_REWRITE, "define_insn_and_rewrite", "sEsTsSV", RTX_EXTRA) + /* Definition of an RTL peephole operation. Follows the same arguments as define_split. */ DEF_RTL_EXPR(DEFINE_PEEPHOLE2, "define_peephole2", "EsES", RTX_EXTRA) -- 2.30.2