On Sun, Dec 30, 2018 at 8:40 AM H.J. Lu <hjl.to...@gmail.com> wrote: > > On Wed, Nov 28, 2018 at 12:17 PM Jeff Law <l...@redhat.com> wrote: > > > > On 11/28/18 12:48 PM, H.J. Lu wrote: > > > On Mon, Nov 5, 2018 at 7:29 AM Jan Hubicka <hubi...@ucw.cz> wrote: > > >> > > >>> On 11/5/18 7:21 AM, Jan Hubicka wrote: > > >>>>> > > >>>>> Did you mean "the nearest common dominator"? > > >>>> > > >>>> If the nearest common dominator appears in the loop while all uses are > > >>>> out of loops, this will result in suboptimal xor placement. > > >>>> In this case you want to split edges out of the loop. > > >>>> > > >>>> In general this is what the LCM framework will do for you if the > > >>>> problem > > >>>> is modelled siimlar way as in mode_swtiching. At entry function mode > > >>>> is > > >>>> "no zero register needed" and all conversions need mode "zero register > > >>>> needed". Mode switching should then do the correct placement decisions > > >>>> (reaching minimal number of executions of xor). > > >>>> > > >>>> Jeff, whan is your optinion on the approach taken by the patch? > > >>>> It seems like a special case of more general issue, but I do not see > > >>>> very elegant way to solve it at least in the GCC 9 horisont, so if > > >>>> the placement is correct we can probalby go either with new pass or > > >>>> making this part of mode swithcing (which is anyway run by x86 backend) > > >>> So I haven't followed this discussion at all, but did touch on this > > >>> issue with some patch a month or two ago with a target patch that was > > >>> trying to avoid the partial stalls. > > >>> > > >>> My assumption is that we're trying to find one or more places to > > >>> initialize the upper half of an avx register so as to avoid partial > > >>> register stall at existing sites that set the upper half. > > >>> > > >>> This sounds like a classic PRE/LCM style problem (of which mode > > >>> switching is just another variant). A common-dominator approach is > > >>> closer to a classic GCSE and is going to result is more initializations > > >>> at sub-optimal points than a PRE/LCM style. > > >> > > >> yes, it is usual code placement problem. It is special case because the > > >> zero register is not modified by the conversion (just we need to have > > >> zero somewhere). So basically we do not have kills to the zero except > > >> for entry block. > > >> > > > > > > Do you have testcase to show thatf the nearest common dominator > > > in the loop, while all uses areout of loops, leads to suboptimal xor > > > placement? > > I don't have a testcase, but it's all but certain nearest common > > dominator is going to be a suboptimal placement. That's going to create > > paths where you're going to emit the xor when it's not used. > > > > The whole point of the LCM algorithms is they are optimal in terms of > > expression evaluations. > > We tried LCM and it didn't work well for this case. LCM places a single > VXOR close to the location where it is needed, which can be inside a > loop. There is nothing wrong with the LCM algorithms. But this doesn't > solve > > https://gcc.gnu.org/bugzilla/show_bug.cgi?id=87007 > > where VXOR is executed multiple times inside of a function, instead of > just once. We are investigating to generate a single VXOR at entry of the > nearest dominator for basic blocks with SF/DF conversions, which is in > the the fake loop that contains the whole function: > > bb = nearest_common_dominator_for_set (CDI_DOMINATORS, > convert_bbs); > while (bb->loop_father->latch > != EXIT_BLOCK_PTR_FOR_FN (cfun)) > bb = get_immediate_dominator (CDI_DOMINATORS, > bb->loop_father->header); > > insn = BB_HEAD (bb); > if (!NONDEBUG_INSN_P (insn)) > insn = next_nonnote_nondebug_insn (insn); > set = gen_rtx_SET (v4sf_const0, CONST0_RTX (V4SFmode)); > set_insn = emit_insn_before (set, insn); >
Here is the updated patch. OK for trunk? Thanks. -- H.J.
From 6eca7dbf282d7e2a5cde41bffeca66195d72d48e Mon Sep 17 00:00:00 2001 From: "H.J. Lu" <hjl.to...@gmail.com> Date: Mon, 7 Jan 2019 05:44:59 -0800 Subject: [PATCH] i386: Add pass_remove_partial_avx_dependency With -mavx, for $ cat foo.i extern float f; extern double d; extern int i; void foo (void) { d = f; f = i; } we need to generate vxorp[ds] %xmmN, %xmmN, %xmmN ... vcvtss2sd f(%rip), %xmmN, %xmmX ... vcvtsi2ss i(%rip), %xmmN, %xmmY to avoid partial XMM register stall. This patch adds a pass to generate a single vxorps %xmmN, %xmmN, %xmmN at entry of the nearest dominator for basic blocks with SF/DF conversions, which is in the fake loop that contains the whole function, instead of generating one vxorp[ds] %xmmN, %xmmN, %xmmN for each SF/DF conversion. NB: The LCM algorithm isn't appropriate here since it may place a vxorps inside the loop. Simple testcase show this: $ cat badcase.c extern float f; extern double d; void foo (int n, int k) { for (int j = 0; j != n; j++) if (j < k) d = f; } It generates ... loop: if(j < k) vxorps %xmm0, %xmm0, %xmm0 vcvtss2sd %xmm1, %xmm0, %xmm0 ... loopend ... This is because LCM only works when there is a certain benifit. But for conditional branch, LCM wouldn't move vxorps %xmm0, %xmm0, %xmm0 out of loop. SPEC CPU 2017 on Intel Xeon with AVX512 shows: 1. The nearest dominator |RATE |Improvement| |500.perlbench_r | 0.55% | |538.imagick_r | 8.43% | |544.nab_r | 0.71% | 2. LCM |RATE |Improvement| |500.perlbench_r | -0.76% | |538.imagick_r | 7.96% | |544.nab_r | -0.13% | Performance impacts of SPEC CPU 2017 rate on Intel Xeon with AVX512 using -Ofast -flto -march=skylake-avx512 -mfpmath=sse -funroll-loops are: |INT RATE |Improvement| |500.perlbench_r | 0.55% | |502.gcc_r | 0.14% | |505.mcf_r | 0.08% | |523.xalancbmk_r | 0.18% | |525.x264_r |-0.49% | |531.deepsjeng_r |-0.04% | |541.leela_r |-0.26% | |548.exchange2_r |-0.3% | |557.xz_r |BuildSame| |FP RATE |Improvement| |503.bwaves_r |-0.29% | |507.cactuBSSN_r | 0.04% | |508.namd_r |-0.74% | |510.parest_r |-0.01% | |511.povray_r | 2.23% | |519.lbm_r | 0.1% | |521.wrf_r | 0.49% | |526.blender_r | 0.13% | |527.cam4_r | 0.65% | |538.imagick_r | 8.43% | |544.nab_r | 0.71% | |549.fotonik3d_r | 0.15% | |554.roms_r | 0.08% | On Skylake client, impacts on 538.imagick_r are size before: text data bss dec hex filename 3018743 8432 4472 3031647 2e425f imagick_r size after: text data bss dec hex filename 2992351 8432 4472 3005255 2ddb47 imagick_r number of vxorp[ds]: before after difference 10453 3816 -63.49% gcc/ 2019-01-07 H.J. Lu <hongjiu...@intel.com> Hongtao Liu <hongtao....@intel.com> Sunil K Pandey <sunil.k.pan...@intel.com> PR target/87007 * config/i386/i386-passes.def: Add pass_remove_partial_avx_dependency. * config/i386/i386-protos.h (make_pass_remove_partial_avx_dependency): New. * config/i386/i386.c (make_pass_remove_partial_avx_dependency): New function. (pass_data_remove_partial_avx_dependency): New. (pass_remove_partial_avx_dependency): Likewise. (make_pass_remove_partial_avx_dependency): Likewise. * config/i386/i386.md (vec_scalar_convert): New attribute. (*extendsfdf2): Add vec_scalar_convert. (truncdfsf2): Likewise. (*float<SWI48:mode><MODEF:mode>2): Likewise. (SF/DF conversion splitters): Disabled for TARGET_AVX. gcc/testsuite/ 2019-01-07 H.J. Lu <hongjiu...@intel.com> Hongtao Liu <hongtao....@intel.com> Sunil K Pandey <sunil.k.pan...@intel.com> PR target/87007 * gcc.target/i386/pr87007-1.c: New test. * gcc.target/i386/pr87007-2.c: Likewise. --- gcc/config/i386/i386-passes.def | 2 + gcc/config/i386/i386-protos.h | 2 + gcc/config/i386/i386.c | 174 ++++++++++++++++++++++ gcc/config/i386/i386.md | 16 +- gcc/testsuite/gcc.target/i386/pr87007-1.c | 15 ++ gcc/testsuite/gcc.target/i386/pr87007-2.c | 18 +++ 6 files changed, 224 insertions(+), 3 deletions(-) create mode 100644 gcc/testsuite/gcc.target/i386/pr87007-1.c create mode 100644 gcc/testsuite/gcc.target/i386/pr87007-2.c diff --git a/gcc/config/i386/i386-passes.def b/gcc/config/i386/i386-passes.def index 4a6a95cc2d9..b439f3a9028 100644 --- a/gcc/config/i386/i386-passes.def +++ b/gcc/config/i386/i386-passes.def @@ -31,3 +31,5 @@ along with GCC; see the file COPYING3. If not see INSERT_PASS_BEFORE (pass_cse2, 1, pass_stv, true /* timode_p */); INSERT_PASS_BEFORE (pass_shorten_branches, 1, pass_insert_endbranch); + + INSERT_PASS_AFTER (pass_combine, 1, pass_remove_partial_avx_dependency); diff --git a/gcc/config/i386/i386-protos.h b/gcc/config/i386/i386-protos.h index 1e802bac1ea..b8f166a0b25 100644 --- a/gcc/config/i386/i386-protos.h +++ b/gcc/config/i386/i386-protos.h @@ -367,3 +367,5 @@ class rtl_opt_pass; extern rtl_opt_pass *make_pass_insert_vzeroupper (gcc::context *); extern rtl_opt_pass *make_pass_stv (gcc::context *); extern rtl_opt_pass *make_pass_insert_endbranch (gcc::context *); +extern rtl_opt_pass *make_pass_remove_partial_avx_dependency + (gcc::context *); diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c index 61dbc95c086..57f9666554a 100644 --- a/gcc/config/i386/i386.c +++ b/gcc/config/i386/i386.c @@ -2793,6 +2793,180 @@ make_pass_insert_endbranch (gcc::context *ctxt) return new pass_insert_endbranch (ctxt); } +/* At entry of the nearest common dominator for basic blocks with + conversions, generate a single + vxorps %xmmN, %xmmN, %xmmN + for all + vcvtss2sd op, %xmmN, %xmmX + vcvtsd2ss op, %xmmN, %xmmX + vcvtsi2ss op, %xmmN, %xmmX + vcvtsi2sd op, %xmmN, %xmmX + + NB: We want to generate only a single vxorps to cover the whole + function. The LCM algorithm isn't appropriate here since it may + place a vxorps inside the loop. */ + +static unsigned int +remove_partial_avx_dependency (void) +{ + timevar_push (TV_MACH_DEP); + + calculate_dominance_info (CDI_DOMINATORS); + df_set_flags (DF_DEFER_INSN_RESCAN); + df_chain_add_problem (DF_DU_CHAIN | DF_UD_CHAIN); + df_md_add_problem (); + df_analyze (); + + bitmap_obstack_initialize (NULL); + bitmap convert_bbs = BITMAP_ALLOC (NULL); + + basic_block bb; + rtx_insn *insn, *set_insn; + rtx set; + rtx v4sf_const0 = NULL_RTX; + + FOR_EACH_BB_FN (bb, cfun) + { + FOR_BB_INSNS (bb, insn) + { + if (!NONDEBUG_INSN_P (insn)) + continue; + + set = single_set (insn); + if (!set) + continue; + + if (get_attr_vec_scalar_convert (insn) + != VEC_SCALAR_CONVERT_TRUE) + continue; + + if (!v4sf_const0) + v4sf_const0 = gen_reg_rtx (V4SFmode); + + /* Convert VEC_SCALAR_CONVERT_TRUE insns, DF -> SF, SF -> DF, + SI -> SF, SI -> DF, DI -> SF, DI -> DF, to vec_dup and + vec_merge with subreg. */ + rtx src = SET_SRC (set); + rtx dest = SET_DEST (set); + machine_mode dest_mode = GET_MODE (dest); + + rtx zero; + machine_mode dest_vecmode; + if (dest_mode == E_SFmode) + { + dest_vecmode = V4SFmode; + zero = v4sf_const0; + } + else + { + dest_vecmode = V2DFmode; + zero = gen_rtx_SUBREG (V2DFmode, v4sf_const0, 0); + } + + /* Change source to vector mode. */ + src = gen_rtx_VEC_DUPLICATE (dest_vecmode, src); + src = gen_rtx_VEC_MERGE (dest_vecmode, src, zero, + GEN_INT (HOST_WIDE_INT_1U)); + /* Change destination to vector mode. */ + rtx vec = gen_reg_rtx (dest_vecmode); + /* Generate an XMM vector SET. */ + set = gen_rtx_SET (vec, src); + set_insn = emit_insn_before (set, insn); + df_insn_rescan (set_insn); + + src = gen_rtx_SUBREG (dest_mode, vec, 0); + set = gen_rtx_SET (dest, src); + + /* Drop possible dead definitions. */ + PATTERN (insn) = set; + + INSN_CODE (insn) = -1; + recog_memoized (insn); + df_insn_rescan (insn); + bitmap_set_bit (convert_bbs, bb->index); + } + } + + if (v4sf_const0) + { + /* (Re-)discover loops so that bb->loop_father can be used in the + analysis below. */ + loop_optimizer_init (AVOID_CFG_MODIFICATIONS); + + /* Generate a vxorps at entry of the nearest dominator for basic + blocks with conversions, which is in the the fake loop that + contains the whole function, so that there is only a single + vxorps in the whole function. */ + bb = nearest_common_dominator_for_set (CDI_DOMINATORS, + convert_bbs); + while (bb->loop_father->latch + != EXIT_BLOCK_PTR_FOR_FN (cfun)) + bb = get_immediate_dominator (CDI_DOMINATORS, + bb->loop_father->header); + + insn = BB_HEAD (bb); + if (!NONDEBUG_INSN_P (insn)) + insn = next_nonnote_nondebug_insn (insn); + set = gen_rtx_SET (v4sf_const0, CONST0_RTX (V4SFmode)); + set_insn = emit_insn_before (set, insn); + df_insn_rescan (set_insn); + df_process_deferred_rescans (); + loop_optimizer_finalize (); + } + + bitmap_obstack_release (NULL); + BITMAP_FREE (convert_bbs); + + timevar_pop (TV_MACH_DEP); + return 0; +} + +namespace { + +const pass_data pass_data_remove_partial_avx_dependency = +{ + RTL_PASS, /* type */ + "rpad", /* name */ + OPTGROUP_NONE, /* optinfo_flags */ + TV_MACH_DEP, /* tv_id */ + 0, /* properties_required */ + 0, /* properties_provided */ + 0, /* properties_destroyed */ + 0, /* todo_flags_start */ + TODO_df_finish, /* todo_flags_finish */ +}; + +class pass_remove_partial_avx_dependency : public rtl_opt_pass +{ +public: + pass_remove_partial_avx_dependency (gcc::context *ctxt) + : rtl_opt_pass (pass_data_remove_partial_avx_dependency, ctxt) + {} + + /* opt_pass methods: */ + virtual bool gate (function *) + { + return (TARGET_AVX + && TARGET_SSE_PARTIAL_REG_DEPENDENCY + && TARGET_SSE_MATH + && optimize + && optimize_function_for_speed_p (cfun)); + } + + virtual unsigned int execute (function *) + { + return remove_partial_avx_dependency (); + } +}; // class pass_rpad + +} // anon namespace + +rtl_opt_pass * +make_pass_remove_partial_avx_dependency (gcc::context *ctxt) +{ + return new pass_remove_partial_avx_dependency (ctxt); +} + /* Return true if a red-zone is in use. We can't use red-zone when there are local indirect jumps, like "indirect_jump" or "tablejump", which jumps to another place in the function, since "call" in the diff --git a/gcc/config/i386/i386.md b/gcc/config/i386/i386.md index b2d27faa4fd..31bcb67843d 100644 --- a/gcc/config/i386/i386.md +++ b/gcc/config/i386/i386.md @@ -777,6 +777,10 @@ (define_attr "i387_cw" "trunc,floor,ceil,uninitialized,any" (const_string "any")) +;; Define attribute to indicate scalar ssecvt/sseicvt insns. +(define_attr "vec_scalar_convert" "false,true" + (const_string "false")) + ;; Define attribute to classify add/sub insns that consumes carry flag (CF) (define_attr "use_carry" "0,1" (const_string "0")) @@ -4388,6 +4392,7 @@ } } [(set_attr "type" "fmov,fmov,ssecvt") + (set_attr "vec_scalar_convert" "false,false,true") (set_attr "prefix" "orig,orig,maybe_vex") (set_attr "mode" "SF,XF,DF") (set (attr "enabled") @@ -4477,7 +4482,8 @@ [(set (match_operand:DF 0 "sse_reg_operand") (float_extend:DF (match_operand:SF 1 "nonimmediate_operand")))] - "TARGET_SSE_PARTIAL_REG_DEPENDENCY && epilogue_completed + "!TARGET_AVX + && TARGET_SSE_PARTIAL_REG_DEPENDENCY && epilogue_completed && optimize_function_for_speed_p (cfun) && (!REG_P (operands[1]) || REGNO (operands[0]) != REGNO (operands[1])) @@ -4552,6 +4558,7 @@ } } [(set_attr "type" "fmov,fmov,ssecvt") + (set_attr "vec_scalar_convert" "false,false,true") (set_attr "mode" "SF") (set (attr "enabled") (if_then_else @@ -4635,7 +4642,8 @@ [(set (match_operand:SF 0 "sse_reg_operand") (float_truncate:SF (match_operand:DF 1 "nonimmediate_operand")))] - "TARGET_SSE_PARTIAL_REG_DEPENDENCY && epilogue_completed + "!TARGET_AVX + && TARGET_SSE_PARTIAL_REG_DEPENDENCY && epilogue_completed && optimize_function_for_speed_p (cfun) && (!REG_P (operands[1]) || REGNO (operands[0]) != REGNO (operands[1])) @@ -5011,6 +5019,7 @@ %vcvtsi2<MODEF:ssemodesuffix><SWI48:rex64suffix>\t{%1, %d0|%d0, %1} %vcvtsi2<MODEF:ssemodesuffix><SWI48:rex64suffix>\t{%1, %d0|%d0, %1}" [(set_attr "type" "fmov,sseicvt,sseicvt") + (set_attr "vec_scalar_convert" "false,true,true") (set_attr "prefix" "orig,maybe_vex,maybe_vex") (set_attr "mode" "<MODEF:MODE>") (set (attr "prefix_rex") @@ -5139,7 +5148,8 @@ (define_split [(set (match_operand:MODEF 0 "sse_reg_operand") (float:MODEF (match_operand:SWI48 1 "nonimmediate_operand")))] - "TARGET_SSE_PARTIAL_REG_DEPENDENCY && epilogue_completed + "!TARGET_AVX + && TARGET_SSE_PARTIAL_REG_DEPENDENCY && epilogue_completed && optimize_function_for_speed_p (cfun) && (!EXT_REX_SSE_REG_P (operands[0]) || TARGET_AVX512VL)" diff --git a/gcc/testsuite/gcc.target/i386/pr87007-1.c b/gcc/testsuite/gcc.target/i386/pr87007-1.c new file mode 100644 index 00000000000..93cf1dcdfa5 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/pr87007-1.c @@ -0,0 +1,15 @@ +/* { dg-do compile } */ +/* { dg-options "-O2 -march=skylake" } */ + +extern float f; +extern double d; +extern int i; + +void +foo (void) +{ + d = f; + f = i; +} + +/* { dg-final { scan-assembler-times "vxorps\[^\n\r\]*xmm\[0-9\]" 1 } } */ diff --git a/gcc/testsuite/gcc.target/i386/pr87007-2.c b/gcc/testsuite/gcc.target/i386/pr87007-2.c new file mode 100644 index 00000000000..cca7ae7afbc --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/pr87007-2.c @@ -0,0 +1,18 @@ +/* { dg-do compile } */ +/* { dg-options "-O2 -march=skylake" } */ + +extern float f; +extern double d; +extern int i; + +void +foo (int n, int k) +{ + for (int i = 0; i != n; i++) + if(i < k) + d = f; + else + f = i; +} + +/* { dg-final { scan-assembler-times "vxorps\[^\n\r\]*xmm\[0-9\]" 1 } } */ -- 2.19.2