Hi! As has been suggested by Alexandre in the PR, this patch allows merging basic blocks which couldn't be merged before because of user (non-forced) labels at the beginning of the second basic blocks. With this patch the user label is thrown away (for -g0 or -g -fno-var-tracking-assignments) or turned into a debug bind stmt which contains the label. BB duplication is then taught not to duplicate these (both at GIMPLE and RTL level) so that we don't end up with multiple labels, and finally during var-tracking when DEBUG_INSNs are deleted this is turned into a special note. Initially I used NOTE_INSN_DELETED_LABEL, but that has a few problems - as NOTE_INSN_DELETED_LABEL notes use the same file-wide numbers with CODE_LABELs, if we end up creating extra such labels only in -g build and not in -g0 build, that results in -fcompare-debug differences, even if we ignore all NOTE_INSN_DELETED_LABELs in the dump suddenly the CODE_LABEL_NUMBERs will differ in the next function. Furthermore, darwin is doing some terrible hacks to insert nops because it can't have labels at the end of function or what, which would result into code differences between -g and -g0. So this patch introduces a new note kind, NOTE_INSN_DELETED_DEBUG_LABEL which uses a different label namespace / numbering and which the darwin hacks can disable if needed.
With this patch we can now vectorize the very obfuscated testcase from the PR. Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk? 2011-11-04 Jakub Jelinek <ja...@redhat.com> PR tree-optimization/50693 * tree-cfg.c (gimple_can_merge_blocks_p): Allow merging with non-forced user labels. (gimple_merge_blocks): Turn non-forced user labels into debug bind stmt with the label as first operand and reset value. (gimple_duplicate_bb): Don't duplicate label debug stmts. * dwarf2out.c (gen_label_die): Handle NOTE_INSN_DELETED_DEBUG_LABEL. * final.c (final_scan_insn): Likewise. (rest_of_clean_state): Don't dump NOTE_INSN_DELETED_DEBUG_LABEL. * var-tracking.c (debug_label_num): New variable. (delete_debug_insns): Don't delete DEBUG_INSNs for LABEL_DECLs, instead turn them into NOTE_INSN_DELETED_DEBUG_LABEL notes. * cfglayout.c (skip_insns_after_block, duplicate_insn_chain): Handle NOTE_INSN_DELETED_DEBUG_LABEL. (duplicate_insn_chain): Don't duplicate LABEL_DECL DEBUG_INSNs. * insn-notes.def (DELETED_DEBUG_LABEL): New note kind. * print-rtl.c (print_rtx): Handle NOTE_INSN_DELETED_DEBUG_LABEL. * gengtype.c (adjust_field_rtx_def): Likewise. * config/i386/i386.c (ix86_output_function_epilogue): For MachO clear CODE_LABEL_NUMBER of NOTE_INSN_DELETED_DEBUG_LABEL if their are at the end of function and nop hasn't been emitted. * config/rs6000/rs6000.c (rs6000_output_function_epilogue): Likewise. --- gcc/tree-cfg.c.jj 2011-10-31 20:44:14.000000000 +0100 +++ gcc/tree-cfg.c 2011-11-04 18:01:25.000000000 +0100 @@ -1454,8 +1454,8 @@ gimple_can_merge_blocks_p (basic_block a break; lab = gimple_label_label (stmt); - /* Do not remove user labels. */ - if (!DECL_ARTIFICIAL (lab)) + /* Do not remove user forced labels. */ + if (!DECL_ARTIFICIAL (lab) && FORCED_LABEL (lab)) return false; } @@ -1701,6 +1701,15 @@ gimple_merge_blocks (basic_block a, basi gimple_stmt_iterator dest_gsi = gsi_start_bb (a); gsi_insert_before (&dest_gsi, stmt, GSI_NEW_STMT); } + /* Other user labels keep around in a form of a debug stmt. */ + else if (!DECL_ARTIFICIAL (label) && MAY_HAVE_DEBUG_STMTS) + { + gimple dbg = gimple_build_debug_bind (label, + integer_zero_node, + stmt); + gimple_debug_bind_reset_value (dbg); + gsi_insert_before (&gsi, dbg, GSI_SAME_STMT); + } lp_nr = EH_LANDING_PAD_NR (label); if (lp_nr) @@ -5207,6 +5216,12 @@ gimple_duplicate_bb (basic_block bb) if (gimple_code (stmt) == GIMPLE_LABEL) continue; + /* Don't duplicate label debug stmts. */ + if (gimple_debug_bind_p (stmt) + && TREE_CODE (gimple_debug_bind_get_var (stmt)) + == LABEL_DECL) + continue; + /* Create a new copy of STMT and duplicate STMT's virtual operands. */ copy = gimple_copy (stmt); --- gcc/dwarf2out.c.jj 2011-11-01 09:04:37.000000000 +0100 +++ gcc/dwarf2out.c 2011-11-04 17:41:25.000000000 +0100 @@ -18020,6 +18020,14 @@ gen_label_die (tree decl, dw_die_ref con ASM_GENERATE_INTERNAL_LABEL (label, "L", CODE_LABEL_NUMBER (insn)); add_AT_lbl_id (lbl_die, DW_AT_low_pc, label); } + else if (insn + && NOTE_P (insn) + && NOTE_KIND (insn) == NOTE_INSN_DELETED_DEBUG_LABEL + && CODE_LABEL_NUMBER (insn) != -1) + { + ASM_GENERATE_INTERNAL_LABEL (label, "LDL", CODE_LABEL_NUMBER (insn)); + add_AT_lbl_id (lbl_die, DW_AT_low_pc, label); + } } } --- gcc/final.c.jj 2011-09-21 13:04:06.000000000 +0200 +++ gcc/final.c 2011-11-04 17:38:17.000000000 +0100 @@ -2080,6 +2080,12 @@ final_scan_insn (rtx insn, FILE *file, i ASM_OUTPUT_DEBUG_LABEL (file, "L", CODE_LABEL_NUMBER (insn)); break; + case NOTE_INSN_DELETED_DEBUG_LABEL: + /* Similarly, but need to use different namespace for it. */ + if (CODE_LABEL_NUMBER (insn) != -1) + ASM_OUTPUT_DEBUG_LABEL (file, "LDL", CODE_LABEL_NUMBER (insn)); + break; + case NOTE_INSN_VAR_LOCATION: case NOTE_INSN_CALL_ARG_LOCATION: if (!DECL_IGNORED_P (current_function_decl)) @@ -4369,7 +4375,8 @@ rest_of_clean_state (void) (NOTE_KIND (insn) != NOTE_INSN_VAR_LOCATION && NOTE_KIND (insn) != NOTE_INSN_CALL_ARG_LOCATION && NOTE_KIND (insn) != NOTE_INSN_BLOCK_BEG - && NOTE_KIND (insn) != NOTE_INSN_BLOCK_END))) + && NOTE_KIND (insn) != NOTE_INSN_BLOCK_END + && NOTE_KIND (insn) != NOTE_INSN_DELETED_DEBUG_LABEL))) print_rtl_single (final_output, insn); } --- gcc/var-tracking.c.jj 2011-11-01 09:04:37.000000000 +0100 +++ gcc/var-tracking.c 2011-11-04 17:45:22.000000000 +0100 @@ -9517,6 +9517,12 @@ vt_initialize (void) return true; } +/* This is *not* reset after each function. It gives each + NOTE_INSN_DELETED_DEBUG_LABEL in the entire compilation + a unique label number. */ + +static int debug_label_num = 1; + /* Get rid of all debug insns from the insn stream. */ static void @@ -9532,7 +9538,22 @@ delete_debug_insns (void) { FOR_BB_INSNS_SAFE (bb, insn, next) if (DEBUG_INSN_P (insn)) - delete_insn (insn); + { + tree decl = INSN_VAR_LOCATION_DECL (insn); + if (TREE_CODE (decl) == LABEL_DECL + && DECL_NAME (decl) + && !DECL_RTL_SET_P (decl)) + { + PUT_CODE (insn, NOTE); + NOTE_KIND (insn) = NOTE_INSN_DELETED_DEBUG_LABEL; + NOTE_DELETED_LABEL_NAME (insn) + = IDENTIFIER_POINTER (DECL_NAME (decl)); + SET_DECL_RTL (decl, insn); + CODE_LABEL_NUMBER (insn) = debug_label_num++; + } + else + delete_insn (insn); + } } } --- gcc/cfglayout.c.jj 2011-08-28 12:36:57.000000000 +0200 +++ gcc/cfglayout.c 2011-11-04 17:42:35.000000000 +0100 @@ -149,6 +149,7 @@ skip_insns_after_block (basic_block bb) break; case NOTE_INSN_DELETED: case NOTE_INSN_DELETED_LABEL: + case NOTE_INSN_DELETED_DEBUG_LABEL: continue; default: reorder_insns (insn, insn, last_insn); @@ -1174,6 +1175,10 @@ duplicate_insn_chain (rtx from, rtx to) switch (GET_CODE (insn)) { case DEBUG_INSN: + /* Don't duplicate label debug insns. */ + if (TREE_CODE (INSN_VAR_LOCATION_DECL (insn)) == LABEL_DECL) + break; + /* FALLTHRU */ case INSN: case CALL_INSN: case JUMP_INSN: @@ -1219,6 +1224,7 @@ duplicate_insn_chain (rtx from, rtx to) case NOTE_INSN_DELETED: case NOTE_INSN_DELETED_LABEL: + case NOTE_INSN_DELETED_DEBUG_LABEL: /* No problem to strip these. */ case NOTE_INSN_FUNCTION_BEG: /* There is always just single entry to function. */ --- gcc/insn-notes.def.jj 2011-07-27 23:25:36.000000000 +0200 +++ gcc/insn-notes.def 2011-11-04 17:31:08.000000000 +0100 @@ -36,6 +36,10 @@ INSN_NOTE (DELETED) /* Generated in place of user-declared labels when they are deleted. */ INSN_NOTE (DELETED_LABEL) +/* Similarly, but for labels that have been present in debug stmts + earlier and thus will only appear with -g. These must use different + label namespace. */ +INSN_NOTE (DELETED_DEBUG_LABEL) /* These are used to mark the beginning and end of a lexical block. See NOTE_BLOCK and reorder_blocks. */ --- gcc/print-rtl.c.jj 2011-08-28 12:36:57.000000000 +0200 +++ gcc/print-rtl.c 2011-11-04 17:33:40.000000000 +0100 @@ -283,6 +283,7 @@ print_rtx (const_rtx in_rtx) } case NOTE_INSN_DELETED_LABEL: + case NOTE_INSN_DELETED_DEBUG_LABEL: { const char *label = NOTE_DELETED_LABEL_NAME (in_rtx); if (label) @@ -442,7 +443,8 @@ print_rtx (const_rtx in_rtx) { /* This field is only used for NOTE_INSN_DELETED_LABEL, and other times often contains garbage from INSN->NOTE death. */ - if (NOTE_KIND (in_rtx) == NOTE_INSN_DELETED_LABEL) + if (NOTE_KIND (in_rtx) == NOTE_INSN_DELETED_LABEL + || NOTE_KIND (in_rtx) == NOTE_INSN_DELETED_DEBUG_LABEL) fprintf (outfile, " %d", XINT (in_rtx, i)); } #if !defined(GENERATOR_FILE) && NUM_UNSPECV_VALUES > 0 --- gcc/gengtype.c.jj 2011-10-17 09:20:38.000000000 +0200 +++ gcc/gengtype.c 2011-11-04 17:34:55.000000000 +0100 @@ -1015,6 +1015,7 @@ adjust_field_rtx_def (type_p t, options_ { case NOTE_INSN_MAX: case NOTE_INSN_DELETED_LABEL: + case NOTE_INSN_DELETED_DEBUG_LABEL: note_flds = create_field (note_flds, &string_type, "rt_str"); break; --- gcc/config/i386/i386.c.jj 2011-11-04 12:48:16.000000000 +0100 +++ gcc/config/i386/i386.c 2011-11-04 17:51:41.000000000 +0100 @@ -10879,15 +10879,28 @@ ix86_output_function_epilogue (FILE *fil it looks like we might want one, insert a NOP. */ { rtx insn = get_last_insn (); + rtx deleted_debug_label = NULL_RTX; while (insn && NOTE_P (insn) && NOTE_KIND (insn) != NOTE_INSN_DELETED_LABEL) - insn = PREV_INSN (insn); + { + /* Don't insert a nop for NOTE_INSN_DELETED_DEBUG_LABEL + notes only, instead set their CODE_LABEL_NUMBER to -1, + otherwise there would be code generation differences + in between -g and -g0. */ + if (NOTE_P (insn) && NOTE_KIND (insn) == NOTE_INSN_DELETED_DEBUG_LABEL) + deleted_debug_label = insn; + insn = PREV_INSN (insn); + } if (insn && (LABEL_P (insn) || (NOTE_P (insn) && NOTE_KIND (insn) == NOTE_INSN_DELETED_LABEL))) fputs ("\tnop\n", file); + else if (deleted_debug_label) + for (insn = deleted_debug_label; insn; insn = NEXT_INSN (insn)) + if (NOTE_KIND (insn) == NOTE_INSN_DELETED_DEBUG_LABEL) + CODE_LABEL_NUMBER (insn) = -1; } #endif --- gcc/config/rs6000/rs6000.c.jj 2011-11-04 07:49:43.000000000 +0100 +++ gcc/config/rs6000/rs6000.c 2011-11-04 17:52:14.000000000 +0100 @@ -21461,15 +21461,28 @@ rs6000_output_function_epilogue (FILE *f it looks like we might want one, insert a NOP. */ { rtx insn = get_last_insn (); + rtx deleted_debug_label = NULL_RTX; while (insn && NOTE_P (insn) && NOTE_KIND (insn) != NOTE_INSN_DELETED_LABEL) - insn = PREV_INSN (insn); + { + /* Don't insert a nop for NOTE_INSN_DELETED_DEBUG_LABEL + notes only, instead set their CODE_LABEL_NUMBER to -1, + otherwise there would be code generation differences + in between -g and -g0. */ + if (NOTE_P (insn) && NOTE_KIND (insn) == NOTE_INSN_DELETED_DEBUG_LABEL) + deleted_debug_label = insn; + insn = PREV_INSN (insn); + } if (insn && (LABEL_P (insn) || (NOTE_P (insn) && NOTE_KIND (insn) == NOTE_INSN_DELETED_LABEL))) fputs ("\tnop\n", file); + else if (deleted_debug_label) + for (insn = deleted_debug_label; insn; insn = NEXT_INSN (insn)) + if (NOTE_KIND (insn) == NOTE_INSN_DELETED_DEBUG_LABEL) + CODE_LABEL_NUMBER (insn) = -1; } #endif Jakub