Merged
[Backport] Align tight and hot loops without a max-skip limit #393
haibaooAdd on Aug 17
[Backport] Align tight and hot loops without a max-skip limit #393
Merged
Merged fromqq_56722097/gccbackport-align-tight-loopsintoopeneuler/gccmaster
Total: 9 files changed+236-6
| @@ -2163,6 +2163,203 @@ make_pass_insert_endbr_and_patchable_area (gcc::context *ctxt) | |||
| 2163 | return new pass_insert_endbr_and_patchable_area (ctxt); | 2163 | return new pass_insert_endbr_and_patchable_area (ctxt); |
| 2164 | } | 2164 | } |
| 2165 | 2165 | ||
| 2166 | +/* When a hot loop can be fit into one cacheline, | ||
| 2167 | + force align the loop without considering the max skip. */ | ||
| 2168 | +static void | ||
| 2169 | +ix86_align_loops () | ||
| 2170 | +{ | ||
| 2171 | + basic_block bb; | ||
| 2172 | + | ||
| 2173 | + /* Don't do this when we don't know cache line size. */ | ||
| 2174 | + if (ix86_cost->prefetch_block == 0) | ||
| 2175 | + return; | ||
| 2176 | + | ||
| 2177 | + loop_optimizer_init (AVOID_CFG_MODIFICATIONS); | ||
| 2178 | + profile_count count_threshold = cfun->cfg->count_max.apply_scale | ||
| 2179 | + (1, param_align_threshold); | ||
| 2180 | + FOR_EACH_BB_FN (bb, cfun) | ||
| 2181 | + { | ||
| 2182 | + rtx_insn *label = BB_HEAD (bb); | ||
| 2183 | + bool has_fallthru = 0; | ||
| 2184 | + edge e; | ||
| 2185 | + edge_iterator ei; | ||
| 2186 | + | ||
| 2187 | + if (!LABEL_P (label)) | ||
| 2188 | + continue; | ||
| 2189 | + | ||
| 2190 | + profile_count fallthru_count = profile_count::zero (); | ||
| 2191 | + profile_count branch_count = profile_count::zero (); | ||
| 2192 | + | ||
| 2193 | + FOR_EACH_EDGE (e, ei, bb->preds) | ||
| 2194 | + { | ||
| 2195 | + if (e->flags & EDGE_FALLTHRU) | ||
| 2196 | + has_fallthru = 1, fallthru_count += e->count (); | ||
| 2197 | + else | ||
| 2198 | + branch_count += e->count (); | ||
| 2199 | + } | ||
| 2200 | + | ||
| 2201 | + if (!fallthru_count.initialized_p () || !branch_count.initialized_p ()) | ||
| 2202 | + continue; | ||
| 2203 | + | ||
| 2204 | + if (bb->loop_father | ||
| 2205 | + && bb->loop_father->latch != EXIT_BLOCK_PTR_FOR_FN (cfun) | ||
| 2206 | + && (has_fallthru | ||
| 2207 | + ? (!(single_succ_p (bb) | ||
| 2208 | + && single_succ (bb) == EXIT_BLOCK_PTR_FOR_FN (cfun)) | ||
| 2209 | + && optimize_bb_for_speed_p (bb) | ||
| 2210 | + && branch_count + fallthru_count > count_threshold | ||
| 2211 | + && (branch_count | ||
| 2212 | + > fallthru_count.apply_scale | ||
| 2213 | + (param_align_loop_iterations, 1))) | ||
| 2214 | + /* In case there'no fallthru for the loop. | ||
| 2215 | + Nops inserted won't be executed. */ | ||
| 2216 | + : (branch_count > count_threshold | ||
| 2217 | + || (bb->count > bb->prev_bb->count.apply_scale (10, 1) | ||
| 2218 | + && (bb->prev_bb->count | ||
| 2219 | + <= ENTRY_BLOCK_PTR_FOR_FN (cfun) | ||
| 2220 | + ->count.apply_scale (1, 2)))))) | ||
| 2221 | + { | ||
| 2222 | + rtx_insn* insn, *end_insn; | ||
| 2223 | + HOST_WIDE_INT size = 0; | ||
| 2224 | + bool padding_p = true; | ||
| 2225 | + basic_block tbb = bb; | ||
| 2226 | + unsigned cond_branch_num = 0; | ||
| 2227 | + bool detect_tight_loop_p = false; | ||
| 2228 | + | ||
| 2229 | + for (unsigned int i = 0; i != bb->loop_father->num_nodes; | ||
| 2230 | + i++, tbb = tbb->next_bb) | ||
| 2231 | + { | ||
| 2232 | + /* Only handle continuous cfg layout. */ | ||
| 2233 | + if (bb->loop_father != tbb->loop_father) | ||
| 2234 | + { | ||
| 2235 | + padding_p = false; | ||
| 2236 | + break; | ||
| 2237 | + } | ||
| 2238 | + | ||
| 2239 | + FOR_BB_INSNS (tbb, insn) | ||
| 2240 | + { | ||
| 2241 | + if (!NONDEBUG_INSN_P (insn)) | ||
| 2242 | + continue; | ||
| 2243 | + size += ix86_min_insn_size (insn); | ||
| 2244 | + | ||
| 2245 | + /* We don't know size of inline asm. | ||
| 2246 | + Don't align loop for call. */ | ||
| 2247 | + if (asm_noperands (PATTERN (insn)) >= 0 | ||
| 2248 | + || CALL_P (insn)) | ||
| 2249 | + { | ||
| 2250 | + size = -1; | ||
| 2251 | + break; | ||
| 2252 | + } | ||
| 2253 | + } | ||
| 2254 | + | ||
| 2255 | + if (size == -1 || size > ix86_cost->prefetch_block) | ||
| 2256 | + { | ||
| 2257 | + padding_p = false; | ||
| 2258 | + break; | ||
| 2259 | + } | ||
| 2260 | + | ||
| 2261 | + FOR_EACH_EDGE (e, ei, tbb->succs) | ||
| 2262 | + { | ||
| 2263 | + /* It could be part of the loop. */ | ||
| 2264 | + if (e->dest == bb) | ||
| 2265 | + { | ||
| 2266 | + detect_tight_loop_p = true; | ||
| 2267 | + break; | ||
| 2268 | + } | ||
| 2269 | + } | ||
| 2270 | + | ||
| 2271 | + if (detect_tight_loop_p) | ||
| 2272 | + break; | ||
| 2273 | + | ||
| 2274 | + end_insn = BB_END (tbb); | ||
| 2275 | + if (JUMP_P (end_insn)) | ||
| 2276 | + { | ||
| 2277 | + /* For decoded icache: | ||
| 2278 | + 1. Up to two branches are allowed per Way. | ||
| 2279 | + 2. A non-conditional branch is the last micro-op in a Way. | ||
| 2280 | + */ | ||
| 2281 | + if (onlyjump_p (end_insn) | ||
| 2282 | + && (any_uncondjump_p (end_insn) | ||
| 2283 | + || single_succ_p (tbb))) | ||
| 2284 | + { | ||
| 2285 | + padding_p = false; | ||
| 2286 | + break; | ||
| 2287 | + } | ||
| 2288 | + else if (++cond_branch_num >= 2) | ||
| 2289 | + { | ||
| 2290 | + padding_p = false; | ||
| 2291 | + break; | ||
| 2292 | + } | ||
| 2293 | + } | ||
| 2294 | + | ||
| 2295 | + } | ||
| 2296 | + | ||
| 2297 | + if (padding_p && detect_tight_loop_p) | ||
| 2298 | + { | ||
| 2299 | + emit_insn_before (gen_max_skip_align (GEN_INT (ceil_log2 (size)), | ||
| 2300 | + GEN_INT (0)), label); | ||
| 2301 | + /* End of function. */ | ||
| 2302 | + if (!tbb || tbb == EXIT_BLOCK_PTR_FOR_FN (cfun)) | ||
| 2303 | + break; | ||
| 2304 | + /* Skip bb which already fits into one cacheline. */ | ||
| 2305 | + bb = tbb; | ||
| 2306 | + } | ||
| 2307 | + } | ||
| 2308 | + } | ||
| 2309 | + | ||
| 2310 | + loop_optimizer_finalize (); | ||
| 2311 | + free_dominance_info (CDI_DOMINATORS); | ||
| 2312 | +} | ||
| 2313 | + | ||
| 2314 | +namespace { | ||
| 2315 | + | ||
| 2316 | +const pass_data pass_data_align_tight_loops = | ||
| 2317 | +{ | ||
| 2318 | + RTL_PASS, /* type */ | ||
| 2319 | + "align_tight_loops", /* name */ | ||
| 2320 | + OPTGROUP_NONE, /* optinfo_flags */ | ||
| 2321 | + TV_MACH_DEP, /* tv_id */ | ||
| 2322 | + 0, /* properties_required */ | ||
| 2323 | + 0, /* properties_provided */ | ||
| 2324 | + 0, /* properties_destroyed */ | ||
| 2325 | + 0, /* todo_flags_start */ | ||
| 2326 | + 0, /* todo_flags_finish */ | ||
| 2327 | +}; | ||
| 2328 | + | ||
| 2329 | +class pass_align_tight_loops : public rtl_opt_pass | ||
| 2330 | +{ | ||
| 2331 | +public: | ||
| 2332 | + pass_align_tight_loops (gcc::context *ctxt) | ||
| 2333 | + : rtl_opt_pass (pass_data_align_tight_loops, ctxt) | ||
| 2334 | + {} | ||
| 2335 | + | ||
| 2336 | + /* opt_pass methods: */ | ||
| 2337 | + bool gate (function *) final override | ||
| 2338 | + { | ||
| 2339 | + return TARGET_ALIGN_TIGHT_LOOPS | ||
| 2340 | + && optimize | ||
| 2341 | + && optimize_function_for_speed_p (cfun); | ||
| 2342 | + } | ||
| 2343 | + | ||
| 2344 | + unsigned int execute (function *) final override | ||
| 2345 | + { | ||
| 2346 | + timevar_push (TV_MACH_DEP); | ||
| 2347 | +#ifdef ASM_OUTPUT_MAX_SKIP_ALIGN | ||
| 2348 | + ix86_align_loops (); | ||
| 2349 | +#endif | ||
| 2350 | + timevar_pop (TV_MACH_DEP); | ||
| 2351 | + return 0; | ||
| 2352 | + } | ||
| 2353 | +}; // class pass_align_tight_loops | ||
| 2354 | + | ||
| 2355 | +} // anon namespace | ||
| 2356 | + | ||
| 2357 | +rtl_opt_pass * | ||
| 2358 | +make_pass_align_tight_loops (gcc::context *ctxt) | ||
| 2359 | +{ | ||
| 2360 | + return new pass_align_tight_loops (ctxt); | ||
| 2361 | +} | ||
| 2362 | + | ||
| 2166 | /* At entry of the nearest common dominator for basic blocks with | 2363 | /* At entry of the nearest common dominator for basic blocks with |
| 2167 | conversions/rcp/sqrt/rsqrt/round, generate a single | 2364 | conversions/rcp/sqrt/rsqrt/round, generate a single |
| 2168 | vxorps %xmmN, %xmmN, %xmmN | 2365 | vxorps %xmmN, %xmmN, %xmmN |
| @@ -2965,4 +3162,3 @@ ix86_generate_version_dispatcher_body (void *node_p) | |||
| 2965 | return resolver_decl; | 3162 | return resolver_decl; |
| 2966 | } | 3163 | } |
| 2967 | 3164 | ||
| 2968 | - | ||
| @@ -31,5 +31,8 @@ along with GCC; see the file COPYING3. If not see | |||
| 31 | INSERT_PASS_BEFORE (pass_cse2, 1, pass_stv, true /* timode_p */); | 31 | INSERT_PASS_BEFORE (pass_cse2, 1, pass_stv, true /* timode_p */); |
| 32 | 32 | ||
| 33 | INSERT_PASS_BEFORE (pass_shorten_branches, 1, pass_insert_endbr_and_patchable_area); | 33 | INSERT_PASS_BEFORE (pass_shorten_branches, 1, pass_insert_endbr_and_patchable_area); |
| 34 | + /* pass_align_tight_loops must be after pass_insert_endbr_and_patchable_area. | ||
| 35 | + PR116174. */ | ||
| 36 | + INSERT_PASS_BEFORE (pass_shorten_branches, 1, pass_align_tight_loops); | ||
| 34 | 37 | ||
| 35 | INSERT_PASS_AFTER (pass_combine, 1, pass_remove_partial_avx_dependency); | 38 | INSERT_PASS_AFTER (pass_combine, 1, pass_remove_partial_avx_dependency); |
| @@ -397,6 +397,7 @@ extern rtl_opt_pass *make_pass_insert_vzeroupper (gcc::context *); | |||
| 397 | extern rtl_opt_pass *make_pass_stv (gcc::context *); | 397 | extern rtl_opt_pass *make_pass_stv (gcc::context *); |
| 398 | extern rtl_opt_pass *make_pass_insert_endbr_and_patchable_area | 398 | extern rtl_opt_pass *make_pass_insert_endbr_and_patchable_area |
| 399 | (gcc::context *); | 399 | (gcc::context *); |
| 400 | +extern rtl_opt_pass *make_pass_align_tight_loops (gcc::context *); | ||
| 400 | extern rtl_opt_pass *make_pass_remove_partial_avx_dependency | 401 | extern rtl_opt_pass *make_pass_remove_partial_avx_dependency |
| 401 | (gcc::context *); | 402 | (gcc::context *); |
| 402 | 403 | ||
| @@ -21793,7 +21793,8 @@ ix86_avoid_jump_mispredicts (void) | |||
| 21793 | if (dump_file) | 21793 | if (dump_file) |
| 21794 | fprintf (dump_file, "Padding insn %i by %i bytes!\n", | 21794 | fprintf (dump_file, "Padding insn %i by %i bytes!\n", |
| 21795 | INSN_UID (insn), padsize); | 21795 | INSN_UID (insn), padsize); |
| 21796 | - emit_insn_before (gen_pad (GEN_INT (padsize)), insn); | 21796 | + emit_insn_before (gen_max_skip_align (GEN_INT (4), |
| 21797 | + GEN_INT (padsize)), insn); | ||
| 21797 | } | 21798 | } |
| 21798 | } | 21799 | } |
| 21799 | } | 21800 | } |
| @@ -369,6 +369,8 @@ extern unsigned char ix86_tune_features[X86_TUNE_LAST]; | |||
| 369 | #define TARGET_INTER_UNIT_CONVERSIONS \ | 369 | #define TARGET_INTER_UNIT_CONVERSIONS \ |
| 370 | ix86_tune_features[X86_TUNE_INTER_UNIT_CONVERSIONS] | 370 | ix86_tune_features[X86_TUNE_INTER_UNIT_CONVERSIONS] |
| 371 | #define TARGET_FOUR_JUMP_LIMIT ix86_tune_features[X86_TUNE_FOUR_JUMP_LIMIT] | 371 | #define TARGET_FOUR_JUMP_LIMIT ix86_tune_features[X86_TUNE_FOUR_JUMP_LIMIT] |
| 372 | +#define TARGET_ALIGN_TIGHT_LOOPS \ | ||
| 373 | + ix86_tune_features[X86_TUNE_ALIGN_TIGHT_LOOPS] | ||
| 372 | #define TARGET_SCHEDULE ix86_tune_features[X86_TUNE_SCHEDULE] | 374 | #define TARGET_SCHEDULE ix86_tune_features[X86_TUNE_SCHEDULE] |
| 373 | #define TARGET_USE_BT ix86_tune_features[X86_TUNE_USE_BT] | 375 | #define TARGET_USE_BT ix86_tune_features[X86_TUNE_USE_BT] |
| 374 | #define TARGET_USE_INCDEC ix86_tune_features[X86_TUNE_USE_INCDEC] | 376 | #define TARGET_USE_INCDEC ix86_tune_features[X86_TUNE_USE_INCDEC] |
| @@ -15539,16 +15539,19 @@ | |||
| 15539 | (set_attr "length_immediate" "0") | 15539 | (set_attr "length_immediate" "0") |
| 15540 | (set_attr "modrm" "0")]) | 15540 | (set_attr "modrm" "0")]) |
| 15541 | 15541 | ||
| 15542 | -;; Pad to 16-byte boundary, max skip in op0. Used to avoid | 15542 | +;; Pad to 1 << op0 byte boundary, max skip in op1. Used to avoid |
| 15543 | ;; branch prediction penalty for the third jump in a 16-byte | 15543 | ;; branch prediction penalty for the third jump in a 16-byte |
| 15544 | ;; block on K8. | 15544 | ;; block on K8. |
| 15545 | +;; Also it's used to align tight loops which can be fix into 1 cacheline. | ||
| 15546 | +;; It can help code prefetch and reduce DSB miss. | ||
| 15545 | 15547 | ||
| 15546 | -(define_insn "pad" | 15548 | +(define_insn "max_skip_align" |
| 15547 | - [(unspec_volatile [(match_operand 0)] UNSPECV_ALIGN)] | 15549 | + [(unspec_volatile [(match_operand 0) (match_operand 1)] UNSPECV_ALIGN)] |
| 15548 | "" | 15550 | "" |
| 15549 | { | 15551 | { |
| 15550 | #ifdef ASM_OUTPUT_MAX_SKIP_ALIGN | 15552 | #ifdef ASM_OUTPUT_MAX_SKIP_ALIGN |
| 15551 | - ASM_OUTPUT_MAX_SKIP_ALIGN (asm_out_file, 4, (int)INTVAL (operands[0])); | 15553 | + ASM_OUTPUT_MAX_SKIP_ALIGN (asm_out_file, (int)INTVAL (operands[0]), |
| 15554 | + (int)INTVAL (operands[1])); | ||
| 15552 | #else | 15555 | #else |
| 15553 | /* It is tempting to use ASM_OUTPUT_ALIGN here, but we don't want to do that. | 15556 | /* It is tempting to use ASM_OUTPUT_ALIGN here, but we don't want to do that. |
| 15554 | The align insn is used to avoid 3 jump instructions in the row to improve | 15557 | The align insn is used to avoid 3 jump instructions in the row to improve |
| @@ -224,6 +224,9 @@ DEF_TUNE (X86_TUNE_FOUR_JUMP_LIMIT, "four_jump_limit", | |||
| 224 | m_PPRO | m_P4_NOCONA | m_BONNELL | m_SILVERMONT | m_KNL | m_KNM | 224 | m_PPRO | m_P4_NOCONA | m_BONNELL | m_SILVERMONT | m_KNL | m_KNM |
| 225 | | m_GOLDMONT | m_GOLDMONT_PLUS | m_INTEL | m_ATHLON_K8 | m_AMDFAM10) | 225 | | m_GOLDMONT | m_GOLDMONT_PLUS | m_INTEL | m_ATHLON_K8 | m_AMDFAM10) |
| 226 | 226 | ||
| 227 | +/* X86_TUNE_ALIGN_TIGHT_LOOPS: if false, tight loops are not aligned. */ | ||
| 228 | +DEF_TUNE (X86_TUNE_ALIGN_TIGHT_LOOPS, "align_tight_loops", m_C86_4G_M7) | ||
| 229 | + | ||
| 227 | /*****************************************************************************/ | 230 | /*****************************************************************************/ |
| 228 | /* Integer instruction selection tuning */ | 231 | /* Integer instruction selection tuning */ |
| 229 | /*****************************************************************************/ | 232 | /*****************************************************************************/ |
| @@ -0,0 +1,11 @@ | |||
| 1 | +/* { dg-do compile { target { *-*-linux* && lp64 } } } */ | ||
| 2 | +/* { dg-options "-O2 -g0 -fcf-protection=branch -mtune=generic" } */ | ||
| 3 | +/* { dg-final { scan-assembler-not "\\.p2align 5" } } */ | ||
| 4 | + | ||
| 5 | +char * | ||
| 6 | +foo (char *dest, const char *src) | ||
| 7 | +{ | ||
| 8 | + while ((*dest++ = *src++) != '\0') | ||
| 9 | + /* nothing */; | ||
| 10 | + return --dest; | ||
| 11 | +} | ||
| @@ -0,0 +1,10 @@ | |||
| 1 | +/* { dg-do compile { target { *-*-linux* && lp64 } } } */ | ||
| 2 | +/* { dg-options "-O2 -g0 -fcf-protection=branch -mtune=c86-4g-m7" } */ | ||
| 3 | +/* { dg-final { scan-assembler "endbr64\\n\\t\\.p2align 5" } } */ | ||
| 4 | +char * | ||
| 5 | +foo (char *dest, const char *src) | ||
| 6 | +{ | ||
| 7 | + while ((*dest++ = *src++) != '\0') | ||
| 8 | + /* nothing */; | ||
| 9 | + return --dest; | ||
| 10 | +} | ||