From 5273f7e17982f6321b930286465674d83a297279 Mon Sep 17 00:00:00 2001 From: vmakarov Date: Mon, 17 Dec 2007 19:53:03 +0000 Subject: [PATCH] 2007-12-17 Vladimir Makarov * common.opt (fira-assign-after-call-split, fira-biased-coloring, fira-split-around-calls): Remove. (fdump-simple-rtl, fira-verbose): New options. (fira, fira-coalesce): Initiate by 0. * opts.c (common_handle_option): Process flag_ira_verbose. * flags.h (flag_ira_verbose): New external definition. * toplev.h (flag_ira_assign_after_call_split, flag_ira_biased_coloring, flag_ira_split_around_calls): Remove. * toplev.c (flag_ira_verbose): New external declaration. (finalize): Call finish_ira_once. * print-rtl.c (flag_simple): Rename to flag_dump_simple_rtl. * doc/invoke.texi (-fno-ira-assign-after-call-split, -fira-biased-coloring, -fno-ira-split-around-calls): Remove. (-fdump-simple-rtl, -fira-verbose): New options. * caller-save.c (insert_restore, insert_save): Use adjust_address_nv instead of adjust_address. Check the mode by reg_save_code. * ira-call.c: Remove file. * ira-lives.c: New file. * Makefile.in (ira-live.o): Add (ira-call.o): Remove. (IRA_INT_H): Add alloc-pool.h. * ira.h (finish_ira_once): New external definition. * ira-int.h (internal_flag_ira_verbose, alloc_pool allocno_pool, copy_pool, allocno_live_range_pool, ira_loop_tree_height, max_point, start_point_ranges, finish_point_ranges, prohibited_mode_move_regs, important_class_nums, ira_curr_regno_allocno_map, allocno_set_words): New external definitions. (loop_tree_node_t, allocno_live_range_t): New typedefs. Use them everywhere. (ira_loop_tree_node): Rename to loop_tree_node. (loop_tree_node): New member level. (allocno_live_range): New structure. (allocno): New members nrefs, live_ranges, total_conflict_hard_regs, conflict_allocnos_num, total_conflict_allocnos_num, mem_optimized_dest, mem_optimized_dest_p, total_no_stack_reg_p. Remove members conflict_allocno_vec_active_size and original_memory_cost. Rename member curr_hard_reg_costs to updated_hard_reg_costs. (ALLOCNO_TOTAL_CONFLICT_ALLOCNOS_NUM, ALLOCNO_TOTAL_CONFLICT_HARD_REGS, ALLOCNO_CONFLICT_ALLOCNOS_NUM, ALLOCNO_TOTAL_CONFLICT_ALLOCNOS_NUM, ALLOCNO_NREFS, ALLOCNO_MEM_OPTIMIZED_DEST, ALLOCNO_MEM_OPTIMIZED_DEST_P, ALLOCNO_TOTAL_NO_STACK_REG_P, ALLOCNO_LIVE_RANGES): New access macros. (ALLOCNO_CONFLICT_ALLOCNO_VEC_ACTIVE_SIZE, ALLOCNO_ORIGINAL_MEMORY_COST): Remove. (ALLOCNO_CURR_CONFLICT_HARD_REG_COSTS): Rename to ALLOCNO_UPDATED_CONFLICT_HARD_REG_COSTS. (allocno_copy): New member loop_tree_node. (INT_BITS, INT_TYPE, EXECUTE_IF_SET_IN_ALLOCNO_SET): Move from ira-conflicts.c. (SET_ALLOCNO_SET_BIT, CLEAR_ALLOCNO_SET_BIT, TEST_ALLOCNO_SET_BIT): New macros. (ira_reallocate, allocno_conflict_index, add_allocno_conflict, create_allocno_live_range, finish_allocno_live_range, add_allocno_copy_to_list, swap_allocno_copy_ends_if_necessary, remove_allocno_copy_from_list, add_allocno_copy, ira_flattening, finish_ira_costs_once, rebuild_start_finish_chains, print_live_range_list, debug_live_range_list, debug_allocno_live_ranges, debug_live_ranges, create_allocno_live_ranges, finish_allocno_live_ranges, allocno_conflict_p, loop_edge_freq): New external function definitions. (hard_reg_in_set_p, original_regno_call_crossed_p, ira_max_regno_before, ira_max_regno_call_before): Remove. (traverse_loop_tree): Add new parameter type. (create_copy): Ditto. (debug_conflicts, ira_emit, add_allocno_copy): Add parameter. (debug_ira_call_data, split_around_calls, get_around_calls_regno): Remove. * ira.c (setup_prohibited_mode_move_regs): New function. (internal_flag_ira_verbose, allocno_pool, copy_pool, allocno_live_range_pool, important_class_nums): New external variables. (IRA_NO_OBSTACK): Uncomment it. (ira_reallocate): New function. (setup_cover_classes): Set up important_class_nums. (finish_ira_once): New function. (prohibited_mode_move_regs, prohibited_mode_move_regs_initialized_p): New global variables. (setup_prohibited_mode_move_regs): New function. (setup_reg_renumber): Remove parameters. (setup_allocno_assignment_from_reg_renumber): Rename to setup_allocno_assignment_flags. Set up ALLOCNO_ASSIGNED_P. (calculate_allocation_cost): Use ALLOCNO_UPDATED_MEMORY_COST. (ira_max_regno_before, ira_max_regno_call_before): Remove. (ira): Set up internal_flag_ira_verbose. Call setup_prohibited_mode_move_regs. Create and destroy allocno, copy, and live range pools. Remove original_regno_call_crossed_p. Report major passes. Use argument for ira_emit. Call ira_flattening and df_analyze. Remove live range splitting around calls. * ira-build.c (setup_loop_tree_level, rebuild_regno_allocno_maps, expand_calls, compress_calls, allocno_conflict_index, add_to_allocno_conflict_vec, propagate_info_to_cap, copy_allocno_live_range, copy_allocno_live_range_list, finish_allocno, finish_copy, propagate_info_to_loop_tree_node_caps, merge_ranges, common_loop_tree_node_dominator, check_and_add_conflicts, add_conflict_with_underlying_allocnos, ira_flattening): New functions. (check_coalesced_allocnos): Remove. (ira_loop_tree_height): New external variable. (form_loop_tree): Call setup_loop_tree_level. (initiate_calls): Allocate a bit more. (create_allocno): Use pool_alloc. Set up ALLOCNO_TOTAL_CONFLICT_ALLOCNOS_NUM, ALLOCNO_TOTAL_CONFLICT_HARD_REGS, ALLOCNO_NREFS, ALLOCNO_TOTAL_NO_STACK_REG_P, ALLOCNO_MEM_OPTIMIZED_DEST, ALLOCNO_MEM_OPTIMIZED_DEST_P, ALLOCNO_UPDATED_MEMORY_COST, and ALLOCNO_LIVE_RANGES. Rename ALLOCNO_CONFLICT_ALLOCNO_VEC_ACTIVE_SIZE, ALLOCNO_CURR_HARD_REG_COSTS, and ALLOCNO_CURR_CONFLICT_HARD_REG_COSTS to ALLOCNO_CONFLICT_ALLOCNOS_NUM, ALLOCNO_UPDATED_HARD_REG_COSTS, and ALLOCNO_UPDATED_CONFLICT_HARD_REG_COSTS. (add_allocno_conflict): Make it external. Use add_to_allocno_conflict_vec. (create_cap_allocno): Move part of code to propagate_info_to_cap. (finish_allocnos): Use finish_allocno. (add_allocno_copy_to_list, remove_allocno_copy_from_list, swap_allocno_copy_ends_if_necessary, add_allocno_copy): Move from ira-conflicts.c. Make them external. (create_copy): Use pool_alloc. Add parameter loop_tree_node. (finish_copies): Use finish_copy. (ira_curr_regno_allocno_map): New external variable. (traverse_loop_tree): Add parameter bb_first_p. (create_insn_allocnos): Update ALLOCNO_NREFS. (create_loop_tree_node_allocnos): Traverse loop tree nodes with BBs first. Update ALLOCNO_NREFS. (create_loop_tree_node_caps): Move part of code to propagate_info_to_loop_tree_node_cap. (regno_top_level_allocno_map): New global variable. (ira_build): Create caps before build conflicts. Call create_allocno_live_ranges. Propagate info to caps. Report statistics. (ira_destroy): Call finish_allocno_live_ranges. * ira-conflicts.c (set_allocno_live, clear_allocno_live, record_regno_conflict, mark_reg_store, mark_reg_clobber, mark_reg_conflicts, mark_reg_death): Remove. (add_insn_allocno_copies, add_copies): New functions. (add_allocno_copy_to_list, remove_allocno_copy_from_list, swap_allocno_copy_ends_if_necessary, add_allocno_copy): Remove. (add_allocno_copies): Rename to add_insn_allocno_copies. Use ira_curr_regno_allocno_map and add_insn_allocno_copies. (single_reg_class, single_reg_operand_class, process_single_reg_class_operands, process_bb_node_for_conflicts): Remove. (INT_BITS, INT_TYPE, EXECUTE_IF_SET_IN_ALLOCNO_SET): Move to ira-int.h. (allocnos_live_bitmap, SET_ALLOCNO_CONFLICT_ROW, CLEAR_ALLOCNO_CONFLICT_ROW, TEST_ALLOCNO_CONFLICT_ROW): Remove. (SET_ALLOCNO_LIVE, CLEAR_ALLOCNO_LIVE, TEST_ALLOCNO_LIVE): Use {SET, CLEAR, TEST}_ALLOCNO_SET_BIT. (allocno_row_words, hard_regs_live, curr_bb_node, curr_regno_allocno_map, curr_reg_pressure): Remove. (CONFLICTP): Rename to CONFLICT_P. (regs_set): Remove. (propagate_allocno_info): Use ALLOCNO_TOTAL_CONFLICT_HARD_REGS. Set up ALLOCNO_CALLS_CROSSED_START. (allocno_conflict_p): New function. (allocno_reg_conflict_p): Use allocno_conflict_p. (build_allocno_conflict_vects): Divide original and propagated conflicts. (print_hard_reg_set): New function. (print_conflicts): Use print_hard_reg_set. Set up mark for the start of propagated conflicts. (debug_conflicts): Add parameter. (ira_build_conflicts): Call add_copies. Set up ALLOCNO_TOTAL_CONFLICT_HARD_REGS. * ira-costs.c (struct costs): Make cost allocated dynamically. (struct_costs_size, temp_cots, op_costs, this_op_costs): New variables. (costs): Rename to total_costs. (COSTS_OF_ALLOCNO): New macro. (curr_regno_allocno_map): Remove. Use ira_curr_regno_allocno_map instead. (record_reg_classes): Check NO_REGS in allocno_pref. (record_address_regs): Use COSTS_OF_ALLOCNO. (scan_one_insn): Ditto. (print_costs): Ditto. (find_allocno_class_costs): Ditto. Pass additional argument to traverse_loop_tree. Set up NO_REGS if memory is cheaper. (setup_allocno_cover_class_and_costs): Use ALLOCNO_UPDATED_MEMORY_COST instead of ALLOCNO_ORIGINAL_MEMORY_COST, ALLOCNO_UPDATED_HARD_REG_COSTS instead of ALLOCNO_CURR_HARD_REG_COSTS, and ALLOCNO_UPDATED_CONFLICT_HARD_REG_COSTS instead of ALLOCNO_CURR_CONFLICT_HARD_REG_COSTS. Pass additional argument to traverse_loop_tree. (init_ira_costs_once): Allocate init_cost, op_costs, this_op_costs, temp_costs. (finish_ira_costs_once): New function. * ira-color.c: Use ALLOCNO_UPDATED_MEMORY_COST instead of ALLOCNO_ORIGINAL_MEMORY_COST, ALLOCNO_UPDATED_HARD_REG_COSTS instead of ALLOCNO_CURR_HARD_REG_COSTS, and ALLOCNO_UPDATED_CONFLICT_HARD_REG_COSTS instead of ALLOCNO_CURR_CONFLICT_HARD_REG_COSTS. Use ALLOCNO_TOTAL_CONFLICT_HARD_REGS instead of ALLOCNO_CONFLICT_HARD_REGS. (processed_allocno_bitmap): Rename into processed_coalesced_allocno_bitmap. (allocno_cost_compare_func): Use ALLOCNO_UPDATED_MEMORY_COST instead of ALLOCNO_MEMORY_COST. (assign_hard_reg): Remove biased coloring. (add_allocno_to_ordered_bucket): Check cover class first. (loop_edge_freq): Make it external. (setup_allocno_left_conflicts_num): Check that conflicting allocnos of one class. (allocno_conflict_p): Rename to coalesced_allocno_conflict_p. (color_pass): Check pressure in the loop (not in subloops) for mixed algorithm. (allocno_priorities): New variable. (start_allocno_priorities, finish_allocno_priorities): New functions. (allocno_priority_compare_func): Use allocno_priorities. (priority_coloring): Call start_allocno_priorities and finish_allocno_priorities. (do_coloring): Pass additional argument to traverse_loop_tree. * ira-emit.c (struct move): Add member insn. (create_move): Initialize member insn. (generate_edge_moves): Set up ALLOCNO_MEM_OPTIMIZED_DEST and ALLOCNO_MEM_OPTIMIZED_DEST_P. (change_loop): Check prohibited_mode_move_regs. (curr_jump_map, can_move_through_p): Remove. (unify_moves): Don't move through jump. (modify_move_list): Set up attributes for new allocno used for breaking dependency loops. (emit_move_list): Set up insn codes. (update_costs, add_range_and_copies_from_move_list, add_ranges_and_copies): New functions. (ira_emit): Add a parameter. Call add_ranges_and_copies. git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/branches/ira@131014 138bc75d-0d04-0410-961f-82ee72b054a4 --- gcc/ChangeLog | 254 +++++ gcc/Makefile.in | 12 +- gcc/caller-save.c | 10 +- gcc/common.opt | 22 +- gcc/doc/invoke.texi | 34 +- gcc/flags.h | 2 + gcc/ira-build.c | 1296 ++++++++++++++++++++++---- gcc/ira-call.c | 1070 --------------------- gcc/ira-color.c | 439 ++++----- gcc/ira-conflicts.c | 2571 +++++++++++++++++++-------------------------------- gcc/ira-costs.c | 489 ++++++---- gcc/ira-emit.c | 391 +++++--- gcc/ira-int.h | 391 +++++--- gcc/ira-lives.c | 891 ++++++++++++++++++ gcc/ira.c | 283 +++--- gcc/ira.h | 1 + gcc/opts.c | 4 + gcc/print-rtl.c | 20 +- gcc/toplev.c | 6 + gcc/toplev.h | 3 - 20 files changed, 4493 insertions(+), 3696 deletions(-) delete mode 100644 gcc/ira-call.c rewrite gcc/ira-conflicts.c (64%) create mode 100644 gcc/ira-lives.c diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 6c64d784fa6..cba749ebb74 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,257 @@ +2007-12-17 Vladimir Makarov + + * common.opt (fira-assign-after-call-split, fira-biased-coloring, + fira-split-around-calls): Remove. + (fdump-simple-rtl, fira-verbose): New options. + (fira, fira-coalesce): Initiate by 0. + + * opts.c (common_handle_option): Process flag_ira_verbose. + + * flags.h (flag_ira_verbose): New external definition. + + * toplev.h (flag_ira_assign_after_call_split, + flag_ira_biased_coloring, flag_ira_split_around_calls): Remove. + + * toplev.c (flag_ira_verbose): New external declaration. + (finalize): Call finish_ira_once. + + * print-rtl.c (flag_simple): Rename to flag_dump_simple_rtl. + + * doc/invoke.texi (-fno-ira-assign-after-call-split, + -fira-biased-coloring, -fno-ira-split-around-calls): Remove. + (-fdump-simple-rtl, -fira-verbose): New options. + + * caller-save.c (insert_restore, insert_save): Use + adjust_address_nv instead of adjust_address. Check the mode by + reg_save_code. + + * ira-call.c: Remove file. + + * ira-lives.c: New file. + + * Makefile.in (ira-live.o): Add + (ira-call.o): Remove. + (IRA_INT_H): Add alloc-pool.h. + + * ira.h (finish_ira_once): New external definition. + + * ira-int.h (internal_flag_ira_verbose, alloc_pool allocno_pool, + copy_pool, allocno_live_range_pool, ira_loop_tree_height, + max_point, start_point_ranges, finish_point_ranges, + prohibited_mode_move_regs, important_class_nums, + ira_curr_regno_allocno_map, allocno_set_words): New external + definitions. + (loop_tree_node_t, allocno_live_range_t): New typedefs. Use them + everywhere. + (ira_loop_tree_node): Rename to loop_tree_node. + (loop_tree_node): New member level. + (allocno_live_range): New structure. + (allocno): New members nrefs, live_ranges, + total_conflict_hard_regs, conflict_allocnos_num, + total_conflict_allocnos_num, mem_optimized_dest, + mem_optimized_dest_p, total_no_stack_reg_p. Remove members + conflict_allocno_vec_active_size and original_memory_cost. Rename + member curr_hard_reg_costs to updated_hard_reg_costs. + (ALLOCNO_TOTAL_CONFLICT_ALLOCNOS_NUM, + ALLOCNO_TOTAL_CONFLICT_HARD_REGS, ALLOCNO_CONFLICT_ALLOCNOS_NUM, + ALLOCNO_TOTAL_CONFLICT_ALLOCNOS_NUM, ALLOCNO_NREFS, + ALLOCNO_MEM_OPTIMIZED_DEST, ALLOCNO_MEM_OPTIMIZED_DEST_P, + ALLOCNO_TOTAL_NO_STACK_REG_P, ALLOCNO_LIVE_RANGES): New access + macros. + (ALLOCNO_CONFLICT_ALLOCNO_VEC_ACTIVE_SIZE, + ALLOCNO_ORIGINAL_MEMORY_COST): Remove. + (ALLOCNO_CURR_CONFLICT_HARD_REG_COSTS): Rename to + ALLOCNO_UPDATED_CONFLICT_HARD_REG_COSTS. + (allocno_copy): New member loop_tree_node. + (INT_BITS, INT_TYPE, EXECUTE_IF_SET_IN_ALLOCNO_SET): Move from + ira-conflicts.c. + (SET_ALLOCNO_SET_BIT, CLEAR_ALLOCNO_SET_BIT, + TEST_ALLOCNO_SET_BIT): New macros. + (ira_reallocate, allocno_conflict_index, add_allocno_conflict, + create_allocno_live_range, finish_allocno_live_range, + add_allocno_copy_to_list, swap_allocno_copy_ends_if_necessary, + remove_allocno_copy_from_list, add_allocno_copy, ira_flattening, + finish_ira_costs_once, rebuild_start_finish_chains, + print_live_range_list, debug_live_range_list, + debug_allocno_live_ranges, debug_live_ranges, + create_allocno_live_ranges, finish_allocno_live_ranges, + allocno_conflict_p, loop_edge_freq): New external function + definitions. + (hard_reg_in_set_p, original_regno_call_crossed_p, + ira_max_regno_before, ira_max_regno_call_before): Remove. + (traverse_loop_tree): Add new parameter type. + (create_copy): Ditto. + (debug_conflicts, ira_emit, add_allocno_copy): Add parameter. + (debug_ira_call_data, split_around_calls, get_around_calls_regno): + Remove. + + * ira.c (setup_prohibited_mode_move_regs): New function. + (internal_flag_ira_verbose, allocno_pool, copy_pool, + allocno_live_range_pool, important_class_nums): New external + variables. + (IRA_NO_OBSTACK): Uncomment it. + (ira_reallocate): New function. + (setup_cover_classes): Set up important_class_nums. + (finish_ira_once): New function. + (prohibited_mode_move_regs, + prohibited_mode_move_regs_initialized_p): New global variables. + (setup_prohibited_mode_move_regs): New function. + (setup_reg_renumber): Remove parameters. + (setup_allocno_assignment_from_reg_renumber): Rename to + setup_allocno_assignment_flags. Set up ALLOCNO_ASSIGNED_P. + (calculate_allocation_cost): Use ALLOCNO_UPDATED_MEMORY_COST. + (ira_max_regno_before, ira_max_regno_call_before): Remove. + (ira): Set up internal_flag_ira_verbose. Call + setup_prohibited_mode_move_regs. Create and destroy allocno, + copy, and live range pools. Remove original_regno_call_crossed_p. + Report major passes. Use argument for ira_emit. Call + ira_flattening and df_analyze. Remove live range splitting around + calls. + + * ira-build.c (setup_loop_tree_level, rebuild_regno_allocno_maps, + expand_calls, compress_calls, allocno_conflict_index, + add_to_allocno_conflict_vec, propagate_info_to_cap, + copy_allocno_live_range, copy_allocno_live_range_list, + finish_allocno, finish_copy, + propagate_info_to_loop_tree_node_caps, merge_ranges, + common_loop_tree_node_dominator, check_and_add_conflicts, + add_conflict_with_underlying_allocnos, ira_flattening): New + functions. + (check_coalesced_allocnos): Remove. + (ira_loop_tree_height): New external variable. + (form_loop_tree): Call setup_loop_tree_level. + (initiate_calls): Allocate a bit more. (create_allocno): Use + pool_alloc. Set up ALLOCNO_TOTAL_CONFLICT_ALLOCNOS_NUM, + ALLOCNO_TOTAL_CONFLICT_HARD_REGS, ALLOCNO_NREFS, + ALLOCNO_TOTAL_NO_STACK_REG_P, ALLOCNO_MEM_OPTIMIZED_DEST, + ALLOCNO_MEM_OPTIMIZED_DEST_P, ALLOCNO_UPDATED_MEMORY_COST, and + ALLOCNO_LIVE_RANGES. Rename + ALLOCNO_CONFLICT_ALLOCNO_VEC_ACTIVE_SIZE, + ALLOCNO_CURR_HARD_REG_COSTS, and + ALLOCNO_CURR_CONFLICT_HARD_REG_COSTS to + ALLOCNO_CONFLICT_ALLOCNOS_NUM, ALLOCNO_UPDATED_HARD_REG_COSTS, and + ALLOCNO_UPDATED_CONFLICT_HARD_REG_COSTS. + (add_allocno_conflict): Make it external. Use + add_to_allocno_conflict_vec. + (create_cap_allocno): Move part of code to propagate_info_to_cap. + (finish_allocnos): Use finish_allocno. + (add_allocno_copy_to_list, remove_allocno_copy_from_list, + swap_allocno_copy_ends_if_necessary, add_allocno_copy): Move from + ira-conflicts.c. Make them external. + (create_copy): Use pool_alloc. Add parameter loop_tree_node. + (finish_copies): Use finish_copy. + (ira_curr_regno_allocno_map): New external variable. + (traverse_loop_tree): Add parameter bb_first_p. + (create_insn_allocnos): Update ALLOCNO_NREFS. + (create_loop_tree_node_allocnos): Traverse loop tree nodes with + BBs first. Update ALLOCNO_NREFS. + (create_loop_tree_node_caps): Move part of code to + propagate_info_to_loop_tree_node_cap. + (regno_top_level_allocno_map): New global variable. + (ira_build): Create caps before build conflicts. Call + create_allocno_live_ranges. Propagate info to caps. Report + statistics. + (ira_destroy): Call finish_allocno_live_ranges. + + * ira-conflicts.c (set_allocno_live, clear_allocno_live, + record_regno_conflict, mark_reg_store, mark_reg_clobber, + mark_reg_conflicts, mark_reg_death): Remove. + (add_insn_allocno_copies, add_copies): New functions. + (add_allocno_copy_to_list, remove_allocno_copy_from_list, + swap_allocno_copy_ends_if_necessary, add_allocno_copy): Remove. + (add_allocno_copies): Rename to add_insn_allocno_copies. Use + ira_curr_regno_allocno_map and add_insn_allocno_copies. + (single_reg_class, single_reg_operand_class, + process_single_reg_class_operands, process_bb_node_for_conflicts): + Remove. + (INT_BITS, INT_TYPE, EXECUTE_IF_SET_IN_ALLOCNO_SET): Move to + ira-int.h. + (allocnos_live_bitmap, SET_ALLOCNO_CONFLICT_ROW, + CLEAR_ALLOCNO_CONFLICT_ROW, TEST_ALLOCNO_CONFLICT_ROW): Remove. + (SET_ALLOCNO_LIVE, CLEAR_ALLOCNO_LIVE, TEST_ALLOCNO_LIVE): + Use {SET, CLEAR, TEST}_ALLOCNO_SET_BIT. + (allocno_row_words, hard_regs_live, curr_bb_node, + curr_regno_allocno_map, curr_reg_pressure): Remove. + (CONFLICTP): Rename to CONFLICT_P. + (regs_set): Remove. + (propagate_allocno_info): Use ALLOCNO_TOTAL_CONFLICT_HARD_REGS. + Set up ALLOCNO_CALLS_CROSSED_START. + (allocno_conflict_p): New function. + (allocno_reg_conflict_p): Use allocno_conflict_p. + (build_allocno_conflict_vects): Divide original and propagated + conflicts. + (print_hard_reg_set): New function. + (print_conflicts): Use print_hard_reg_set. Set up mark for the + start of propagated conflicts. + (debug_conflicts): Add parameter. + (ira_build_conflicts): Call add_copies. Set up + ALLOCNO_TOTAL_CONFLICT_HARD_REGS. + + * ira-costs.c (struct costs): Make cost allocated dynamically. + (struct_costs_size, temp_cots, op_costs, this_op_costs): New + variables. + (costs): Rename to total_costs. + (COSTS_OF_ALLOCNO): New macro. + (curr_regno_allocno_map): Remove. Use ira_curr_regno_allocno_map + instead. + (record_reg_classes): Check NO_REGS in allocno_pref. + (record_address_regs): Use COSTS_OF_ALLOCNO. + (scan_one_insn): Ditto. + (print_costs): Ditto. + (find_allocno_class_costs): Ditto. Pass additional argument to + traverse_loop_tree. Set up NO_REGS if memory is cheaper. + (setup_allocno_cover_class_and_costs): Use + ALLOCNO_UPDATED_MEMORY_COST instead of + ALLOCNO_ORIGINAL_MEMORY_COST, ALLOCNO_UPDATED_HARD_REG_COSTS + instead of ALLOCNO_CURR_HARD_REG_COSTS, and + ALLOCNO_UPDATED_CONFLICT_HARD_REG_COSTS instead of + ALLOCNO_CURR_CONFLICT_HARD_REG_COSTS. Pass additional argument to + traverse_loop_tree. + (init_ira_costs_once): Allocate init_cost, op_costs, + this_op_costs, temp_costs. + (finish_ira_costs_once): New function. + + * ira-color.c: Use ALLOCNO_UPDATED_MEMORY_COST instead of + ALLOCNO_ORIGINAL_MEMORY_COST, ALLOCNO_UPDATED_HARD_REG_COSTS + instead of ALLOCNO_CURR_HARD_REG_COSTS, and + ALLOCNO_UPDATED_CONFLICT_HARD_REG_COSTS instead of + ALLOCNO_CURR_CONFLICT_HARD_REG_COSTS. Use + ALLOCNO_TOTAL_CONFLICT_HARD_REGS instead of + ALLOCNO_CONFLICT_HARD_REGS. + (processed_allocno_bitmap): Rename into + processed_coalesced_allocno_bitmap. + (allocno_cost_compare_func): Use ALLOCNO_UPDATED_MEMORY_COST + instead of ALLOCNO_MEMORY_COST. + (assign_hard_reg): Remove biased coloring. + (add_allocno_to_ordered_bucket): Check cover class first. + (loop_edge_freq): Make it external. + (setup_allocno_left_conflicts_num): Check that conflicting + allocnos of one class. + (allocno_conflict_p): Rename to coalesced_allocno_conflict_p. + (color_pass): Check pressure in the loop (not in subloops) for + mixed algorithm. + (allocno_priorities): New variable. + (start_allocno_priorities, finish_allocno_priorities): New + functions. + (allocno_priority_compare_func): Use allocno_priorities. + (priority_coloring): Call start_allocno_priorities and + finish_allocno_priorities. + (do_coloring): Pass additional argument to traverse_loop_tree. + + * ira-emit.c (struct move): Add member insn. + (create_move): Initialize member insn. + (generate_edge_moves): Set up ALLOCNO_MEM_OPTIMIZED_DEST and + ALLOCNO_MEM_OPTIMIZED_DEST_P. + (change_loop): Check prohibited_mode_move_regs. + (curr_jump_map, can_move_through_p): Remove. + (unify_moves): Don't move through jump. + (modify_move_list): Set up attributes for new allocno used for + breaking dependency loops. + (emit_move_list): Set up insn codes. + (update_costs, add_range_and_copies_from_move_list, + add_ranges_and_copies): New functions. + (ira_emit): Add a parameter. Call add_ranges_and_copies. + 2007-10-09 Vladimir Makarov * toplev.h (flag_ira_coalesce): New external variable. diff --git a/gcc/Makefile.in b/gcc/Makefile.in index 3d19bfc5ea7..88445b7e12b 100644 --- a/gcc/Makefile.in +++ b/gcc/Makefile.in @@ -833,7 +833,7 @@ TREE_DATA_REF_H = tree-data-ref.h $(LAMBDA_H) omega.h graphds.h VARRAY_H = varray.h $(MACHMODE_H) $(SYSTEM_H) coretypes.h $(TM_H) TREE_INLINE_H = tree-inline.h $(VARRAY_H) pointer-set.h REAL_H = real.h $(MACHMODE_H) -IRA_INT_H = ira.h ira-int.h $(CFGLOOP_H) +IRA_INT_H = ira.h ira-int.h $(CFGLOOP_H) alloc-pool.h DBGCNT_H = dbgcnt.h dbgcnt.def EBIMAP_H = ebitmap.h sbitmap.h @@ -1068,11 +1068,11 @@ OBJS-common = \ intl.o \ ira.o \ ira-build.o \ - ira-call.o \ ira-costs.o \ ira-conflicts.o \ ira-color.o \ ira-emit.o \ + ira-lives.o \ jump.o \ lambda-code.o \ lambda-mat.o \ @@ -2760,10 +2760,6 @@ ira-build.o: ira-build.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) \ $(TARGET_H) $(RTL_H) $(REGS_H) hard-reg-set.h $(FLAGS_H) \ insn-config.h $(RECOG_H) $(BASIC_BLOCK_H) toplev.h $(TM_P_H) \ $(PARAMS_H) $(DF_H) $(IRA_INT_H) -ira-call.o: ira-call.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) \ - $(TARGET_H) $(RTL_H) $(REGS_H) hard-reg-set.h $(FLAGS_H) errors.h \ - $(BASIC_BLOCK_H) $(TM_P_H) \ - $(IRA_INT_H) ira-costs.o: ira-costs.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) \ $(TARGET_H) $(RTL_H) insn-config.h $(RECOG_H) \ $(REGS_H) hard-reg-set.h $(FLAGS_H) errors.h \ @@ -2781,6 +2777,10 @@ ira-emit.o: ira-emit.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) \ $(TARGET_H) $(RTL_H) $(REGS_H) hard-reg-set.h $(FLAGS_H) \ $(EXPR_H) $(BASIC_BLOCK_H) toplev.h $(TM_P_H) $(PARAMS_H) \ $(IRA_INT_H) +ira-lives.o: ira-lives.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) \ + $(TARGET_H) $(RTL_H) $(REGS_H) hard-reg-set.h $(FLAGS_H) \ + insn-config.h $(RECOG_H) $(BASIC_BLOCK_H) toplev.h $(TM_P_H) $(PARAMS_H) \ + $(DF_H) $(IRA_INT_H) ira.o: ira.c $(CONFIG_H) $(SYSTEM_H) coretypes.h \ $(TARGET_H) $(TM_H) $(RTL_H) $(RECOG_H) \ $(REGS_H) hard-reg-set.h $(FLAGS_H) $(OBSTACK_H) \ diff --git a/gcc/caller-save.c b/gcc/caller-save.c index 7670f81f894..e2ec2fd8741 100644 --- a/gcc/caller-save.c +++ b/gcc/caller-save.c @@ -1524,8 +1524,9 @@ insert_restore (struct insn_chain *chain, int before_p, int regno, mem = regno_save_mem [regno][numregs]; if (save_mode [regno] != VOIDmode && save_mode [regno] != GET_MODE (mem) - && numregs == (unsigned int) hard_regno_nregs[regno][save_mode [regno]]) - mem = adjust_address (mem, save_mode[regno], 0); + && numregs == (unsigned int) hard_regno_nregs[regno][save_mode [regno]] + && reg_save_code (regno, save_mode[regno]) >= 0) + mem = adjust_address_nv (mem, save_mode[regno], 0); else mem = copy_rtx (mem); pat = gen_rtx_SET (VOIDmode, @@ -1596,8 +1597,9 @@ insert_save (struct insn_chain *chain, int before_p, int regno, mem = regno_save_mem [regno][numregs]; if (save_mode [regno] != VOIDmode && save_mode [regno] != GET_MODE (mem) - && numregs == (unsigned int) hard_regno_nregs[regno][save_mode [regno]]) - mem = adjust_address (mem, save_mode[regno], 0); + && numregs == (unsigned int) hard_regno_nregs[regno][save_mode [regno]] + && reg_save_code (regno, save_mode[regno]) >= 0) + mem = adjust_address_nv (mem, save_mode[regno], 0); else mem = copy_rtx (mem); pat = gen_rtx_SET (VOIDmode, mem, diff --git a/gcc/common.opt b/gcc/common.opt index dcc8c4b2125..246dc249c0e 100644 --- a/gcc/common.opt +++ b/gcc/common.opt @@ -433,6 +433,10 @@ fdump-noaddr Common Report Var(flag_dump_noaddr) Suppress output of addresses in debugging dumps +fdump-simple-rtl +Common Report Var(flag_dump_simple_rtl) Init(0) +Switch on dumping simple RTL code + fdump-unnumbered Common Report Var(flag_dump_unnumbered) VarExists Suppress output of instruction numbers, line number notes and addresses in debugging dumps @@ -613,23 +617,15 @@ Perform matrix layout flattening and transposing based on profiling information. fira -Common Report Var(flag_ira) +Common Report Var(flag_ira) Init(0) Use integrated register allocator. fira-algorithm= Common Joined RejectNegative -fira-algorithm=[regional|CB|mixed|priority] Set the used IRA algorithm -fira-assign-after-call-split -Common Report Var(flag_ira_assign_after_call_split) Init(1) -Assign hard regs to pseudos after splitting pseudo live range arround calls. - -fira-biased-coloring -Common Report Var(flag_ira_biased_coloring) -Use biased coloring for the integrated register allocator. - fira-coalesce -Common Report Var(flag_ira_coalesce) +Common Report Var(flag_ira_coalesce) Init(0) Do optimistic coalescing. fira-ipra @@ -652,9 +648,9 @@ fira-share-spill-slots Common Report Var(flag_ira_share_spill_slots) Init(1) Share stack slots for spilled pseudo-registers. -fira-split-around-calls -Common Report Var(flag_ira_split_around_calls) Init(1) -Split pseudo live range arround calls. +fira-verbose= +Common RejectNegative Joined UInteger +-fira-verbose= Control IRA's level of diagnostic messages. fivopts Common Report Var(flag_ivopts) Init(1) Optimization diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi index f1c6506f5f9..4ca15d2d807 100644 --- a/gcc/doc/invoke.texi +++ b/gcc/doc/invoke.texi @@ -274,6 +274,7 @@ Objective-C and Objective-C++ Dialects}. -fdbg-cnt-list -fdbg-cnt=@var{counter-value-list} @gol -fdump-noaddr -fdump-unnumbered -fdump-translation-unit@r{[}-@var{n}@r{]} @gol -fdump-class-hierarchy@r{[}-@var{n}@r{]} @gol +-fdump-simple-rtl @gol -fdump-ipa-all -fdump-ipa-cgraph @gol -fdump-tree-all @gol -fdump-tree-original@r{[}-@var{n}@r{]} @gol @@ -329,11 +330,10 @@ Objective-C and Objective-C++ Dialects}. -fcrossjumping -fif-conversion -fif-conversion2 @gol -finline-functions -finline-functions-called-once @gol -finline-limit=@var{n} -fira -fira-algorithm=@var{algorithm} @gol --fno-ira-assign-after-call-split @gol --fira-biased-coloring -fira-coalesce -fira-ipra -fno-ira-move-spills @gol +-fira-coalesce -fira-ipra -fno-ira-move-spills @gol -fira-propagate-cost @gol -fno-ira-share-spill-slots -fno-ira-share-save-slots @gol --fno-ira-split-around-calls @gol +-fira-verbose=@var{n} @gol -fkeep-inline-functions -fkeep-static-consts @gol -fmerge-constants -fmerge-all-constants @gol -fmodulo-sched -fmodulo-sched-allow-regmoves -fno-branch-count-reg @gol @@ -4578,6 +4578,11 @@ numbers and address output. This makes it more feasible to use diff on debugging dumps for compiler invocations with different options, in particular with and without @option{-g}. +@item -fdump-simple-rtl +@opindex fdump-simple-rtl +When doing debugging dumps (see @option{-d} option above), suppress +output of flags, modes, etc. This makes rtl dumps more compact. + @item -fdump-translation-unit @r{(C++ only)} @itemx -fdump-translation-unit-@var{options} @r{(C++ only)} @opindex fdump-translation-unit @@ -5541,19 +5546,6 @@ decent code and the smallest size code, the priority-based one is the fastest one, and the mixed algorithm can give the best result in some cases. -@item -fno-ira-assign-after-call-split -@opindex fno-ira-assign-after-call-split -Switch off additional assignment after splitting pseudos around calls. -After splitting pseudos there is a chance that spilled pseudos -conflicting with the new pseudos living through calls gets a hard -register. - -@item -fira-biased-coloring -@opindex fira-biased-coloring -Use biased coloring for the integrated register allocator for possible -improvement of the generated code. It makes register allocator -slower. - @item -fira-coalescing @opindex fira-coalescing Do optimistic register coalescing. This option might be profitable for @@ -5588,11 +5580,11 @@ Switch off sharing stack slots allocated for pseudo-registers. Each pseudo-register which did not get a hard register will get a separate stack slot and as a result function stack frame will be bigger. -@item -fno-ira-split-around-calls -@opindex fno-ira-split-around-calls -Switch off splitting pseudos around calls before the reload. By -default save restore insns are generated by splitting pseudos around -the calls in the integrated register allocator instead of the reload. +@item -fira-verbose=@var{n} +@opindex fira-verbose +Set up how verbose dump file for the integrated register allocator +will be. Default value is 5. If the value is greater or equal to 10, +the dump file will be stderr as if the value were @var{n} minus 10. @item -fdelayed-branch @opindex fdelayed-branch diff --git a/gcc/flags.h b/gcc/flags.h index 94fb36c3fed..41c7a05a9cb 100644 --- a/gcc/flags.h +++ b/gcc/flags.h @@ -231,6 +231,8 @@ enum ira_algorithm { extern enum ira_algorithm flag_ira_algorithm; +extern unsigned int flag_ira_verbose; + /* Other basic status info about current function. */ diff --git a/gcc/ira-build.c b/gcc/ira-build.c index 32ca60145de..a28f1c4970c 100644 --- a/gcc/ira-build.c +++ b/gcc/ira-build.c @@ -43,49 +43,70 @@ Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA static void create_loop_tree_nodes (int); static void finish_loop_tree_nodes (void); static void add_loop_to_tree (struct loop *); +static int setup_loop_tree_level (loop_tree_node_t, int); static void form_loop_tree (void); +static void rebuild_regno_allocno_maps (void); + static void initiate_calls (void); +static void expand_calls (void); +static void compress_calls (void); static void finish_calls (void); static void initiate_allocnos (void); static void check_allocno_conflict_vec (allocno_t, int); -static void add_allocno_conflict (allocno_t, allocno_t); +static void add_to_allocno_conflict_vec (allocno_t, allocno_t); static allocno_t create_cap_allocno (allocno_t); +static void propagate_info_to_cap (allocno_t); +static allocno_live_range_t copy_allocno_live_range (allocno_live_range_t); +static allocno_live_range_t + copy_allocno_live_range_list (allocno_live_range_t); + +static void finish_allocno (allocno_t); static void finish_allocnos (void); static void initiate_copies (void); +static void finish_copy (copy_t); static void finish_copies (void); static void create_insn_allocnos (rtx, int); -static void create_bb_allocnos (struct ira_loop_tree_node *); +static void create_bb_allocnos (loop_tree_node_t); static void create_loop_allocnos (edge); -static void create_loop_tree_node_allocnos (struct ira_loop_tree_node *); +static void create_loop_tree_node_allocnos (loop_tree_node_t); static void create_allocnos (void); -static void create_loop_tree_node_caps (struct ira_loop_tree_node *); -#ifdef ENABLE_IRA_CHECKING -static void check_coalesced_allocnos (void); -#endif + +static void create_loop_tree_node_caps (loop_tree_node_t); +static void propagate_info_to_loop_tree_node_caps (loop_tree_node_t); +static allocno_live_range_t merge_ranges (allocno_live_range_t, + allocno_live_range_t); +static loop_tree_node_t common_loop_tree_node_dominator (loop_tree_node_t, + loop_tree_node_t); +static void check_and_add_conflicts (allocno_t, allocno_t *); +static void add_conflict_with_underlying_allocnos (allocno_t, + loop_tree_node_t, int); /* All natural loops. */ struct loops ira_loops; /* The root of the loop tree corresponding to the all function. */ -struct ira_loop_tree_node *ira_loop_tree_root; +loop_tree_node_t ira_loop_tree_root; + +/* Height of the loop tree. */ +int ira_loop_tree_height; /* All basic block data are referred through the following array. We can not use member `aux' for this because it is used for insertion of insns on edges. */ -struct ira_loop_tree_node *ira_bb_nodes; +loop_tree_node_t ira_bb_nodes; /* All loop data are referred through the following array. */ -struct ira_loop_tree_node *ira_loop_nodes; +loop_tree_node_t ira_loop_nodes; -/* Map regno -> allocno for the current loop tree node. */ +/* Map regno -> allocnos. */ allocno_t *regno_allocno_map; -/* Array of references to all allocnos and its size. The order number - of the allocno corresponds to the index in the array. */ +/* Array of references to all allocnos and their size. The order + number of the allocno corresponds to the index in the array. */ allocno_t *allocnos; int allocnos_num; @@ -116,7 +137,7 @@ create_loop_tree_nodes (int loops_p) loop_p loop; ira_bb_nodes - = ira_allocate (sizeof (struct ira_loop_tree_node) * last_basic_block); + = ira_allocate (sizeof (struct loop_tree_node) * last_basic_block); last_basic_block_before_change = last_basic_block; for (i = 0; i < (unsigned int) last_basic_block; i++) { @@ -128,7 +149,7 @@ create_loop_tree_nodes (int loops_p) ira_bb_nodes [i].border_allocnos = NULL; ira_bb_nodes [i].local_copies = NULL; } - ira_loop_nodes = ira_allocate (sizeof (struct ira_loop_tree_node) + ira_loop_nodes = ira_allocate (sizeof (struct loop_tree_node) * VEC_length (loop_p, ira_loops.larray)); max_regno = max_reg_num (); for (i = 0; VEC_iterate (loop_p, ira_loops.larray, i, loop); i++) @@ -213,7 +234,7 @@ static void add_loop_to_tree (struct loop *loop) { struct loop *father; - struct ira_loop_tree_node *loop_node, *father_node; + loop_tree_node_t loop_node, father_node; /* Can not use loop node access macros because of potential checking and because the nodes are not initialized yet. */ @@ -246,6 +267,29 @@ add_loop_to_tree (struct loop *loop) } } +/* Enumerate loops in loop with root LOOP_NODE starting with LEVEL and + return maximal value of level in the tree + 1. */ +static int +setup_loop_tree_level (loop_tree_node_t loop_node, int level) +{ + int height, max_height; + loop_tree_node_t subloop_node; + + ira_assert (loop_node->bb == NULL); + loop_node->level = level; + max_height = level + 1; + for (subloop_node = loop_node->inner; + subloop_node != NULL; + subloop_node = subloop_node->next) + if (subloop_node->bb == NULL) + { + height = setup_loop_tree_level (subloop_node, level + 1); + if (height > max_height) + max_height = height; + } + return max_height; +} + /* The following function creates the loop tree. The algorithm is designed to provide correct order of loops (by the last loop BB) and basic blocks in chain formed by next. */ @@ -255,7 +299,7 @@ form_loop_tree (void) unsigned int i; basic_block bb; struct loop *father; - struct ira_loop_tree_node *bb_node, *loop_node; + loop_tree_node_t bb_node, loop_node; loop_p loop; /* Can not use loop/bb node access macros because of potential @@ -282,11 +326,54 @@ form_loop_tree (void) loop_node->inner = bb_node; } ira_loop_tree_root = IRA_LOOP_NODE_BY_INDEX (ira_loops.tree_root->num); + ira_loop_tree_height = setup_loop_tree_level (ira_loop_tree_root, 0); ira_assert (ira_loop_tree_root->regno_allocno_map != NULL); } +/* The function rebuilds REGNO_ALLOCNO_MAP and REGNO_ALLOCNO_MAPs of + the loops using only allocno info. */ +static void +rebuild_regno_allocno_maps (void) +{ + unsigned int l; + int i, max_regno, regno; + allocno_t a; + loop_tree_node_t loop_tree_node; + loop_p loop; + + max_regno = max_reg_num (); + for (l = 0; VEC_iterate (loop_p, ira_loops.larray, l, loop); l++) + if (ira_loop_nodes [l].regno_allocno_map != NULL) + { + ira_free (ira_loop_nodes [l].regno_allocno_map); + ira_loop_nodes [l].regno_allocno_map + = ira_allocate (sizeof (allocno_t) * max_regno); + memset (ira_loop_nodes [l].regno_allocno_map, 0, + sizeof (allocno_t) * max_regno); + } + ira_free (regno_allocno_map); + regno_allocno_map = ira_allocate (max_regno * sizeof (allocno_t)); + memset (regno_allocno_map, 0, max_regno * sizeof (allocno_t)); + for (i = 0; i < allocnos_num; i++) + { + a = allocnos [i]; + if (ALLOCNO_CAP_MEMBER (a) != NULL) + continue; + regno = ALLOCNO_REGNO (a); + loop_tree_node = ALLOCNO_LOOP_TREE_NODE (a); + ALLOCNO_NEXT_REGNO_ALLOCNO (a) = regno_allocno_map [regno]; + regno_allocno_map [regno] = a; + if (loop_tree_node->regno_allocno_map [regno] == NULL) + /* Remember that we can create temporary allocnos to break + cycles in register shuffle. */ + loop_tree_node->regno_allocno_map [regno] = a; + } +} + + + /* Array of vectors containing calls intersected by pseudo-registers. */ VEC(rtx, heap) **regno_calls; @@ -298,11 +385,47 @@ static int regno_calls_num; static void initiate_calls (void) { - regno_calls_num = max_reg_num (); + regno_calls_num = max_reg_num () * 3 / 2; regno_calls = ira_allocate (regno_calls_num * sizeof (VEC(rtx, heap) *)); memset (regno_calls, 0, regno_calls_num * sizeof (VEC(rtx, heap) *)); } +/* The function expands array of vectors containing calls for all + pseudo-registers. */ +static void +expand_calls (void) +{ + int max_regno = max_reg_num (); + + if (max_regno > regno_calls_num) + { + regno_calls = ira_reallocate (regno_calls, + max_regno * sizeof (VEC(rtx, heap) *)); + memset (regno_calls + regno_calls_num, 0, + (max_regno - regno_calls_num) * sizeof (VEC(rtx, heap) *)); + regno_calls_num = max_regno; + } +} + +/* The function removes NULL elements from vectors containing calls + intersected by pseudo-registers. */ +static void +compress_calls (void) +{ + int regno, curr, length, last; + rtx *allocno_calls; + + for (regno = 0; regno < regno_calls_num; regno++) + { + allocno_calls = VEC_address (rtx, regno_calls [regno]); + length = VEC_length (rtx, regno_calls [regno]); + for (last = curr = 0; curr < length; curr++) + if (allocno_calls [curr] != NULL_RTX) + allocno_calls [last++] = allocno_calls [curr]; + VEC_truncate (rtx, regno_calls [regno], last); + } +} + /* The function adds CALL to REGNO's vector of intersected calls. */ int add_regno_call (int regno, rtx call) @@ -351,11 +474,11 @@ initiate_allocnos (void) LOOP_TREE_NODE. Add the allocno to the list of allocnos with the same regno if ! CAP_P. */ allocno_t -create_allocno (int regno, int cap_p, struct ira_loop_tree_node *loop_tree_node) +create_allocno (int regno, int cap_p, loop_tree_node_t loop_tree_node) { allocno_t a; - a = ira_allocate (sizeof (struct allocno)); + a = pool_alloc (allocno_pool); ALLOCNO_REGNO (a) = regno; ALLOCNO_LOOP_TREE_NODE (a) = loop_tree_node; if (! cap_p) @@ -371,8 +494,11 @@ create_allocno (int regno, int cap_p, struct ira_loop_tree_node *loop_tree_node) ALLOCNO_CAP_MEMBER (a) = NULL; ALLOCNO_NUM (a) = allocnos_num; ALLOCNO_CONFLICT_ALLOCNO_VEC (a) = NULL; - ALLOCNO_CONFLICT_ALLOCNO_VEC_ACTIVE_SIZE (a) = 0; + ALLOCNO_CONFLICT_ALLOCNOS_NUM (a) = 0; + ALLOCNO_TOTAL_CONFLICT_ALLOCNOS_NUM (a) = 0; CLEAR_HARD_REG_SET (ALLOCNO_CONFLICT_HARD_REGS (a)); + CLEAR_HARD_REG_SET (ALLOCNO_TOTAL_CONFLICT_HARD_REGS (a)); + ALLOCNO_NREFS (a) = 0; ALLOCNO_FREQ (a) = 1; ALLOCNO_HARD_REGNO (a) = -1; ALLOCNO_CALL_FREQ (a) = 0; @@ -380,7 +506,10 @@ create_allocno (int regno, int cap_p, struct ira_loop_tree_node *loop_tree_node) ALLOCNO_CALLS_CROSSED_START (a) = -1; #ifdef STACK_REGS ALLOCNO_NO_STACK_REG_P (a) = FALSE; + ALLOCNO_TOTAL_NO_STACK_REG_P (a) = FALSE; #endif + ALLOCNO_MEM_OPTIMIZED_DEST (a) = NULL; + ALLOCNO_MEM_OPTIMIZED_DEST_P (a) = FALSE; ALLOCNO_IN_GRAPH_P (a) = FALSE; ALLOCNO_ASSIGNED_P (a) = FALSE; ALLOCNO_MAY_BE_SPILLED_P (a) = FALSE; @@ -388,23 +517,39 @@ create_allocno (int regno, int cap_p, struct ira_loop_tree_node *loop_tree_node) ALLOCNO_COPIES (a) = NULL; ALLOCNO_HARD_REG_COSTS (a) = NULL; ALLOCNO_CONFLICT_HARD_REG_COSTS (a) = NULL; - ALLOCNO_CURR_HARD_REG_COSTS (a) = NULL; - ALLOCNO_CURR_CONFLICT_HARD_REG_COSTS (a) = NULL; + ALLOCNO_UPDATED_HARD_REG_COSTS (a) = NULL; + ALLOCNO_UPDATED_CONFLICT_HARD_REG_COSTS (a) = NULL; ALLOCNO_LEFT_CONFLICTS_NUM (a) = -1; ALLOCNO_COVER_CLASS (a) = NO_REGS; ALLOCNO_BEST_CLASS (a) = NO_REGS; ALLOCNO_COVER_CLASS_COST (a) = 0; - ALLOCNO_MEMORY_COST (a) = 0; + ALLOCNO_UPDATED_MEMORY_COST (a) = 0; ALLOCNO_NEXT_BUCKET_ALLOCNO (a) = NULL; ALLOCNO_PREV_BUCKET_ALLOCNO (a) = NULL; ALLOCNO_FIRST_COALESCED_ALLOCNO (a) = a; ALLOCNO_NEXT_COALESCED_ALLOCNO (a) = a; + ALLOCNO_LIVE_RANGES (a) = NULL; VARRAY_PUSH_GENERIC_PTR (allocno_varray, a); allocnos = (allocno_t *) &VARRAY_GENERIC_PTR (allocno_varray, 0); allocnos_num = VARRAY_ACTIVE_SIZE (allocno_varray); return a; } +/* The function returns index of A2 in conflict vector of A1, or -1 if + it is absent. */ +int +allocno_conflict_index (allocno_t a1, allocno_t a2) +{ + int i; + allocno_t conflict_allocno, *allocno_vec; + + allocno_vec = ALLOCNO_CONFLICT_ALLOCNO_VEC (a1); + for (i = 0; (conflict_allocno = allocno_vec [i]) != NULL; i++) + if (conflict_allocno == a2) + return i; + return -1; +} + /* The function allocates conflict vector of A for NUM allocnos. */ void allocate_allocno_conflicts (allocno_t a, int num) @@ -413,7 +558,8 @@ allocate_allocno_conflicts (allocno_t a, int num) ALLOCNO_CONFLICT_ALLOCNO_VEC (a) = ira_allocate (sizeof (allocno_t) * (num + 1)); ALLOCNO_CONFLICT_ALLOCNO_VEC (a) [0] = NULL; - ALLOCNO_CONFLICT_ALLOCNO_VEC_ACTIVE_SIZE (a) = 0; + ALLOCNO_CONFLICT_ALLOCNOS_NUM (a) = 0; + ALLOCNO_TOTAL_CONFLICT_ALLOCNOS_NUM (a) = 0; ALLOCNO_CONFLICT_ALLOCNO_VEC_SIZE (a) = num; } @@ -433,31 +579,33 @@ check_allocno_conflict_vec (allocno_t a, int num) vec = ira_allocate (sizeof (allocno_t) * (size + 1)); memcpy (vec, ALLOCNO_CONFLICT_ALLOCNO_VEC (a), sizeof (allocno_t) - * (ALLOCNO_CONFLICT_ALLOCNO_VEC_ACTIVE_SIZE (a) + 1)); + * (ALLOCNO_TOTAL_CONFLICT_ALLOCNOS_NUM (a) + 1)); ira_free (ALLOCNO_CONFLICT_ALLOCNO_VEC (a)); ALLOCNO_CONFLICT_ALLOCNO_VEC (a) = vec; ALLOCNO_CONFLICT_ALLOCNO_VEC_SIZE (a) = size; } -/* The function adds A1 to conflict vector of A2 and vise versa. */ +/* The function adds A2 to conflict vector of A1. */ static void -add_allocno_conflict (allocno_t a1, allocno_t a2) +add_to_allocno_conflict_vec (allocno_t a1, allocno_t a2) { - int size1, size2; - allocno_t *vec1, *vec2; + int size; + allocno_t *vec; + + size = ALLOCNO_TOTAL_CONFLICT_ALLOCNOS_NUM (a1); + check_allocno_conflict_vec (a1, size + 1); + vec = ALLOCNO_CONFLICT_ALLOCNO_VEC (a1); + vec [size] = a2; + vec [size + 1] = NULL; + ALLOCNO_TOTAL_CONFLICT_ALLOCNOS_NUM (a1)++; +} - size1 = ALLOCNO_CONFLICT_ALLOCNO_VEC_ACTIVE_SIZE (a1); - size2 = ALLOCNO_CONFLICT_ALLOCNO_VEC_ACTIVE_SIZE (a2); - check_allocno_conflict_vec (a1, size1 + 1); - check_allocno_conflict_vec (a2, size2 + 1); - vec1 = ALLOCNO_CONFLICT_ALLOCNO_VEC (a1); - vec2 = ALLOCNO_CONFLICT_ALLOCNO_VEC (a2); - vec1 [size1] = a2; - vec2 [size2] = a1; - vec1 [size1 + 1] = NULL; - vec2 [size2 + 1] = NULL; - ALLOCNO_CONFLICT_ALLOCNO_VEC_ACTIVE_SIZE (a1)++; - ALLOCNO_CONFLICT_ALLOCNO_VEC_ACTIVE_SIZE (a2)++; +/* The function adds A1 to conflict vector of A2 and vise versa. */ +void +add_allocno_conflict (allocno_t a1, allocno_t a2) +{ + add_to_allocno_conflict_vec (a1, a2); + add_to_allocno_conflict_vec (a2, a1); } /* This recursive function outputs allocno A and if it is cap the @@ -485,12 +633,8 @@ print_expanded_allocno (allocno_t a) static allocno_t create_cap_allocno (allocno_t a) { - int i, regno, hard_regs_num, conflicts_num; - int *reg_costs, *conflict_reg_costs; - basic_block bb; - allocno_t cap, conflict_allocno, conflict_father_allocno; - allocno_t *allocno_vec; - struct ira_loop_tree_node *father; + allocno_t cap; + loop_tree_node_t father; ira_assert (ALLOCNO_FIRST_COALESCED_ALLOCNO (a) == a && ALLOCNO_NEXT_COALESCED_ALLOCNO (a) == a); @@ -502,7 +646,39 @@ create_cap_allocno (allocno_t a) ALLOCNO_BEST_CLASS (cap) = ALLOCNO_BEST_CLASS (a); ALLOCNO_AVAILABLE_REGS_NUM (cap) = ALLOCNO_AVAILABLE_REGS_NUM (a); ALLOCNO_CAP_MEMBER (cap) = a; - hard_regs_num = class_hard_regs_num [ALLOCNO_COVER_CLASS (a)]; + bitmap_set_bit (father->mentioned_allocnos, ALLOCNO_NUM (cap)); + ALLOCNO_CAP (a) = cap; + ALLOCNO_COVER_CLASS_COST (cap) = ALLOCNO_COVER_CLASS_COST (a); + ALLOCNO_UPDATED_MEMORY_COST (cap) = ALLOCNO_UPDATED_MEMORY_COST (a); + if (internal_flag_ira_verbose > 2 && ira_dump_file != NULL) + { + fprintf (ira_dump_file, " Creating cap "); + print_expanded_allocno (cap); + fprintf (ira_dump_file, "\n"); + } + return cap; +} + +/* The function propagate info obtained during conflicts building to + CAP. */ +static void +propagate_info_to_cap (allocno_t cap) +{ + int i, regno, hard_regs_num, conflicts_num; + int *reg_costs, *conflict_reg_costs; + allocno_t a, conflict_allocno, conflict_father_allocno; + allocno_t another_a, father_a; + allocno_t *allocno_vec; + loop_tree_node_t father; + copy_t cp, next_cp; + + ira_assert (ALLOCNO_FIRST_COALESCED_ALLOCNO (cap) == cap + && ALLOCNO_NEXT_COALESCED_ALLOCNO (cap) == cap + && ALLOCNO_CONFLICT_ALLOCNO_VEC (cap) == NULL + && ALLOCNO_CALLS_CROSSED_NUM (cap) == 0); + a = ALLOCNO_CAP_MEMBER (cap); + father = ALLOCNO_LOOP_TREE_NODE (cap); + hard_regs_num = class_hard_regs_num [ALLOCNO_COVER_CLASS (cap)]; ALLOCNO_HARD_REG_COSTS (cap) = reg_costs = ira_allocate (hard_regs_num * sizeof (int)); memcpy (reg_costs, ALLOCNO_HARD_REG_COSTS (a), hard_regs_num * sizeof (int)); @@ -510,61 +686,170 @@ create_cap_allocno (allocno_t a) = ira_allocate (hard_regs_num * sizeof (int)); memcpy (conflict_reg_costs, ALLOCNO_CONFLICT_HARD_REG_COSTS (a), hard_regs_num * sizeof (int)); - ALLOCNO_CURR_HARD_REG_COSTS (cap) + ALLOCNO_UPDATED_HARD_REG_COSTS (cap) = ira_allocate (hard_regs_num * sizeof (int)); - ALLOCNO_CURR_CONFLICT_HARD_REG_COSTS (cap) + ALLOCNO_UPDATED_CONFLICT_HARD_REG_COSTS (cap) = ira_allocate (hard_regs_num * sizeof (int)); - /* ??? costs, call_p etc. */ - bitmap_set_bit (father->mentioned_allocnos, ALLOCNO_NUM (cap)); - ALLOCNO_CAP (a) = cap; - ALLOCNO_AVAILABLE_REGS_NUM (cap) = ALLOCNO_AVAILABLE_REGS_NUM (a); - ALLOCNO_COVER_CLASS_COST (cap) = ALLOCNO_COVER_CLASS_COST (a); - ALLOCNO_MEMORY_COST (cap) = ALLOCNO_MEMORY_COST (a); + ALLOCNO_NREFS (cap) = ALLOCNO_NREFS (a); ALLOCNO_FREQ (cap) = ALLOCNO_FREQ (a); ALLOCNO_CALL_FREQ (cap) = ALLOCNO_CALL_FREQ (a); IOR_HARD_REG_SET (ALLOCNO_CONFLICT_HARD_REGS (cap), ALLOCNO_CONFLICT_HARD_REGS (a)); + IOR_HARD_REG_SET (ALLOCNO_TOTAL_CONFLICT_HARD_REGS (cap), + ALLOCNO_TOTAL_CONFLICT_HARD_REGS (a)); ALLOCNO_CALLS_CROSSED_NUM (cap) = ALLOCNO_CALLS_CROSSED_NUM (a); ALLOCNO_CALLS_CROSSED_START (cap) = ALLOCNO_CALLS_CROSSED_START (a); #ifdef STACK_REGS ALLOCNO_NO_STACK_REG_P (cap) = ALLOCNO_NO_STACK_REG_P (a); + ALLOCNO_TOTAL_NO_STACK_REG_P (cap) = ALLOCNO_TOTAL_NO_STACK_REG_P (a); #endif + /* Add copies to the cap. */ + for (cp = ALLOCNO_COPIES (a); cp != NULL; cp = next_cp) + { + if (cp->first == a) + { + next_cp = cp->next_first_allocno_copy; + another_a = cp->second; + } + else if (cp->second == a) + { + next_cp = cp->next_second_allocno_copy; + another_a = cp->first; + } + else + gcc_unreachable (); + father_a = father->regno_allocno_map [ALLOCNO_REGNO (another_a)]; + if (father_a == NULL) + father_a = ALLOCNO_CAP (another_a); + if (father_a != NULL) + /* Upper level allocno might be not existing because it is + not mentioned or lived on the border. It is just + living on bb start of the loop. */ + add_allocno_copy (cap, father_a, cp->freq, cp->move_insn, + cp->loop_tree_node); + } allocno_vec = ALLOCNO_CONFLICT_ALLOCNO_VEC (a); - for (conflicts_num = i = 0; - (conflict_allocno = allocno_vec [i]) != NULL; - i++) - conflicts_num++; - allocate_allocno_conflicts (cap, conflicts_num); - for (conflicts_num = i = 0; - (conflict_allocno = allocno_vec [i]) != NULL; - i++) - { - regno = ALLOCNO_REGNO (conflict_allocno); - conflict_father_allocno = father->regno_allocno_map [regno]; - if (conflict_father_allocno == NULL) - conflict_father_allocno = ALLOCNO_CAP (conflict_allocno); - if (conflict_father_allocno != NULL) - add_allocno_conflict (cap, conflict_father_allocno); - } - if (ira_dump_file != NULL) - { - fprintf (ira_dump_file, " Creating cap "); + if (allocno_vec != NULL) + { + conflicts_num = 0; + for (i = 0; + (conflict_allocno = allocno_vec [i]) != NULL; + i++) + conflicts_num++; + allocate_allocno_conflicts (cap, conflicts_num); + for (conflicts_num = i = 0; + (conflict_allocno = allocno_vec [i]) != NULL; + i++) + { + regno = ALLOCNO_REGNO (conflict_allocno); + conflict_father_allocno = father->regno_allocno_map [regno]; + if (conflict_father_allocno == NULL) + conflict_father_allocno = ALLOCNO_CAP (conflict_allocno); + if (conflict_father_allocno != NULL + && (ALLOCNO_CONFLICT_ALLOCNO_VEC (conflict_father_allocno) + != NULL)) + add_allocno_conflict (cap, conflict_father_allocno); + } + } + if (internal_flag_ira_verbose > 2 && ira_dump_file != NULL) + { + fprintf (ira_dump_file, " Propagate info to cap "); print_expanded_allocno (cap); - fprintf (ira_dump_file, "\n Conflicts:"); allocno_vec = ALLOCNO_CONFLICT_ALLOCNO_VEC (cap); - for (i = 0; (conflict_allocno = allocno_vec [i]) != NULL; i++) + if (allocno_vec != NULL) { - fprintf (ira_dump_file, " a%d(r%d,", ALLOCNO_NUM (conflict_allocno), - ALLOCNO_REGNO (conflict_allocno)); - if ((bb = ALLOCNO_LOOP_TREE_NODE (conflict_allocno)->bb) != NULL) - fprintf (ira_dump_file, "b%d)", bb->index); - else - fprintf (ira_dump_file, "l%d)", - ALLOCNO_LOOP_TREE_NODE (conflict_allocno)->loop->num); + fprintf (ira_dump_file, "\n Conflicts:"); + for (i = 0; (conflict_allocno = allocno_vec [i]) != NULL; i++) + { + fprintf (ira_dump_file, " a%d(r%d,", + ALLOCNO_NUM (conflict_allocno), + ALLOCNO_REGNO (conflict_allocno)); + ira_assert + (ALLOCNO_LOOP_TREE_NODE (conflict_allocno)->bb == NULL); + fprintf (ira_dump_file, "l%d)", + ALLOCNO_LOOP_TREE_NODE (conflict_allocno)->loop->num); + } } fprintf (ira_dump_file, "\n"); } - return cap; +} + + +/* Create and return allocno live range with give attributes. */ +allocno_live_range_t +create_allocno_live_range (allocno_t a, int start, int finish, + allocno_live_range_t next) +{ + allocno_live_range_t p; + + p = pool_alloc (allocno_live_range_pool); + p->allocno = a; + p->start = start; + p->finish = finish; + p->next = next; + return p; +} + +/* Create and return allocno live range with give attributes. */ +allocno_live_range_t +copy_allocno_live_range (allocno_live_range_t r) +{ + allocno_live_range_t p; + + p = pool_alloc (allocno_live_range_pool); + *p = *r; + return p; +} + +/* Create and return allocno live range with give attributes. */ +allocno_live_range_t +copy_allocno_live_range_list (allocno_live_range_t r) +{ + allocno_live_range_t p, first, last; + + if (r == NULL) + return NULL; + for (first = last = NULL; r != NULL; r = r->next) + { + p = copy_allocno_live_range (r); + if (first == NULL) + first = p; + else + last->next = p; + last = p; + } + return first; +} + +/* Free allocno live range R. */ +void +finish_allocno_live_range (allocno_live_range_t r) +{ + pool_free (allocno_live_range_pool, r); +} + +/* The function frees memory allocated for allocno A. */ +static void +finish_allocno (allocno_t a) +{ + allocno_live_range_t r, next_r; + + if (ALLOCNO_CONFLICT_ALLOCNO_VEC (a) != NULL) + ira_free (ALLOCNO_CONFLICT_ALLOCNO_VEC (a)); + if (ALLOCNO_HARD_REG_COSTS (a) != NULL) + ira_free (ALLOCNO_HARD_REG_COSTS (a)); + if (ALLOCNO_CONFLICT_HARD_REG_COSTS (a) != NULL) + ira_free (ALLOCNO_CONFLICT_HARD_REG_COSTS (a)); + if (ALLOCNO_UPDATED_HARD_REG_COSTS (a) != NULL) + ira_free (ALLOCNO_UPDATED_HARD_REG_COSTS (a)); + if (ALLOCNO_UPDATED_CONFLICT_HARD_REG_COSTS (a) != NULL) + ira_free (ALLOCNO_UPDATED_CONFLICT_HARD_REG_COSTS (a)); + for (r = ALLOCNO_LIVE_RANGES (a); r != NULL; r = next_r) + { + next_r = r->next; + finish_allocno_live_range (r); + } + pool_free (allocno_pool, a); } /* The function frees memory allocated for all allocnos. */ @@ -572,23 +857,9 @@ static void finish_allocnos (void) { int i; - allocno_t a; for (i = 0; i < allocnos_num; i++) - { - a = allocnos [i]; - if (ALLOCNO_CONFLICT_ALLOCNO_VEC (a) != NULL) - ira_free (ALLOCNO_CONFLICT_ALLOCNO_VEC (a)); - if (ALLOCNO_HARD_REG_COSTS (a) != NULL) - ira_free (ALLOCNO_HARD_REG_COSTS (a)); - if (ALLOCNO_CONFLICT_HARD_REG_COSTS (a) != NULL) - ira_free (ALLOCNO_CONFLICT_HARD_REG_COSTS (a)); - if (ALLOCNO_CURR_HARD_REG_COSTS (a) != NULL) - ira_free (ALLOCNO_CURR_HARD_REG_COSTS (a)); - if (ALLOCNO_CURR_CONFLICT_HARD_REG_COSTS (a) != NULL) - ira_free (ALLOCNO_CURR_CONFLICT_HARD_REG_COSTS (a)); - ira_free (a); - } + finish_allocno (allocnos [i]); ira_free (regno_allocno_map); VARRAY_FREE (allocno_varray); } @@ -608,25 +879,146 @@ initiate_copies (void) copies_num = 0; } -/* The function creates and returns copy of allocnos FIRST and SECOND - with frequency FREQ and move insn MOVE_INSN. */ +/* The function creates and returns created in LOOP_TREE_NODE copy of + allocnos FIRST and SECOND with frequency FREQ, move insn + MOVE_INSN. */ copy_t -create_copy (allocno_t first, allocno_t second, int freq, rtx move_insn) +create_copy (allocno_t first, allocno_t second, int freq, rtx move_insn, + loop_tree_node_t loop_tree_node) { copy_t cp; - cp = ira_allocate (sizeof (struct allocno_copy)); + cp = pool_alloc (copy_pool); cp->num = copies_num; cp->first = first; cp->second = second; cp->freq = freq; cp->move_insn = move_insn; + cp->loop_tree_node = loop_tree_node; VARRAY_PUSH_GENERIC_PTR (copy_varray, cp); copies = (copy_t *) &VARRAY_GENERIC_PTR (copy_varray, 0); copies_num = VARRAY_ACTIVE_SIZE (copy_varray); return cp; } +/* The function attaches copy CP to allocnos involved into the copy. */ +void +add_allocno_copy_to_list (copy_t cp) +{ + allocno_t first = cp->first, second = cp->second; + + cp->prev_first_allocno_copy = NULL; + cp->prev_second_allocno_copy = NULL; + cp->next_first_allocno_copy = ALLOCNO_COPIES (first); + if (cp->next_first_allocno_copy != NULL) + { + if (cp->next_first_allocno_copy->first == first) + cp->next_first_allocno_copy->prev_first_allocno_copy = cp; + else + cp->next_first_allocno_copy->prev_second_allocno_copy = cp; + } + cp->next_second_allocno_copy = ALLOCNO_COPIES (second); + if (cp->next_second_allocno_copy != NULL) + { + if (cp->next_second_allocno_copy->second == second) + cp->next_second_allocno_copy->prev_second_allocno_copy = cp; + else + cp->next_second_allocno_copy->prev_first_allocno_copy = cp; + } + ALLOCNO_COPIES (first) = cp; + ALLOCNO_COPIES (second) = cp; +} + +/* The function detaches copy CP from allocnos involved into the copy. */ +void +remove_allocno_copy_from_list (copy_t cp) +{ + allocno_t first = cp->first, second = cp->second; + copy_t prev, next; + + next = cp->next_first_allocno_copy; + prev = cp->prev_first_allocno_copy; + if (prev == NULL) + ALLOCNO_COPIES (first) = next; + else if (prev->first == first) + prev->next_first_allocno_copy = next; + else + prev->next_second_allocno_copy = next; + if (next != NULL) + { + if (next->first == first) + next->prev_first_allocno_copy = prev; + else + next->prev_second_allocno_copy = prev; + } + cp->prev_first_allocno_copy = cp->next_first_allocno_copy = NULL; + + next = cp->next_second_allocno_copy; + prev = cp->prev_second_allocno_copy; + if (prev == NULL) + ALLOCNO_COPIES (second) = next; + else if (prev->second == second) + prev->next_second_allocno_copy = next; + else + prev->next_first_allocno_copy = next; + if (next != NULL) + { + if (next->second == second) + next->prev_second_allocno_copy = prev; + else + next->prev_first_allocno_copy = prev; + } + cp->prev_second_allocno_copy = cp->next_second_allocno_copy = NULL; +} + +/* The function makes copy CP a canonical copy where number of the + first allocno is less than the second one. */ +void +swap_allocno_copy_ends_if_necessary (copy_t cp) +{ + allocno_t temp; + copy_t temp_cp; + + if (ALLOCNO_NUM (cp->first) <= ALLOCNO_NUM (cp->second)) + return; + + temp = cp->first; + cp->first = cp->second; + cp->second = temp; + + temp_cp = cp->prev_first_allocno_copy; + cp->prev_first_allocno_copy = cp->prev_second_allocno_copy; + cp->prev_second_allocno_copy = temp_cp; + + temp_cp = cp->next_first_allocno_copy; + cp->next_first_allocno_copy = cp->next_second_allocno_copy; + cp->next_second_allocno_copy = temp_cp; +} + +/* The function creates and returns new copy of allocnos FIRST and + SECOND with frequency FREQ corresponding to move insn INSN (if + any) and originated from LOOP_TREE_NODE. */ +copy_t +add_allocno_copy (allocno_t first, allocno_t second, int freq, rtx insn, + loop_tree_node_t loop_tree_node) +{ + copy_t cp; + + cp = create_copy (first, second, freq, insn, loop_tree_node); + ira_assert (first != NULL && second != NULL); + add_allocno_copy_to_list (cp); + swap_allocno_copy_ends_if_necessary (cp); + return cp; +} + +/* The function frees memory allocated for copy CP. */ +static void +finish_copy (copy_t cp) +{ + pool_free (copy_pool, cp); +} + + /* The function frees memory allocated for all copies. */ static void finish_copies (void) @@ -634,38 +1026,60 @@ finish_copies (void) int i; for (i = 0; i < copies_num; i++) - ira_free (copies [i]); + finish_copy (copies [i]); VARRAY_FREE (copy_varray); } -/* The current loop tree node. */ -struct ira_loop_tree_node *ira_curr_loop_tree_node; +/* The current loop tree node and its map. */ +loop_tree_node_t ira_curr_loop_tree_node; +allocno_t *ira_curr_regno_allocno_map; /* The recursive function traverses loop tree node with root LOOP_NODE calling non-null functions PREORDER_FUNC and POSTORDER_FUNC correspondingly in preorder and postorder. The function sets up - IRA_CURR_LOOP_TREE_NODE. */ + IRA_CURR_LOOP_TREE_NODE. If BB_FIRST_P, BB of LOOP_NODE is + processed before its subloops. */ void -traverse_loop_tree (struct ira_loop_tree_node *loop_node, - void (*preorder_func) (struct ira_loop_tree_node *), - void (*postorder_func) (struct ira_loop_tree_node *)) +traverse_loop_tree (int bb_first_p, loop_tree_node_t loop_node, + void (*preorder_func) (loop_tree_node_t), + void (*postorder_func) (loop_tree_node_t)) { - struct ira_loop_tree_node *subloop_node; + loop_tree_node_t subloop_node; if (loop_node->bb == NULL) - ira_curr_loop_tree_node = loop_node; + { + ira_curr_loop_tree_node = loop_node; + ira_curr_regno_allocno_map = ira_curr_loop_tree_node->regno_allocno_map; + } if (preorder_func != NULL) (*preorder_func) (loop_node); for (subloop_node = loop_node->inner; subloop_node != NULL; subloop_node = subloop_node->next) - { - traverse_loop_tree (subloop_node, preorder_func, postorder_func); - ira_curr_loop_tree_node = loop_node; - } + if (! bb_first_p || subloop_node->bb != NULL) + { + traverse_loop_tree (bb_first_p, subloop_node, + preorder_func, postorder_func); + ira_curr_loop_tree_node = loop_node; + ira_curr_regno_allocno_map + = ira_curr_loop_tree_node->regno_allocno_map; + } + + if (bb_first_p) + for (subloop_node = loop_node->inner; + subloop_node != NULL; + subloop_node = subloop_node->next) + if (subloop_node->bb == NULL) + { + traverse_loop_tree (bb_first_p, subloop_node, + preorder_func, postorder_func); + ira_curr_loop_tree_node = loop_node; + ira_curr_regno_allocno_map + = ira_curr_loop_tree_node->regno_allocno_map; + } if (postorder_func != NULL) (*postorder_func) (loop_node); @@ -697,6 +1111,7 @@ create_insn_allocnos (rtx x, int output_p) if ((a = ira_curr_loop_tree_node->regno_allocno_map [regno]) == NULL) a = create_allocno (regno, FALSE, ira_curr_loop_tree_node); + ALLOCNO_NREFS (a)++; ALLOCNO_FREQ (a) += REG_FREQ_FROM_BB (curr_bb); bitmap_set_bit (ira_curr_loop_tree_node->mentioned_allocnos, ALLOCNO_NUM (a)); @@ -744,7 +1159,7 @@ create_insn_allocnos (rtx x, int output_p) living in basic blocks represented by the corresponding loop tree node BB_NODE. */ static void -create_bb_allocnos (struct ira_loop_tree_node *bb_node) +create_bb_allocnos (loop_tree_node_t bb_node) { basic_block bb; rtx insn; @@ -793,7 +1208,7 @@ create_loop_allocnos (edge e) living in loop represented by the corresponding loop tree node LOOP_NODE. */ static void -create_loop_tree_node_allocnos (struct ira_loop_tree_node *loop_node) +create_loop_tree_node_allocnos (loop_tree_node_t loop_node) { if (loop_node->bb != NULL) create_bb_allocnos (loop_node); @@ -823,20 +1238,27 @@ create_allocnos (void) { int i; allocno_t a, father_a; - struct ira_loop_tree_node *father; + loop_tree_node_t father; - traverse_loop_tree (ira_loop_tree_root, create_loop_tree_node_allocnos, NULL); + /* We need to process BB first to correctly link allocnos by + next_regno_allocno field. */ + traverse_loop_tree (TRUE, ira_loop_tree_root, + create_loop_tree_node_allocnos, NULL); if (flag_ira_algorithm != IRA_ALGORITHM_REGIONAL && flag_ira_algorithm != IRA_ALGORITHM_MIXED) return; - /* Propagate frequencies for regional register allocator. */ + /* Propagate number of references and frequencies for regional + register allocator. */ for (i = max_reg_num () - 1; i >= FIRST_PSEUDO_REGISTER; i--) for (a = regno_allocno_map [i]; a != NULL; a = ALLOCNO_NEXT_REGNO_ALLOCNO (a)) if ((father = ALLOCNO_LOOP_TREE_NODE (a)->father) != NULL && (father_a = father->regno_allocno_map [i]) != NULL) - ALLOCNO_FREQ (father_a) += ALLOCNO_FREQ (a); + { + ALLOCNO_NREFS (father_a) += ALLOCNO_NREFS (a); + ALLOCNO_FREQ (father_a) += ALLOCNO_FREQ (a); + } } @@ -847,13 +1269,11 @@ static bitmap local_allocnos_bitmap; /* The function creates caps representing allocnos living only inside the loop given by LOOP_NODE on higher loop level. */ static void -create_loop_tree_node_caps (struct ira_loop_tree_node *loop_node) +create_loop_tree_node_caps (loop_tree_node_t loop_node) { unsigned int i; bitmap_iterator bi; - allocno_t a, cap, another_a, father_a; - copy_t cp, next_cp; - struct ira_loop_tree_node *father; + loop_tree_node_t father; if (loop_node->bb != NULL || loop_node == ira_loop_tree_root) return; @@ -863,60 +1283,587 @@ create_loop_tree_node_caps (struct ira_loop_tree_node *loop_node) EXECUTE_IF_SET_IN_BITMAP (local_allocnos_bitmap, 0, i, bi) if (father->regno_allocno_map [ALLOCNO_REGNO (allocnos [i])] == NULL) create_cap_allocno (allocnos [i]); - EXECUTE_IF_SET_IN_BITMAP (local_allocnos_bitmap, 0, i, bi) +} + +/* The function propagate info to caps mentioned in LOOP_NODE. */ +static void +propagate_info_to_loop_tree_node_caps (loop_tree_node_t loop_node) +{ + unsigned int i; + bitmap_iterator bi; + allocno_t a; + + if (loop_node->bb != NULL) + return; + EXECUTE_IF_SET_IN_BITMAP (loop_node->mentioned_allocnos, 0, i, bi) { a = allocnos [i]; - cap = ALLOCNO_CAP (a); - if (cap == NULL) - continue; - for (cp = ALLOCNO_COPIES (a); cp != NULL; cp = next_cp) + if (ALLOCNO_CAP_MEMBER (a) != NULL) + propagate_info_to_cap (a); + } +} + + + +/* The function merges ranges R1 and R2 and returns the result. */ +static allocno_live_range_t +merge_ranges (allocno_live_range_t r1, allocno_live_range_t r2) +{ + allocno_live_range_t first, last, temp; + + if (r1 == NULL) + return r2; + if (r2 == NULL) + return r1; + for (first = last = NULL; r1 != NULL && r2 != NULL;) + { + if (r1->start < r2->start) { - if (cp->first == a) + temp = r1; + r1 = r2; + r2 = temp; + } + if (r1->start <= r2->finish + 1) + { + /* Intersected ranges: merge r1 and r2 into r1. */ + r1->start = r2->start; + if (r1->finish < r2->finish) + r1->finish = r2->finish; + temp = r2; + r2 = r2->next; + finish_allocno_live_range (temp); + if (r2 == NULL) { - next_cp = cp->next_first_allocno_copy; - another_a = cp->second; + /* To try to merge with subsequent ranges in r1. */ + r2 = r1->next; + r1->next = NULL; } - else if (cp->second == a) + } + else + { + /* Add r1 to the result. */ + if (first == NULL) + first = last = r1; + else { - next_cp = cp->next_second_allocno_copy; - another_a = cp->first; + last->next = r1; + last = r1; + } + r1 = r1->next; + if (r1 == NULL) + { + /* To try to merge with subsequent ranges in r2. */ + r1 = r2->next; + r2->next = NULL; } - else - gcc_unreachable (); - father_a = father->regno_allocno_map [ALLOCNO_REGNO (another_a)]; - if (father_a != NULL) - /* Upper level allocno might be not existing because it is - not mentioned or lived on the border. It is just - living on bb start of the loop. */ - add_allocno_copy (cap, father_a, cp->freq, cp->move_insn); } } + if (r1 != NULL) + { + if (first == NULL) + first = r1; + else + last->next = r1; + ira_assert (r1->next == NULL); + } + else if (r2 != NULL) + { + if (first == NULL) + first = r2; + else + last->next = r2; + ira_assert (r2->next == NULL); + } + else + ira_assert (last->next == NULL); + return first; } - +/* The function returns immediate common dominator of two loop tree + nodes N1 and N2. */ +static loop_tree_node_t +common_loop_tree_node_dominator (loop_tree_node_t n1, loop_tree_node_t n2) +{ + ira_assert (n1 != NULL && n2 != NULL); + if (n1 == n2) + return n1; + if (n1->level < n2->level) + return common_loop_tree_node_dominator (n1, n2->father); + else if (n1->level > n2->level) + return common_loop_tree_node_dominator (n1->father, n2); + else + return common_loop_tree_node_dominator (n1->father, n2->father); +} + +/* Map: regno -> allocnos which will finally represent the regno for + IR with one region. */ +static allocno_t *regno_top_level_allocno_map; + -#ifdef ENABLE_IRA_CHECKING -/* The function checks that there are no coalesced allocnos. */ +/* The function check conflicts A with allocnos from CONFLICT_VECT and + add them (more accurately corresponding final IR allocnos) if it is + necessary. */ static void -check_coalesced_allocnos (void) +check_and_add_conflicts (allocno_t a, allocno_t *conflict_vec) { + allocno_t conflict_a; int i; - allocno_t a; + for (i = 0; (conflict_a = conflict_vec [i]) != NULL; i++) + { + conflict_a + = regno_top_level_allocno_map [REGNO (ALLOCNO_REG (conflict_a))]; + if (allocno_conflict_p (conflict_a, a) + && allocno_conflict_index (conflict_a, a) < 0) + { + ira_assert (allocno_conflict_index (a, conflict_a) < 0); + add_to_allocno_conflict_vec (conflict_a, a); + add_to_allocno_conflict_vec (a, conflict_a); + if (internal_flag_ira_verbose > 3 && ira_dump_file != NULL) + fprintf (ira_dump_file, + " Add underlying conflict a%dr%d-a%dr%d\n", + ALLOCNO_NUM (a), REGNO (ALLOCNO_REG (a)), + ALLOCNO_NUM (conflict_a),\ + REGNO (ALLOCNO_REG (conflict_a))); + } + } +} + +/* This recursive function checks and adds (if necessary) conflicts + with A processing conflicts of allocnos corresponding to A in + subloops. If GO_DEEPER_P is false, the function stops to go deeper + in loop tree when allocno which will represent allocno in final IR + is achieved. */ +static void +add_conflict_with_underlying_allocnos (allocno_t a, + loop_tree_node_t loop_node, + int go_deeper_p) +{ + loop_tree_node_t subloop_node; + allocno_t subloop_a; + + for (subloop_node = loop_node->inner; + subloop_node != NULL; + subloop_node = subloop_node->next) + { + if (subloop_node->bb != NULL) + continue; + subloop_a = subloop_node->regno_allocno_map [ALLOCNO_REGNO (a)]; + if (subloop_a == NULL) + continue; + if (! go_deeper_p + && subloop_a == regno_top_level_allocno_map [REGNO (ALLOCNO_REG + (subloop_a))]) + continue; + check_and_add_conflicts (a, ALLOCNO_CONFLICT_ALLOCNO_VEC (subloop_a)); + add_conflict_with_underlying_allocnos (a, subloop_node, go_deeper_p); + } +} + +/* The function flattens IR. In other words, the function transforms + IR as it were build with one region (without loops). We could make + it much simpler by rebuilding IR with one region, but unfortunately + it takes a lot of time. */ +void +ira_flattening (int max_regno_before_emit, int max_point_before_emit) +{ + int i, j, k, free, propagate_p, stop_p, keep_p, hard_regs_num; + unsigned int n; + enum reg_class cover_class; + rtx call, *allocno_calls; + allocno_t a, father_a, conflict_a, first, second, node_first, node_second; + allocno_t dominator_a, *allocno_vec; + copy_t cp; + loop_tree_node_t father, node, dominator; + allocno_live_range_t r; + bitmap live_allocnos; + bitmap_iterator bi; + + regno_top_level_allocno_map + = ira_allocate (max_reg_num () * sizeof (allocno_t)); + memset (regno_top_level_allocno_map, 0, max_reg_num () * sizeof (allocno_t)); + expand_calls (); + /* Updating accumulated attributes. */ + for (i = max_regno_before_emit - 1; i >= FIRST_PSEUDO_REGISTER; i--) + { + propagate_p = FALSE; + for (a = regno_allocno_map [i]; + a != NULL; + a = ALLOCNO_NEXT_REGNO_ALLOCNO (a)) + { + if (ALLOCNO_CAP_MEMBER (a) == NULL + && (unsigned int) ALLOCNO_REGNO (a) != REGNO (ALLOCNO_REG (a)) + && ALLOCNO_CALLS_CROSSED_NUM (a) != 0) + { + allocno_calls = (VEC_address (rtx, + regno_calls [ALLOCNO_REGNO (a)]) + + ALLOCNO_CALLS_CROSSED_START (a)); + for (j = ALLOCNO_CALLS_CROSSED_NUM (a) - 1; j >= 0; j--) + { + call = allocno_calls [j]; + if (call == NULL_RTX) + continue; + add_regno_call (REGNO (ALLOCNO_REG (a)), call); + allocno_calls [j] = NULL_RTX; + } + } + if ((father = ALLOCNO_LOOP_TREE_NODE (a)->father) == NULL + || (father_a = father->regno_allocno_map [ALLOCNO_REGNO (a)]) + == NULL) + { + ALLOCNO_COPIES (a) = NULL; + regno_top_level_allocno_map [REGNO (ALLOCNO_REG (a))] = a; + continue; + } + if (propagate_p) + { + COPY_HARD_REG_SET (ALLOCNO_TOTAL_CONFLICT_HARD_REGS (father_a), + ALLOCNO_CONFLICT_HARD_REGS (father_a)); + IOR_HARD_REG_SET (ALLOCNO_TOTAL_CONFLICT_HARD_REGS (father_a), + ALLOCNO_TOTAL_CONFLICT_HARD_REGS (a)); +#ifdef STACK_REGS + ALLOCNO_TOTAL_NO_STACK_REG_P (father_a) + = (ALLOCNO_NO_STACK_REG_P (father_a) + || ALLOCNO_TOTAL_NO_STACK_REG_P (a)); +#endif + } + if (REGNO (ALLOCNO_REG (a)) == REGNO (ALLOCNO_REG (father_a))) + { + if (internal_flag_ira_verbose > 3 && ira_dump_file != NULL) + { + fprintf (ira_dump_file, + " Moving ranges of a%dr%d to a%dr%d: ", + ALLOCNO_NUM (a), REGNO (ALLOCNO_REG (a)), + ALLOCNO_NUM (father_a), + REGNO (ALLOCNO_REG (father_a))); + print_live_range_list (ira_dump_file, + ALLOCNO_LIVE_RANGES (a)); + } + ALLOCNO_LIVE_RANGES (father_a) + = merge_ranges (ALLOCNO_LIVE_RANGES (a), + ALLOCNO_LIVE_RANGES (father_a)); + ALLOCNO_LIVE_RANGES (a) = NULL; + ALLOCNO_MEM_OPTIMIZED_DEST_P (father_a) + = (ALLOCNO_MEM_OPTIMIZED_DEST_P (father_a) + || ALLOCNO_MEM_OPTIMIZED_DEST_P (a)); + continue; + } + propagate_p = TRUE; + first = ALLOCNO_MEM_OPTIMIZED_DEST (a) == NULL ? NULL : a; + for (;;) + { + if (first == NULL + && ALLOCNO_MEM_OPTIMIZED_DEST (father_a) != NULL) + first = father_a; + ALLOCNO_NREFS (father_a) -= ALLOCNO_NREFS (a); + ALLOCNO_FREQ (father_a) -= ALLOCNO_FREQ (a); + ALLOCNO_CALL_FREQ (father_a) -= ALLOCNO_CALL_FREQ (a); + cover_class = ALLOCNO_COVER_CLASS (father_a); + hard_regs_num = class_hard_regs_num [cover_class]; + for (j = 0; j < hard_regs_num; j++) + { + ALLOCNO_HARD_REG_COSTS (father_a) [j] + -= ALLOCNO_HARD_REG_COSTS (a) [j]; + ALLOCNO_CONFLICT_HARD_REG_COSTS (father_a) [j] + -= ALLOCNO_CONFLICT_HARD_REG_COSTS (a) [j]; + } + ALLOCNO_COVER_CLASS_COST (father_a) + -= ALLOCNO_COVER_CLASS_COST (a); + ALLOCNO_MEMORY_COST (father_a) -= ALLOCNO_MEMORY_COST (a); + if ((father = ALLOCNO_LOOP_TREE_NODE (father_a)->father) == NULL + || (father_a = (father->regno_allocno_map + [ALLOCNO_REGNO (father_a)])) == NULL) + break; + } + if (first != NULL) + { + father_a = ALLOCNO_MEM_OPTIMIZED_DEST (first); + dominator = common_loop_tree_node_dominator + (ALLOCNO_LOOP_TREE_NODE (father_a), + ALLOCNO_LOOP_TREE_NODE (first)); + dominator_a = dominator->regno_allocno_map [ALLOCNO_REGNO (a)]; + ira_assert (father_a != NULL); + stop_p = first != a; + /* Remember that exit can be to a grandparent (not only + a parent) or a child of grandparent. */ + for (first = a;;) + { + if (internal_flag_ira_verbose > 3 && ira_dump_file != NULL) + { + fprintf + (ira_dump_file, + " Coping ranges of a%dr%d to a%dr%d: ", + ALLOCNO_NUM (first), REGNO (ALLOCNO_REG (first)), + ALLOCNO_NUM (father_a), + REGNO (ALLOCNO_REG (father_a))); + print_live_range_list (ira_dump_file, + ALLOCNO_LIVE_RANGES (first)); + } + ALLOCNO_LIVE_RANGES (father_a) + = merge_ranges (copy_allocno_live_range_list + (ALLOCNO_LIVE_RANGES (first)), + ALLOCNO_LIVE_RANGES (father_a)); + if (stop_p) + break; + father = ALLOCNO_LOOP_TREE_NODE (first)->father; + ira_assert (father != NULL); + first = father->regno_allocno_map [ALLOCNO_REGNO (a)]; + ira_assert (first != NULL); + if (first == dominator_a) + break; + } + } + ALLOCNO_COPIES (a) = NULL; + regno_top_level_allocno_map [REGNO (ALLOCNO_REG (a))] = a; + } + } + /* Fix final allocnos attributes concerning calls. */ + compress_calls (); + for (i = 0; i < allocnos_num; i++) + { + a = allocnos [i]; + if (a != regno_top_level_allocno_map [REGNO (ALLOCNO_REG (a))] + || ALLOCNO_CAP_MEMBER (a) != NULL) + continue; + ALLOCNO_CALLS_CROSSED_START (a) = 0; + ALLOCNO_CALLS_CROSSED_NUM (a) + = VEC_length (rtx, regno_calls [REGNO (ALLOCNO_REG (a))]); + } + /* Mark copies for removing and change allocnos in copies. */ + for (i = 0; i < copies_num; i++) + { + cp = copies [i]; + if (ALLOCNO_CAP_MEMBER (cp->first) != NULL + || ALLOCNO_CAP_MEMBER (cp->second) != NULL) + { + if (internal_flag_ira_verbose > 3 && ira_dump_file != NULL) + fprintf + (ira_dump_file, " Remove cp%d:%c%dr%d-%c%dr%d\n", + cp->num, ALLOCNO_CAP_MEMBER (cp->first) != NULL ? 'c' : 'a', + ALLOCNO_NUM (cp->first), REGNO (ALLOCNO_REG (cp->first)), + ALLOCNO_CAP_MEMBER (cp->second) != NULL ? 'c' : 'a', + ALLOCNO_NUM (cp->second), REGNO (ALLOCNO_REG (cp->second))); + cp->loop_tree_node = NULL; + continue; + } + first = regno_top_level_allocno_map [REGNO (ALLOCNO_REG (cp->first))]; + second = regno_top_level_allocno_map [REGNO (ALLOCNO_REG (cp->second))]; + node = cp->loop_tree_node; + if (node == NULL) + keep_p = TRUE; /* It copy generated in ira-emit.c. */ + else + { + /* Check that the copy was not propagated from level on + which we will have different pseudos. */ + node_first = node->regno_allocno_map [ALLOCNO_REGNO (cp->first)]; + node_second = node->regno_allocno_map [ALLOCNO_REGNO (cp->second)]; + keep_p = ((REGNO (ALLOCNO_REG (first)) + == REGNO (ALLOCNO_REG (node_first))) + && (REGNO (ALLOCNO_REG (second)) + == REGNO (ALLOCNO_REG (node_second)))); + } + if (keep_p) + { + cp->loop_tree_node = ira_loop_tree_root; + cp->first = first; + cp->second = second; + } + else + { + cp->loop_tree_node = NULL; + if (internal_flag_ira_verbose > 3 && ira_dump_file != NULL) + fprintf (ira_dump_file, " Remove cp%d:a%dr%d-a%dr%d\n", + cp->num, ALLOCNO_NUM (cp->first), + REGNO (ALLOCNO_REG (cp->first)), ALLOCNO_NUM (cp->second), + REGNO (ALLOCNO_REG (cp->second))); + } + } + /* Add conflicting allocnos from lower levels. If we have a situation + A1----conflict---B1 + A2----conflict---B2 + + and A1 and A2 will be presented by themselves in final IR and B1 + and B2 will be presented by B1, then we need to check conflict A2 + and B1. + + There is another situation when we should check and add conflicts + too. It should be done when we removed restoring allocno value + at the loop exits because the allocno value is stored in memory + and the value is not changed in the loop. In this case the + allocno lives in the loop and can conflict with allocnos inside + the loop. */ for (i = 0; i < allocnos_num; i++) { a = allocnos [i]; - ira_assert (ALLOCNO_FIRST_COALESCED_ALLOCNO (a) == a - && ALLOCNO_NEXT_COALESCED_ALLOCNO (a) == a); + if (a != regno_top_level_allocno_map [REGNO (ALLOCNO_REG (a))] + || ALLOCNO_CAP_MEMBER (a) != NULL) + continue; + add_conflict_with_underlying_allocnos (a, ALLOCNO_LOOP_TREE_NODE (a), + FALSE); + if ((first = ALLOCNO_MEM_OPTIMIZED_DEST (a)) != NULL) + { + first = regno_top_level_allocno_map [REGNO (ALLOCNO_REG (first))]; + check_and_add_conflicts + (first, ALLOCNO_CONFLICT_ALLOCNO_VEC (a)); + add_conflict_with_underlying_allocnos + (first, ALLOCNO_LOOP_TREE_NODE (a), TRUE); + } + } + /* Change allocnos regno, conflicting allocnos, and range allocnos. */ + for (i = 0; i < allocnos_num; i++) + { + a = allocnos [i]; + if (a != regno_top_level_allocno_map [REGNO (ALLOCNO_REG (a))] + || ALLOCNO_CAP_MEMBER (a) != NULL) + continue; + ALLOCNO_LOOP_TREE_NODE (a) = ira_loop_tree_root; + ALLOCNO_REGNO (a) = REGNO (ALLOCNO_REG (a)); + allocno_vec = ALLOCNO_CONFLICT_ALLOCNO_VEC (a); + for (j = 0; (conflict_a = allocno_vec [j]) != NULL; j++) + allocno_vec [j] + = regno_top_level_allocno_map [REGNO (ALLOCNO_REG (conflict_a))]; + for (r = ALLOCNO_LIVE_RANGES (a); r != NULL; r = r->next) + r->allocno = a; + } + /* Remove allocnos on lower levels of the loop tree and + enumerate allocnos. */ + for (free = 0, i = 0; i < allocnos_num; i++) + { + a = allocnos [i]; + if (ALLOCNO_LOOP_TREE_NODE (a) != ira_loop_tree_root + || ALLOCNO_CAP_MEMBER (a) != NULL) + { + if (internal_flag_ira_verbose > 3 && ira_dump_file != NULL) + fprintf (ira_dump_file, " Remove a%dr%d\n", + ALLOCNO_NUM (a), REGNO (ALLOCNO_REG (a))); + finish_allocno (a); + continue; + } + ALLOCNO_CAP (a) = NULL; + /* Remove conflicts. */ + allocno_vec = ALLOCNO_CONFLICT_ALLOCNO_VEC (a); + for (k = j = ALLOCNO_CONFLICT_ALLOCNOS_NUM (a); + (conflict_a = allocno_vec [j]) != NULL; + j++) + { + if (allocno_conflict_p (a, conflict_a)) + allocno_vec [k++] = conflict_a; + else + { + if (internal_flag_ira_verbose > 3 && ira_dump_file != NULL) + fprintf (ira_dump_file, + " Remove conflict a%dr%d - a%dr%d\n", + ALLOCNO_NUM (a), REGNO (ALLOCNO_REG (a)), + ALLOCNO_NUM (conflict_a), + REGNO (ALLOCNO_REG (conflict_a))); + + } + } + allocno_vec [k] = NULL; + ALLOCNO_TOTAL_CONFLICT_ALLOCNOS_NUM (a) = k; + if (i == free) + { + free++; + continue; + } + if (internal_flag_ira_verbose > 3 && ira_dump_file != NULL) + fprintf (ira_dump_file, " Enumerate a%dr%d to a%d\n", + ALLOCNO_NUM (a), REGNO (ALLOCNO_REG (a)), free); + ALLOCNO_NUM (a) = free; + allocnos [free++] = a; + } + for (i = free; i < allocnos_num; i++) + VARRAY_POP (allocno_varray); + allocnos = (allocno_t *) &VARRAY_GENERIC_PTR (allocno_varray, 0); + allocnos_num = VARRAY_ACTIVE_SIZE (allocno_varray); + /* Remove unnecessary copies, and enumerate copies. */ + for (free = i = 0; i < copies_num; i++) + { + cp = copies [i]; + if (cp->loop_tree_node == NULL) + { + finish_copy (cp); + continue; + } + ira_assert + (ALLOCNO_LOOP_TREE_NODE (cp->first) == ira_loop_tree_root + && ALLOCNO_LOOP_TREE_NODE (cp->second) == ira_loop_tree_root); + add_allocno_copy_to_list (cp); + swap_allocno_copy_ends_if_necessary (cp); + if (i == free) + { + free++; + continue; + } + if (internal_flag_ira_verbose > 3 && ira_dump_file != NULL) + fprintf (ira_dump_file, + " Enumerate cp%d to cp%d\n", cp->num, free); + cp->num = free; + copies [free++] = cp; + } + for (i = free; i < copies_num; i++) + VARRAY_POP (copy_varray); + copies = (copy_t *) &VARRAY_GENERIC_PTR (copy_varray, 0); + copies_num = VARRAY_ACTIVE_SIZE (copy_varray); + rebuild_regno_allocno_maps (); + rebuild_start_finish_chains (); + ira_free (regno_top_level_allocno_map); + live_allocnos = ira_allocate_bitmap (); + for (i = max_point_before_emit; i < max_point; i++) + { + for (r = start_point_ranges [i]; r != NULL; r = r->start_next) + { + a = r->allocno; + j = ALLOCNO_NUM (a); + EXECUTE_IF_SET_IN_BITMAP (live_allocnos, 0, n, bi) + { + if (n == (unsigned int) j) + continue; + conflict_a = allocnos [n]; + if (ALLOCNO_COVER_CLASS (a) == ALLOCNO_COVER_CLASS (conflict_a)) + { + if (allocno_conflict_index (a, conflict_a) < 0) + { + if (internal_flag_ira_verbose > 3 + && ira_dump_file != NULL) + fprintf + (ira_dump_file, + " Add a%dr%d to conflict vec of a%dr%d\n", + ALLOCNO_NUM (conflict_a), + REGNO (ALLOCNO_REG (conflict_a)), + ALLOCNO_NUM (a), REGNO (ALLOCNO_REG (a))); + add_to_allocno_conflict_vec (a, conflict_a); + } + if (allocno_conflict_index (conflict_a, a) < 0) + { + if (internal_flag_ira_verbose > 3 + && ira_dump_file != NULL) + fprintf + (ira_dump_file, + " Add a%dr%d to conflict vec of a%dr%d\n", + ALLOCNO_NUM (a), REGNO (ALLOCNO_REG (a)), + ALLOCNO_NUM (conflict_a), + REGNO (ALLOCNO_REG (conflict_a))); + add_to_allocno_conflict_vec (conflict_a, a); + } + } + } + bitmap_set_bit (live_allocnos, j); + } + + for (r = finish_point_ranges [i]; r != NULL; r = r->finish_next) + bitmap_clear_bit (live_allocnos, ALLOCNO_NUM (r->allocno)); } + ira_free_bitmap (live_allocnos); } -#endif + + /* This entry function creates internal representation for IRA (allocnos, copies, loop tree nodes). If LOOPS_P is zero the nodes corresponding to the loops (except the root which corresponds the - all function) and correspondingly allocnos for the loops will be not - created (it will be done only for basic blocks). Such value is + all function) and correspondingly allocnos for the loops will be + not created (it will be done only for basic blocks). Such value is used for Chaitin-Briggs and Chow's priority coloring. The function returns nonzero if we generates loop structure (besides node representing all function) for regional allocation. */ @@ -939,15 +1886,41 @@ ira_build (int loops_p) form_loop_tree (); create_allocnos (); ira_costs (); - ira_build_conflicts (); - tune_allocno_costs_and_cover_classes (); if (flag_ira_algorithm == IRA_ALGORITHM_REGIONAL || flag_ira_algorithm == IRA_ALGORITHM_MIXED) { local_allocnos_bitmap = ira_allocate_bitmap (); - traverse_loop_tree (ira_loop_tree_root, NULL, + traverse_loop_tree (FALSE, ira_loop_tree_root, NULL, create_loop_tree_node_caps); ira_free_bitmap (local_allocnos_bitmap); + } + create_allocno_live_ranges (); + ira_build_conflicts (); + if (internal_flag_ira_verbose > 0 && ira_dump_file != NULL) + { + int i, n, nr; + allocno_live_range_t r; + + for (nr = n = i = 0; i < allocnos_num; i++) + n += ALLOCNO_TOTAL_CONFLICT_ALLOCNOS_NUM (allocnos [i]); + for (nr = i = 0; i < allocnos_num; i++) + for (r = ALLOCNO_LIVE_RANGES (allocnos [i]); r != NULL; r = r->next) + nr++; + fprintf (ira_dump_file, " regions=%d, blocks=%d, points=%d\n", + VEC_length (loop_p, ira_loops.larray), n_basic_blocks, + max_point); + fprintf (ira_dump_file, + " allocnos=%d, copies=%d, conflicts=%d, ranges=%d\n", + allocnos_num, copies_num, n, nr); + } + if (flag_ira_algorithm == IRA_ALGORITHM_REGIONAL + || flag_ira_algorithm == IRA_ALGORITHM_MIXED) + traverse_loop_tree (FALSE, ira_loop_tree_root, NULL, + propagate_info_to_loop_tree_node_caps); + tune_allocno_costs_and_cover_classes (); + if (flag_ira_algorithm == IRA_ALGORITHM_REGIONAL + || flag_ira_algorithm == IRA_ALGORITHM_MIXED) + { for (i = 0; VEC_iterate (loop_p, ira_loops.larray, i, loop); i++) if (ira_loop_nodes [i].regno_allocno_map != NULL && ira_loop_tree_root != &ira_loop_nodes [i]) @@ -963,7 +1936,9 @@ ira_destroy (void) basic_block bb; finish_loop_tree_nodes (); +#if 0 ira_assert (current_loops == &ira_loops); +#endif flow_loops_free (&ira_loops); free_dominance_info (CDI_DOMINATORS); FOR_ALL_BB (bb) @@ -972,4 +1947,5 @@ ira_destroy (void) finish_copies (); finish_allocnos (); finish_calls (); + finish_allocno_live_ranges (); } diff --git a/gcc/ira-call.c b/gcc/ira-call.c deleted file mode 100644 index 145d00dd8fb..00000000000 --- a/gcc/ira-call.c +++ /dev/null @@ -1,1070 +0,0 @@ -/* Splitting ranges around calls for IRA. - Copyright (C) 2006, 2007 - Free Software Foundation, Inc. - Contributed by Vladimir Makarov . - -This file is part of GCC. - -GCC is free software; you can redistribute it and/or modify it under -the terms of the GNU General Public License as published by the Free -Software Foundation; either version 2, or (at your option) any later -version. - -GCC is distributed in the hope that it will be useful, but WITHOUT ANY -WARRANTY; without even the implied warranty of MERCHANTABILITY or -FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -for more details. - -You should have received a copy of the GNU General Public License -along with GCC; see the file COPYING. If not, write to the Free -Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA -02110-1301, USA. */ - -#include "config.h" -#include "system.h" -#include "coretypes.h" -#include "tm.h" -#include "hard-reg-set.h" -#include "rtl.h" -#include "tm_p.h" -#include "target.h" -#include "regs.h" -#include "flags.h" -#include "basic-block.h" -#include "toplev.h" -#include "expr.h" -#include "params.h" -#include "reload.h" -#include "df.h" -#include "output.h" -#include "ira-int.h" - -/* The file is responsible for splitting the live range of - pseudo-registers living through calls which are assigned to - call-used hard-registers in two parts: one range (a new - pseudo-register is created for this) which lives through the calls - and another range (the original pseudo-register is used for the - range) lives between the calls. Memory is assigned to new - pseudo-registers. Move instructions connecting the two live ranges - (the original and new pseudo-registers) will be transformed into - load/store instructions in the reload pass. - - This file also does global save/restore code redundancy - elimination. It calculates points to put save/restore instructions - according the following data flow equations: - - SaveOut(b) = intersect (SaveIn(p) - SaveIgnore(pb)) - for each p in pred(b) - - | 0 if depth (b) <= depth (p) - SaveIgnore(pb) = | - | Ref(loop(b)) if depth (b) > depth (p) - - SaveIn(b) = (SaveOut(b) - Kill(b)) U SaveGen(b) - - RestoreIn(b) = intersect (RestoreOut(s) - RestoreIgnore(bs)) - for each s in succ(b) - - | 0 if depth (b) <= depth (s) - RestoreIgnore(bs) = | - | Ref(loop(b))if depth (b) > depth (s) - - RestoreOut(b) = (RestoreIn(b) - Kill(b)) U RestoreGen(b) - - Here, Kill(b) is the set of allocnos referenced in basic block b - and SaveGen(b) and RestoreGen(b) is the set of allocnos which - should be correspondingly saved and restored in basic block b and - which are not referenced correspondingly before the last and after - the first calls they live through in basic block b. SaveIn(b), - SaveOut(b), RestoreIn(b), RestoreOut(b) are allocnos - correspondingly to save and to restore at the start and the end of - basic block b. Save and restore code is not moved to more - frequently executed points (inside loops). The code can be moved - through a loop unless it is referenced in the loop (this set of - allocnos is denoted by Ref(loop)). - - We should put code to save/restore an allocno on an edge (p,s) if - the allocno lives on the edge and the corresponding values of the - sets at end of p and at the start of s are different. In practice, - code unification is done: if the save/restore code should be on all - outgoing edges or all incoming edges, it is placed at the edge - source and destination correspondingly. - - Putting live ranges living through calls into memory means that - some conflicting pseudo-registers (such pseudo-registers should not - live through calls) assigned to memory have a chance to be assigned - to the corresponding call-used hard-register. It is done by - ira-color.c:reassign_conflict_allocnos using simple priority-based - colouring for the conflicting pseudo-registers. The bigger the - live range of pseudo-register living through calls, the better such - a chance is. Therefore, we move spill/restore code as far as - possible inside basic blocks. - - The implementation of save/restore code generation before the - reload pass has several advantages: - - o simpler implementation of sharing stack slots used for spilled - pseudos and for saving pseudo values around calls. Actually, - the same code for sharing stack slots allocated for pseudos is - used in this case. - - o simpler implementation of moving save/restore code to increase - the range of memory pseudo can be stored in. - - o simpler implementation of improving allocation by assigning - hard-registers to spilled pseudos which conflict with new - pseudos living through calls. - - The disadvantage of such an approach is mainly in the reload pass, - whose behavior is hard to predict. If the reload pass decides that - the original pseudos should be spilled, save/restore code will be - transformed into a memory-memory move. To remove such nasty moves, - IRA is trying to use the same stack slot for the two pseudos. It - is achieved using a standard preference technique to use the same - stack slot for pseudos involved in moves. A move between pseudos - assigned to the same memory could be removed by post-reload - optimizations, but it is implemented in the reload pass because, if - it is not done earlier, a hard-register would be required for this - and most probably a pseudo-register would be spilled by the reload - to free the hard-register. - -*/ - -/* ??? TODO: Abnormal edges */ -/* The following structure contains basic block data flow information - used to calculate registers to save restore. */ -struct bb_info -{ - /* Local bb info: */ - /* Registers mentioned in the BB. */ - bitmap kill; - /* Registers needed to be saved and this save not killed (see above) - by an insn in the BB before that. */ - bitmap saveloc; - /* Registers needed to be restored and this restore not killed by an - insn in the BB after that. */ - bitmap restoreloc; - /* Global save and restore info. */ - bitmap savein, saveout, restorein, restoreout; -}; - -/* Macros for accessing data flow information of basic blocks. */ -#define BB_INFO(BB) ((struct bb_info *) (BB)->aux) -#define BB_INFO_BY_INDEX(N) BB_INFO (BASIC_BLOCK(N)) - -/* DF infrastructure data. */ -static struct df_problem problem; -static struct dataflow dflow; - -/* Basic blocks in postorder. */ -static int *postorder; -/* The size of the previous array. */ -static int n_blocks; -/* Bitmap of all basic blocks. */ -static bitmap current_all_blocks; - -static rtx *reg_map; - -/* Numbers of currently live pseudo-registers. */ -static bitmap regs_live; - -/* Numbers of all registers which should be split around calls. */ -static bitmap regs_to_save_restore; - -/* Bitmap used to collect numbers of referenced regs inside a rtx. */ -static bitmap referenced_regs; - -/* Array of bitmaps for each loop node. The bitmap contains numbers - of registers mentioned int the corresponding loop (and all its - subloops). */ -static bitmap *loop_referenced_regs_array; -/* The size of the previous array. */ -static int loop_referenced_regs_array_len; - -/* Bitmaps used for saving intermediate results. */ -static bitmap temp_bitmap; -static bitmap temp_bitmap2; - -/* Set of hard regs (except eliminable ones) currently live (during - scan of all insns). */ -static HARD_REG_SET hard_regs_live; - -/* Allocate and initialize data used for splitting allocnos around - calls. */ -static void -init_ira_call_data (void) -{ - int i; - - postorder = ira_allocate (sizeof (int) * last_basic_block); - current_all_blocks = ira_allocate_bitmap (); - - n_blocks = post_order_compute (postorder, true, false); - - if (n_blocks != n_basic_blocks) - delete_unreachable_blocks (); - - alloc_aux_for_blocks (sizeof (struct bb_info)); - for (i = 0; i < n_blocks; i++) - { - struct bb_info *bb_info; - - bitmap_set_bit (current_all_blocks, postorder [i]); - bb_info = BB_INFO_BY_INDEX (postorder [i]); - bb_info->kill = ira_allocate_bitmap (); - bb_info->saveloc = ira_allocate_bitmap (); - bb_info->restoreloc = ira_allocate_bitmap (); - bb_info->savein = ira_allocate_bitmap (); - bb_info->saveout = ira_allocate_bitmap (); - bb_info->restorein = ira_allocate_bitmap (); - bb_info->restoreout = ira_allocate_bitmap (); - } - - loop_referenced_regs_array_len = VEC_length (loop_p, ira_loops.larray); - loop_referenced_regs_array - = ira_allocate (loop_referenced_regs_array_len * sizeof (bitmap)); - for (i = 0; i < loop_referenced_regs_array_len; i++) - loop_referenced_regs_array [i] = ira_allocate_bitmap (); - - memset (&problem, 0, sizeof (problem)); - memset (&dflow, 0, sizeof (dflow)); - dflow.problem= &problem; - - reg_map = ira_allocate (sizeof (rtx) * max_reg_num ()); - memset (reg_map, 0, sizeof (rtx) * max_reg_num ()); - regs_live = ira_allocate_bitmap (); - referenced_regs = ira_allocate_bitmap (); - regs_to_save_restore = ira_allocate_bitmap (); - temp_bitmap = ira_allocate_bitmap (); - temp_bitmap2 = ira_allocate_bitmap (); -} - -/* Print bitmap B with TITLE to file F. */ -static void -print_bitmap (FILE *f, bitmap b, const char *title) -{ - unsigned int j; - bitmap_iterator bi; - - fprintf (f, "%s:", title); - EXECUTE_IF_SET_IN_BITMAP (b, FIRST_PSEUDO_REGISTER, j, bi) - fprintf (f, " %d", j); - fprintf (f, "\n"); -} - -/* Print data used for splitting allocnos around calls to file F. */ -static void -print_ira_call_data (FILE *f) -{ - int i; - basic_block bb; - - print_bitmap (f, regs_to_save_restore, "to save/restore") ; - for (i = 0; i < loop_referenced_regs_array_len; i++) - { - fprintf (f, "Loop %d -- ", i); - print_bitmap (f, loop_referenced_regs_array [i], "referenced"); - } - FOR_EACH_BB (bb) - { - struct bb_info *bb_info; - - bb_info = BB_INFO (bb); - fprintf (f, "BB %d (loop %d)\n", bb->index, bb->loop_father->num); - print_bitmap (f, bb_info->kill, " kill") ; - print_bitmap (f, bb_info->saveloc, " saveloc") ; - print_bitmap (f, bb_info->restoreloc, " restoreloc") ; - print_bitmap (f, bb_info->savein, " savein") ; - print_bitmap (f, bb_info->saveout, " saveout") ; - print_bitmap (f, bb_info->restorein, " restorein") ; - print_bitmap (f, bb_info->restoreout, " restoreout") ; - } -} - -/* Print data used for splitting allocnos around calls to STDERR. */ -extern void -debug_ira_call_data (void) -{ - print_ira_call_data (stderr); -} - -/* Finish data used for splitting allocnos around calls. */ -static void -finish_ira_call_data (void) -{ - int i; - - ira_free_bitmap (temp_bitmap2); - ira_free_bitmap (temp_bitmap); - ira_free_bitmap (regs_to_save_restore); - ira_free_bitmap (referenced_regs); - ira_free_bitmap (regs_live); - ira_free (reg_map); - for (i = 0; i < loop_referenced_regs_array_len; i++) - ira_free_bitmap (loop_referenced_regs_array [i]); - ira_free (loop_referenced_regs_array); - for (i = 0; i < n_blocks; i++) - { - struct bb_info *bb_info; - - bb_info = BB_INFO_BY_INDEX (postorder [i]); - ira_free_bitmap (bb_info->restoreout); - ira_free_bitmap (bb_info->restorein); - ira_free_bitmap (bb_info->saveout); - ira_free_bitmap (bb_info->savein); - ira_free_bitmap (bb_info->restoreloc); - ira_free_bitmap (bb_info->saveloc); - ira_free_bitmap (bb_info->kill); - } - free_aux_for_blocks (); - ira_free_bitmap (current_all_blocks); - ira_free (postorder); -} - -/* TRUE if insns and new registers are created. */ -static int change_p; - -/* Record all regs that are set in any one insn. Communication from - mark_reg_{store,clobber}. */ -static VEC(rtx, heap) *regs_set; - -/* Handle the case where REG is set by the insn being scanned. Store - a 1 in hard_regs_live or regs_live for this register. - - REG might actually be something other than a register; if so, we do - nothing. - - SETTER is 0 if this register was modified by an auto-increment - (i.e., a REG_INC note was found for it). */ -static void -mark_reg_store (rtx reg, const_rtx setter ATTRIBUTE_UNUSED, - void *data ATTRIBUTE_UNUSED) -{ - int regno; - - if (GET_CODE (reg) == SUBREG) - reg = SUBREG_REG (reg); - - if (! REG_P (reg)) - return; - - VEC_safe_push (rtx, heap, regs_set, reg); - - regno = REGNO (reg); - - if (regno >= FIRST_PSEUDO_REGISTER) - bitmap_set_bit (regs_live, regno); - else if (! TEST_HARD_REG_BIT (no_alloc_regs, regno)) - { - int last = regno + hard_regno_nregs [regno] [GET_MODE (reg)]; - - while (regno < last) - { - if (! TEST_HARD_REG_BIT (eliminable_regset, regno)) - SET_HARD_REG_BIT (hard_regs_live, regno); - regno++; - } - } -} - -/* Like mark_reg_store except notice just CLOBBERs; ignore SETs. */ -static void -mark_reg_clobber (rtx reg, const_rtx setter, void *data) -{ - if (GET_CODE (setter) == CLOBBER) - mark_reg_store (reg, setter, data); -} - -/* Mark REG as being dead (following the insn being scanned now). - Store a 0 in hard_regs_live or regs_live for this register. */ -static void -mark_reg_death (rtx reg) -{ - int regno = REGNO (reg); - - if (regno >= FIRST_PSEUDO_REGISTER) - bitmap_clear_bit (regs_live, regno); - else if (! TEST_HARD_REG_BIT (no_alloc_regs, regno)) - { - int last = regno + hard_regno_nregs [regno] [GET_MODE (reg)]; - - while (regno < last) - { - CLEAR_HARD_REG_BIT (hard_regs_live, regno); - regno++; - } - } -} - -/* The recursive function walks X and records all referenced registers - in REFERENCED_REGS. */ -static void -mark_referenced_regs (rtx x) -{ - enum rtx_code code; - const char *fmt; - int i, j; - - if (x == NULL_RTX) - return; - code = GET_CODE (x); - if (code == SET) - mark_referenced_regs (SET_SRC (x)); - if (code == SET || code == CLOBBER) - { - x = SET_DEST (x); - code = GET_CODE (x); - if ((code == REG && REGNO (x) < FIRST_PSEUDO_REGISTER) - || code == PC || code == CC0) - return; - } - if (code == MEM || code == SUBREG) - { - x = XEXP (x, 0); - code = GET_CODE (x); - } - - if (code == REG) - { - int regno = REGNO (x); - - bitmap_set_bit (referenced_regs, regno); - return; - } - - fmt = GET_RTX_FORMAT (code); - for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) - { - if (fmt[i] == 'e') - mark_referenced_regs (XEXP (x, i)); - else if (fmt[i] == 'E') - for (j = XVECLEN (x, i) - 1; j >= 0; j--) - mark_referenced_regs (XVECEXP (x, i, j)); - } -} - -/* The function sets up REFERENCED_REGS for rtx X and all their - equivalences. */ -static void -mark_all_referenced_regs (rtx x) -{ - rtx list, note; - unsigned int j; - bitmap_iterator bi; - - mark_referenced_regs (x); - bitmap_copy (temp_bitmap, referenced_regs); - bitmap_copy (temp_bitmap2, referenced_regs); - for (;;) - { - bitmap_clear (referenced_regs); - EXECUTE_IF_SET_IN_BITMAP (temp_bitmap2, FIRST_PSEUDO_REGISTER, j, bi) - if (j < (unsigned) ira_max_regno_before) - for (list = reg_equiv_init [j]; - list != NULL_RTX; - list = XEXP (list, 1)) - { - note = find_reg_note (XEXP (list, 0), REG_EQUIV, NULL_RTX); - - if (note == NULL_RTX) - continue; - - mark_referenced_regs (XEXP (note, 0)); - } - bitmap_and_compl (temp_bitmap2, temp_bitmap, referenced_regs); - if (! bitmap_ior_into (temp_bitmap, referenced_regs)) - break; - } - bitmap_copy (referenced_regs, temp_bitmap); -} - -/* The function emits a new save/restore insn with pattern PATH before - (if BEFORE_P) or after INSN. */ -static void -insert_one_insn (rtx insn, int before_p, rtx pat) -{ - rtx new_insn; - - change_p = TRUE; -#ifdef HAVE_cc0 - /* If INSN references CC0, put our insns in front of the insn that - sets CC0. This is always safe, since the only way we could be - passed an insn that references CC0 is for a restore, and doing a - restore earlier isn't a problem. We do, however, assume here - that CALL_INSNs don't reference CC0. Guard against non-INSN's - like CODE_LABEL. */ - - if ((NONJUMP_INSN_P (insn) || JUMP_P (insn)) - && before_p && reg_referenced_p (cc0_rtx, PATTERN (insn))) - insn = PREV_INSN (insn); -#endif - - if (before_p) - { - new_insn = emit_insn_before (pat, insn); - if (insn == BB_HEAD (BLOCK_FOR_INSN (insn))) - BB_HEAD (BLOCK_FOR_INSN (insn)) = new_insn; - } - else - { - if (GET_CODE (insn) != CODE_LABEL) - new_insn = emit_insn_after (pat, insn); - else - { - /* Put the insn after bb note in a empty basic block. */ - gcc_assert (NEXT_INSN (insn) && NOTE_P (NEXT_INSN (insn))); - new_insn = emit_insn_after (pat, NEXT_INSN (insn)); - } - if (insn == BB_END (BLOCK_FOR_INSN (insn))) - BB_END (BLOCK_FOR_INSN (insn)) = new_insn; - } - if (ira_dump_file != NULL) - fprintf (ira_dump_file, - "Generating save/restore insn %d:%d<-%d in bb %d\n", - INSN_UID (new_insn), REGNO (SET_DEST (pat)), - REGNO (SET_SRC (pat)), BLOCK_FOR_INSN (insn)->index); -} - -/* The function creates a new register (if it is not created yet) and - returns it for allocno with REGNO. */ -static rtx -get_new_reg (unsigned int regno) -{ - rtx reg, newreg; - - reg = regno_reg_rtx [regno]; - if ((newreg = reg_map [regno]) == NULL_RTX) - { - newreg = gen_reg_rtx (GET_MODE (reg)); - REG_USERVAR_P (newreg) = REG_USERVAR_P (reg); - REG_POINTER (newreg) = REG_POINTER (reg); - REG_ATTRS (newreg) = REG_ATTRS (reg); - reg_map [regno] = newreg; - } - return newreg; -} - -/* Return move insn dest<-src. */ -static rtx -get_move_insn (rtx src, rtx dest) -{ - rtx result; - - start_sequence (); - emit_move_insn (src, dest); - result = get_insns (); - end_sequence (); - return result; -} - -/* The function inserts save/restore code which can be placed in any - case inside the BB and caclulate local bb info (kill, saveloc, - restoreloc). */ -static void -put_save_restore_and_calculate_local_info (void) -{ - int i; - unsigned int j; - basic_block bb; - rtx insn, reg, newreg, pat; - bitmap_iterator bi; - bitmap reg_live_in, reg_live_out, saveloc, restoreloc, kill; - - /* Make a vector that mark_reg_{store,clobber} will store in. */ - if (! regs_set) - regs_set = VEC_alloc (rtx, heap, 10); - FOR_EACH_BB (bb) - { - rtx first_insn, last_insn; - struct loop *loop; - struct bb_info *bb_info = BB_INFO (bb); - - saveloc = bb_info->saveloc; - restoreloc = bb_info->restoreloc; - kill = bb_info->kill; - reg_live_in = DF_LR_IN (bb); - reg_live_out = DF_LR_OUT (bb); - REG_SET_TO_HARD_REG_SET (hard_regs_live, reg_live_in); - AND_COMPL_HARD_REG_SET (hard_regs_live, eliminable_regset); - bitmap_copy (regs_live, reg_live_in); - first_insn = last_insn = NULL_RTX; - /* Scan the code of this basic block, noting which regs and hard - regs are born or die. */ - FOR_BB_INSNS (bb, insn) - { - rtx link; - - if (! INSN_P (insn)) - continue; - - if (first_insn == NULL_RTX) - first_insn = insn; - last_insn = insn; - - bitmap_clear (referenced_regs); - mark_all_referenced_regs (insn); - - EXECUTE_IF_SET_IN_BITMAP (restoreloc, FIRST_PSEUDO_REGISTER, j, bi) - if (bitmap_bit_p (referenced_regs, j)) - insert_one_insn (insn, TRUE, - get_move_insn (regno_reg_rtx [j], - get_new_reg (j))); - - bitmap_ior_into (kill, referenced_regs); - bitmap_and_compl_into (restoreloc, referenced_regs); - - /* Check that regs_set is an empty set. */ - gcc_assert (VEC_empty (rtx, regs_set)); - - /* Mark any regs clobbered by INSN as live, so they - conflict with the inputs. */ - note_stores (PATTERN (insn), mark_reg_clobber, NULL); - - /* Mark any regs dead after INSN as dead now. */ - for (link = REG_NOTES (insn); link; link = XEXP (link, 1)) - if (REG_NOTE_KIND (link) == REG_DEAD) - mark_reg_death (XEXP (link, 0)); - - if (CALL_P (insn) && ! find_reg_note (insn, REG_NORETURN, NULL)) - { - EXECUTE_IF_SET_IN_BITMAP (regs_live, FIRST_PSEUDO_REGISTER, - j, bi) - if ((j >= (unsigned) ira_max_regno_before - || (reg_equiv_const [j] == NULL_RTX - && ! reg_equiv_invariant_p [j])) - && reg_renumber [j] >= 0 - && ! hard_reg_not_in_set_p (reg_renumber [j], - GET_MODE (regno_reg_rtx [j]), - call_used_reg_set)) - { - bitmap_set_bit (regs_to_save_restore, j); - if (! bitmap_bit_p (restoreloc, j) - && bitmap_bit_p (kill, j)) - { - for (i = 0; i < (int) VEC_length (rtx, regs_set); i++) - if (REGNO (VEC_index (rtx, regs_set, i)) == j) - break; - if (i < (int) VEC_length (rtx, regs_set)) - continue; - /* Insert save insn */ - reg = regno_reg_rtx [j]; - newreg = get_new_reg (j); - pat = get_move_insn (newreg, reg); - insert_one_insn (insn, TRUE, pat); - } - if (! bitmap_bit_p (kill, j)) - bitmap_set_bit (saveloc, j); - bitmap_set_bit (restoreloc, j); - } - } - - /* Mark any regs set in INSN as live. */ - note_stores (PATTERN (insn), mark_reg_store, NULL); - -#ifdef AUTO_INC_DEC - for (link = REG_NOTES (insn); link; link = XEXP (link, 1)) - if (REG_NOTE_KIND (link) == REG_INC) - mark_reg_store (XEXP (link, 0), NULL_RTX, NULL); -#endif - - /* Mark any regs set in INSN and then never used. */ - while (! VEC_empty (rtx, regs_set)) - { - rtx reg = VEC_pop (rtx, regs_set); - rtx note = find_regno_note (insn, REG_UNUSED, REGNO (reg)); - - if (note) - mark_reg_death (XEXP (note, 0)); - } - } - if (! flag_ira_move_spills) - { - EXECUTE_IF_SET_IN_BITMAP (saveloc, FIRST_PSEUDO_REGISTER, j, bi) - insert_one_insn (first_insn, TRUE, - get_move_insn (get_new_reg (j), - regno_reg_rtx [j])); - EXECUTE_IF_SET_IN_BITMAP (restoreloc, FIRST_PSEUDO_REGISTER, j, bi) - insert_one_insn (last_insn, JUMP_P (last_insn), - get_move_insn (regno_reg_rtx [j], - get_new_reg (j))); - } - for (loop = bb->loop_father; loop != NULL; loop = loop_outer (loop)) - bitmap_ior_into (loop_referenced_regs_array [loop->num], kill); - } -} - - - -/* The function used by the DF equation solver to propagate restore - info through block with BB_INDEX. */ -static bool -save_trans_fun (int bb_index) -{ - struct bb_info *bb_info = BB_INFO_BY_INDEX (bb_index); - bitmap in = bb_info->savein; - bitmap out = bb_info->saveout; - bitmap loc = bb_info->saveloc; - bitmap kill = bb_info->kill; - - return bitmap_ior_and_compl (in, loc, out, kill); -} - -/* The function used by the DF equation solver to set up save info - for a block BB without successors. */ -static void -save_con_fun_0 (basic_block bb) -{ - bitmap_clear (BB_INFO (bb)->saveout); -} - -/* The function used by the DF equation solver to propagate save info - from successor to predecessor on edge E. */ -static void -save_con_fun_n (edge e) -{ - bitmap op1 = BB_INFO (e->src)->saveout; - bitmap op2 = BB_INFO (e->dest)->savein; - - if (e->src->loop_depth > e->dest->loop_depth) - { - bitmap_and_into (op1, op2); - bitmap_and_compl_into - (op1, loop_referenced_regs_array [e->src->loop_father->num]); - } - else - bitmap_and_into (op1, op2); -} - -/* The function calculates savein/saveout sets. */ -static void -calculate_save (void) -{ - basic_block bb; - - /* Initialize relations to find maximal solution. */ - FOR_ALL_BB (bb) - { - bitmap_copy (BB_INFO (bb)->savein, regs_to_save_restore); - bitmap_copy (BB_INFO (bb)->saveout, regs_to_save_restore); - } - df_simple_dataflow (DF_BACKWARD, NULL, save_con_fun_0, save_con_fun_n, - save_trans_fun, current_all_blocks, - df_get_postorder (DF_BACKWARD), - df_get_n_blocks (DF_BACKWARD)); -} - - - -/* The function used by the DF equation solver to propagate restore - info through block with BB_INDEX. */ -static bool -restore_trans_fun (int bb_index) -{ - struct bb_info *bb_info = BB_INFO_BY_INDEX (bb_index); - bitmap in = bb_info->restorein; - bitmap out = bb_info->restoreout; - bitmap loc = bb_info->restoreloc; - bitmap kill = bb_info->kill; - - return bitmap_ior_and_compl (out, loc, in, kill); -} - -/* The function used by the DF equation solver to set up restore info - for a block BB without predecessors. */ -static void -restore_con_fun_0 (basic_block bb) -{ - bitmap_clear (BB_INFO (bb)->restorein); -} - -/* The function used by the DF equation solver to propagate restore - info from predecessor to successor on edge E. */ -static void -restore_con_fun_n (edge e) -{ - bitmap op1 = BB_INFO (e->dest)->restorein; - bitmap op2 = BB_INFO (e->src)->restoreout; - - if (e->dest->loop_depth > e->src->loop_depth) - { - bitmap_and_into (op1, op2); - bitmap_and_compl_into - (op1, loop_referenced_regs_array [e->dest->loop_father->num]); - } - else - bitmap_and_into (op1, op2); -} - -/* The function calculates restorein/restoreout sets. */ -static void -calculate_restore (void) -{ - basic_block bb; - - /* Initialize relations to find maximal solution. */ - FOR_ALL_BB (bb) - { - bitmap_copy (BB_INFO (bb)->restoreout, regs_to_save_restore); - bitmap_copy (BB_INFO (bb)->restorein, regs_to_save_restore); - } - df_simple_dataflow (DF_FORWARD, NULL, restore_con_fun_0, restore_con_fun_n, - restore_trans_fun, current_all_blocks, - df_get_postorder (DF_FORWARD), - df_get_n_blocks (DF_FORWARD)); -} - - - -/* The function put save/restores insn according to the calculated - equation. The function even puts the insns inside blocks to - increase live range of allocnos which will be in memory. */ -static void -put_save_restore (void) -{ - basic_block bb; - edge e; - edge_iterator ei; - unsigned int j; - bitmap_iterator bi; - rtx pat; - int first_p; - bitmap save_at_end, restore_at_start, progress; - - save_at_end = ira_allocate_bitmap (); - restore_at_start = ira_allocate_bitmap (); - progress = ira_allocate_bitmap (); - FOR_EACH_BB (bb) - { - struct bb_info *bb_info = BB_INFO (bb); - bitmap kill = bb_info->kill; - bitmap restoreout = bb_info->restoreout; - bitmap saveout = bb_info->saveout; - bitmap savein = bb_info->savein; - bitmap restorein = bb_info->restorein; - bitmap live_at_start; - rtx bb_head, bb_end; - rtx insn, next_insn; - - for (bb_head = BB_HEAD (bb); - bb_head != BB_END (bb) && ! INSN_P (bb_head); - bb_head = NEXT_INSN (bb_head)) - ; - for (bb_end = BB_END (bb); - bb_end != BB_HEAD (bb) && ! INSN_P (bb_end); - bb_end = PREV_INSN (bb_end)) - ; - - bitmap_clear (save_at_end); - first_p = TRUE; - FOR_EACH_EDGE (e, ei, bb->succs) - { - bitmap savein = BB_INFO (e->dest)->savein; - - live_at_start = DF_LR_IN (e->dest); - /* (savein - restoreout) ^ (kill U ! saveout) == - ^ live_at_start == - (savein - restoreout) ^ live_at_start ^ kill - U (savein - restoreout) ^ live_at_start - saveout - */ - bitmap_and_compl (temp_bitmap2, savein, restoreout); - bitmap_and_into (temp_bitmap2, live_at_start); - bitmap_and (temp_bitmap, temp_bitmap2, kill); - bitmap_ior_and_compl_into (temp_bitmap, temp_bitmap2, saveout); - if (first_p) - { - bitmap_copy (save_at_end, temp_bitmap); - first_p = FALSE; - } - else - bitmap_and_into (save_at_end, temp_bitmap); - } - - bitmap_copy (progress, save_at_end); - for (insn = BB_END (bb); - insn != PREV_INSN (BB_HEAD (bb)); - insn = next_insn) - { - next_insn = PREV_INSN (insn); - if (! INSN_P (insn)) - continue; - bitmap_clear (referenced_regs); - mark_all_referenced_regs (insn); - EXECUTE_IF_SET_IN_BITMAP (referenced_regs, FIRST_PSEUDO_REGISTER, - j, bi) - if (bitmap_bit_p (progress, j)) - { - pat = get_move_insn (get_new_reg (j), regno_reg_rtx [j]); - insert_one_insn (insn, JUMP_P (insn), pat); - bitmap_clear_bit (progress, j); - } - } - /* When we don't move info inside the loop, progress can - be not empty here. */ - EXECUTE_IF_SET_IN_BITMAP (progress, FIRST_PSEUDO_REGISTER, j, bi) - { - pat = get_move_insn (get_new_reg (j), regno_reg_rtx [j]); - insert_one_insn (bb_head, TRUE, pat); - } - - bitmap_clear (restore_at_start); - first_p = TRUE; - live_at_start = DF_LR_IN (bb); - FOR_EACH_EDGE (e, ei, bb->preds) - { - bitmap restoreout = BB_INFO (e->src)->restoreout; - - /* (restoreout - savein) ^ (kill U ! restorein) - ^ live_at_start == - (((restoreout - savein) ^ live_at_start) ^ kill - U ((restoreout - savein) ^ live_at_start) - restorein) - */ - bitmap_and_compl (temp_bitmap2, restoreout, savein); - bitmap_and_into (temp_bitmap2, live_at_start); - bitmap_and (temp_bitmap, temp_bitmap2, kill); - bitmap_ior_and_compl_into (temp_bitmap, temp_bitmap2, restorein); - if (first_p) - { - bitmap_copy (restore_at_start, temp_bitmap); - first_p = FALSE; - } - else - bitmap_and_into (restore_at_start, temp_bitmap); - } - - bitmap_copy (progress, restore_at_start); - for (insn = BB_HEAD (bb); - insn != NEXT_INSN (BB_END (bb)); - insn = next_insn) - { - next_insn = NEXT_INSN (insn); - if (! INSN_P (insn)) - continue; - bitmap_clear (referenced_regs); - mark_all_referenced_regs (insn); - EXECUTE_IF_SET_IN_BITMAP (referenced_regs, FIRST_PSEUDO_REGISTER, - j, bi) - if (bitmap_bit_p (progress, j)) - { - pat = get_move_insn (regno_reg_rtx [j], get_new_reg (j)); - insert_one_insn (insn, TRUE, pat); - bitmap_clear_bit (progress, j); - } - } - /* When we don't move info inside the loop, progress can - be not empty here. */ - EXECUTE_IF_SET_IN_BITMAP (progress, FIRST_PSEUDO_REGISTER, j, bi) - { - pat = get_move_insn (regno_reg_rtx [j], get_new_reg (j)); - insert_one_insn (bb_end, JUMP_P (bb_end), pat); - } - - FOR_EACH_EDGE (e, ei, bb->succs) - { - bitmap savein = BB_INFO (e->dest)->savein; - - live_at_start = DF_LR_IN (e->dest); - EXECUTE_IF_SET_IN_BITMAP (savein, FIRST_PSEUDO_REGISTER, j, bi) - if (! bitmap_bit_p (restoreout, j) - && (bitmap_bit_p (kill, j) - || ! bitmap_bit_p (saveout, j)) - && ! bitmap_bit_p (save_at_end, j) - && bitmap_bit_p (live_at_start, j)) - { - pat = get_move_insn (get_new_reg (j), - regno_reg_rtx [j]); - insert_insn_on_edge (pat, e); - change_p = TRUE; - if (ira_dump_file != NULL) - fprintf - (ira_dump_file, - "Generating save/restore insn %d<-%d on edge %d->%d\n", - REGNO (SET_DEST (pat)), REGNO (SET_SRC (pat)), - e->src->index, e->dest->index); - } - } - - live_at_start = DF_LR_IN (bb); - FOR_EACH_EDGE (e, ei, bb->preds) - { - bitmap restoreout = BB_INFO (e->src)->restoreout; - - EXECUTE_IF_SET_IN_BITMAP (restoreout, FIRST_PSEUDO_REGISTER, j, bi) - if (! bitmap_bit_p (savein, j) - && (bitmap_bit_p (kill, j) - || ! bitmap_bit_p (restorein, j)) - && ! bitmap_bit_p (restore_at_start, j) - && bitmap_bit_p (live_at_start, j)) - { - pat = get_move_insn (regno_reg_rtx [j], - get_new_reg (j)); - insert_insn_on_edge (pat, e); - change_p = TRUE; - if (ira_dump_file != NULL) - fprintf - (ira_dump_file, - "Generating save/restore insn %d<-%d on edge %d->%d\n", - REGNO (SET_DEST (pat)), REGNO (SET_SRC (pat)), - e->src->index, e->dest->index); - } - } - } - ira_free_bitmap (progress); - ira_free_bitmap (save_at_end); - ira_free_bitmap (restore_at_start); -} - -/* The function splits allocnos living through calls and assigned to a - call used register. If FLAG_IRA_MOVE_SPILLS, the function moves - save/restore insns to correspondingly to the top and bottom of the - CFG but not moving them to more frequently executed places. */ -int -split_around_calls (void) -{ - change_p = FALSE; - init_ira_call_data (); - put_save_restore_and_calculate_local_info (); - if (flag_ira_move_spills) - { - calculate_save (); - calculate_restore (); - put_save_restore (); - } - finish_ira_call_data (); - fixup_abnormal_edges (); - commit_edge_insertions (); - return change_p; -} - - - -/* The function returns regno of living through call allocno which is - result of splitting allocno with ORIGINAL_REGNO. If there is no - such regno, the function returns -1. */ -int -get_around_calls_regno (int original_regno) -{ - allocno_t a, another_a; - copy_t cp, next_cp; - - if (original_regno >= ira_max_regno_call_before) - return -1; - a = regno_allocno_map [original_regno]; - for (cp = ALLOCNO_COPIES (a); cp != NULL; cp = next_cp) - { - if (cp->first == a) - { - another_a = cp->second; - next_cp = cp->next_first_allocno_copy; - } - else - { - another_a = cp->first; - next_cp = cp->next_second_allocno_copy; - } - if (cp->move_insn == NULL_RTX) - continue; - if (ALLOCNO_REGNO (another_a) >= ira_max_regno_call_before) - return ALLOCNO_REGNO (another_a); - } - return -1; -} diff --git a/gcc/ira-color.c b/gcc/ira-color.c index 06b896e1c04..58564b28cde 100644 --- a/gcc/ira-color.c +++ b/gcc/ira-color.c @@ -41,7 +41,7 @@ Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA #include "df.h" #include "ira-int.h" -/* We use optimistic colouring with optional biased colouring. */ +/* We use optimistic colouring. */ static void update_copy_costs (allocno_t, int); static int assign_hard_reg (allocno_t, int); @@ -53,19 +53,24 @@ static void push_allocno_to_stack (allocno_t); static void remove_allocno_from_bucket_and_push (allocno_t, int); static void push_only_colorable (void); static void push_allocno_to_spill (allocno_t); -static int loop_edge_freq (struct ira_loop_tree_node *, int, int); static int calculate_allocno_spill_cost (allocno_t); static void push_allocnos_to_stack (void); static void pop_allocnos_from_stack (void); static void setup_allocno_available_regs_num (allocno_t); static void setup_allocno_left_conflicts_num (allocno_t); static void put_allocno_into_bucket (allocno_t); +static int copy_freq_compare_func (const void *, const void *); +static void merge_allocnos (allocno_t, allocno_t); +static int coalesced_allocno_conflict_p (allocno_t, allocno_t); +static void coalesce_allocnos (void); static void color_allocnos (void); -static void print_loop_title (struct ira_loop_tree_node *); -static void color_pass (struct ira_loop_tree_node *); +static void print_loop_title (loop_tree_node_t); +static void color_pass (loop_tree_node_t); static int allocno_priority_compare_func (const void *, const void *); +static void finish_allocno_priorities (void); static void priority_coloring (void); +static void start_allocno_priorities (allocno_t *, int); static void do_coloring (void); static void move_spill_restore (void); @@ -79,8 +84,9 @@ static bitmap coloring_allocno_bitmap; allocnos. */ static bitmap consideration_allocno_bitmap; -/* ??? */ -static bitmap processed_allocno_bitmap; +/* Bitmap used to prevent a repeated allocno processing because of + coalescing. */ +static bitmap processed_coalesced_allocno_bitmap; /* All allocnos sorted accoring their priorities. */ static allocno_t *sorted_allocnos; @@ -136,8 +142,8 @@ update_copy_costs (allocno_t allocno, int decr_p) [ALLOCNO_COVER_CLASS (another_allocno)] [class]); if (decr_p) cost = -cost; - ALLOCNO_CURR_HARD_REG_COSTS (another_allocno) [i] += cp->freq * cost; - ALLOCNO_CURR_CONFLICT_HARD_REG_COSTS (another_allocno) [i] + ALLOCNO_UPDATED_HARD_REG_COSTS (another_allocno) [i] += cp->freq * cost; + ALLOCNO_UPDATED_CONFLICT_HARD_REG_COSTS (another_allocno) [i] += cp->freq * cost; } } @@ -148,12 +154,12 @@ static int allocno_cost_compare_func (const void *v1p, const void *v2p) { allocno_t p1 = *(const allocno_t *) v1p, p2 = *(const allocno_t *) v2p; - int cost1, cost2; + int c1, c2; - cost1 = ALLOCNO_MEMORY_COST (p1) - ALLOCNO_COVER_CLASS_COST (p1); - cost2 = ALLOCNO_MEMORY_COST (p2) - ALLOCNO_COVER_CLASS_COST (p2); - if (cost1 - cost2) - return cost1 - cost2; + c1 = ALLOCNO_UPDATED_MEMORY_COST (p1) - ALLOCNO_COVER_CLASS_COST (p1); + c2 = ALLOCNO_UPDATED_MEMORY_COST (p2) - ALLOCNO_COVER_CLASS_COST (p2); + if (c1 - c2) + return c1 - c2; /* If regs are equally good, sort by allocnos, so that the results of qsort leave nothing to chance. */ @@ -185,18 +191,18 @@ static varray_type allocno_stack_varray; static int assign_hard_reg (allocno_t allocno, int retry_p) { - HARD_REG_SET conflicting_regs, biased_regs; + HARD_REG_SET conflicting_regs; int i, j, hard_regno, best_hard_regno, class_size; int cost, mem_cost, min_cost, full_cost, min_full_cost, add_cost; - int costs [FIRST_PSEUDO_REGISTER], *a_costs; + int *a_costs; int *conflict_costs; enum reg_class cover_class, class; enum machine_mode mode; - allocno_t a, conflict_allocno, conflict_allocno2; - allocno_t *allocno_vec, *allocno_vec2; + allocno_t a, conflict_allocno; + allocno_t *allocno_vec; allocno_t another_allocno; copy_t cp, next_cp; - static int full_costs [FIRST_PSEUDO_REGISTER]; + static int costs [FIRST_PSEUDO_REGISTER], full_costs [FIRST_PSEUDO_REGISTER]; #ifdef STACK_REGS int no_stack_reg_p; #endif @@ -204,14 +210,15 @@ assign_hard_reg (allocno_t allocno, int retry_p) ira_assert (! ALLOCNO_ASSIGNED_P (allocno)); cover_class = ALLOCNO_COVER_CLASS (allocno); class_size = class_hard_regs_num [cover_class]; - CLEAR_HARD_REG_SET (biased_regs); + mode = ALLOCNO_MODE (allocno); COPY_HARD_REG_SET (conflicting_regs, no_alloc_regs); + IOR_HARD_REG_SET (conflicting_regs, + prohibited_class_mode_regs [cover_class] [mode]); IOR_COMPL_HARD_REG_SET (conflicting_regs, reg_class_contents [cover_class]); best_hard_regno = -1; - mode = ALLOCNO_MODE (allocno); memset (full_costs, 0, sizeof (int) * class_size); mem_cost = 0; - bitmap_clear (processed_allocno_bitmap); + bitmap_clear (processed_coalesced_allocno_bitmap); memset (costs, 0, sizeof (int) * class_size); memset (full_costs, 0, sizeof (int) * class_size); #ifdef STACK_REGS @@ -220,12 +227,13 @@ assign_hard_reg (allocno_t allocno, int retry_p) for (a = ALLOCNO_NEXT_COALESCED_ALLOCNO (allocno);; a = ALLOCNO_NEXT_COALESCED_ALLOCNO (a)) { - mem_cost += ALLOCNO_MEMORY_COST (a); + mem_cost += ALLOCNO_UPDATED_MEMORY_COST (a); allocno_vec = ALLOCNO_CONFLICT_ALLOCNO_VEC (a); - IOR_HARD_REG_SET (conflicting_regs, ALLOCNO_CONFLICT_HARD_REGS (a)); - a_costs = ALLOCNO_CURR_HARD_REG_COSTS (a); + IOR_HARD_REG_SET (conflicting_regs, + ALLOCNO_TOTAL_CONFLICT_HARD_REGS (a)); + a_costs = ALLOCNO_UPDATED_HARD_REG_COSTS (a); #ifdef STACK_REGS - no_stack_reg_p = no_stack_reg_p || ALLOCNO_NO_STACK_REG_P (a); + no_stack_reg_p = no_stack_reg_p || ALLOCNO_TOTAL_NO_STACK_REG_P (a); #endif for (i = 0; i < class_size; i++) { @@ -235,15 +243,14 @@ assign_hard_reg (allocno_t allocno, int retry_p) for (i = 0; (conflict_allocno = allocno_vec [i]) != NULL; i++) /* Reload can give another class so we need to check all allocnos. */ - if (retry_p - || (cover_class == ALLOCNO_COVER_CLASS (conflict_allocno) - && bitmap_bit_p (consideration_allocno_bitmap, - ALLOCNO_NUM (conflict_allocno)))) + if (retry_p || bitmap_bit_p (consideration_allocno_bitmap, + ALLOCNO_NUM (conflict_allocno))) { - if (bitmap_bit_p (processed_allocno_bitmap, + ira_assert (cover_class == ALLOCNO_COVER_CLASS (conflict_allocno)); + if (bitmap_bit_p (processed_coalesced_allocno_bitmap, ALLOCNO_NUM (conflict_allocno))) continue; - bitmap_set_bit (processed_allocno_bitmap, + bitmap_set_bit (processed_coalesced_allocno_bitmap, ALLOCNO_NUM (conflict_allocno)); if (ALLOCNO_ASSIGNED_P (conflict_allocno)) { @@ -263,24 +270,11 @@ assign_hard_reg (allocno_t allocno, int retry_p) else if (! ALLOCNO_MAY_BE_SPILLED_P (conflict_allocno)) { conflict_costs - = ALLOCNO_CURR_CONFLICT_HARD_REG_COSTS (conflict_allocno); + = ALLOCNO_UPDATED_CONFLICT_HARD_REG_COSTS (conflict_allocno); if (conflict_costs != NULL) for (j = class_size - 1; j >= 0; j--) full_costs [j] -= conflict_costs [j]; } - if (retry_p || ! flag_ira_biased_coloring) - continue; - allocno_vec2 = ALLOCNO_CONFLICT_ALLOCNO_VEC (conflict_allocno); - for (j = 0; (conflict_allocno2 = allocno_vec2 [j]) != NULL; j++) - if (cover_class == ALLOCNO_COVER_CLASS (conflict_allocno2) - && ALLOCNO_ASSIGNED_P (conflict_allocno2) - && (hard_regno = ALLOCNO_HARD_REGNO (conflict_allocno2)) >= 0 - && (retry_p - || bitmap_bit_p (consideration_allocno_bitmap, - ALLOCNO_NUM (conflict_allocno2)))) - IOR_HARD_REG_SET (biased_regs, - reg_mode_hard_regset [hard_regno] - [ALLOCNO_MODE (conflict_allocno2)]); } if (a == allocno) break; @@ -306,7 +300,7 @@ assign_hard_reg (allocno_t allocno, int retry_p) || ALLOCNO_ASSIGNED_P (another_allocno)) continue; conflict_costs - = ALLOCNO_CURR_CONFLICT_HARD_REG_COSTS (another_allocno); + = ALLOCNO_UPDATED_CONFLICT_HARD_REG_COSTS (another_allocno); if (conflict_costs != NULL && ! ALLOCNO_MAY_BE_SPILLED_P (another_allocno)) for (j = class_size - 1; j >= 0; j--) @@ -328,37 +322,29 @@ assign_hard_reg (allocno_t allocno, int retry_p) && FIRST_STACK_REG <= hard_regno && hard_regno <= LAST_STACK_REG) continue; #endif - if (TEST_HARD_REG_BIT (prohibited_class_mode_regs - [cover_class] [mode], hard_regno)) + if (! hard_reg_not_in_set_p (hard_regno, mode, conflicting_regs)) continue; - if (hard_reg_not_in_set_p (hard_regno, mode, conflicting_regs)) + cost = costs [i]; + full_cost = full_costs [i]; + if (! allocated_hardreg_p [hard_regno] + && hard_reg_not_in_set_p (hard_regno, mode, call_used_reg_set)) + /* We need to save/restore the register in epilogue/prologue. + Therefore we increase the cost. */ { - cost = costs [i]; - full_cost = full_costs [i]; - if (! allocated_hardreg_p [hard_regno] - && hard_reg_not_in_set_p (hard_regno, mode, call_used_reg_set)) - /* We need to save/restore the register in - epilogue/prologue. Therefore we increase the cost. */ - { - /* ??? If only part is call clobbered. */ - class = REGNO_REG_CLASS (hard_regno); - add_cost = (memory_move_cost [mode] [class] [0] - + memory_move_cost [mode] [class] [1] - 1); - cost += add_cost; - full_cost += add_cost; - } - if (min_cost > cost) - min_cost = cost; - if (min_full_cost > full_cost) - { - min_full_cost = full_cost; - best_hard_regno = hard_regno; - ira_assert (hard_regno >= 0); - } - else if (min_full_cost == full_cost && flag_ira_biased_coloring != 0 - && TEST_HARD_REG_BIT (biased_regs, hard_regno) - && ! TEST_HARD_REG_BIT (biased_regs, best_hard_regno)) - best_hard_regno = hard_regno; + /* ??? If only part is call clobbered. */ + class = REGNO_REG_CLASS (hard_regno); + add_cost = (memory_move_cost [mode] [class] [0] + + memory_move_cost [mode] [class] [1] - 1); + cost += add_cost; + full_cost += add_cost; + } + if (min_cost > cost) + min_cost = cost; + if (min_full_cost > full_cost) + { + min_full_cost = full_cost; + best_hard_regno = hard_regno; + ira_assert (hard_regno >= 0); } } if (min_cost > mem_cost) @@ -382,9 +368,9 @@ assign_hard_reg (allocno_t allocno, int retry_p) ALLOCNO_FIRST_COALESCED_ALLOCNO (a) = a; ALLOCNO_NEXT_COALESCED_ALLOCNO (a) = a; VARRAY_PUSH_GENERIC_PTR (allocno_stack_varray, a); - if (ira_dump_file != NULL) + if (internal_flag_ira_verbose > 3 && ira_dump_file != NULL) { - fprintf (ira_dump_file, " Pushing"); + fprintf (ira_dump_file, " Pushing"); print_coalesced_allocno (a); } } @@ -470,6 +456,10 @@ add_allocno_to_ordered_bucket (allocno_t allocno, allocno_t *bucket_ptr) before != NULL; after = before, before = ALLOCNO_NEXT_BUCKET_ALLOCNO (before)) { + if (ALLOCNO_COVER_CLASS (before) < cover_class) + continue; + if (ALLOCNO_COVER_CLASS (before) > cover_class) + break; get_coalesced_allocnos_best_class_and_freq (before, &best_class_before, &freq_before); if (best_class != best_class_before @@ -528,20 +518,20 @@ push_allocno_to_stack (allocno_t allocno) if (cover_class == NO_REGS) return; size = reg_class_nregs [cover_class] [ALLOCNO_MODE (allocno)]; - bitmap_clear (processed_allocno_bitmap); + bitmap_clear (processed_coalesced_allocno_bitmap); for (a = ALLOCNO_NEXT_COALESCED_ALLOCNO (allocno);; a = ALLOCNO_NEXT_COALESCED_ALLOCNO (a)) { allocno_vec = ALLOCNO_CONFLICT_ALLOCNO_VEC (a); for (i = 0; (conflict_allocno = allocno_vec [i]) != NULL; i++) - if (cover_class == ALLOCNO_COVER_CLASS (conflict_allocno) - && bitmap_bit_p (coloring_allocno_bitmap, - ALLOCNO_NUM (conflict_allocno))) + if (bitmap_bit_p (coloring_allocno_bitmap, + ALLOCNO_NUM (conflict_allocno))) { - if (bitmap_bit_p (processed_allocno_bitmap, + ira_assert (cover_class == ALLOCNO_COVER_CLASS (conflict_allocno)); + if (bitmap_bit_p (processed_coalesced_allocno_bitmap, ALLOCNO_NUM (conflict_allocno))) continue; - bitmap_set_bit (processed_allocno_bitmap, + bitmap_set_bit (processed_coalesced_allocno_bitmap, ALLOCNO_NUM (conflict_allocno)); if (ALLOCNO_IN_GRAPH_P (conflict_allocno) && ! ALLOCNO_ASSIGNED_P (conflict_allocno)) @@ -584,9 +574,9 @@ remove_allocno_from_bucket_and_push (allocno_t allocno, int colorable_p) bucket_ptr = (colorable_p ? &colorable_allocno_bucket : &uncolorable_allocno_bucket); delete_allocno_from_bucket (allocno, bucket_ptr); - if (ira_dump_file != NULL) + if (internal_flag_ira_verbose > 3 && ira_dump_file != NULL) { - fprintf (ira_dump_file, " Pushing"); + fprintf (ira_dump_file, " Pushing"); print_coalesced_allocno (allocno); fprintf (ira_dump_file, "%s\n", colorable_p ? "" : "(potential spill)"); } @@ -609,6 +599,7 @@ remove_allocno_from_bucket_and_push (allocno_t allocno, int colorable_p) static void push_only_colorable (void) { + /* ??? sort here instead of putting it into ordered bucket. */ for (;colorable_allocno_bucket != NULL;) remove_allocno_from_bucket_and_push (colorable_allocno_bucket, TRUE); } @@ -620,16 +611,16 @@ push_allocno_to_spill (allocno_t allocno) { delete_allocno_from_bucket (allocno, &uncolorable_allocno_bucket); ALLOCNO_MAY_BE_SPILLED_P (allocno) = TRUE; - if (ira_dump_file != NULL) - fprintf (ira_dump_file, " Pushing p%d(%d) (potential spill)\n", + if (internal_flag_ira_verbose > 3 && ira_dump_file != NULL) + fprintf (ira_dump_file, " Pushing p%d(%d) (potential spill)\n", ALLOCNO_NUM (allocno), ALLOCNO_REGNO (allocno)); push_allocno_to_stack (allocno); } /* The function returns frequency of exit edges (if EXIT_P) or enter from/to the loop given by its LOOP_NODE. */ -static int -loop_edge_freq (struct ira_loop_tree_node *loop_node, int regno, int exit_p) +int +loop_edge_freq (loop_tree_node_t loop_node, int regno, int exit_p) { int freq, i; edge_iterator ei; @@ -671,10 +662,10 @@ calculate_allocno_spill_cost (allocno_t a) enum machine_mode mode; enum reg_class class; allocno_t father_allocno; - struct ira_loop_tree_node *father_node, *loop_node; + loop_tree_node_t father_node, loop_node; regno = ALLOCNO_REGNO (a); - cost = ALLOCNO_MEMORY_COST (a) - ALLOCNO_COVER_CLASS_COST (a); + cost = ALLOCNO_UPDATED_MEMORY_COST (a) - ALLOCNO_COVER_CLASS_COST (a); if (ALLOCNO_CAP (a) != NULL) return cost; loop_node = ALLOCNO_LOOP_TREE_NODE (a); @@ -838,9 +829,9 @@ pop_allocnos_from_stack (void) allocno = VARRAY_TOP_GENERIC_PTR (allocno_stack_varray); VARRAY_POP (allocno_stack_varray); cover_class = ALLOCNO_COVER_CLASS (allocno); - if (ira_dump_file != NULL) + if (internal_flag_ira_verbose > 3 && ira_dump_file != NULL) { - fprintf (ira_dump_file, " Popping"); + fprintf (ira_dump_file, " Popping"); print_coalesced_allocno (allocno); fprintf (ira_dump_file, " -- "); } @@ -848,18 +839,18 @@ pop_allocnos_from_stack (void) { ALLOCNO_HARD_REGNO (allocno) = -1; ALLOCNO_ASSIGNED_P (allocno) = TRUE; - if (ira_dump_file != NULL) + if (internal_flag_ira_verbose > 3 && ira_dump_file != NULL) fprintf (ira_dump_file, "assign memory\n"); } else if (assign_hard_reg (allocno, FALSE)) { - if (ira_dump_file != NULL) + if (internal_flag_ira_verbose > 3 && ira_dump_file != NULL) fprintf (ira_dump_file, "assign reg %d\n", ALLOCNO_HARD_REGNO (allocno)); } else if (ALLOCNO_ASSIGNED_P (allocno)) { - if (ira_dump_file != NULL) + if (internal_flag_ira_verbose > 3 && ira_dump_file != NULL) fprintf (ira_dump_file, "spill\n"); } ALLOCNO_IN_GRAPH_P (allocno) = TRUE; @@ -884,15 +875,15 @@ setup_allocno_available_regs_num (allocno_t allocno) for (a = ALLOCNO_NEXT_COALESCED_ALLOCNO (allocno);; a = ALLOCNO_NEXT_COALESCED_ALLOCNO (a)) { - IOR_HARD_REG_SET (temp_set, ALLOCNO_CONFLICT_HARD_REGS (a)); + IOR_HARD_REG_SET (temp_set, ALLOCNO_TOTAL_CONFLICT_HARD_REGS (a)); if (a == allocno) break; } for (n = 0, i = class_hard_regs_num [cover_class] - 1; i >= 0; i--) if (TEST_HARD_REG_BIT (temp_set, class_hard_regs [cover_class] [i])) n++; - if (n > 0 && ira_dump_file != NULL) - fprintf (ira_dump_file, "reg %d of %s has %d regs less\n", + if (internal_flag_ira_verbose > 2 && n > 0 && ira_dump_file != NULL) + fprintf (ira_dump_file, " Reg %d of %s has %d regs less\n", ALLOCNO_REGNO (allocno), reg_class_names [cover_class], n); ALLOCNO_AVAILABLE_REGS_NUM (allocno) -= n; } @@ -911,9 +902,9 @@ setup_allocno_left_conflicts_num (allocno_t allocno) hard_regs_num = class_hard_regs_num [cover_class]; if (hard_regs_num != 0) { - memcpy (ALLOCNO_CURR_HARD_REG_COSTS (allocno), + memcpy (ALLOCNO_UPDATED_HARD_REG_COSTS (allocno), ALLOCNO_HARD_REG_COSTS (allocno), sizeof (int) * hard_regs_num); - memcpy (ALLOCNO_CURR_CONFLICT_HARD_REG_COSTS (allocno), + memcpy (ALLOCNO_UPDATED_CONFLICT_HARD_REG_COSTS (allocno), ALLOCNO_CONFLICT_HARD_REG_COSTS (allocno), sizeof (int) * hard_regs_num); } @@ -922,7 +913,7 @@ setup_allocno_left_conflicts_num (allocno_t allocno) for (a = ALLOCNO_NEXT_COALESCED_ALLOCNO (allocno);; a = ALLOCNO_NEXT_COALESCED_ALLOCNO (a)) { - IOR_HARD_REG_SET (temp_set, ALLOCNO_CONFLICT_HARD_REGS (a)); + IOR_HARD_REG_SET (temp_set, ALLOCNO_TOTAL_CONFLICT_HARD_REGS (a)); if (a == allocno) break; } @@ -942,21 +933,22 @@ setup_allocno_left_conflicts_num (allocno_t allocno) } } CLEAR_HARD_REG_SET (temp_set); - bitmap_clear (processed_allocno_bitmap); + bitmap_clear (processed_coalesced_allocno_bitmap); if (cover_class != NO_REGS) for (a = ALLOCNO_NEXT_COALESCED_ALLOCNO (allocno);; a = ALLOCNO_NEXT_COALESCED_ALLOCNO (a)) { allocno_vec = ALLOCNO_CONFLICT_ALLOCNO_VEC (a); for (i = 0; (conflict_allocno = allocno_vec [i]) != NULL; i++) - if (cover_class == ALLOCNO_COVER_CLASS (conflict_allocno) - && bitmap_bit_p (consideration_allocno_bitmap, - ALLOCNO_NUM (conflict_allocno))) + if (bitmap_bit_p (consideration_allocno_bitmap, + ALLOCNO_NUM (conflict_allocno))) { - if (bitmap_bit_p (processed_allocno_bitmap, + ira_assert (cover_class + == ALLOCNO_COVER_CLASS (conflict_allocno)); + if (bitmap_bit_p (processed_coalesced_allocno_bitmap, ALLOCNO_NUM (conflict_allocno))) continue; - bitmap_set_bit (processed_allocno_bitmap, + bitmap_set_bit (processed_coalesced_allocno_bitmap, ALLOCNO_NUM (conflict_allocno)); if (! ALLOCNO_ASSIGNED_P (conflict_allocno)) conflict_allocnos_size @@ -998,9 +990,9 @@ put_allocno_into_bucket (allocno_t allocno) hard_regs_num = class_hard_regs_num [cover_class]; if (hard_regs_num != 0) { - memcpy (ALLOCNO_CURR_HARD_REG_COSTS (allocno), + memcpy (ALLOCNO_UPDATED_HARD_REG_COSTS (allocno), ALLOCNO_HARD_REG_COSTS (allocno), sizeof (int) * hard_regs_num); - memcpy (ALLOCNO_CURR_CONFLICT_HARD_REG_COSTS (allocno), + memcpy (ALLOCNO_UPDATED_CONFLICT_HARD_REG_COSTS (allocno), ALLOCNO_CONFLICT_HARD_REG_COSTS (allocno), sizeof (int) * hard_regs_num); } @@ -1042,6 +1034,7 @@ merge_allocnos (allocno_t a1, allocno_t a2) { allocno_t a, first, last, next; + ira_assert (ALLOCNO_MODE (a1) == ALLOCNO_MODE (a2)); first = ALLOCNO_FIRST_COALESCED_ALLOCNO (a1); if (first == ALLOCNO_FIRST_COALESCED_ALLOCNO (a2)) return; @@ -1062,16 +1055,16 @@ merge_allocnos (allocno_t a1, allocno_t a2) from two sets of coalesced allocnos given by allocnos A1 and A2. */ static int -allocno_conflict_p (allocno_t a1, allocno_t a2) +coalesced_allocno_conflict_p (allocno_t a1, allocno_t a2) { allocno_t a, conflict_allocno, *allocno_vec; int i; - bitmap_clear (processed_allocno_bitmap); + bitmap_clear (processed_coalesced_allocno_bitmap); for (a = ALLOCNO_NEXT_COALESCED_ALLOCNO (a1);; a = ALLOCNO_NEXT_COALESCED_ALLOCNO (a)) { - bitmap_set_bit (processed_allocno_bitmap, ALLOCNO_NUM (a)); + bitmap_set_bit (processed_coalesced_allocno_bitmap, ALLOCNO_NUM (a)); if (a == a1) break; } @@ -1080,7 +1073,7 @@ allocno_conflict_p (allocno_t a1, allocno_t a2) { allocno_vec = ALLOCNO_CONFLICT_ALLOCNO_VEC (a); for (i = 0; (conflict_allocno = allocno_vec [i]) != NULL; i++) - if (bitmap_bit_p (processed_allocno_bitmap, + if (bitmap_bit_p (processed_coalesced_allocno_bitmap, ALLOCNO_NUM (conflict_allocno))) return TRUE; if (a == a2) @@ -1116,11 +1109,7 @@ coalesce_allocnos (void) { next_cp = cp->next_first_allocno_copy; if (ALLOCNO_COVER_CLASS (cp->second) == cover_class - && ALLOCNO_MODE (cp->second) == mode -#if 0 - && (1 || cover_class == SSE_REGS || cover_class == MMX_REGS) -#endif - ) + && ALLOCNO_MODE (cp->second) == mode) sorted_copies [cp_num++] = cp; } else if (cp->second == a) @@ -1135,10 +1124,10 @@ coalesce_allocnos (void) for (i = 0; i < cp_num; i++) { cp = sorted_copies [i]; - if (! allocno_conflict_p (cp->first, cp->second)) + if (! coalesced_allocno_conflict_p (cp->first, cp->second)) { - if (ira_dump_file != NULL) - fprintf (stderr, "Coalescing copy %d (freq=%d)\n", + if (internal_flag_ira_verbose > 3 && ira_dump_file != NULL) + fprintf (ira_dump_file, " Coalescing copy %d (freq=%d)\n", cp->num, cp->freq); merge_allocnos (cp->first, cp->second); i++; @@ -1166,7 +1155,7 @@ color_allocnos (void) unsigned int i; bitmap_iterator bi; - processed_allocno_bitmap = ira_allocate_bitmap (); + processed_coalesced_allocno_bitmap = ira_allocate_bitmap (); if (flag_ira_coalesce) coalesce_allocnos (); /* Put the allocnos into the corresponding buckets. */ @@ -1185,7 +1174,7 @@ color_allocnos (void) ALLOCNO_FIRST_COALESCED_ALLOCNO (a) = a; ALLOCNO_NEXT_COALESCED_ALLOCNO (a) = a; } - ira_free_bitmap (processed_allocno_bitmap); + ira_free_bitmap (processed_coalesced_allocno_bitmap); } @@ -1193,7 +1182,7 @@ color_allocnos (void) /* The function outputs information about the loop given by its LOOP_TREE_NODE. */ static void -print_loop_title (struct ira_loop_tree_node *loop_tree_node) +print_loop_title (loop_tree_node_t loop_tree_node) { unsigned int j; bitmap_iterator bi; @@ -1232,7 +1221,7 @@ print_loop_title (struct ira_loop_tree_node *loop_tree_node) loop (in extreme case it can be all function) given by the corresponding LOOP_TREE_NODE. */ static void -color_pass (struct ira_loop_tree_node *loop_tree_node) +color_pass (loop_tree_node_t loop_tree_node) { int regno, hard_regno, index = -1; int cost, exit_freq, enter_freq; @@ -1241,11 +1230,11 @@ color_pass (struct ira_loop_tree_node *loop_tree_node) enum machine_mode mode; enum reg_class class; allocno_t a, subloop_allocno; - struct ira_loop_tree_node *subloop_node; + loop_tree_node_t subloop_node; if (loop_tree_node->loop == NULL) return; - if (ira_dump_file != NULL) + if (internal_flag_ira_verbose > 1 && ira_dump_file != NULL) print_loop_title (loop_tree_node); bitmap_copy (coloring_allocno_bitmap, loop_tree_node->mentioned_allocnos); @@ -1284,7 +1273,7 @@ color_pass (struct ira_loop_tree_node *loop_tree_node) if (subloop_allocno == NULL) continue; if ((flag_ira_algorithm == IRA_ALGORITHM_MIXED - && subloop_node->reg_pressure [class] + && loop_tree_node->reg_pressure [class] <= available_class_regs [class])) { if (! ALLOCNO_ASSIGNED_P (subloop_allocno)) @@ -1311,7 +1300,7 @@ color_pass (struct ira_loop_tree_node *loop_tree_node) } else if (hard_regno < 0) { - ALLOCNO_MEMORY_COST (subloop_allocno) + ALLOCNO_UPDATED_MEMORY_COST (subloop_allocno) -= ((memory_move_cost [mode] [class] [1] * enter_freq) + (memory_move_cost [mode] [class] [0] * exit_freq)); } @@ -1322,7 +1311,7 @@ color_pass (struct ira_loop_tree_node *loop_tree_node) ALLOCNO_HARD_REG_COSTS (subloop_allocno) [index] -= cost; ALLOCNO_CONFLICT_HARD_REG_COSTS (subloop_allocno) [index] -= cost; - ALLOCNO_MEMORY_COST (subloop_allocno) + ALLOCNO_UPDATED_MEMORY_COST (subloop_allocno) += (memory_move_cost [mode] [class] [0] * enter_freq + memory_move_cost [mode] [class] [1] * exit_freq); if (ALLOCNO_COVER_CLASS_COST (subloop_allocno) @@ -1334,7 +1323,7 @@ color_pass (struct ira_loop_tree_node *loop_tree_node) else { if ((flag_ira_algorithm == IRA_ALGORITHM_MIXED - && subloop_node->reg_pressure [class] + && loop_tree_node->reg_pressure [class] <= available_class_regs [class])) { subloop_allocno = ALLOCNO_CAP_MEMBER (a); @@ -1356,7 +1345,7 @@ color_pass (struct ira_loop_tree_node *loop_tree_node) ALLOCNO_HARD_REG_COSTS (subloop_allocno) [index] -= cost; ALLOCNO_CONFLICT_HARD_REG_COSTS (subloop_allocno) [index] -= cost; - ALLOCNO_MEMORY_COST (subloop_allocno) + ALLOCNO_UPDATED_MEMORY_COST (subloop_allocno) += (memory_move_cost [mode] [class] [0] * enter_freq + memory_move_cost [mode] [class] [1] * exit_freq); if (ALLOCNO_COVER_CLASS_COST (subloop_allocno) @@ -1368,35 +1357,60 @@ color_pass (struct ira_loop_tree_node *loop_tree_node) } } +/* Map: allocno number -> allocno prioirity. */ +static int *allocno_priorities; + +/* Allocate array ALLOCNO_PRIORITIES and set up priorities for N allocnos in + array CONSIDERATION_ALLOCNOS. */ +static void +start_allocno_priorities (allocno_t *consideration_allocnos, int n) +{ + int i, length; + allocno_t a; + allocno_live_range_t r; + + allocno_priorities = ira_allocate (sizeof (int) * allocnos_num); + for (i = 0; i < n; i++) + { + a = consideration_allocnos [i]; + for (length = 0, r = ALLOCNO_LIVE_RANGES (a); r != NULL; r = r->next) + length += r->finish - r->start + 1; + if (length == 0) + { + allocno_priorities [ALLOCNO_NUM (a)] = 0; + continue; + } + ira_assert (length > 0 && ALLOCNO_NREFS (a) > 0); + allocno_priorities [ALLOCNO_NUM (a)] + = (((double) (floor_log2 (ALLOCNO_NREFS (a)) * ALLOCNO_FREQ (a)) + / length) + * (10000 / REG_FREQ_MAX) * PSEUDO_REGNO_SIZE (ALLOCNO_REGNO (a))); + } +} + /* The function is used to sort allocnos according to their priorities which are calculated analogous to ones in file `global.c'. */ static int allocno_priority_compare_func (const void *v1p, const void *v2p) { - allocno_t p1 = *(const allocno_t *) v1p, p2 = *(const allocno_t *) v2p; + allocno_t a1 = *(const allocno_t *) v1p, a2 = *(const allocno_t *) v2p; int pri1, pri2; -#if 0 - pri1 = (((double) (floor_log2 (REG_N_REFS (ALLOCNO_REGNO (p1))) - * ALLOCNO_FREQ (p1)) - / REG_LIVE_LENGTH (ALLOCNO_REGNO (p1))) - * (10000 / REG_FREQ_MAX) * PSEUDO_REGNO_SIZE (ALLOCNO_REGNO (p1))); - pri2 = (((double) (floor_log2 (REG_N_REFS (ALLOCNO_REGNO (p2))) - * ALLOCNO_FREQ (p2)) - / REG_LIVE_LENGTH (ALLOCNO_REGNO (p2))) - * (10000 / REG_FREQ_MAX) * PSEUDO_REGNO_SIZE (ALLOCNO_REGNO (p2))); -#else - pri1 = (ALLOCNO_FREQ (p1) * (10000 / REG_FREQ_MAX) - * PSEUDO_REGNO_SIZE (ALLOCNO_REGNO (p1))); - pri2 = (ALLOCNO_FREQ (p2) * (10000 / REG_FREQ_MAX) - * PSEUDO_REGNO_SIZE (ALLOCNO_REGNO (p2))); -#endif + pri1 = allocno_priorities [ALLOCNO_NUM (a1)]; + pri2 = allocno_priorities [ALLOCNO_NUM (a2)]; if (pri2 - pri1) return pri2 - pri1; /* If regs are equally good, sort by allocnos, so that the results of qsort leave nothing to chance. */ - return ALLOCNO_NUM (p1) - ALLOCNO_NUM (p2); + return ALLOCNO_NUM (a1) - ALLOCNO_NUM (a2); +} + +/* Free ALLOCATE_PRIORITIES. */ +static void +finish_allocno_priorities (void) +{ + ira_free (allocno_priorities); } /* The function implements Chow's prioity-based coloring. */ @@ -1406,7 +1420,7 @@ priority_coloring (void) int i, hard_regs_num; allocno_t a; - processed_allocno_bitmap = ira_allocate_bitmap (); + processed_coalesced_allocno_bitmap = ira_allocate_bitmap (); memcpy (sorted_allocnos, allocnos, allocnos_num * sizeof (allocno_t)); for (i = 0; i < allocnos_num; i++) { @@ -1415,38 +1429,40 @@ priority_coloring (void) hard_regs_num = class_hard_regs_num [ALLOCNO_COVER_CLASS (a)]; if (hard_regs_num == 0) continue; - memcpy (ALLOCNO_CURR_HARD_REG_COSTS (a), + memcpy (ALLOCNO_UPDATED_HARD_REG_COSTS (a), ALLOCNO_HARD_REG_COSTS (a), sizeof (int) * hard_regs_num); - memcpy (ALLOCNO_CURR_CONFLICT_HARD_REG_COSTS (a), + memcpy (ALLOCNO_UPDATED_CONFLICT_HARD_REG_COSTS (a), ALLOCNO_CONFLICT_HARD_REG_COSTS (a), sizeof (int) * hard_regs_num); } bitmap_copy (consideration_allocno_bitmap, coloring_allocno_bitmap); + start_allocno_priorities (sorted_allocnos, allocnos_num); qsort (sorted_allocnos, allocnos_num, sizeof (allocno_t), allocno_priority_compare_func); + finish_allocno_priorities (); for (i = 0; i < allocnos_num; i++) { a = sorted_allocnos [i]; - if (ira_dump_file != NULL) + if (internal_flag_ira_verbose > 3 && ira_dump_file != NULL) { - fprintf (ira_dump_file, " "); + fprintf (ira_dump_file, " "); print_expanded_allocno (a); fprintf (ira_dump_file, " -- "); } if (assign_hard_reg (a, FALSE)) { - if (ira_dump_file != NULL) + if (internal_flag_ira_verbose > 3 && ira_dump_file != NULL) fprintf (ira_dump_file, "assign reg %d\n", ALLOCNO_HARD_REGNO (a)); } else { - if (ira_dump_file != NULL) + if (internal_flag_ira_verbose > 3 && ira_dump_file != NULL) fprintf (ira_dump_file, "spill\n"); } ALLOCNO_IN_GRAPH_P (a) = TRUE; } - ira_free_bitmap (processed_allocno_bitmap); + ira_free_bitmap (processed_coalesced_allocno_bitmap); } /* The function initialized common data for cloring and calls @@ -1462,13 +1478,13 @@ do_coloring (void) priority_coloring (); else { - if (ira_dump_file != NULL) + if (internal_flag_ira_verbose > 0 && ira_dump_file != NULL) fprintf (ira_dump_file, "\n**** Allocnos coloring:\n\n"); - traverse_loop_tree (ira_loop_tree_root, color_pass, NULL); + traverse_loop_tree (FALSE, ira_loop_tree_root, color_pass, NULL); } - if (ira_dump_file != NULL) + if (internal_flag_ira_verbose > 1 && ira_dump_file != NULL) print_disposition (ira_dump_file); ira_free_bitmap (consideration_allocno_bitmap); @@ -1489,12 +1505,12 @@ move_spill_restore (void) enum machine_mode mode; enum reg_class class; allocno_t a, father_allocno, subloop_allocno; - struct ira_loop_tree_node *father, *loop_node, *subloop_node; + loop_tree_node_t father, loop_node, subloop_node; for (;;) { changed_p = FALSE; - if (ira_dump_file != NULL) + if (internal_flag_ira_verbose > 0 && ira_dump_file != NULL) fprintf (ira_dump_file, "New iteration of spill/restore move\n"); for (i = 0; i < allocnos_num; i++) { @@ -1509,8 +1525,7 @@ move_spill_restore (void) class = ALLOCNO_COVER_CLASS (a); index = class_hard_reg_index [class] [hard_regno]; ira_assert (index >= 0); - cost = (ALLOCNO_ORIGINAL_MEMORY_COST (a) - - ALLOCNO_HARD_REG_COSTS (a) [index]); + cost = ALLOCNO_MEMORY_COST (a) - ALLOCNO_HARD_REG_COSTS (a) [index]; for (subloop_node = loop_node->inner; subloop_node != NULL; subloop_node = subloop_node->next) @@ -1520,7 +1535,7 @@ move_spill_restore (void) subloop_allocno = subloop_node->regno_allocno_map [regno]; if (subloop_allocno == NULL) continue; - cost -= (ALLOCNO_ORIGINAL_MEMORY_COST (subloop_allocno) + cost -= (ALLOCNO_MEMORY_COST (subloop_allocno) - ALLOCNO_HARD_REG_COSTS (subloop_allocno) [index]); exit_freq = loop_edge_freq (subloop_node, regno, TRUE); enter_freq = loop_edge_freq (subloop_node, regno, FALSE); @@ -1556,10 +1571,14 @@ move_spill_restore (void) if (cost < 0) { ALLOCNO_HARD_REGNO (a) = -1; - if (ira_dump_file != NULL) - fprintf (ira_dump_file, - "Moving spill/restore for a%dr%d up from loop %d - profit %d\n", - ALLOCNO_NUM (a), regno, loop_node->loop->num, -cost); + if (internal_flag_ira_verbose > 3 && ira_dump_file != NULL) + { + fprintf + (ira_dump_file, + " Moving spill/restore for a%dr%d up from loop %d", + ALLOCNO_NUM (a), regno, loop_node->loop->num); + fprintf (ira_dump_file, " - profit %d\n", -cost); + } changed_p = TRUE; } } @@ -1589,12 +1608,10 @@ setup_curr_costs (allocno_t a) if (hard_regs_num == 0) return; mode = ALLOCNO_MODE (a); - memcpy (ALLOCNO_CURR_HARD_REG_COSTS (a), - ALLOCNO_HARD_REG_COSTS (a), - sizeof (int) * hard_regs_num); - memcpy (ALLOCNO_CURR_CONFLICT_HARD_REG_COSTS (a), - ALLOCNO_CONFLICT_HARD_REG_COSTS (a), - sizeof (int) * hard_regs_num); + memcpy (ALLOCNO_UPDATED_HARD_REG_COSTS (a), + ALLOCNO_HARD_REG_COSTS (a), sizeof (int) * hard_regs_num); + memcpy (ALLOCNO_UPDATED_CONFLICT_HARD_REG_COSTS (a), + ALLOCNO_CONFLICT_HARD_REG_COSTS (a), sizeof (int) * hard_regs_num); for (cp = ALLOCNO_COPIES (a); cp != NULL; cp = next_cp) { if (cp->first == a) @@ -1619,8 +1636,8 @@ setup_curr_costs (allocno_t a) cost = (cp->first == a ? register_move_cost [mode] [class] [cover_class] : register_move_cost [mode] [cover_class] [class]); - ALLOCNO_CURR_HARD_REG_COSTS (a) [i] -= cp->freq * cost; - ALLOCNO_CURR_CONFLICT_HARD_REG_COSTS (a) [i] -= cp->freq * cost; + ALLOCNO_UPDATED_HARD_REG_COSTS (a) [i] -= cp->freq * cost; + ALLOCNO_UPDATED_CONFLICT_HARD_REG_COSTS (a) [i] -= cp->freq * cost; } } @@ -1655,14 +1672,14 @@ reassign_conflict_allocnos (int start_regno, int no_call_cross_p) } bitmap_set_bit (allocnos_to_color, ALLOCNO_NUM (a)); } - if (ALLOCNO_REGNO (a) < start_regno) + if (ALLOCNO_REGNO (a) < start_regno + || (cover_class = ALLOCNO_COVER_CLASS (a)) == NO_REGS) continue; allocno_vec = ALLOCNO_CONFLICT_ALLOCNO_VEC (a); for (j = 0; (conflict_a = allocno_vec [j]) != NULL; j++) { - if ((cover_class = ALLOCNO_COVER_CLASS (conflict_a)) == NO_REGS - || (no_call_cross_p - && ALLOCNO_CALLS_CROSSED_NUM (conflict_a) != 0) + ira_assert (cover_class == ALLOCNO_COVER_CLASS (conflict_a)); + if ((no_call_cross_p && ALLOCNO_CALLS_CROSSED_NUM (conflict_a) != 0) || bitmap_bit_p (allocnos_to_color, ALLOCNO_NUM (conflict_a))) continue; bitmap_set_bit (allocnos_to_color, ALLOCNO_NUM (conflict_a)); @@ -1671,8 +1688,12 @@ reassign_conflict_allocnos (int start_regno, int no_call_cross_p) } ira_free_bitmap (allocnos_to_color); if (allocnos_to_color_num > 1) - qsort (sorted_allocnos, allocnos_to_color_num, sizeof (allocno_t), - allocno_priority_compare_func); + { + start_allocno_priorities (sorted_allocnos, allocnos_to_color_num); + qsort (sorted_allocnos, allocnos_to_color_num, sizeof (allocno_t), + allocno_priority_compare_func); + finish_allocno_priorities (); + } for (i = 0; i < allocnos_to_color_num; i++) { a = sorted_allocnos [i]; @@ -1684,10 +1705,11 @@ reassign_conflict_allocnos (int start_regno, int no_call_cross_p) a = sorted_allocnos [i]; if (assign_hard_reg (a, TRUE)) { - if (ira_dump_file != NULL) - fprintf (ira_dump_file, - "after call opt: assign hard reg %d to reg %d\n", - ALLOCNO_HARD_REGNO (a), ALLOCNO_REGNO (a)); + if (internal_flag_ira_verbose > 3 && ira_dump_file != NULL) + fprintf + (ira_dump_file, + " Secondary allocation: assign hard reg %d to reg %d\n", + ALLOCNO_HARD_REGNO (a), ALLOCNO_REGNO (a)); } } ira_free (sorted_allocnos); @@ -1709,7 +1731,7 @@ mark_allocation_change (int regno) if ((old_hard_regno = ALLOCNO_HARD_REGNO (a)) == hard_regno) return; if (old_hard_regno < 0) - cost = -ALLOCNO_MEMORY_COST (a); + cost = -ALLOCNO_UPDATED_MEMORY_COST (a); else { ira_assert (class_hard_reg_index [cover_class] [old_hard_regno] >= 0); @@ -1720,7 +1742,7 @@ mark_allocation_change (int regno) overall_cost -= cost; ALLOCNO_HARD_REGNO (a) = hard_regno; if (hard_regno < 0) - cost += ALLOCNO_MEMORY_COST (a); + cost += ALLOCNO_UPDATED_MEMORY_COST (a); else if (class_hard_reg_index [cover_class] [hard_regno] >= 0) { cost += (ALLOCNO_HARD_REG_COSTS (a) @@ -1746,13 +1768,13 @@ retry_ira_color (int regno, HARD_REG_SET forbidden_regs) a = regno_allocno_map [regno]; mark_allocation_change (regno); ira_assert (reg_renumber [regno] < 0); - if (ira_dump_file != NULL) - fprintf (ira_dump_file, "spill %d(a%d), cost=%d", regno, ALLOCNO_NUM (a), - ALLOCNO_MEMORY_COST (a) - ALLOCNO_COVER_CLASS_COST (a)); - IOR_HARD_REG_SET (ALLOCNO_CONFLICT_HARD_REGS (a), forbidden_regs); - if ((! flag_caller_saves || flag_ira_split_around_calls) - && ALLOCNO_CALLS_CROSSED_NUM (a) != 0) - IOR_HARD_REG_SET (ALLOCNO_CONFLICT_HARD_REGS (a), call_used_reg_set); + if (internal_flag_ira_verbose > 3 && ira_dump_file != NULL) + fprintf (ira_dump_file, + " Spill %d(a%d), cost=%d", regno, ALLOCNO_NUM (a), + ALLOCNO_UPDATED_MEMORY_COST (a) - ALLOCNO_COVER_CLASS_COST (a)); + IOR_HARD_REG_SET (ALLOCNO_TOTAL_CONFLICT_HARD_REGS (a), forbidden_regs); + if (! flag_caller_saves && ALLOCNO_CALLS_CROSSED_NUM (a) != 0) + IOR_HARD_REG_SET (ALLOCNO_TOTAL_CONFLICT_HARD_REGS (a), call_used_reg_set); ALLOCNO_ASSIGNED_P (a) = FALSE; cover_class = ALLOCNO_COVER_CLASS (a); setup_curr_costs (a); @@ -1762,15 +1784,14 @@ retry_ira_color (int regno, HARD_REG_SET forbidden_regs) if (hard_regno >= 0) { ira_assert (class_hard_reg_index [cover_class] [hard_regno] >= 0); - overall_cost -= (ALLOCNO_MEMORY_COST (a) + overall_cost -= (ALLOCNO_UPDATED_MEMORY_COST (a) - (ALLOCNO_HARD_REG_COSTS (a) - [class_hard_reg_index - [cover_class] [hard_regno]])); + [class_hard_reg_index [cover_class] [hard_regno]])); if (ALLOCNO_CALLS_CROSSED_NUM (a) != 0 && ! hard_reg_not_in_set_p (hard_regno, ALLOCNO_MODE (a), call_used_reg_set)) { - ira_assert (flag_caller_saves && ! flag_ira_split_around_calls); + ira_assert (flag_caller_saves); caller_save_needed = 1; } } @@ -1779,17 +1800,13 @@ retry_ira_color (int regno, HARD_REG_SET forbidden_regs) the hard register, and mark that register live. */ if (reg_renumber[regno] >= 0) { - if (ira_dump_file != NULL) + if (internal_flag_ira_verbose > 3 && ira_dump_file != NULL) fprintf (ira_dump_file, ": reassign to %d", reg_renumber[regno]); SET_REGNO (regno_reg_rtx[regno], reg_renumber[regno]); mark_home_live (regno); } - else if (ira_dump_file != NULL && original_regno_call_crossed_p [regno] - && ! flag_caller_saves && flag_ira_split_around_calls - && get_around_calls_regno (regno) >= 0) - fprintf (ira_dump_file, "+++spilling %d\n", regno); - if (ira_dump_file != NULL) + if (internal_flag_ira_verbose > 3 && ira_dump_file != NULL) fprintf (ira_dump_file, "\n"); } @@ -1881,9 +1898,9 @@ reuse_stack_slot (int regno, unsigned int inherent_size, } if (x) { - if (ira_dump_file) + if (internal_flag_ira_verbose > 3 && ira_dump_file) { - fprintf (ira_dump_file, " Assigning %d slot of", regno); + fprintf (ira_dump_file, " Assigning %d slot of", regno); EXECUTE_IF_SET_IN_BITMAP (&slot->spilled_regs, FIRST_PSEUDO_REGISTER, i, bi) { @@ -1909,8 +1926,8 @@ mark_new_stack_slot (rtx x, int regno, unsigned int total_size) SET_REGNO_REG_SET (&slot->spilled_regs, regno); slot->mem = x; slot->width = total_size; - if (ira_dump_file) - fprintf (ira_dump_file, " Assigning %d a new slot\n", regno); + if (internal_flag_ira_verbose > 3 && ira_dump_file) + fprintf (ira_dump_file, " Assigning %d a new slot\n", regno); } diff --git a/gcc/ira-conflicts.c b/gcc/ira-conflicts.c dissimilarity index 64% index 323ec65f054..ef850376800 100644 --- a/gcc/ira-conflicts.c +++ b/gcc/ira-conflicts.c @@ -1,1619 +1,952 @@ -/* IRA conflict builder. - Copyright (C) 2006, 2007 - Free Software Foundation, Inc. - Contributed by Vladimir Makarov . - -This file is part of GCC. - -GCC is free software; you can redistribute it and/or modify it under -the terms of the GNU General Public License as published by the Free -Software Foundation; either version 2, or (at your option) any later -version. - -GCC is distributed in the hope that it will be useful, but WITHOUT ANY -WARRANTY; without even the implied warranty of MERCHANTABILITY or -FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -for more details. - -You should have received a copy of the GNU General Public License -along with GCC; see the file COPYING. If not, write to the Free -Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA -02110-1301, USA. */ - -#include "config.h" -#include "system.h" -#include "coretypes.h" -#include "tm.h" -#include "regs.h" -#include "rtl.h" -#include "tm_p.h" -#include "target.h" -#include "flags.h" -#include "hard-reg-set.h" -#include "basic-block.h" -#include "insn-config.h" -#include "recog.h" -#include "toplev.h" -#include "params.h" -#include "df.h" -#include "ira-int.h" - -/* The file contains code is analogous to one in global but the code - works on the allocno basis. */ - -static void set_allocno_live (allocno_t); -static void clear_allocno_live (allocno_t); -static void record_regno_conflict (int); -static void mark_reg_store (rtx, const_rtx, void *); -static void mark_reg_clobber (rtx, const_rtx, void *); -static void mark_reg_conflicts (rtx); -static void mark_reg_death (rtx); -static int commutative_constraint_p (const char *); -static int get_dup_num (int, int); -static rtx get_dup (int, int); -static void add_allocno_copy_to_list (copy_t); -static void remove_allocno_copy_from_list (copy_t); -static void swap_allocno_copy_ends_if_necessary (copy_t); -static void add_allocno_copies (rtx); -static enum reg_class single_reg_class (const char *, rtx op, rtx); -static enum reg_class single_reg_operand_class (int); -static void process_single_reg_class_operands (int); -static void process_bb_node_for_conflicts (struct ira_loop_tree_node *); -static void build_conflict_bit_table (void); -static void propagate_allocno_info (allocno_t); -static void propagate_info (void); -static void mirror_conflicts (void); -static void remove_conflict_allocno_copies (void); -static void build_allocno_conflict_vects (void); -static void propagate_modified_regnos (struct ira_loop_tree_node *); -static void print_conflicts (FILE *); - -/* The number of bits in each element of `conflicts' and what - type that element has. We use the largest integer format on the - host machine. */ -#define INT_BITS HOST_BITS_PER_WIDE_INT -#define INT_TYPE HOST_WIDE_INT - -/* Bit mask for allocnos live at current point in the scan. */ -static INT_TYPE *allocnos_live; - -/* The same as previous but as bitmap. */ -static bitmap allocnos_live_bitmap; - -/* Set, clear or test bit number I in R, a bit vector indexed by - allocno number. */ -#define SET_ALLOCNO_CONFLICT_ROW(R, I) \ - ((R)[(unsigned) (I) / INT_BITS] \ - |= ((INT_TYPE) 1 << ((unsigned) (I) % INT_BITS))) - -#define CLEAR_ALLOCNO_CONFLICT_ROW(R, I) \ - ((R) [(unsigned) (I) / INT_BITS] \ - &= ~((INT_TYPE) 1 << ((unsigned) (I) % INT_BITS))) - -#define TEST_ALLOCNO_CONFLICT_ROW(R, I) \ - ((R) [(unsigned) (I) / INT_BITS] \ - & ((INT_TYPE) 1 << ((unsigned) (I) % INT_BITS))) - -/* Set, clear or test bit number I in `allocnos_live', - a bit vector indexed by allocno number. */ -#define SET_ALLOCNO_LIVE(I) SET_ALLOCNO_CONFLICT_ROW (allocnos_live, I) - -#define CLEAR_ALLOCNO_LIVE(I) CLEAR_ALLOCNO_CONFLICT_ROW (allocnos_live, I) - -#define TEST_ALLOCNO_LIVE(I) TEST_ALLOCNO_CONFLICT_ROW (allocnos_live, I) - -/* allocnos_num by allocnos_num array of bits, recording whether two - allocno's conflict (can't go in the same hardware register). - - `conflicts' is symmetric after the call to mirror_conflicts. */ -static INT_TYPE *conflicts; - -/* Number of ints required to hold allocnos_num bits. This is the - length of a row in `conflicts'. */ -static int allocno_row_words; - -/* Two macros to test 1 in an element of `conflicts'. */ -#define CONFLICTP(I, J) \ - (conflicts[(I) * allocno_row_words + (unsigned) (J) / INT_BITS] \ - & ((INT_TYPE) 1 << ((unsigned) (J) % INT_BITS))) - -/* For each allocno set in ALLOCNO_SET, set ALLOCNO to that allocno, and - execute CODE. */ -#define EXECUTE_IF_SET_IN_ALLOCNO_SET(ALLOCNO_SET, ALLOCNO, CODE) \ -do { \ - int i_; \ - int allocno_; \ - INT_TYPE *p_ = (ALLOCNO_SET); \ - \ - for (i_ = allocno_row_words - 1, allocno_ = 0; i_ >= 0; \ - i_--, allocno_ += INT_BITS) \ - { \ - unsigned INT_TYPE word_ = (unsigned INT_TYPE) *p_++; \ - \ - for ((ALLOCNO) = allocno_; word_; word_ >>= 1, (ALLOCNO)++) \ - { \ - if (word_ & 1) \ - {CODE;} \ - } \ - } \ -} while (0) - - -/* Set of hard regs (except eliminable ones) currently live (during - scan of all insns). */ -static HARD_REG_SET hard_regs_live; - -/* Loop tree node corresponding to the current basic block. */ -static struct ira_loop_tree_node *curr_bb_node; - -/* Current map regno -> allocno. */ -static allocno_t *curr_regno_allocno_map; - -/* The current pressure for the current basic block. */ -static int curr_reg_pressure [N_REG_CLASSES]; - -/* The function marks allocno A as currently living. */ -static void -set_allocno_live (allocno_t a) -{ - enum reg_class cover_class; - - if (TEST_ALLOCNO_LIVE (ALLOCNO_NUM (a))) - return; - SET_ALLOCNO_LIVE (ALLOCNO_NUM (a)); - bitmap_set_bit (allocnos_live_bitmap, ALLOCNO_NUM (a)); - cover_class = ALLOCNO_COVER_CLASS (a); - curr_reg_pressure [cover_class] - += reg_class_nregs [cover_class] [ALLOCNO_MODE (a)]; - if (curr_bb_node->reg_pressure [cover_class] - < curr_reg_pressure [cover_class]) - curr_bb_node->reg_pressure [cover_class] - = curr_reg_pressure [cover_class]; -} - -/* The function marks allocno A as currently not living. */ -static void -clear_allocno_live (allocno_t a) -{ - enum reg_class cover_class; - - if (bitmap_bit_p (allocnos_live_bitmap, ALLOCNO_NUM (a))) - { - cover_class = ALLOCNO_COVER_CLASS (a); - curr_reg_pressure [cover_class] - -= reg_class_nregs [cover_class] [ALLOCNO_MODE (a)]; - ira_assert (curr_reg_pressure [cover_class] >= 0); - } - CLEAR_ALLOCNO_LIVE (ALLOCNO_NUM (a)); - bitmap_clear_bit (allocnos_live_bitmap, ALLOCNO_NUM (a)); -} - -/* Record all regs that are set in any one insn. Communication from - mark_reg_{store,clobber} and build_conflict_bit_table. */ -static VEC(rtx, heap) *regs_set; - -/* Record a conflict between hard register REGNO or allocno - corresponding to pseudo-register REGNO and everything currently - live. */ -static void -record_regno_conflict (int regno) -{ - int j; - - if (regno < FIRST_PSEUDO_REGISTER) - { - /* When a hard register becomes live, record conflicts with live - allocno regs. */ - EXECUTE_IF_SET_IN_ALLOCNO_SET (allocnos_live, j, - { - SET_HARD_REG_BIT (ALLOCNO_CONFLICT_HARD_REGS (allocnos [j]), regno); - }); - } - else - { - /* When a pseudo-register becomes live, record conflicts first - with hard regs, then with other allocnos. */ - allocno_t a = curr_regno_allocno_map [regno]; - int pn, pn_prod; - - ira_assert (a != NULL || REG_N_REFS (regno) == 0); - if (a == NULL) - return; - pn = ALLOCNO_NUM (a); - pn_prod = pn * allocno_row_words; - IOR_HARD_REG_SET (ALLOCNO_CONFLICT_HARD_REGS (a), hard_regs_live); - for (j = allocno_row_words - 1; j >= 0; j--) - conflicts [pn_prod + j] |= allocnos_live [j]; - /* Don't set up conflict for the allocno with itself. */ - CLEAR_ALLOCNO_CONFLICT_ROW (conflicts + pn_prod, pn); - } -} - -/* Handle the case where REG is set by the insn being scanned, during - the scan to accumulate conflicts. Store a 1 in hard_regs_live or - allocnos_live for this register or the corresponding allocno, record - how many consecutive hardware registers it actually needs, and - record a conflict with all other allocnos already live. - - Note that even if REG does not remain alive after this insn, we - must mark it here as live, to ensure a conflict between REG and any - other allocnos set in this insn that really do live. This is - because those other allocnos could be considered after this. - - REG might actually be something other than a register; if so, we do - nothing. - - SETTER is 0 if this register was modified by an auto-increment - (i.e., a REG_INC note was found for it). */ -static void -mark_reg_store (rtx reg, const_rtx setter ATTRIBUTE_UNUSED, - void *data ATTRIBUTE_UNUSED) -{ - int regno; - - if (GET_CODE (reg) == SUBREG) - reg = SUBREG_REG (reg); - - if (! REG_P (reg)) - return; - - VEC_safe_push (rtx, heap, regs_set, reg); - - regno = REGNO (reg); - - if (regno >= FIRST_PSEUDO_REGISTER) - { - allocno_t a = curr_regno_allocno_map [regno]; - - ira_assert (a != NULL || REG_N_REFS (regno) == 0); - if (a != NULL) - set_allocno_live (a); - record_regno_conflict (regno); - } - else if (! TEST_HARD_REG_BIT (no_alloc_regs, regno)) - { - int last = regno + hard_regno_nregs [regno] [GET_MODE (reg)]; - enum reg_class cover_class; - - while (regno < last) - { - record_regno_conflict (regno); - if (! TEST_HARD_REG_BIT (eliminable_regset, regno) - && ! TEST_HARD_REG_BIT (hard_regs_live, regno)) - { - cover_class = class_translate [REGNO_REG_CLASS (regno)]; - curr_reg_pressure [cover_class]++; - SET_HARD_REG_BIT (hard_regs_live, regno); - if (curr_bb_node->reg_pressure [cover_class] - < curr_reg_pressure [cover_class]) - curr_bb_node->reg_pressure [cover_class] - = curr_reg_pressure [cover_class]; - } - regno++; - } - } -} - -/* Like mark_reg_store except notice just CLOBBERs; ignore SETs. */ -static void -mark_reg_clobber (rtx reg, const_rtx setter, void *data) -{ - if (GET_CODE (setter) == CLOBBER) - mark_reg_store (reg, setter, data); -} - -/* Record that REG (or the corresponding allocno) has conflicts with - all the allocno currently live. Do not mark REG (or the allocno) - itself as live. */ -static void -mark_reg_conflicts (rtx reg) -{ - int regno; - - if (GET_CODE (reg) == SUBREG) - reg = SUBREG_REG (reg); - - if (! REG_P (reg)) - return; - - regno = REGNO (reg); - - if (regno >= FIRST_PSEUDO_REGISTER) - record_regno_conflict (regno); - else if (! TEST_HARD_REG_BIT (no_alloc_regs, regno)) - { - int last = regno + hard_regno_nregs [regno] [GET_MODE (reg)]; - - while (regno < last) - { - record_regno_conflict (regno); - regno++; - } - } -} - -/* Mark REG (or the corresponding allocno) as being dead (following the - insn being scanned now). Store a 0 in hard_regs_live or - allocnos_live for this register. */ -static void -mark_reg_death (rtx reg) -{ - int regno = REGNO (reg); - - if (regno >= FIRST_PSEUDO_REGISTER) - { - allocno_t a = curr_regno_allocno_map [regno]; - - ira_assert (a != NULL || REG_N_REFS (regno) == 0); - if (a != NULL) - clear_allocno_live (a); - } - else if (! TEST_HARD_REG_BIT (no_alloc_regs, regno)) - { - int last = regno + hard_regno_nregs [regno] [GET_MODE (reg)]; - enum reg_class cover_class; - - while (regno < last) - { - if (TEST_HARD_REG_BIT (hard_regs_live, regno)) - { - cover_class = class_translate [REGNO_REG_CLASS (regno)]; - curr_reg_pressure [cover_class]--; - ira_assert (curr_reg_pressure [cover_class] >= 0); - CLEAR_HARD_REG_BIT (hard_regs_live, regno); - } - regno++; - } - } -} - -/* The function returns nonzero, if the operand constraint STR is - commutative. */ -static int -commutative_constraint_p (const char *str) -{ - int ignore_p; - int c; - - for (ignore_p = FALSE;;) - { - c = *str; - if (c == '\0') - break; - str += CONSTRAINT_LEN (c, str); - if (c == '#') - ignore_p = TRUE; - else if (c == ',') - ignore_p = FALSE; - else if (! ignore_p) - { - if (c == '%') - return TRUE; - } - } - return FALSE; -} - -/* The function returns number of the operand which should be the same - in any case as operand with number OP_NUM. If USE_COMMUT_OP_P is - non-zero, the function makes temporarily commutative operand - exchange before this. The function takes only really possible - alternatives into consideration. */ -static int -get_dup_num (int op_num, int use_commut_op_p) -{ - int curr_alt, c, original, dup; - int ignore_p, commut_op_used_p; - const char *str; - rtx op, equiv_const = NULL_RTX; /* ??? */ - - if (op_num < 0 || recog_data.n_alternatives == 0) - return -1; - op = recog_data.operand [op_num]; - ira_assert (REG_P (op)); - commut_op_used_p = TRUE; - if (use_commut_op_p) - { - if (commutative_constraint_p (recog_data.constraints [op_num])) - op_num++; - else if (op_num > 0 && commutative_constraint_p (recog_data.constraints - [op_num - 1])) - op_num--; - else - commut_op_used_p = FALSE; - } - str = recog_data.constraints [op_num]; - for (ignore_p = FALSE, original = -1, curr_alt = 0;;) - { - c = *str; - if (c == '\0') - break; - if (c == '#') - ignore_p = TRUE; - else if (c == ',') - { - curr_alt++; - ignore_p = FALSE; - } - else if (! ignore_p) - switch (c) - { - case 'X': - return -1; - - case 'i': - if (equiv_const != NULL_RTX && CONSTANT_P (equiv_const)) - return -1; - break; - - case 'n': - if (equiv_const != NULL_RTX - && (GET_CODE (equiv_const) == CONST_INT - || (GET_CODE (equiv_const) == CONST_DOUBLE - && GET_MODE (equiv_const) == VOIDmode))) - return -1; - break; - - case 's': - if (equiv_const != NULL_RTX - && CONSTANT_P (equiv_const) - && GET_CODE (equiv_const) != CONST_INT - && (GET_CODE (equiv_const) != CONST_DOUBLE - || GET_MODE (equiv_const) != VOIDmode)) - return -1; - break; - - case 'I': - case 'J': - case 'K': - case 'L': - case 'M': - case 'N': - case 'O': - case 'P': - if ((equiv_const != NULL_RTX - && GET_CODE (equiv_const) == CONST_INT - && CONST_OK_FOR_CONSTRAINT_P (INTVAL (equiv_const), - c, str))) - return -1; - break; - - case 'E': - case 'F': - if (equiv_const != NULL_RTX - && (GET_CODE (equiv_const) == CONST_DOUBLE - || (GET_CODE (equiv_const) == CONST_VECTOR - && (GET_MODE_CLASS (GET_MODE (equiv_const)) - == MODE_VECTOR_FLOAT)))) - return -1; - break; - - case 'G': - case 'H': - if (equiv_const != NULL_RTX - && GET_CODE (equiv_const) == CONST_DOUBLE - && CONST_DOUBLE_OK_FOR_CONSTRAINT_P (equiv_const, c, str)) - return -1; - break; - - case 'm': - case 'o': - /* Accept a register which might be placed in memory. */ - return -1; - break; - - case 'V': - case '<': - case '>': - break; - - case 'p': - GO_IF_LEGITIMATE_ADDRESS (VOIDmode, op, win_p); - break; - - win_p: - return -1; - - case 'g': - return -1; - - case 'r': - case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': - case 'h': case 'j': case 'k': case 'l': - case 'q': case 't': case 'u': - case 'v': case 'w': case 'x': case 'y': case 'z': - case 'A': case 'B': case 'C': case 'D': - case 'Q': case 'R': case 'S': case 'T': case 'U': - case 'W': case 'Y': case 'Z': - { - enum reg_class cl; - - cl = (c == 'r' - ? GENERAL_REGS : REG_CLASS_FROM_CONSTRAINT (c, str)); - if (cl != NO_REGS) - return -1; -#ifdef EXTRA_CONSTRAINT_STR - else if (EXTRA_CONSTRAINT_STR (op, c, str)) - return -1; -#endif - break; - } - - case '0': case '1': case '2': case '3': case '4': - case '5': case '6': case '7': case '8': case '9': - if (original != -1 && original != c) - return -1; - original = c; - break; - } - str += CONSTRAINT_LEN (c, str); - } - if (original == -1) - return -1; - dup = original - '0'; - if (use_commut_op_p) - { - if (commutative_constraint_p (recog_data.constraints [dup])) - dup++; - else if (dup > 0 - && commutative_constraint_p (recog_data.constraints [dup -1])) - dup--; - else if (! commut_op_used_p) - return -1; - } - return dup; -} - -/* The function returns operand which should be in any case the same - as operand with number OP_NUM. If USE_COMMUT_OP_P is non-zero, the - function makes temporarily commutative operand exchange before - this. */ -static rtx -get_dup (int op_num, int use_commut_op_p) -{ - int n = get_dup_num (op_num, use_commut_op_p); - - if (n < 0) - return NULL_RTX; - else - return recog_data.operand [n]; -} - -/* The function attaches copy CP to allocnos involved into the copy. */ -static void -add_allocno_copy_to_list (copy_t cp) -{ - allocno_t first = cp->first, second = cp->second; - - cp->prev_first_allocno_copy = NULL; - cp->prev_second_allocno_copy = NULL; - cp->next_first_allocno_copy = ALLOCNO_COPIES (first); - if (cp->next_first_allocno_copy != NULL) - { - if (cp->next_first_allocno_copy->first == first) - cp->next_first_allocno_copy->prev_first_allocno_copy = cp; - else - cp->next_first_allocno_copy->prev_second_allocno_copy = cp; - } - cp->next_second_allocno_copy = ALLOCNO_COPIES (second); - if (cp->next_second_allocno_copy != NULL) - { - if (cp->next_second_allocno_copy->second == second) - cp->next_second_allocno_copy->prev_second_allocno_copy = cp; - else - cp->next_second_allocno_copy->prev_first_allocno_copy = cp; - } - ALLOCNO_COPIES (first) = cp; - ALLOCNO_COPIES (second) = cp; -} - -/* The function detaches copy CP from allocnos involved into the copy. */ -static void -remove_allocno_copy_from_list (copy_t cp) -{ - allocno_t first = cp->first, second = cp->second; - copy_t prev, next; - - next = cp->next_first_allocno_copy; - prev = cp->prev_first_allocno_copy; - if (prev == NULL) - ALLOCNO_COPIES (first) = next; - else if (prev->first == first) - prev->next_first_allocno_copy = next; - else - prev->next_second_allocno_copy = next; - if (next != NULL) - { - if (next->first == first) - next->prev_first_allocno_copy = prev; - else - next->prev_second_allocno_copy = prev; - } - cp->prev_first_allocno_copy = cp->next_first_allocno_copy = NULL; - - next = cp->next_second_allocno_copy; - prev = cp->prev_second_allocno_copy; - if (prev == NULL) - ALLOCNO_COPIES (second) = next; - else if (prev->second == second) - prev->next_second_allocno_copy = next; - else - prev->next_first_allocno_copy = next; - if (next != NULL) - { - if (next->second == second) - next->prev_second_allocno_copy = prev; - else - next->prev_first_allocno_copy = prev; - } - cp->prev_second_allocno_copy = cp->next_second_allocno_copy = NULL; -} - -/* The function makes copy CP a canonical copy where number of the - first allocno is less than the second one. */ -static void -swap_allocno_copy_ends_if_necessary (copy_t cp) -{ - allocno_t temp; - copy_t temp_cp; - - if (ALLOCNO_NUM (cp->first) <= ALLOCNO_NUM (cp->second)) - return; - - temp = cp->first; - cp->first = cp->second; - cp->second = temp; - - temp_cp = cp->prev_first_allocno_copy; - cp->prev_first_allocno_copy = cp->prev_second_allocno_copy; - cp->prev_second_allocno_copy = temp_cp; - - temp_cp = cp->next_first_allocno_copy; - cp->next_first_allocno_copy = cp->next_second_allocno_copy; - cp->next_second_allocno_copy = temp_cp; -} - -/* The function creates and returns new copy of allocnos FIRST and - SECOND with frequency FREQ corresponding to move insn INSN (if - any). */ -copy_t -add_allocno_copy (allocno_t first, allocno_t second, int freq, rtx insn) -{ - copy_t cp; - - cp = create_copy (first, second, freq, insn); - ira_assert (first != NULL && second != NULL); - add_allocno_copy_to_list (cp); - swap_allocno_copy_ends_if_necessary (cp); - return cp; -} - -/* The function processes INSN and create allocno copies if - necessary. For example, it might be because INSN is a - pseudo-register move or INSN is two operand insn. */ -static void -add_allocno_copies (rtx insn) -{ - rtx set, operand, dup; - const char *str; - int commut_p, bound_p; - int i, j, freq, hard_regno, cost, index; - copy_t cp; - allocno_t a; - enum reg_class class, cover_class; - enum machine_mode mode; - - freq = REG_FREQ_FROM_BB (BLOCK_FOR_INSN (insn)); - if (freq == 0) - freq = 1; - if ((set = single_set (insn)) != NULL_RTX - && REG_P (SET_DEST (set)) && REG_P (SET_SRC (set)) - && ! side_effects_p (set) - && find_reg_note (insn, REG_DEAD, SET_SRC (set)) != NULL_RTX - && find_reg_note (insn, REG_RETVAL, NULL_RTX) == NULL_RTX) - { - if (HARD_REGISTER_P (SET_SRC (set)) || HARD_REGISTER_P (SET_DEST (set))) - { - if (HARD_REGISTER_P (SET_DEST (set))) - { - if (HARD_REGISTER_P (SET_SRC (set))) - return; - hard_regno = REGNO (SET_DEST (set)); - a = curr_regno_allocno_map [REGNO (SET_SRC (set))]; - } - else - { - hard_regno = REGNO (SET_SRC (set)); - a = curr_regno_allocno_map [REGNO (SET_DEST (set))]; - } - class = REGNO_REG_CLASS (hard_regno); - mode = ALLOCNO_MODE (a); - cover_class = ALLOCNO_COVER_CLASS (a); - if (! class_subset_p [class] [cover_class]) - return; - if (reg_class_size [class] - <= (unsigned) CLASS_MAX_NREGS (class, mode)) - /* It is already taken into account in ira-costs.c. */ - return; - index = class_hard_reg_index [cover_class] [hard_regno]; - if (index < 0) - return; - if (HARD_REGISTER_P (SET_DEST (set))) - cost = register_move_cost [mode] [cover_class] [class] * freq; - else - cost = register_move_cost [mode] [class] [cover_class] * freq; - ALLOCNO_HARD_REG_COSTS (a) [index] -= cost; - ALLOCNO_CONFLICT_HARD_REG_COSTS (a) [index] -= cost; - } - else - { - cp = add_allocno_copy (curr_regno_allocno_map [REGNO (SET_DEST (set))], - curr_regno_allocno_map [REGNO (SET_SRC (set))], - freq, insn); - bitmap_set_bit (ira_curr_loop_tree_node->local_copies, cp->num); - } - } - else - { - extract_insn (insn); - for (i = 0; i < recog_data.n_operands; i++) - { - operand = recog_data.operand [i]; - if (REG_P (operand) - && find_reg_note (insn, REG_DEAD, operand) != NULL_RTX) - { - str = recog_data.constraints [i]; - while (*str == ' ' && *str == '\t') - str++; - bound_p = FALSE; - for (j = 0, commut_p = FALSE; j < 2; j++, commut_p = TRUE) - if ((dup = get_dup (i, commut_p)) != NULL_RTX - && REG_P (dup) && GET_MODE (operand) == GET_MODE (dup)) - { - if (HARD_REGISTER_NUM_P (REGNO (operand)) - || HARD_REGISTER_NUM_P (REGNO (dup))) - { - if (HARD_REGISTER_P (operand)) - { - if (HARD_REGISTER_P (dup)) - continue; - hard_regno = REGNO (operand); - a = curr_regno_allocno_map [REGNO (dup)]; - } - else - { - hard_regno = REGNO (dup); - a = curr_regno_allocno_map [REGNO (operand)]; - } - class = REGNO_REG_CLASS (hard_regno); - mode = ALLOCNO_MODE (a); - cover_class = ALLOCNO_COVER_CLASS (a); - if (! class_subset_p [class] [cover_class]) - continue; - index - = class_hard_reg_index [cover_class] [hard_regno]; - if (index < 0) - continue; - if (HARD_REGISTER_P (operand)) - cost - = register_move_cost [mode] [cover_class] [class]; - else - cost - = register_move_cost [mode] [class] [cover_class]; - cost *= freq; - ALLOCNO_HARD_REG_COSTS (a) [index] -= cost; - ALLOCNO_CONFLICT_HARD_REG_COSTS (a) [index] -= cost; - bound_p = TRUE; - } - else - { - bound_p = TRUE; - cp = add_allocno_copy - (curr_regno_allocno_map [REGNO (dup)], - curr_regno_allocno_map [REGNO (operand)], - freq, NULL_RTX); - bitmap_set_bit - (ira_curr_loop_tree_node->local_copies, cp->num); - } - } - if (bound_p) - continue; - /* If an operand dies, prefer its hard register for the - output operands by decreasing the hard register cost - or creating the corresponding allocno copies. */ - for (j = 0; j < recog_data.n_operands; j++) - { - dup = recog_data.operand [j]; - - if (i == j || recog_data.operand_type [j] != OP_OUT - || !REG_P (dup)) - continue; - - if (HARD_REGISTER_NUM_P (REGNO (operand)) - || HARD_REGISTER_NUM_P (REGNO (dup))) - { - if (HARD_REGISTER_P (operand)) - { - if (HARD_REGISTER_P (dup)) - continue; - hard_regno = REGNO (operand); - a = curr_regno_allocno_map [REGNO (dup)]; - } - else - { - hard_regno = REGNO (dup); - a = curr_regno_allocno_map [REGNO (operand)]; - } - class = REGNO_REG_CLASS (hard_regno); - mode = ALLOCNO_MODE (a); - cover_class = ALLOCNO_COVER_CLASS (a); - if (! class_subset_p [class] [cover_class]) - continue; - index - = class_hard_reg_index [cover_class] [hard_regno]; - if (index < 0) - continue; - if (HARD_REGISTER_P (operand)) - cost - = register_move_cost [mode] [cover_class] [class]; - else - cost - = register_move_cost [mode] [class] [cover_class]; - cost *= (freq < 8 ? 1 : freq / 8); - ALLOCNO_HARD_REG_COSTS (a) [index] -= cost; - ALLOCNO_CONFLICT_HARD_REG_COSTS (a) [index] -= cost; - } - else - { - cp = add_allocno_copy - (curr_regno_allocno_map [REGNO (dup)], - curr_regno_allocno_map [REGNO (operand)], - (freq < 8 ? 1 : freq / 8), NULL_RTX); - bitmap_set_bit - (ira_curr_loop_tree_node->local_copies, cp->num); - } - } - } - } - } -} - -/* The function checks that CONSTRAINTS permits to use only one hard - register. If it is so, the function returns the class of the hard - register. Otherwise it returns NO_REGS. - - EQUIV_COSNT ??? */ -static enum reg_class -single_reg_class (const char *constraints, rtx op, rtx equiv_const) -{ - int ignore_p; - enum reg_class cl, next_cl; - int c; - - cl = NO_REGS; - for (ignore_p = FALSE; - (c = *constraints); - constraints += CONSTRAINT_LEN (c, constraints)) - if (c == '#') - ignore_p = TRUE; - else if (c == ',') - ignore_p = FALSE; - else if (! ignore_p) - switch (c) - { - case ' ': - case '\t': - case '=': - case '+': - case '*': - case '&': - case '%': - case '!': - case '?': - break; - case 'i': - if (CONSTANT_P (op) - || (equiv_const != NULL_RTX && CONSTANT_P (equiv_const))) - return NO_REGS; - break; - - case 'n': - if (GET_CODE (op) == CONST_INT - || (GET_CODE (op) == CONST_DOUBLE && GET_MODE (op) == VOIDmode) - || (equiv_const != NULL_RTX - && (GET_CODE (equiv_const) == CONST_INT - || (GET_CODE (equiv_const) == CONST_DOUBLE - && GET_MODE (equiv_const) == VOIDmode)))) - return NO_REGS; - break; - - case 's': - if ((CONSTANT_P (op) && GET_CODE (op) != CONST_INT - && (GET_CODE (op) != CONST_DOUBLE || GET_MODE (op) != VOIDmode)) - || (equiv_const != NULL_RTX - && CONSTANT_P (equiv_const) - && GET_CODE (equiv_const) != CONST_INT - && (GET_CODE (equiv_const) != CONST_DOUBLE - || GET_MODE (equiv_const) != VOIDmode))) - return NO_REGS; - break; - - case 'I': - case 'J': - case 'K': - case 'L': - case 'M': - case 'N': - case 'O': - case 'P': - if ((GET_CODE (op) == CONST_INT - && CONST_OK_FOR_CONSTRAINT_P (INTVAL (op), c, constraints)) - || (equiv_const != NULL_RTX - && GET_CODE (equiv_const) == CONST_INT - && CONST_OK_FOR_CONSTRAINT_P (INTVAL (equiv_const), - c, constraints))) - return NO_REGS; - break; - - case 'E': - case 'F': - if (GET_CODE (op) == CONST_DOUBLE - || (GET_CODE (op) == CONST_VECTOR - && GET_MODE_CLASS (GET_MODE (op)) == MODE_VECTOR_FLOAT) - || (equiv_const != NULL_RTX - && (GET_CODE (equiv_const) == CONST_DOUBLE - || (GET_CODE (equiv_const) == CONST_VECTOR - && (GET_MODE_CLASS (GET_MODE (equiv_const)) - == MODE_VECTOR_FLOAT))))) - return NO_REGS; - break; - - case 'G': - case 'H': - if ((GET_CODE (op) == CONST_DOUBLE - && CONST_DOUBLE_OK_FOR_CONSTRAINT_P (op, c, constraints)) - || (equiv_const != NULL_RTX - && GET_CODE (equiv_const) == CONST_DOUBLE - && CONST_DOUBLE_OK_FOR_CONSTRAINT_P (equiv_const, - c, constraints))) - return NO_REGS; - /* ??? what about memory */ - case 'r': - case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': - case 'h': case 'j': case 'k': case 'l': - case 'q': case 't': case 'u': - case 'v': case 'w': case 'x': case 'y': case 'z': - case 'A': case 'B': case 'C': case 'D': - case 'Q': case 'R': case 'S': case 'T': case 'U': - case 'W': case 'Y': case 'Z': - next_cl = (c == 'r' - ? GENERAL_REGS - : REG_CLASS_FROM_CONSTRAINT (c, constraints)); - if ((cl != NO_REGS && next_cl != cl) - || available_class_regs [next_cl] > 1) - return NO_REGS; - cl = next_cl; - break; - - case '0': case '1': case '2': case '3': case '4': - case '5': case '6': case '7': case '8': case '9': - next_cl - = single_reg_class (recog_data.constraints [c - '0'], - recog_data.operand [c - '0'], NULL_RTX); - if ((cl != NO_REGS && next_cl != cl) || next_cl == NO_REGS - || available_class_regs [next_cl] > 1) - return NO_REGS; - cl = next_cl; - break; - - default: - return NO_REGS; - } - return cl; -} - -/* The function checks that operand OP_NUM of the current insn can use - only one hard register. If it is so, the function returns the - class of the hard register. Otherwise it returns NO_REGS. */ -static enum reg_class -single_reg_operand_class (int op_num) -{ - if (op_num < 0 || recog_data.n_alternatives == 0) - return NO_REGS; - return single_reg_class (recog_data.constraints [op_num], - recog_data.operand [op_num], NULL_RTX); -} - -/* The function processes input (if IN_P) or output operands to find - allocno which can use only one hard register and makes other - currently living allocnos conflicting with the hard register. */ -static void -process_single_reg_class_operands (int in_p) -{ - int i, regno, px; - enum reg_class cl, cover_class; - rtx operand; - allocno_t operand_a, a; - - for (i = 0; i < recog_data.n_operands; i++) - { - operand = recog_data.operand [i]; - if (in_p && recog_data.operand_type [i] != OP_IN - && recog_data.operand_type [i] != OP_INOUT) - continue; - if (! in_p && recog_data.operand_type [i] != OP_OUT - && recog_data.operand_type [i] != OP_INOUT) - continue; - cl = single_reg_operand_class (i); - if (cl == NO_REGS) - continue; - - operand_a = NULL; - - if (GET_CODE (operand) == SUBREG) - operand = SUBREG_REG (operand); - - if (REG_P (operand) - && (regno = REGNO (operand)) >= FIRST_PSEUDO_REGISTER) - { - enum machine_mode mode; - enum reg_class cover_class; - - operand_a = curr_regno_allocno_map [regno]; - mode = ALLOCNO_MODE (operand_a); - cover_class = ALLOCNO_MODE (operand_a); - if (class_subset_p [cl] [cover_class] - && (reg_class_size [cl] - <= (unsigned) CLASS_MAX_NREGS (cl, mode))) - ALLOCNO_CONFLICT_HARD_REG_COSTS (operand_a) - [class_hard_reg_index [cover_class] [class_hard_regs [cl] [0]]] - -= (in_p - ? register_move_cost [mode] [cover_class] [cl] - : register_move_cost [mode] [cl] [cover_class]); - } - - EXECUTE_IF_SET_IN_ALLOCNO_SET (allocnos_live, px, - { - a = allocnos [px]; - cover_class = ALLOCNO_COVER_CLASS (a); - if (a != operand_a) - /* We could increase costs of A instead of making it - conflicting with the hard register. But it works worse - because it will be spilled in reload in anyway. */ - IOR_HARD_REG_SET (ALLOCNO_CONFLICT_HARD_REGS (a), - reg_class_contents [cl]); - }); - } -} - -/* The function processes insns of the basic block given by its - LOOP_TREE_NODE to update allocno conflict table. */ -static void -process_bb_node_for_conflicts (struct ira_loop_tree_node *loop_tree_node) -{ - int i, index; - unsigned int j; - basic_block bb; - rtx insn; - edge e; - edge_iterator ei; - bitmap_iterator bi; - bitmap reg_live_in; - int px = 0; - - bb = loop_tree_node->bb; - if (bb != NULL) - { - for (i = 0; i < reg_class_cover_size; i++) - curr_reg_pressure [reg_class_cover [i]] = 0; - curr_bb_node = loop_tree_node; - curr_regno_allocno_map = ira_curr_loop_tree_node->regno_allocno_map; - reg_live_in = DF_LR_IN (bb); - memset (allocnos_live, 0, allocno_row_words * sizeof (INT_TYPE)); - REG_SET_TO_HARD_REG_SET (hard_regs_live, reg_live_in); - AND_COMPL_HARD_REG_SET (hard_regs_live, eliminable_regset); - for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) - if (TEST_HARD_REG_BIT (hard_regs_live, i)) - { - enum reg_class cover_class; - - cover_class = class_translate [REGNO_REG_CLASS (i)]; - curr_reg_pressure [cover_class]++; - if (curr_bb_node->reg_pressure [cover_class] - < curr_reg_pressure [cover_class]) - curr_bb_node->reg_pressure [cover_class] - = curr_reg_pressure [cover_class]; - } - bitmap_clear (allocnos_live_bitmap); - EXECUTE_IF_SET_IN_BITMAP (reg_live_in, FIRST_PSEUDO_REGISTER, j, bi) - { - allocno_t a = curr_regno_allocno_map [j]; - - ira_assert (a != NULL || REG_N_REFS (j) == 0); - if (a == NULL) - continue; - set_allocno_live (a); - record_regno_conflict (j); - } - -#ifdef EH_RETURN_DATA_REGNO - if (bb_has_eh_pred (bb)) - { - for (j = 0; ; ++j) - { - unsigned int regno = EH_RETURN_DATA_REGNO (j); - - if (regno == INVALID_REGNUM) - break; - record_regno_conflict (regno); - } - } -#endif - - /* Allocnos can't go in stack regs at the start of a basic block - that is reached by an abnormal edge. Likewise for call - clobbered regs, because caller-save, fixup_abnormal_edges and - possibly the table driven EH machinery are not quite ready to - handle such allocnos live across such edges. */ - FOR_EACH_EDGE (e, ei, bb->preds) - if (e->flags & EDGE_ABNORMAL) - break; - - if (e != NULL) - { -#ifdef STACK_REGS - EXECUTE_IF_SET_IN_ALLOCNO_SET (allocnos_live, px, - { - ALLOCNO_NO_STACK_REG_P (allocnos [px]) = TRUE; - }); - for (px = FIRST_STACK_REG; px <= LAST_STACK_REG; px++) - record_regno_conflict (px); -#endif - /* No need to record conflicts for call clobbered regs if we - have nonlocal labels around, as we don't ever try to - allocate such regs in this case. */ - if (! current_function_has_nonlocal_label) - for (px = 0; px < FIRST_PSEUDO_REGISTER; px++) - if (call_used_regs [px]) - record_regno_conflict (px); - } - - /* Scan the code of this basic block, noting which allocnos and - hard regs are born or die. When one is born, record a - conflict with all others currently live. */ - FOR_BB_INSNS (bb, insn) - { - rtx link; - - if (! INSN_P (insn)) - continue; - - /* Check regs_set is an empty set. */ - gcc_assert (VEC_empty (rtx, regs_set)); - - /* Mark any allocnos clobbered by INSN as live, so they - conflict with the inputs. */ - note_stores (PATTERN (insn), mark_reg_clobber, NULL); - - extract_insn (insn); - process_single_reg_class_operands (TRUE); - - /* Mark any allocnos dead after INSN as dead now. */ - for (link = REG_NOTES (insn); link; link = XEXP (link, 1)) - if (REG_NOTE_KIND (link) == REG_DEAD) - mark_reg_death (XEXP (link, 0)); - - if (CALL_P (insn)) - { - HARD_REG_SET clobbered_regs; - - get_call_invalidated_used_regs (insn, &clobbered_regs, FALSE); - IOR_HARD_REG_SET (cfun->emit->call_used_regs, clobbered_regs); - EXECUTE_IF_SET_IN_ALLOCNO_SET (allocnos_live, i, - { - int freq; - allocno_t a = allocnos [i]; - - freq = REG_FREQ_FROM_BB (BLOCK_FOR_INSN (insn)); - if (freq == 0) - freq = 1; - ALLOCNO_CALL_FREQ (a) += freq; - index = add_regno_call (ALLOCNO_REGNO (a), insn); - if (ALLOCNO_CALLS_CROSSED_START (a) < 0) - ALLOCNO_CALLS_CROSSED_START (a) = index; - ALLOCNO_CALLS_CROSSED_NUM (a)++; - /* Don't allocate allocnos that cross calls, if this - function receives a nonlocal goto. */ - if (current_function_has_nonlocal_label) - SET_HARD_REG_SET (ALLOCNO_CONFLICT_HARD_REGS (a)); - }); - } - - /* Mark any allocnos set in INSN as live, and mark them as - conflicting with all other live allocnos. Clobbers are - processed again, so they conflict with the allocnos that - are set. */ - note_stores (PATTERN (insn), mark_reg_store, NULL); - -#ifdef AUTO_INC_DEC - for (link = REG_NOTES (insn); link; link = XEXP (link, 1)) - if (REG_NOTE_KIND (link) == REG_INC) - mark_reg_store (XEXP (link, 0), NULL_RTX, NULL); -#endif - - /* If INSN has multiple outputs, then any allocno that dies - here and is used inside of an output must conflict with - the other outputs. - - It is unsafe to use !single_set here since it will ignore - an unused output. Just because an output is unused does - not mean the compiler can assume the side effect will not - occur. Consider if ALLOCNO appears in the address of an - output and we reload the output. If we allocate ALLOCNO - to the same hard register as an unused output we could - set the hard register before the output reload insn. */ - if (GET_CODE (PATTERN (insn)) == PARALLEL && multiple_sets (insn)) - for (link = REG_NOTES (insn); link; link = XEXP (link, 1)) - if (REG_NOTE_KIND (link) == REG_DEAD) - { - int i; - int used_in_output = 0; - rtx reg = XEXP (link, 0); - - for (i = XVECLEN (PATTERN (insn), 0) - 1; i >= 0; i--) - { - rtx set = XVECEXP (PATTERN (insn), 0, i); - - if (GET_CODE (set) == SET - && ! REG_P (SET_DEST (set)) - && ! rtx_equal_p (reg, SET_DEST (set)) - && reg_overlap_mentioned_p (reg, SET_DEST (set))) - used_in_output = 1; - } - if (used_in_output) - mark_reg_conflicts (reg); - } - - process_single_reg_class_operands (FALSE); - - /* Mark any allocnos set in INSN and then never used. */ - while (! VEC_empty (rtx, regs_set)) - { - rtx reg = VEC_pop (rtx, regs_set); - rtx note = find_regno_note (insn, REG_UNUSED, REGNO (reg)); - - if (note) - mark_reg_death (XEXP (note, 0)); - } - add_allocno_copies (insn); - } - } - /* Propagate register pressure: */ - if (loop_tree_node != ira_loop_tree_root) - for (i = 0; i < reg_class_cover_size; i++) - { - enum reg_class cover_class; - - cover_class = reg_class_cover [i]; - if (loop_tree_node->reg_pressure [cover_class] - > loop_tree_node->father->reg_pressure [cover_class]) - loop_tree_node->father->reg_pressure [cover_class] - = loop_tree_node->reg_pressure [cover_class]; - } -} - -/* The function builds allocno conflict table by traversing all basic - blocks and their insns. */ -static void -build_conflict_bit_table (void) -{ - allocno_row_words = (allocnos_num + INT_BITS - 1) / INT_BITS; - conflicts = ira_allocate (sizeof (INT_TYPE) - * allocnos_num * allocno_row_words); - memset (conflicts, 0, sizeof (INT_TYPE) * allocnos_num * allocno_row_words); - allocnos_live = ira_allocate (sizeof (INT_TYPE) * allocno_row_words); - allocnos_live_bitmap = ira_allocate_bitmap (); - /* Make a vector that mark_reg_{store,clobber} will store in. */ - if (!regs_set) - regs_set = VEC_alloc (rtx, heap, 10); - traverse_loop_tree (ira_loop_tree_root, NULL, process_bb_node_for_conflicts); - /* Clean up. */ - ira_free_bitmap (allocnos_live_bitmap); - ira_free (allocnos_live); -} - -/* The function propagates info about allocno A to the corresponding - allocno on upper loop tree level. So allocnos on upper levels - accumulate information about the corresponding allocnos in nested - loops. */ -static void -propagate_allocno_info (allocno_t a) -{ - int regno, j, pn, father_pn, another_father_pn; - allocno_t father_a, another_a, another_father_a; - struct ira_loop_tree_node *father; - copy_t cp, next_cp; - - regno = ALLOCNO_REGNO (a); - if ((father = ALLOCNO_LOOP_TREE_NODE (a)->father) != NULL - && (father_a = father->regno_allocno_map [regno]) != NULL) - { - ALLOCNO_CALL_FREQ (father_a) += ALLOCNO_CALL_FREQ (a); -#ifdef STACK_REGS - if (ALLOCNO_NO_STACK_REG_P (a)) - ALLOCNO_NO_STACK_REG_P (father_a) = TRUE; -#endif - IOR_HARD_REG_SET (ALLOCNO_CONFLICT_HARD_REGS (father_a), - ALLOCNO_CONFLICT_HARD_REGS (a)); - pn = ALLOCNO_NUM (a); - EXECUTE_IF_SET_IN_ALLOCNO_SET (conflicts + pn * allocno_row_words, j, - { - another_a = allocnos [j]; - if ((another_father_a = (father->regno_allocno_map - [ALLOCNO_REGNO (another_a)])) == NULL) - continue; - father_pn = ALLOCNO_NUM (father_a); - another_father_pn = ALLOCNO_NUM (another_father_a); - SET_ALLOCNO_CONFLICT_ROW - (conflicts + father_pn * allocno_row_words, another_father_pn); - SET_ALLOCNO_CONFLICT_ROW - (conflicts + another_father_pn * allocno_row_words, father_pn); - }); - if (ALLOCNO_CALLS_CROSSED_START (father_a) < 0) - ALLOCNO_CALLS_CROSSED_START (father_a) - = ALLOCNO_CALLS_CROSSED_START (a); - ALLOCNO_CALLS_CROSSED_NUM (father_a) += ALLOCNO_CALLS_CROSSED_NUM (a); - for (cp = ALLOCNO_COPIES (a); cp != NULL; cp = next_cp) - { - if (cp->first == a) - { - next_cp = cp->next_first_allocno_copy; - another_a = cp->second; - } - else if (cp->second == a) - { - next_cp = cp->next_second_allocno_copy; - another_a = cp->first; - } - else - gcc_unreachable (); - if ((another_father_a = (father->regno_allocno_map - [ALLOCNO_REGNO (another_a)])) != NULL) - add_allocno_copy - (father_a, another_father_a, cp->freq, cp->move_insn); - } - } -} - -/* The function propagates info about allocnos to the corresponding - allocnos on upper loop tree level. */ -static void -propagate_info (void) -{ - int i; - allocno_t a; - - for (i = max_reg_num () - 1; i >= FIRST_PSEUDO_REGISTER; i--) - for (a = regno_allocno_map [i]; - a != NULL; - a = ALLOCNO_NEXT_REGNO_ALLOCNO (a)) - propagate_allocno_info (a); -} - -/* If CONFLICTP (i, j) is TRUE, make sure CONFLICTP (j, i) is also TRUE. */ -static void -mirror_conflicts (void) -{ - int i, j; - unsigned INT_TYPE mask; - int rw = allocno_row_words; - int rwb = rw * INT_BITS; - INT_TYPE *p = conflicts; - INT_TYPE *q0 = conflicts; - INT_TYPE *q1, *q2; - - for (i = allocnos_num - 1, mask = 1; i >= 0; i--, mask <<= 1) - { - if (! mask) - { - mask = 1; - q0++; - } - for (j = allocno_row_words - 1, q1 = q0; j >= 0; j--, q1 += rwb) - { - unsigned INT_TYPE word; - - for (word = (unsigned INT_TYPE) *p++, q2 = q1; - word; - word >>= 1, q2 += rw) - { - if (word & 1) - *q2 |= mask; - } - } - } -} - -/* The function returns TRUE if pseudo-registers REGNO1 and REGNO2 - conflict. The function is called from reload. */ -int -allocno_reg_conflict_p (int regno1, int regno2) -{ - int p_no1, p_no2; - - ira_assert (regno1 >= FIRST_PSEUDO_REGISTER - && regno2 >= FIRST_PSEUDO_REGISTER); - /* Reg info caclulated by dataflow infrastructure can be different - from one calculated by regclass. */ - if (curr_regno_allocno_map [regno1] == NULL - || curr_regno_allocno_map [regno2] == NULL) - return FALSE; - p_no1 = ALLOCNO_NUM (curr_regno_allocno_map [regno1]); - p_no2 = ALLOCNO_NUM (curr_regno_allocno_map [regno2]); - ira_assert (p_no1 >= 0 && p_no1 < allocnos_num - && p_no2 >= 0 && p_no2 < allocnos_num); - return CONFLICTP (p_no1, p_no2) != 0; -} - -/* Remove copies involving conflicting allocnos. */ -static void -remove_conflict_allocno_copies (void) -{ - int i; - allocno_t a; - copy_t cp, next_cp; - varray_type conflict_allocno_copy_varray; - - VARRAY_GENERIC_PTR_NOGC_INIT (conflict_allocno_copy_varray, get_max_uid (), - "copies of conflicting allocnos"); - for (i = 0; i < allocnos_num; i++) - { - a = allocnos [i]; - for (cp = ALLOCNO_COPIES (a); cp != NULL; cp = next_cp) - if (cp->first == a) - next_cp = cp->next_first_allocno_copy; - else - { - next_cp = cp->next_second_allocno_copy; - VARRAY_PUSH_GENERIC_PTR (conflict_allocno_copy_varray, cp); - } - } - for (i = VARRAY_ACTIVE_SIZE (conflict_allocno_copy_varray) - 1; i >= 0; i--) - { - cp = VARRAY_GENERIC_PTR (conflict_allocno_copy_varray, i); - if (CONFLICTP (ALLOCNO_NUM (cp->first), ALLOCNO_NUM (cp->second))) - remove_allocno_copy_from_list (cp); - } - VARRAY_FREE (conflict_allocno_copy_varray); -} - -/* The function builds conflict vectors of all allocnos from the - conflict table. */ -static void -build_allocno_conflict_vects (void) -{ - int i, j, px; - allocno_t a, *conflict_allocnos, *vec; - - conflict_allocnos = ira_allocate (sizeof (allocno_t) * allocnos_num); - for (i = 0; i < allocnos_num; i++) - { - a = allocnos [i]; - ira_assert (i == ALLOCNO_NUM (a)); - px = 0; - EXECUTE_IF_SET_IN_ALLOCNO_SET (conflicts + i * allocno_row_words, j, - { - conflict_allocnos [px++] = allocnos [j]; - }); - allocate_allocno_conflicts (a, px); - vec = ALLOCNO_CONFLICT_ALLOCNO_VEC (a); - memcpy (vec, conflict_allocnos, sizeof (allocno_t) * px); - vec [px] = NULL; - ALLOCNO_CONFLICT_ALLOCNO_VEC_ACTIVE_SIZE (a) = px; - } - ira_free (conflict_allocnos); -} - - - -/* The function propagates information about allocnos modified inside - the loops. */ -static void -propagate_modified_regnos (struct ira_loop_tree_node *loop_tree_node) -{ - if (loop_tree_node->bb != NULL || loop_tree_node == ira_loop_tree_root) - return; - bitmap_ior_into (loop_tree_node->father->modified_regnos, - loop_tree_node->modified_regnos); -} - - - -/* The function outputs information about allocno conflicts to FILE. */ -static void -print_conflicts (FILE *file) -{ - int i; - - for (i = 0; i < allocnos_num; i++) - { - int j; - allocno_t a; - basic_block bb; - - a = allocnos [i]; - fprintf (file, ";; a%d(r%d,", ALLOCNO_NUM (a), ALLOCNO_REGNO (a)); - if ((bb = ALLOCNO_LOOP_TREE_NODE (a)->bb) != NULL) - fprintf (file, "b%d", bb->index); - else - fprintf (file, "l%d", ALLOCNO_LOOP_TREE_NODE (a)->loop->num); - fprintf (file, ") conflicts:"); - for (j = 0; j < allocnos_num; j++) - if (CONFLICTP (j, i)) - { - fprintf (file, " a%d(r%d,", - ALLOCNO_NUM (allocnos [j]), ALLOCNO_REGNO (allocnos [j])); - if ((bb = ALLOCNO_LOOP_TREE_NODE (allocnos [j])->bb) != NULL) - fprintf (file, "b%d)", bb->index); - else - fprintf (file, "l%d)", - ALLOCNO_LOOP_TREE_NODE (allocnos [j])->loop->num); - } - fprintf (file, "\n;; conflict hard regs:"); - for (j = 0; j < FIRST_PSEUDO_REGISTER; j++) - if (TEST_HARD_REG_BIT (ALLOCNO_CONFLICT_HARD_REGS (a), j)) - fprintf (file, " %d", j); - fprintf (file, "\n"); - - } - fprintf (file, "\n"); -} - -/* The function outputs information about allocno conflicts to - stderr. */ -void -debug_conflicts (void) -{ - print_conflicts (stderr); -} - - - -/* Entry function which builds allocno conflicts. */ -void -ira_build_conflicts (void) -{ - int i; - allocno_t a; - - build_conflict_bit_table (); - mirror_conflicts (); - if (flag_ira_algorithm == IRA_ALGORITHM_REGIONAL - || flag_ira_algorithm == IRA_ALGORITHM_MIXED) - propagate_info (); - /* We need finished conflict table for the subsequent call. */ - remove_conflict_allocno_copies (); - build_allocno_conflict_vects (); - for (i = 0; i < allocnos_num; i++) - { - a = allocnos [i]; - if (ALLOCNO_CALLS_CROSSED_NUM (a) != 0) - { - if (! flag_caller_saves - || (flag_ira_split_around_calls - && ((ira_max_regno_before > ALLOCNO_REGNO (a) - && (reg_equiv_const [ALLOCNO_REGNO (a)] - || reg_equiv_invariant_p [ALLOCNO_REGNO (a)])) - || (ira_max_regno_before <= ALLOCNO_REGNO (a) - && ALLOCNO_REGNO (a) < ira_max_regno_call_before)))) - IOR_HARD_REG_SET (ALLOCNO_CONFLICT_HARD_REGS (a), - call_used_reg_set); - else - IOR_HARD_REG_SET (ALLOCNO_CONFLICT_HARD_REGS (a), - no_caller_save_reg_set); - } - } - traverse_loop_tree (ira_loop_tree_root, NULL, propagate_modified_regnos); - if (ira_dump_file != NULL) - print_conflicts (ira_dump_file); -} +/* IRA conflict builder. + Copyright (C) 2006, 2007 + Free Software Foundation, Inc. + Contributed by Vladimir Makarov . + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 2, or (at your option) any later +version. + +GCC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING. If not, write to the Free +Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301, USA. */ + +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "tm.h" +#include "regs.h" +#include "rtl.h" +#include "tm_p.h" +#include "target.h" +#include "flags.h" +#include "hard-reg-set.h" +#include "basic-block.h" +#include "insn-config.h" +#include "recog.h" +#include "toplev.h" +#include "params.h" +#include "df.h" +#include "ira-int.h" + +/* The file contains code is analogous to one in global but the code + works on the allocno basis. */ + +static void build_conflict_bit_table (void); +static void mirror_conflicts (void); +static int commutative_constraint_p (const char *); +static int get_dup_num (int, int); +static rtx get_dup (int, int); +static void add_insn_allocno_copies (rtx); +static void add_copies (loop_tree_node_t); +static void propagate_allocno_info (allocno_t); +static void propagate_info (void); +static void remove_conflict_allocno_copies (void); +static void build_allocno_conflict_vects (void); +static void propagate_modified_regnos (loop_tree_node_t); +static void print_hard_reg_set (FILE *, const char *, HARD_REG_SET); +static void print_conflicts (FILE *, int); + +/* Set, clear or test bit number I in `allocnos_live', + a bit vector indexed by allocno number. */ +#define SET_ALLOCNO_LIVE(I) SET_ALLOCNO_SET_BIT (allocnos_live, I) +#define CLEAR_ALLOCNO_LIVE(I) CLEAR_ALLOCNO_SET_BIT (allocnos_live, I) +#define TEST_ALLOCNO_LIVE(I) TEST_ALLOCNO_SET_BIT (allocnos_live, I) + +/* allocnos_num by allocnos_num array of bits, recording whether two + allocno's conflict (can't go in the same hardware register). + + `conflicts' is symmetric after the call to mirror_conflicts. */ +static INT_TYPE *conflicts; + +/* Two macros to test 1 in an element of `conflicts'. */ +#define CONFLICT_P(I, J) \ + (conflicts[(I) * allocno_set_words + (unsigned) (J) / INT_BITS] \ + & ((INT_TYPE) 1 << ((unsigned) (J) % INT_BITS))) + +/* Bit mask for allocnos live at current point in the scan. */ +static INT_TYPE *allocnos_live; + + + +/* The function builds allocno conflict table by processing allocno + live ranges. */ +static void +build_conflict_bit_table (void) +{ + int i, j, pn, pn_prod; + allocno_live_range_t r; + + allocno_set_words = (allocnos_num + INT_BITS - 1) / INT_BITS; + allocnos_live = ira_allocate (sizeof (INT_TYPE) * allocno_set_words); + memset (allocnos_live, 0, sizeof (INT_TYPE) * allocno_set_words); + conflicts = ira_allocate (sizeof (INT_TYPE) + * allocnos_num * allocno_set_words); + memset (conflicts, 0, sizeof (INT_TYPE) * allocnos_num * allocno_set_words); + for (i = 0; i < max_point; i++) + { + for (r = start_point_ranges [i]; r != NULL; r = r->start_next) + { + pn = ALLOCNO_NUM (r->allocno); + SET_ALLOCNO_LIVE (pn); + pn_prod = pn * allocno_set_words; + for (j = allocno_set_words - 1; j >= 0; j--) + conflicts [pn_prod + j] |= allocnos_live [j]; + /* Don't set up conflict for the allocno with itself. */ + CLEAR_ALLOCNO_SET_BIT (conflicts + pn_prod, pn); + } + + for (r = finish_point_ranges [i]; r != NULL; r = r->finish_next) + CLEAR_ALLOCNO_LIVE (ALLOCNO_NUM (r->allocno)); + } +} + +/* If CONFLICT_P (i, j) is TRUE, make sure CONFLICT_P (j, i) is also TRUE. */ +static void +mirror_conflicts (void) +{ + int i, j; + unsigned INT_TYPE mask; + int rw = allocno_set_words; + int rwb = rw * INT_BITS; + INT_TYPE *p = conflicts; + INT_TYPE *q0 = conflicts; + INT_TYPE *q1, *q2; + + for (i = allocnos_num - 1, mask = 1; i >= 0; i--, mask <<= 1) + { + if (! mask) + { + mask = 1; + q0++; + } + for (j = allocno_set_words - 1, q1 = q0; j >= 0; j--, q1 += rwb) + { + unsigned INT_TYPE word; + + for (word = (unsigned INT_TYPE) *p++, q2 = q1; + word; + word >>= 1, q2 += rw) + { + if (word & 1) + *q2 |= mask; + } + } + } +} + + + +/* The function returns nonzero, if the operand constraint STR is + commutative. */ +static int +commutative_constraint_p (const char *str) +{ + int ignore_p; + int c; + + for (ignore_p = FALSE;;) + { + c = *str; + if (c == '\0') + break; + str += CONSTRAINT_LEN (c, str); + if (c == '#') + ignore_p = TRUE; + else if (c == ',') + ignore_p = FALSE; + else if (! ignore_p) + { + if (c == '%') + return TRUE; + } + } + return FALSE; +} + +/* The function returns number of the operand which should be the same + in any case as operand with number OP_NUM. If USE_COMMUT_OP_P is + non-zero, the function makes temporarily commutative operand + exchange before this. The function takes only really possible + alternatives into consideration. */ +static int +get_dup_num (int op_num, int use_commut_op_p) +{ + int curr_alt, c, original, dup; + int ignore_p, commut_op_used_p; + const char *str; + rtx op, equiv_const = NULL_RTX; + + if (op_num < 0 || recog_data.n_alternatives == 0) + return -1; + op = recog_data.operand [op_num]; + ira_assert (REG_P (op)); + commut_op_used_p = TRUE; + if (use_commut_op_p) + { + if (commutative_constraint_p (recog_data.constraints [op_num])) + op_num++; + else if (op_num > 0 && commutative_constraint_p (recog_data.constraints + [op_num - 1])) + op_num--; + else + commut_op_used_p = FALSE; + } + str = recog_data.constraints [op_num]; + for (ignore_p = FALSE, original = -1, curr_alt = 0;;) + { + c = *str; + if (c == '\0') + break; + if (c == '#') + ignore_p = TRUE; + else if (c == ',') + { + curr_alt++; + ignore_p = FALSE; + } + else if (! ignore_p) + switch (c) + { + case 'X': + return -1; + + case 'i': + if (equiv_const != NULL_RTX && CONSTANT_P (equiv_const)) + return -1; + break; + + case 'n': + if (equiv_const != NULL_RTX + && (GET_CODE (equiv_const) == CONST_INT + || (GET_CODE (equiv_const) == CONST_DOUBLE + && GET_MODE (equiv_const) == VOIDmode))) + return -1; + break; + + case 's': + if (equiv_const != NULL_RTX + && CONSTANT_P (equiv_const) + && GET_CODE (equiv_const) != CONST_INT + && (GET_CODE (equiv_const) != CONST_DOUBLE + || GET_MODE (equiv_const) != VOIDmode)) + return -1; + break; + + case 'I': + case 'J': + case 'K': + case 'L': + case 'M': + case 'N': + case 'O': + case 'P': + if ((equiv_const != NULL_RTX + && GET_CODE (equiv_const) == CONST_INT + && CONST_OK_FOR_CONSTRAINT_P (INTVAL (equiv_const), + c, str))) + return -1; + break; + + case 'E': + case 'F': + if (equiv_const != NULL_RTX + && (GET_CODE (equiv_const) == CONST_DOUBLE + || (GET_CODE (equiv_const) == CONST_VECTOR + && (GET_MODE_CLASS (GET_MODE (equiv_const)) + == MODE_VECTOR_FLOAT)))) + return -1; + break; + + case 'G': + case 'H': + if (equiv_const != NULL_RTX + && GET_CODE (equiv_const) == CONST_DOUBLE + && CONST_DOUBLE_OK_FOR_CONSTRAINT_P (equiv_const, c, str)) + return -1; + break; + + case 'm': + case 'o': + /* Accept a register which might be placed in memory. */ + return -1; + break; + + case 'V': + case '<': + case '>': + break; + + case 'p': + GO_IF_LEGITIMATE_ADDRESS (VOIDmode, op, win_p); + break; + + win_p: + return -1; + + case 'g': + return -1; + + case 'r': + case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': + case 'h': case 'j': case 'k': case 'l': + case 'q': case 't': case 'u': + case 'v': case 'w': case 'x': case 'y': case 'z': + case 'A': case 'B': case 'C': case 'D': + case 'Q': case 'R': case 'S': case 'T': case 'U': + case 'W': case 'Y': case 'Z': + { + enum reg_class cl; + + cl = (c == 'r' + ? GENERAL_REGS : REG_CLASS_FROM_CONSTRAINT (c, str)); + if (cl != NO_REGS) + return -1; +#ifdef EXTRA_CONSTRAINT_STR + else if (EXTRA_CONSTRAINT_STR (op, c, str)) + return -1; +#endif + break; + } + + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + if (original != -1 && original != c) + return -1; + original = c; + break; + } + str += CONSTRAINT_LEN (c, str); + } + if (original == -1) + return -1; + dup = original - '0'; + if (use_commut_op_p) + { + if (commutative_constraint_p (recog_data.constraints [dup])) + dup++; + else if (dup > 0 + && commutative_constraint_p (recog_data.constraints [dup -1])) + dup--; + else if (! commut_op_used_p) + return -1; + } + return dup; +} + +/* The function returns operand which should be in any case the same + as operand with number OP_NUM. If USE_COMMUT_OP_P is non-zero, the + function makes temporarily commutative operand exchange before + this. */ +static rtx +get_dup (int op_num, int use_commut_op_p) +{ + int n = get_dup_num (op_num, use_commut_op_p); + + if (n < 0) + return NULL_RTX; + else + return recog_data.operand [n]; +} + +/* The function processes INSN and create allocno copies if + necessary. For example, it might be because INSN is a + pseudo-register move or INSN is two operand insn. */ +static void +add_insn_allocno_copies (rtx insn) +{ + rtx set, operand, dup; + const char *str; + int commut_p, bound_p; + int i, j, freq, hard_regno, cost, index; + copy_t cp; + allocno_t a; + enum reg_class class, cover_class; + enum machine_mode mode; + + freq = REG_FREQ_FROM_BB (BLOCK_FOR_INSN (insn)); + if (freq == 0) + freq = 1; + if ((set = single_set (insn)) != NULL_RTX + && REG_P (SET_DEST (set)) && REG_P (SET_SRC (set)) + && ! side_effects_p (set) + && find_reg_note (insn, REG_DEAD, SET_SRC (set)) != NULL_RTX + && find_reg_note (insn, REG_RETVAL, NULL_RTX) == NULL_RTX) + { + if (HARD_REGISTER_P (SET_SRC (set)) || HARD_REGISTER_P (SET_DEST (set))) + { + if (HARD_REGISTER_P (SET_DEST (set))) + { + if (HARD_REGISTER_P (SET_SRC (set))) + return; + hard_regno = REGNO (SET_DEST (set)); + a = ira_curr_regno_allocno_map [REGNO (SET_SRC (set))]; + } + else + { + hard_regno = REGNO (SET_SRC (set)); + a = ira_curr_regno_allocno_map [REGNO (SET_DEST (set))]; + } + class = REGNO_REG_CLASS (hard_regno); + mode = ALLOCNO_MODE (a); + cover_class = ALLOCNO_COVER_CLASS (a); + if (! class_subset_p [class] [cover_class]) + return; + if (reg_class_size [class] + <= (unsigned) CLASS_MAX_NREGS (class, mode)) + /* It is already taken into account in ira-costs.c. */ + return; + index = class_hard_reg_index [cover_class] [hard_regno]; + if (index < 0) + return; + if (HARD_REGISTER_P (SET_DEST (set))) + cost = register_move_cost [mode] [cover_class] [class] * freq; + else + cost = register_move_cost [mode] [class] [cover_class] * freq; + ALLOCNO_HARD_REG_COSTS (a) [index] -= cost; + ALLOCNO_CONFLICT_HARD_REG_COSTS (a) [index] -= cost; + } + else + { + cp = add_allocno_copy + (ira_curr_regno_allocno_map [REGNO (SET_DEST (set))], + ira_curr_regno_allocno_map [REGNO (SET_SRC (set))], + freq, insn, ira_curr_loop_tree_node); + bitmap_set_bit (ira_curr_loop_tree_node->local_copies, cp->num); + } + } + else + { + extract_insn (insn); + for (i = 0; i < recog_data.n_operands; i++) + { + operand = recog_data.operand [i]; + if (REG_P (operand) + && find_reg_note (insn, REG_DEAD, operand) != NULL_RTX) + { + str = recog_data.constraints [i]; + while (*str == ' ' && *str == '\t') + str++; + bound_p = FALSE; + for (j = 0, commut_p = FALSE; j < 2; j++, commut_p = TRUE) + if ((dup = get_dup (i, commut_p)) != NULL_RTX + && REG_P (dup) && GET_MODE (operand) == GET_MODE (dup)) + { + if (HARD_REGISTER_NUM_P (REGNO (operand)) + || HARD_REGISTER_NUM_P (REGNO (dup))) + { + if (HARD_REGISTER_P (operand)) + { + if (HARD_REGISTER_P (dup)) + continue; + hard_regno = REGNO (operand); + a = ira_curr_regno_allocno_map [REGNO (dup)]; + } + else + { + hard_regno = REGNO (dup); + a = ira_curr_regno_allocno_map [REGNO (operand)]; + } + class = REGNO_REG_CLASS (hard_regno); + mode = ALLOCNO_MODE (a); + cover_class = ALLOCNO_COVER_CLASS (a); + if (! class_subset_p [class] [cover_class]) + continue; + index + = class_hard_reg_index [cover_class] [hard_regno]; + if (index < 0) + continue; + if (HARD_REGISTER_P (operand)) + cost + = register_move_cost [mode] [cover_class] [class]; + else + cost + = register_move_cost [mode] [class] [cover_class]; + cost *= freq; + ALLOCNO_HARD_REG_COSTS (a) [index] -= cost; + ALLOCNO_CONFLICT_HARD_REG_COSTS (a) [index] -= cost; + bound_p = TRUE; + } + else + { + bound_p = TRUE; + cp = add_allocno_copy + (ira_curr_regno_allocno_map [REGNO (dup)], + ira_curr_regno_allocno_map [REGNO (operand)], + freq, NULL_RTX, ira_curr_loop_tree_node); + bitmap_set_bit + (ira_curr_loop_tree_node->local_copies, cp->num); + } + } + if (bound_p) + continue; + /* If an operand dies, prefer its hard register for the + output operands by decreasing the hard register cost + or creating the corresponding allocno copies. */ + for (j = 0; j < recog_data.n_operands; j++) + { + dup = recog_data.operand [j]; + + if (i == j || recog_data.operand_type [j] != OP_OUT + || !REG_P (dup)) + continue; + + if (HARD_REGISTER_NUM_P (REGNO (operand)) + || HARD_REGISTER_NUM_P (REGNO (dup))) + { + if (HARD_REGISTER_P (operand)) + { + if (HARD_REGISTER_P (dup)) + continue; + hard_regno = REGNO (operand); + a = ira_curr_regno_allocno_map [REGNO (dup)]; + } + else + { + hard_regno = REGNO (dup); + a = ira_curr_regno_allocno_map [REGNO (operand)]; + } + class = REGNO_REG_CLASS (hard_regno); + mode = ALLOCNO_MODE (a); + cover_class = ALLOCNO_COVER_CLASS (a); + if (! class_subset_p [class] [cover_class]) + continue; + index + = class_hard_reg_index [cover_class] [hard_regno]; + if (index < 0) + continue; + if (HARD_REGISTER_P (operand)) + cost + = register_move_cost [mode] [cover_class] [class]; + else + cost + = register_move_cost [mode] [class] [cover_class]; + cost *= (freq < 8 ? 1 : freq / 8); + ALLOCNO_HARD_REG_COSTS (a) [index] -= cost; + ALLOCNO_CONFLICT_HARD_REG_COSTS (a) [index] -= cost; + } + else + { + cp = add_allocno_copy + (ira_curr_regno_allocno_map [REGNO (dup)], + ira_curr_regno_allocno_map [REGNO (operand)], + (freq < 8 ? 1 : freq / 8), NULL_RTX, + ira_curr_loop_tree_node); + bitmap_set_bit + (ira_curr_loop_tree_node->local_copies, cp->num); + } + } + } + } + } +} + +/* The function adds copies originated from bb given by + LOOP_TREE_NODE. */ +static void +add_copies (loop_tree_node_t loop_tree_node) +{ + basic_block bb; + rtx insn; + + bb = loop_tree_node->bb; + if (bb == NULL) + return; + FOR_BB_INSNS (bb, insn) + if (INSN_P (insn)) + add_insn_allocno_copies (insn); +} + +/* The function propagates info about allocno A to the corresponding + allocno on upper loop tree level. So allocnos on upper levels + accumulate information about the corresponding allocnos in nested + loops. */ +static void +propagate_allocno_info (allocno_t a) +{ + int regno; + allocno_t father_a, another_a, another_father_a; + loop_tree_node_t father; + copy_t cp, next_cp; + + regno = ALLOCNO_REGNO (a); + if ((father = ALLOCNO_LOOP_TREE_NODE (a)->father) != NULL + && (father_a = father->regno_allocno_map [regno]) != NULL) + { + ALLOCNO_CALL_FREQ (father_a) += ALLOCNO_CALL_FREQ (a); +#ifdef STACK_REGS + if (ALLOCNO_TOTAL_NO_STACK_REG_P (a)) + ALLOCNO_TOTAL_NO_STACK_REG_P (father_a) = TRUE; +#endif + IOR_HARD_REG_SET (ALLOCNO_TOTAL_CONFLICT_HARD_REGS (father_a), + ALLOCNO_TOTAL_CONFLICT_HARD_REGS (a)); + if (ALLOCNO_CALLS_CROSSED_START (father_a) < 0 + || (ALLOCNO_CALLS_CROSSED_START (a) >= 0 + && (ALLOCNO_CALLS_CROSSED_START (father_a) + > ALLOCNO_CALLS_CROSSED_START (a)))) + ALLOCNO_CALLS_CROSSED_START (father_a) + = ALLOCNO_CALLS_CROSSED_START (a); + ALLOCNO_CALLS_CROSSED_NUM (father_a) += ALLOCNO_CALLS_CROSSED_NUM (a); + for (cp = ALLOCNO_COPIES (a); cp != NULL; cp = next_cp) + { + if (cp->first == a) + { + next_cp = cp->next_first_allocno_copy; + another_a = cp->second; + } + else if (cp->second == a) + { + next_cp = cp->next_second_allocno_copy; + another_a = cp->first; + } + else + gcc_unreachable (); + if ((another_father_a = (father->regno_allocno_map + [ALLOCNO_REGNO (another_a)])) != NULL) + add_allocno_copy (father_a, another_father_a, cp->freq, + cp->move_insn, cp->loop_tree_node); + } + } +} + +/* The function propagates info about allocnos to the corresponding + allocnos on upper loop tree level. */ +static void +propagate_info (void) +{ + int i; + allocno_t a; + + for (i = max_reg_num () - 1; i >= FIRST_PSEUDO_REGISTER; i--) + for (a = regno_allocno_map [i]; + a != NULL; + a = ALLOCNO_NEXT_REGNO_ALLOCNO (a)) + propagate_allocno_info (a); +} + +/* The function returns TRUE if allocnos A1 and A2 conflict. It + checks intersection of the corresponding live ranges for this. */ +int +allocno_conflict_p (allocno_t a1, allocno_t a2) +{ + allocno_live_range_t r1, r2; + + if (a1 == a2) + return FALSE; + if (ALLOCNO_REG (a1) != NULL && ALLOCNO_REG (a2) != NULL + && (ORIGINAL_REGNO (ALLOCNO_REG (a1)) + == ORIGINAL_REGNO (ALLOCNO_REG (a2)))) + return FALSE; + for (r1 = ALLOCNO_LIVE_RANGES (a1), r2 = ALLOCNO_LIVE_RANGES (a2); + r1 != NULL && r2 != NULL;) + { + if ((r2->start <= r1->start && r1->start <= r2->finish) + || (r1->start <= r2->start && r2->start <= r1->finish)) + return TRUE; + if (r1->start > r2->finish) + r1 = r1->next; + else + r2 = r2->next; + } + return FALSE; +} + +/* The function returns TRUE if pseudo-registers REGNO1 and REGNO2 + conflict. It should be used when there is only one region. */ +int +allocno_reg_conflict_p (int regno1, int regno2) +{ + allocno_t a1, a2; + + ira_assert (regno1 >= FIRST_PSEUDO_REGISTER + && regno2 >= FIRST_PSEUDO_REGISTER); + /* Reg info caclulated by dataflow infrastructure can be different + from one calculated by regclass. */ + if ((a1 = ira_loop_tree_root->regno_allocno_map [regno1]) == NULL + || (a2 = ira_loop_tree_root->regno_allocno_map [regno2]) == NULL) + return FALSE; + return allocno_conflict_p (a1, a2); +} + +/* Remove copies involving conflicting allocnos. */ +static void +remove_conflict_allocno_copies (void) +{ + int i; + allocno_t a; + copy_t cp, next_cp; + varray_type conflict_allocno_copy_varray; + + VARRAY_GENERIC_PTR_NOGC_INIT (conflict_allocno_copy_varray, get_max_uid (), + "copies of conflicting allocnos"); + for (i = 0; i < allocnos_num; i++) + { + a = allocnos [i]; + for (cp = ALLOCNO_COPIES (a); cp != NULL; cp = next_cp) + if (cp->first == a) + next_cp = cp->next_first_allocno_copy; + else + { + next_cp = cp->next_second_allocno_copy; + VARRAY_PUSH_GENERIC_PTR (conflict_allocno_copy_varray, cp); + } + } + for (i = VARRAY_ACTIVE_SIZE (conflict_allocno_copy_varray) - 1; i >= 0; i--) + { + cp = VARRAY_GENERIC_PTR (conflict_allocno_copy_varray, i); + if (CONFLICT_P (ALLOCNO_NUM (cp->first), ALLOCNO_NUM (cp->second))) + remove_allocno_copy_from_list (cp); + } + VARRAY_FREE (conflict_allocno_copy_varray); +} + +/* The function builds conflict vectors of all allocnos from the + conflict table. */ +static void +build_allocno_conflict_vects (void) +{ + int i, j, level, px, another_father_pn, conflict_allocnos_num; + int *level_init_p; + enum reg_class cover_class; + loop_tree_node_t father; + allocno_t a, father_a, another_a, another_father_a, *conflict_allocnos, *vec; + INT_TYPE *accumulated_conflicts, *allocno_conflicts, *propagate_conflicts; + + conflict_allocnos = ira_allocate (sizeof (allocno_t) * allocnos_num); + level_init_p = ira_allocate (ira_loop_tree_height * sizeof (int)); + memset (level_init_p, 0, ira_loop_tree_height * sizeof (int)); + accumulated_conflicts + = ira_allocate (ira_loop_tree_height + * allocno_set_words * sizeof (INT_TYPE)); + for (i = max_reg_num () - 1; i >= FIRST_PSEUDO_REGISTER; i--) + for (a = regno_allocno_map [i]; + a != NULL; + a = ALLOCNO_NEXT_REGNO_ALLOCNO (a)) + { + cover_class = ALLOCNO_COVER_CLASS (a); + level = ALLOCNO_LOOP_TREE_NODE (a)->level; + allocno_conflicts = conflicts + ALLOCNO_NUM (a) * allocno_set_words; + px = 0; + EXECUTE_IF_SET_IN_ALLOCNO_SET (allocno_conflicts, j, + { + another_a = allocnos [j]; + if (cover_class == ALLOCNO_COVER_CLASS (another_a)) + conflict_allocnos [px++] = another_a; + }); + conflict_allocnos_num = px; + if (! level_init_p [level]) + propagate_conflicts = allocno_conflicts; + else + { + propagate_conflicts + = accumulated_conflicts + level * allocno_set_words; + EXECUTE_IF_SET_IN_ALLOCNO_SET (propagate_conflicts, j, + { + another_a = allocnos [j]; + ira_assert (cover_class == ALLOCNO_COVER_CLASS (another_a)); + if (! TEST_ALLOCNO_SET_BIT (allocno_conflicts, j)) + conflict_allocnos [px++] = another_a; + }); + for (j = 0; j < allocno_set_words; j++) + propagate_conflicts [j] |= allocno_conflicts [j]; + } + allocate_allocno_conflicts (a, px); + vec = ALLOCNO_CONFLICT_ALLOCNO_VEC (a); + memcpy (vec, conflict_allocnos, sizeof (allocno_t) * px); + vec [px] = NULL; + ALLOCNO_CONFLICT_ALLOCNOS_NUM (a) = conflict_allocnos_num; + ALLOCNO_TOTAL_CONFLICT_ALLOCNOS_NUM (a) = px; + level_init_p [level] = FALSE; + if ((father = ALLOCNO_LOOP_TREE_NODE (a)->father) == NULL + || (father_a = father->regno_allocno_map [i]) == NULL) + continue; + level--; + ira_assert (level == ALLOCNO_LOOP_TREE_NODE (father_a)->level + && cover_class == ALLOCNO_COVER_CLASS (father_a)); + if (! level_init_p [level]) + { + level_init_p [level] = TRUE; + memset (accumulated_conflicts + level * allocno_set_words, 0, + allocno_set_words * sizeof (INT_TYPE)); + } + EXECUTE_IF_SET_IN_ALLOCNO_SET (propagate_conflicts, j, + { + another_a = allocnos [j]; + if ((another_father_a = (father->regno_allocno_map + [ALLOCNO_REGNO (another_a)])) == NULL) + continue; + another_father_pn = ALLOCNO_NUM (another_father_a); + if (another_father_pn < 0) + continue; + ira_assert (ALLOCNO_COVER_CLASS (another_a) + == ALLOCNO_COVER_CLASS (another_father_a)); + if (cover_class == ALLOCNO_COVER_CLASS (another_a)) + SET_ALLOCNO_SET_BIT + (accumulated_conflicts + allocno_set_words * level, + another_father_pn); + }); + } + ira_free (accumulated_conflicts); + ira_free (level_init_p); + ira_free (conflict_allocnos); +} + + + +/* The function propagates information about allocnos modified inside + the loops. */ +static void +propagate_modified_regnos (loop_tree_node_t loop_tree_node) +{ + if (loop_tree_node->bb != NULL || loop_tree_node == ira_loop_tree_root) + return; + bitmap_ior_into (loop_tree_node->father->modified_regnos, + loop_tree_node->modified_regnos); +} + + + +/* The function prints hard reg set SET with TITLE to FILE. */ +static void +print_hard_reg_set (FILE *file, const char *title, HARD_REG_SET set) +{ + int i, start; + + fprintf (file, title); + for (start = -1, i = 0; i < FIRST_PSEUDO_REGISTER; i++) + { + if (TEST_HARD_REG_BIT (set, i)) + { + if (i == 0 || ! TEST_HARD_REG_BIT (set, i - 1)) + start = i; + } + if (start >= 0 + && (i == FIRST_PSEUDO_REGISTER - 1 || ! TEST_HARD_REG_BIT (set, i))) + { + if (start == i - 1) + fprintf (file, " %d", start); + else if (start == i - 2) + fprintf (file, " %d %d", start, start + 1); + else + fprintf (file, " %d-%d", start, i - 1); + start = -1; + } + } + fprintf (file, "\n"); +} + +/* The function outputs information about allocno or regno (if REG_P) + conflicts to FILE. */ +static void +print_conflicts (FILE *file, int reg_p) +{ + int i; + + for (i = 0; i < allocnos_num; i++) + { + int j; + allocno_t a, conflict_a, *allocno_vec; + basic_block bb; + + a = allocnos [i]; + if (reg_p) + fprintf (file, ";; r%d", ALLOCNO_REGNO (a)); + else + { + fprintf (file, ";; a%d(r%d,", ALLOCNO_NUM (a), ALLOCNO_REGNO (a)); + if ((bb = ALLOCNO_LOOP_TREE_NODE (a)->bb) != NULL) + fprintf (file, "b%d", bb->index); + else + fprintf (file, "l%d", ALLOCNO_LOOP_TREE_NODE (a)->loop->num); + fprintf (file, ")"); + } + fprintf (file, " conflicts:"); + allocno_vec = ALLOCNO_CONFLICT_ALLOCNO_VEC (a); + if (allocno_vec != NULL) + for (j = 0; (conflict_a = allocno_vec [j]) != NULL; j++) + { + if (reg_p) + fprintf (file, " r%d,", ALLOCNO_REGNO (conflict_a)); + else + { + fprintf (file, " a%d(r%d,", ALLOCNO_NUM (conflict_a), + ALLOCNO_REGNO (conflict_a)); + if ((bb = ALLOCNO_LOOP_TREE_NODE (conflict_a)->bb) != NULL) + fprintf (file, "b%d)", bb->index); + else + fprintf (file, "l%d)", + ALLOCNO_LOOP_TREE_NODE (conflict_a)->loop->num); + } + if (j + 1 == ALLOCNO_CONFLICT_ALLOCNOS_NUM (a)) + fprintf (file, "*"); + } + print_hard_reg_set (file, "\n;; total conflict hard regs:", + ALLOCNO_TOTAL_CONFLICT_HARD_REGS (a)); + print_hard_reg_set (file, ";; conflict hard regs:", + ALLOCNO_CONFLICT_HARD_REGS (a)); + } + fprintf (file, "\n"); +} + +/* The function outputs information about allocno or regno (if REG_P) + conflicts to stderr. */ +void +debug_conflicts (int reg_p) +{ + print_conflicts (stderr, reg_p); +} + + + +/* Entry function which builds allocno conflicts. */ +void +ira_build_conflicts (void) +{ + int i; + allocno_t a; + + build_conflict_bit_table (); + mirror_conflicts (); + traverse_loop_tree (FALSE, ira_loop_tree_root, NULL, add_copies); + if (flag_ira_algorithm == IRA_ALGORITHM_REGIONAL + || flag_ira_algorithm == IRA_ALGORITHM_MIXED) + propagate_info (); + /* We need finished conflict table for the subsequent call. */ + remove_conflict_allocno_copies (); + build_allocno_conflict_vects (); + for (i = 0; i < allocnos_num; i++) + { + a = allocnos [i]; + if (ALLOCNO_CALLS_CROSSED_NUM (a) == 0) + continue; + if (! flag_caller_saves) + { + IOR_HARD_REG_SET (ALLOCNO_TOTAL_CONFLICT_HARD_REGS (a), + call_used_reg_set); + if (ALLOCNO_CALLS_CROSSED_NUM (a) != 0) + IOR_HARD_REG_SET (ALLOCNO_CONFLICT_HARD_REGS (a), + call_used_reg_set); + } + else + { + IOR_HARD_REG_SET (ALLOCNO_TOTAL_CONFLICT_HARD_REGS (a), + no_caller_save_reg_set); + if (ALLOCNO_CALLS_CROSSED_NUM (a) != 0) + IOR_HARD_REG_SET (ALLOCNO_CONFLICT_HARD_REGS (a), + no_caller_save_reg_set); + } + } + traverse_loop_tree (FALSE, ira_loop_tree_root, NULL, + propagate_modified_regnos); + if (internal_flag_ira_verbose > 2 && ira_dump_file != NULL) + print_conflicts (ira_dump_file, FALSE); +} diff --git a/gcc/ira-costs.c b/gcc/ira-costs.c index 6383b01dc0e..37317eeaaae 100644 --- a/gcc/ira-costs.c +++ b/gcc/ira-costs.c @@ -45,19 +45,19 @@ Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA struct costs; static void record_reg_classes (int, int, rtx *, enum machine_mode *, - const char **, rtx, struct costs *, + const char **, rtx, struct costs **, enum reg_class *); static inline int ok_for_index_p_nonstrict (rtx); static inline int ok_for_base_p_nonstrict (rtx, enum machine_mode, enum rtx_code, enum rtx_code); static void record_address_regs (enum machine_mode, rtx x, int, enum rtx_code, enum rtx_code, int scale); -static void record_operand_costs (rtx, struct costs *, enum reg_class *); +static void record_operand_costs (rtx, struct costs **, enum reg_class *); static rtx scan_one_insn (rtx); static void print_costs (FILE *); -static void process_bb_node_for_costs (struct ira_loop_tree_node *); +static void process_bb_node_for_costs (loop_tree_node_t); static void find_allocno_class_costs (void); -static void process_bb_node_for_hard_reg_moves (struct ira_loop_tree_node *); +static void process_bb_node_for_hard_reg_moves (loop_tree_node_t); static void setup_allocno_cover_class_and_costs (void); #ifdef FORBIDDEN_INC_DEC_CLASSES @@ -71,16 +71,33 @@ static char *in_inc_dec; to set up register and costs. */ struct costs { - int cost [N_REG_CLASSES]; int mem_cost; + /* Costs for important register classes start here. */ + int cost [1]; }; -/* Record the cost of each class for each allocno. */ -static struct costs *costs; +/* Initialized once. It is a size of the allocated struct costs. */ +static int struct_costs_size; -/* Initialized once, and used to initialize cost values for each - insn. */ -static struct costs init_cost; +/* Allocated and initialized once, and used to initialize cost values + for each insn. */ +static struct costs *init_cost; + +/* Allocated once, and used for temporary purposes. */ +static struct costs *temp_costs; + +/* Allocated once, and used for the cost calculation. */ +static struct costs *op_costs [MAX_RECOG_OPERANDS]; +static struct costs *this_op_costs [MAX_RECOG_OPERANDS]; + +/* Record the initial and accumulated cost of each class for each + allocno. */ +static struct costs *total_costs; + +/* Return pointer to structure containing costs of allocno with given + NUM in array ARR. */ +#define COSTS_OF_ALLOCNO(arr, num) \ + ((struct costs *) ((char *) (arr) + (num) * struct_costs_size)) /* Record register class preferences of each allocno. */ static enum reg_class *allocno_pref; @@ -91,9 +108,6 @@ static enum reg_class *allocno_pref_buffer; /* Frequency of executions of the current insn. */ static int frequency; -/* Map regno->allocno for the current loop. */ -static allocno_t *curr_regno_allocno_map; - /* Compute the cost of loading X into (if TO_P is nonzero) or from (if TO_P is zero) a register of class CLASS in mode MODE. X must not be a pseudo register. */ @@ -167,7 +181,7 @@ copy_cost (rtx x, enum machine_mode mode, enum reg_class class, int to_p, static void record_reg_classes (int n_alts, int n_ops, rtx *ops, enum machine_mode *modes, const char **constraints, - rtx insn, struct costs *op_costs, + rtx insn, struct costs **op_costs, enum reg_class *allocno_pref) { int alt; @@ -178,7 +192,6 @@ record_reg_classes (int n_alts, int n_ops, rtx *ops, with the cost for each operand in that alternative. */ for (alt = 0; alt < n_alts; alt++) { - struct costs this_op_costs [MAX_RECOG_OPERANDS]; enum reg_class classes [MAX_RECOG_OPERANDS]; int allows_mem [MAX_RECOG_OPERANDS]; int class; @@ -203,7 +216,7 @@ record_reg_classes (int n_alts, int n_ops, rtx *ops, if (*p == 0) { if (REG_P (op) && REGNO (op) >= FIRST_PSEUDO_REGISTER) - memset (&this_op_costs [i], 0, sizeof this_op_costs [i]); + memset (this_op_costs [i], 0, struct_costs_size); continue; } @@ -264,7 +277,7 @@ record_reg_classes (int n_alts, int n_ops, rtx *ops, other operand since move costs are not symmetric. Moreover, if we cannot tie them, this alternative needs to do a copy, which is one instruction. */ - struct costs *pp = &this_op_costs [i]; + struct costs *pp = this_op_costs [i]; if (move_cost [mode] == NULL) init_move_cost (mode); @@ -272,7 +285,7 @@ record_reg_classes (int n_alts, int n_ops, rtx *ops, for (k = 0; k < important_classes_num; k++) { class = important_classes [k]; - pp->cost [class] + pp->cost [k] = ((recog_data.operand_type [i] != OP_OUT ? may_move_in_cost [mode] [class] [classes [i]] : 0) @@ -299,11 +312,25 @@ record_reg_classes (int n_alts, int n_ops, rtx *ops, cover class here but it is less accurate approximation. */ if (allocno_pref) - alt_cost - += (may_move_in_cost [mode] - [allocno_pref [ALLOCNO_NUM - (curr_regno_allocno_map [REGNO (op)])]] - [classes [i]]); + { + enum reg_class pref_class + = allocno_pref [ALLOCNO_NUM + (ira_curr_regno_allocno_map + [REGNO (op)])]; + + if (pref_class == NO_REGS) + alt_cost + += ((recog_data.operand_type [i] != OP_IN + ? memory_move_cost [mode] [classes [i]] [0] + : 0) + + (recog_data.operand_type [i] != OP_OUT + ? memory_move_cost [mode] [classes [i]] [1] + : 0)); + else + alt_cost + += (may_move_in_cost + [mode] [pref_class] [classes [i]]); + } if (REGNO (ops [i]) != REGNO (ops [j]) && ! find_reg_note (insn, REG_DEAD, op)) alt_cost += 2; @@ -499,7 +526,7 @@ record_reg_classes (int n_alts, int n_ops, rtx *ops, } else { - struct costs *pp = &this_op_costs [i]; + struct costs *pp = this_op_costs [i]; if (move_cost [mode] == NULL) init_move_cost (mode); @@ -507,7 +534,7 @@ record_reg_classes (int n_alts, int n_ops, rtx *ops, for (k = 0; k < important_classes_num; k++) { class = important_classes [k]; - pp->cost [class] + pp->cost [k] = ((recog_data.operand_type [i] != OP_OUT ? may_move_in_cost [mode] [class] [classes [i]] : 0) @@ -534,11 +561,25 @@ record_reg_classes (int n_alts, int n_ops, rtx *ops, cover class here but it is less accurate approximation. */ if (allocno_pref) - alt_cost - += (may_move_in_cost [mode] - [allocno_pref [ALLOCNO_NUM - (curr_regno_allocno_map [REGNO (op)])]] - [classes [i]]); + { + enum reg_class pref_class + = allocno_pref [ALLOCNO_NUM + (ira_curr_regno_allocno_map + [REGNO (op)])]; + + if (pref_class == NO_REGS) + alt_cost + += ((recog_data.operand_type [i] != OP_IN + ? memory_move_cost [mode] [classes [i]] [0] + : 0) + + (recog_data.operand_type [i] != OP_OUT + ? memory_move_cost [mode] [classes [i]] [1] + : 0)); + else + alt_cost + += (may_move_in_cost + [mode] [pref_class] [classes [i]]); + } } } @@ -579,18 +620,15 @@ record_reg_classes (int n_alts, int n_ops, rtx *ops, if (REG_P (ops [i]) && REGNO (ops [i]) >= FIRST_PSEUDO_REGISTER) { - struct costs *pp = &op_costs [i], *qq = &this_op_costs [i]; + struct costs *pp = op_costs [i], *qq = this_op_costs [i]; int scale = 1 + (recog_data.operand_type [i] == OP_INOUT); pp->mem_cost = MIN (pp->mem_cost, (qq->mem_cost + alt_cost) * scale); for (k = 0; k < important_classes_num; k++) - { - class = important_classes [k]; - pp->cost [class] = MIN (pp->cost [class], - (qq->cost [class] + alt_cost) * scale); - } + pp->cost [k] = MIN (pp->cost [k], + (qq->cost [k] + alt_cost) * scale); } } @@ -627,12 +665,14 @@ record_reg_classes (int n_alts, int n_ops, rtx *ops, /* We could use cover class here but it is less accurate approximation. */ pref - = allocno_pref [ALLOCNO_NUM (curr_regno_allocno_map [regno])]; + = allocno_pref [ALLOCNO_NUM (ira_curr_regno_allocno_map + [regno])]; - if ((reg_class_size [pref] - == (unsigned) CLASS_MAX_NREGS (pref, mode)) + if (pref != NO_REGS + && (reg_class_size [pref] + == (unsigned) CLASS_MAX_NREGS (pref, mode)) && register_move_cost [mode] [pref] [pref] < 10 * 2) - op_costs [i].cost [pref] = -1; + op_costs [i]->cost [important_class_nums [pref]] = -1; } else if (regno < FIRST_PSEUDO_REGISTER) for (k = 0; k < important_classes_num; k++) @@ -643,7 +683,7 @@ record_reg_classes (int n_alts, int n_ops, rtx *ops, == (unsigned) CLASS_MAX_NREGS (class, mode))) { if (reg_class_size [class] == 1) - op_costs [i].cost [class] = -1; + op_costs [i]->cost [k] = -1; else { for (nr = 0; @@ -654,7 +694,7 @@ record_reg_classes (int n_alts, int n_ops, rtx *ops, break; if (nr == (unsigned) hard_regno_nregs [regno] [mode]) - op_costs [i].cost [class] = -1; + op_costs [i]->cost [k] = -1; } } } @@ -836,8 +876,8 @@ record_address_regs (enum machine_mode mode, rtx x, int context, #ifdef FORBIDDEN_INC_DEC_CLASSES if (REG_P (XEXP (x, 0)) && REGNO (XEXP (x, 0)) >= FIRST_PSEUDO_REGISTER) - in_inc_dec [ALLOCNO_NUM (curr_regno_allocno_map [REGNO (XEXP (x, 0))])] - = 1; + in_inc_dec [ALLOCNO_NUM (ira_curr_regno_allocno_map + [REGNO (XEXP (x, 0))])] = 1; #endif record_address_regs (mode, XEXP (x, 0), 0, code, SCRATCH, 2 * scale); break; @@ -850,14 +890,16 @@ record_address_regs (enum machine_mode mode, rtx x, int context, if (REGNO (x) < FIRST_PSEUDO_REGISTER) break; - pp = &costs [ALLOCNO_NUM (curr_regno_allocno_map [REGNO (x)])]; + pp = COSTS_OF_ALLOCNO (total_costs, + ALLOCNO_NUM (ira_curr_regno_allocno_map + [REGNO (x)])); pp->mem_cost += (memory_move_cost [Pmode] [class] [1] * scale) / 2; if (move_cost [Pmode] == NULL) init_move_cost (Pmode); for (k = 0; k < important_classes_num; k++) { i = important_classes [k]; - pp->cost [i] += (may_move_in_cost [Pmode] [i] [class] * scale) / 2; + pp->cost [k] += (may_move_in_cost [Pmode] [i] [class] * scale) / 2; } } break; @@ -878,7 +920,7 @@ record_address_regs (enum machine_mode mode, rtx x, int context, /* Calculate the costs of insn operands. */ static void -record_operand_costs (rtx insn, struct costs *op_costs, +record_operand_costs (rtx insn, struct costs **op_costs, enum reg_class *allocno_pref) { const char *constraints [MAX_RECOG_OPERANDS]; @@ -898,7 +940,7 @@ record_operand_costs (rtx insn, struct costs *op_costs, commutative. */ for (i = 0; i < recog_data.n_operands; i++) { - op_costs [i] = init_cost; + memmove (op_costs [i], init_cost, struct_costs_size); if (GET_CODE (recog_data.operand [i]) == SUBREG) recog_data.operand [i] = SUBREG_REG (recog_data.operand [i]); @@ -949,8 +991,7 @@ scan_one_insn (rtx insn) { enum rtx_code pat_code; rtx set, note; - int i, j, k; - struct costs op_costs [MAX_RECOG_OPERANDS]; + int i, k; if (!INSN_P (insn)) return insn; @@ -970,8 +1011,9 @@ scan_one_insn (rtx insn) && (note = find_reg_note (insn, REG_EQUIV, NULL_RTX)) != NULL_RTX && MEM_P (XEXP (note, 0))) { - costs [ALLOCNO_NUM (curr_regno_allocno_map - [REGNO (SET_DEST (set))])].mem_cost + COSTS_OF_ALLOCNO (total_costs, + ALLOCNO_NUM (ira_curr_regno_allocno_map + [REGNO (SET_DEST (set))]))->mem_cost -= (memory_move_cost [GET_MODE (SET_DEST (set))] [GENERAL_REGS] [1] * frequency); record_address_regs (GET_MODE (SET_SRC (set)), XEXP (SET_SRC (set), 0), @@ -988,15 +1030,15 @@ scan_one_insn (rtx insn) && REGNO (recog_data.operand [i]) >= FIRST_PSEUDO_REGISTER) { int regno = REGNO (recog_data.operand [i]); - struct costs *p = &costs [ALLOCNO_NUM (curr_regno_allocno_map [regno])]; - struct costs *q = &op_costs [i]; + struct costs *p + = COSTS_OF_ALLOCNO (total_costs, + ALLOCNO_NUM (ira_curr_regno_allocno_map + [regno])); + struct costs *q = op_costs [i]; p->mem_cost += q->mem_cost * frequency; for (k = 0; k < important_classes_num; k++) - { - j = important_classes [k]; - p->cost [j] += q->cost [j] * frequency; - } + p->cost [k] += q->cost [k] * frequency; } return insn; @@ -1015,14 +1057,14 @@ print_costs (FILE *f) { int class; basic_block bb; - allocno_t p = allocnos [i]; - int regno = ALLOCNO_REGNO (p); + allocno_t a = allocnos [i]; + int regno = ALLOCNO_REGNO (a); - fprintf (f, " p%d(r%d,", i, regno); - if ((bb = ALLOCNO_LOOP_TREE_NODE (p)->bb) != NULL) + fprintf (f, " a%d(r%d,", i, regno); + if ((bb = ALLOCNO_LOOP_TREE_NODE (a)->bb) != NULL) fprintf (f, "b%d", bb->index); else - fprintf (f, "l%d", ALLOCNO_LOOP_TREE_NODE (p)->loop->num); + fprintf (f, "l%d", ALLOCNO_LOOP_TREE_NODE (a)->loop->num); fprintf (f, ") costs:"); for (k = 0; k < important_classes_num; k++) { @@ -1037,16 +1079,16 @@ print_costs (FILE *f) #endif ) fprintf (f, " %s:%d", reg_class_names [class], - costs [i].cost [class]); + COSTS_OF_ALLOCNO (total_costs, i)->cost [k]); } - fprintf (f, " MEM:%i\n", costs [i].mem_cost); + fprintf (f, " MEM:%i\n", COSTS_OF_ALLOCNO (total_costs, i)->mem_cost); } } /* The function traverses basic blocks represented by LOOP_TREE_NODE to find the costs of the allocnos. */ static void -process_bb_node_for_costs (struct ira_loop_tree_node *loop_tree_node) +process_bb_node_for_costs (loop_tree_node_t loop_tree_node) { basic_block bb; rtx insn; @@ -1057,13 +1099,12 @@ process_bb_node_for_costs (struct ira_loop_tree_node *loop_tree_node) frequency = REG_FREQ_FROM_BB (bb); if (frequency == 0) frequency = 1; - curr_regno_allocno_map = ira_curr_loop_tree_node->regno_allocno_map; FOR_BB_INSNS (bb, insn) insn = scan_one_insn (insn); } -/* Entry function to find costs of each class cost for pesudos and - their best classes. */ +/* Entry function to find costs of each class for pesudos and their + best classes. */ static void find_allocno_class_costs (void) { @@ -1083,67 +1124,64 @@ find_allocno_class_costs (void) classes to guide the selection. */ for (pass = 0; pass <= flag_expensive_optimizations; pass++) { - if (ira_dump_file) - fprintf (ira_dump_file, "\nPass %i\n\n",pass); + if (internal_flag_ira_verbose > 0 && ira_dump_file) + fprintf (ira_dump_file, "\nPass %i for finding allocno costs\n\n", + pass); /* Zero out our accumulation of the cost of each class for each allocno. */ - memset (costs, 0, allocnos_num * sizeof (struct costs)); + memset (total_costs, 0, allocnos_num * struct_costs_size); #ifdef FORBIDDEN_INC_DEC_CLASSES memset (in_inc_dec, 0, allocnos_num * sizeof (char)); #endif /* Scan the instructions and record each time it would save code to put a certain allocno in a certain class. */ - traverse_loop_tree (ira_loop_tree_root, process_bb_node_for_costs, NULL); + traverse_loop_tree (FALSE, ira_loop_tree_root, + process_bb_node_for_costs, NULL); /* Now for each allocno look at how desirable each class is and - find which class is preferred. Store that in - `prefclass'. */ + find which class is preferred. */ if (pass == 0) allocno_pref = allocno_pref_buffer; for (i = max_reg_num () - 1; i >= FIRST_PSEUDO_REGISTER; i--) { - allocno_t p, father_p; - int class, p_num, father_p_num; - struct ira_loop_tree_node *father; + allocno_t a, father_a; + int class, a_num, father_a_num; + loop_tree_node_t father; int best_cost; enum reg_class best, common_class; #ifdef FORBIDDEN_INC_DEC_CLASSES int inc_dec_p = FALSE; #endif - struct costs reg_costs; if (regno_allocno_map [i] == NULL) continue; - memset (®_costs, 0, sizeof (struct costs)); - for (p = regno_allocno_map [i]; - p != NULL; - p = ALLOCNO_NEXT_REGNO_ALLOCNO (p)) + memset (temp_costs, 0, struct_costs_size); + for (a = regno_allocno_map [i]; + a != NULL; + a = ALLOCNO_NEXT_REGNO_ALLOCNO (a)) { - p_num = ALLOCNO_NUM (p); + a_num = ALLOCNO_NUM (a); if ((flag_ira_algorithm == IRA_ALGORITHM_REGIONAL || flag_ira_algorithm == IRA_ALGORITHM_MIXED) - && (father = ALLOCNO_LOOP_TREE_NODE (p)->father) != NULL - && (father_p = father->regno_allocno_map [i]) != NULL) + && (father = ALLOCNO_LOOP_TREE_NODE (a)->father) != NULL + && (father_a = father->regno_allocno_map [i]) != NULL) { - father_p_num = ALLOCNO_NUM (father_p); + father_a_num = ALLOCNO_NUM (father_a); for (k = 0; k < important_classes_num; k++) - { - class = important_classes [k]; - costs [father_p_num].cost [class] - += costs [p_num].cost [class]; - } - costs [father_p_num].mem_cost += costs [p_num].mem_cost; + COSTS_OF_ALLOCNO (total_costs, father_a_num)->cost [k] + += COSTS_OF_ALLOCNO (total_costs, a_num)->cost [k]; + COSTS_OF_ALLOCNO (total_costs, father_a_num)->mem_cost + += COSTS_OF_ALLOCNO (total_costs, a_num)->mem_cost; } - for (k = 0; k < important_classes_num; k++) - { - class = important_classes [k]; - reg_costs.cost [class] += costs [p_num].cost [class]; - } - reg_costs.mem_cost += costs [p_num].mem_cost; + for (k = 0; k < important_classes_num; k++) + temp_costs->cost [k] + += COSTS_OF_ALLOCNO (total_costs, a_num)->cost [k]; + temp_costs->mem_cost + += COSTS_OF_ALLOCNO (total_costs, a_num)->mem_cost; #ifdef FORBIDDEN_INC_DEC_CLASSES - if (in_inc_dec [p_num]) + if (in_inc_dec [a_num]) inc_dec_p = TRUE; #endif } @@ -1164,67 +1202,84 @@ find_allocno_class_costs (void) #endif ) ; - else if (reg_costs.cost [class] < best_cost) + else if (temp_costs->cost [k] < best_cost) { - best_cost = reg_costs.cost [class]; + best_cost = temp_costs->cost [k]; best = (enum reg_class) class; } - else if (reg_costs.cost [class] == best_cost) + else if (temp_costs->cost [k] == best_cost) best = reg_class_subunion [best] [class]; } - common_class = best; - if (class_subset_p [best] [class_translate [best]]) - common_class = class_translate [best]; - for (p = regno_allocno_map [i]; - p != NULL; - p = ALLOCNO_NEXT_REGNO_ALLOCNO (p)) + if (best_cost > temp_costs->mem_cost) + common_class = NO_REGS; + else { - p_num = ALLOCNO_NUM (p); - /* Finding best class which is cover class for the - register. */ - best_cost = (1 << (HOST_BITS_PER_INT - 2)) - 1; - best = ALL_REGS; - for (k = 0; k < important_classes_num; k++) - { - class = important_classes [k]; - if (! class_subset_p [class] [common_class]) - continue; - /* Ignore classes that are too small for this operand or - invalid for an operand that was auto-incremented. */ - if (! contains_reg_of_mode [class] [PSEUDO_REGNO_MODE (i)] + common_class = best; + if (class_subset_p [best] [class_translate [best]]) + common_class = class_translate [best]; + } + for (a = regno_allocno_map [i]; + a != NULL; + a = ALLOCNO_NEXT_REGNO_ALLOCNO (a)) + { + a_num = ALLOCNO_NUM (a); + if (common_class == NO_REGS) + best = NO_REGS; + else + { + /* Finding best class which is cover class for the + register. */ + best_cost = (1 << (HOST_BITS_PER_INT - 2)) - 1; + best = ALL_REGS; + for (k = 0; k < important_classes_num; k++) + { + class = important_classes [k]; + if (! class_subset_p [class] [common_class]) + continue; + /* Ignore classes that are too small for this + operand or invalid for an operand that was + auto-incremented. */ + if (! contains_reg_of_mode [class] [PSEUDO_REGNO_MODE + (i)] #ifdef FORBIDDEN_INC_DEC_CLASSES - || (inc_dec_p && forbidden_inc_dec_class [class]) + || (inc_dec_p && forbidden_inc_dec_class [class]) #endif #ifdef CANNOT_CHANGE_MODE_CLASS - || invalid_mode_change_p (i, (enum reg_class) class, - PSEUDO_REGNO_MODE (i))) - ; - else if (costs [p_num].cost [class] < best_cost) - { - best_cost = costs [p_num].cost [class]; - best = (enum reg_class) class; + || invalid_mode_change_p (i, (enum reg_class) class, + PSEUDO_REGNO_MODE (i)) +#endif + ) + ; + else if (COSTS_OF_ALLOCNO (total_costs, a_num)->cost [k] + < best_cost) + { + best_cost + = COSTS_OF_ALLOCNO (total_costs, a_num)->cost [k]; + best = (enum reg_class) class; + } + else if (COSTS_OF_ALLOCNO (total_costs, a_num)->cost [k] + == best_cost) + best = reg_class_subunion [best] [class]; } - else if (costs [p_num].cost [class] == best_cost) - best = reg_class_subunion [best] [class]; } -#endif - if (ira_dump_file && (pass == 0 || allocno_pref [p_num] != best)) + if (internal_flag_ira_verbose > 2 && ira_dump_file != NULL + && (pass == 0 || allocno_pref [a_num] != best)) { - fprintf (ira_dump_file, " p%d (r%d,", p_num, i); - if ((bb = ALLOCNO_LOOP_TREE_NODE (p)->bb) != NULL) + fprintf (ira_dump_file, " a%d (r%d,", a_num, i); + if ((bb = ALLOCNO_LOOP_TREE_NODE (a)->bb) != NULL) fprintf (ira_dump_file, "b%d", bb->index); else fprintf (ira_dump_file, "l%d", - ALLOCNO_LOOP_TREE_NODE (p)->loop->num); + ALLOCNO_LOOP_TREE_NODE (a)->loop->num); fprintf (ira_dump_file, ") best %s, cover %s\n", reg_class_names [best], reg_class_names [class_translate [best]]); } - allocno_pref [p_num] = best; + allocno_pref [a_num] = best; } } - if (ira_dump_file) + if (internal_flag_ira_verbose > 4 && ira_dump_file) { print_costs (ira_dump_file); fprintf (ira_dump_file,"\n"); @@ -1244,10 +1299,10 @@ find_allocno_class_costs (void) register are already taken into account slightly in class costs for the allocno. */ static void -process_bb_node_for_hard_reg_moves (struct ira_loop_tree_node *loop_tree_node) +process_bb_node_for_hard_reg_moves (loop_tree_node_t loop_tree_node) { int i, freq, cost, src_regno, dst_regno, hard_regno, to_p; - allocno_t p; + allocno_t a; enum reg_class class, hard_reg_class; enum machine_mode mode; basic_block bb; @@ -1259,7 +1314,6 @@ process_bb_node_for_hard_reg_moves (struct ira_loop_tree_node *loop_tree_node) freq = REG_FREQ_FROM_BB (bb); if (freq == 0) freq = 1; - curr_regno_allocno_map = ira_curr_loop_tree_node->regno_allocno_map; FOR_BB_INSNS (bb, insn) { if (! INSN_P (insn)) @@ -1278,48 +1332,48 @@ process_bb_node_for_hard_reg_moves (struct ira_loop_tree_node *loop_tree_node) { hard_regno = src_regno; to_p = TRUE; - p = curr_regno_allocno_map [dst_regno]; + a = ira_curr_regno_allocno_map [dst_regno]; } else if (src_regno >= FIRST_PSEUDO_REGISTER && dst_regno < FIRST_PSEUDO_REGISTER) { hard_regno = dst_regno; to_p = FALSE; - p = curr_regno_allocno_map [src_regno]; + a = ira_curr_regno_allocno_map [src_regno]; } else continue; - class = ALLOCNO_COVER_CLASS (p); + class = ALLOCNO_COVER_CLASS (a); if (! TEST_HARD_REG_BIT (reg_class_contents [class], hard_regno)) continue; i = class_hard_reg_index [class] [hard_regno]; if (i < 0) continue; - mode = ALLOCNO_MODE (p); + mode = ALLOCNO_MODE (a); hard_reg_class = REGNO_REG_CLASS (hard_regno); cost = (to_p ? register_move_cost [mode] [hard_reg_class] [class] : register_move_cost [mode] [class] [hard_reg_class]) * freq; - ALLOCNO_HARD_REG_COSTS (p) [i] -= cost; - ALLOCNO_CONFLICT_HARD_REG_COSTS (p) [i] -= cost; - ALLOCNO_COVER_CLASS_COST (p) = MIN (ALLOCNO_COVER_CLASS_COST (p), - ALLOCNO_HARD_REG_COSTS (p) [i]); + ALLOCNO_HARD_REG_COSTS (a) [i] -= cost; + ALLOCNO_CONFLICT_HARD_REG_COSTS (a) [i] -= cost; + ALLOCNO_COVER_CLASS_COST (a) = MIN (ALLOCNO_COVER_CLASS_COST (a), + ALLOCNO_HARD_REG_COSTS (a) [i]); if (flag_ira_algorithm == IRA_ALGORITHM_REGIONAL || flag_ira_algorithm == IRA_ALGORITHM_MIXED) { - struct ira_loop_tree_node *father; - int regno = ALLOCNO_REGNO (p); + loop_tree_node_t father; + int regno = ALLOCNO_REGNO (a); for (;;) { - if ((father = ALLOCNO_LOOP_TREE_NODE (p)->father) == NULL) + if ((father = ALLOCNO_LOOP_TREE_NODE (a)->father) == NULL) break; - if ((p = father->regno_allocno_map [regno]) == NULL) + if ((a = father->regno_allocno_map [regno]) == NULL) break; - ALLOCNO_HARD_REG_COSTS (p) [i] -= cost; - ALLOCNO_CONFLICT_HARD_REG_COSTS (p) [i] -= cost; - ALLOCNO_COVER_CLASS_COST (p) - = MIN (ALLOCNO_COVER_CLASS_COST (p), - ALLOCNO_HARD_REG_COSTS (p) [i]); + ALLOCNO_HARD_REG_COSTS (a) [i] -= cost; + ALLOCNO_CONFLICT_HARD_REG_COSTS (a) [i] -= cost; + ALLOCNO_COVER_CLASS_COST (a) + = MIN (ALLOCNO_COVER_CLASS_COST (a), + ALLOCNO_HARD_REG_COSTS (a) [i]); } } } @@ -1331,45 +1385,46 @@ process_bb_node_for_hard_reg_moves (struct ira_loop_tree_node *loop_tree_node) static void setup_allocno_cover_class_and_costs (void) { - int i, j, n, regno, cost, min_cost; - int *reg_costs, *reg_conflict_costs; + int i, j, n, regno; + int *reg_costs; + int *reg_conflict_costs; enum reg_class cover_class, class; enum machine_mode mode; - allocno_t p; + allocno_t a; for (i = 0; i < allocnos_num; i++) { - p = allocnos [i]; - mode = ALLOCNO_MODE (p); + a = allocnos [i]; + mode = ALLOCNO_MODE (a); cover_class = class_translate [allocno_pref [i]]; ira_assert (allocno_pref [i] == NO_REGS || cover_class != NO_REGS); - ALLOCNO_ORIGINAL_MEMORY_COST (p) - = ALLOCNO_MEMORY_COST (p) = costs [i].mem_cost; - ALLOCNO_COVER_CLASS (p) = cover_class; - ALLOCNO_BEST_CLASS (p) = allocno_pref [i]; + ALLOCNO_MEMORY_COST (a) = ALLOCNO_UPDATED_MEMORY_COST (a) + = COSTS_OF_ALLOCNO (total_costs, i)->mem_cost; + ALLOCNO_COVER_CLASS (a) = cover_class; + ALLOCNO_BEST_CLASS (a) = allocno_pref [i]; if (cover_class == NO_REGS) continue; - ALLOCNO_AVAILABLE_REGS_NUM (p) = available_class_regs [cover_class]; - min_cost = costs [i].cost [allocno_pref [i]]; - ALLOCNO_COVER_CLASS_COST (p) = min_cost; + ALLOCNO_AVAILABLE_REGS_NUM (a) = available_class_regs [cover_class]; + ALLOCNO_COVER_CLASS_COST (a) + = (COSTS_OF_ALLOCNO (total_costs, i) + ->cost [important_class_nums [allocno_pref [i]]]); n = class_hard_regs_num [cover_class]; - ALLOCNO_HARD_REG_COSTS (p) = reg_costs = ira_allocate (n * sizeof (int)); - ALLOCNO_CONFLICT_HARD_REG_COSTS (p) + ALLOCNO_HARD_REG_COSTS (a) = reg_costs = ira_allocate (n * sizeof (int)); + ALLOCNO_CONFLICT_HARD_REG_COSTS (a) = reg_conflict_costs = ira_allocate (n * sizeof (int)); - ALLOCNO_CURR_HARD_REG_COSTS (p) = ira_allocate (n * sizeof (int)); - ALLOCNO_CURR_CONFLICT_HARD_REG_COSTS (p) + ALLOCNO_UPDATED_HARD_REG_COSTS (a) = ira_allocate (n * sizeof (int)); + ALLOCNO_UPDATED_CONFLICT_HARD_REG_COSTS (a) = ira_allocate (n * sizeof (int)); memset (reg_conflict_costs, 0, n * sizeof (int)); for (j = n - 1; j >= 0; j--) { regno = class_hard_regs [cover_class] [j]; class = REGNO_REG_CLASS (regno); - /* ??? what is cost AREG when DImode. Should be ok. */ - cost = costs [i].cost [class]; - reg_costs [j] = cost; + reg_costs [j] = (COSTS_OF_ALLOCNO (total_costs, i) + ->cost [important_class_nums [class]]); } } - traverse_loop_tree (ira_loop_tree_root, + traverse_loop_tree (FALSE, ira_loop_tree_root, process_bb_node_for_hard_reg_moves, NULL); } @@ -1382,9 +1437,33 @@ init_ira_costs_once (void) { int i; - init_cost.mem_cost = 10000; - for (i = 0; i < N_REG_CLASSES; i++) - init_cost.cost [i] = 10000; + struct_costs_size + = sizeof (struct costs) + sizeof (int) * (important_classes_num - 1); + init_cost = xmalloc (struct_costs_size); + init_cost->mem_cost = 10000; + for (i = 0; i < important_classes_num; i++) + init_cost->cost [i] = 10000; + for (i = 0; i < MAX_RECOG_OPERANDS; i++) + { + op_costs [i] = xmalloc (struct_costs_size); + this_op_costs [i] = xmalloc (struct_costs_size); + } + temp_costs = xmalloc (struct_costs_size); +} + +/* Function called once at the end of compiler work. */ +void +finish_ira_costs_once (void) +{ + int i; + + free (temp_costs); + for (i = MAX_RECOG_OPERANDS - 1; i >= 0; i--) + { + free (this_op_costs [i]); + free (op_costs [i]); + } + free (init_cost); } @@ -1394,12 +1473,12 @@ init_ira_costs_once (void) void ira_costs (void) { - costs = ira_allocate (sizeof (struct costs) * allocnos_num); + total_costs = ira_allocate (struct_costs_size * allocnos_num); allocno_pref_buffer = ira_allocate (sizeof (enum reg_class) * allocnos_num); find_allocno_class_costs (); setup_allocno_cover_class_and_costs (); ira_free (allocno_pref_buffer); - ira_free (costs); + ira_free (total_costs); } @@ -1410,24 +1489,25 @@ ira_costs (void) void tune_allocno_costs_and_cover_classes (void) { - int i, j, k, n, regno, cost, min_cost, *reg_costs, freq; + int i, j, k, n, regno, freq; + int cost, min_cost, *reg_costs; enum reg_class cover_class, class; enum machine_mode mode; - allocno_t p; + allocno_t a; rtx call, *allocno_calls; HARD_REG_SET clobbered_regs; for (i = 0; i < allocnos_num; i++) { - p = allocnos [i]; - cover_class = ALLOCNO_COVER_CLASS (p); + a = allocnos [i]; + cover_class = ALLOCNO_COVER_CLASS (a); if (cover_class == NO_REGS) continue; - mode = ALLOCNO_MODE (p); + mode = ALLOCNO_MODE (a); n = class_hard_regs_num [cover_class]; - reg_costs = ALLOCNO_HARD_REG_COSTS (p); + reg_costs = ALLOCNO_HARD_REG_COSTS (a); min_cost = INT_MAX; - if (ALLOCNO_CALLS_CROSSED_NUM (p) != 0) + if (ALLOCNO_CALLS_CROSSED_NUM (a) != 0) for (j = n - 1; j >= 0; j--) { regno = class_hard_regs [cover_class] [j]; @@ -1437,17 +1517,19 @@ tune_allocno_costs_and_cover_classes (void) { /* ??? If only part is call clobbered. */ if (! hard_reg_not_in_set_p (regno, mode, call_used_reg_set)) - cost += (ALLOCNO_CALL_FREQ (p) - * (memory_move_cost [mode] [class] [0] - + memory_move_cost [mode] [class] [1])); + { + cost += (ALLOCNO_CALL_FREQ (a) + * (memory_move_cost [mode] [class] [0] + + memory_move_cost [mode] [class] [1])); + } } else { allocno_calls - = (VEC_address (rtx, regno_calls [ALLOCNO_REGNO (p)]) - + ALLOCNO_CALLS_CROSSED_START (p)); + = (VEC_address (rtx, regno_calls [ALLOCNO_REGNO (a)]) + + ALLOCNO_CALLS_CROSSED_START (a)); ira_assert (allocno_calls != NULL); - for (k = ALLOCNO_CALLS_CROSSED_NUM (p) - 1; k >= 0; k--) + for (k = ALLOCNO_CALLS_CROSSED_NUM (a) - 1; k >= 0; k--) { call = allocno_calls [k]; freq = REG_FREQ_FROM_BB (BLOCK_FOR_INSN (call)); @@ -1457,21 +1539,22 @@ tune_allocno_costs_and_cover_classes (void) FALSE); /* ??? If only part is call clobbered. */ if (! hard_reg_not_in_set_p (regno, mode, clobbered_regs)) - cost += freq * (memory_move_cost [mode] [class] [0] - + memory_move_cost [mode] [class] [1]); + cost + += freq * (memory_move_cost [mode] [class] [0] + + memory_move_cost [mode] [class] [1]); } } #ifdef IRA_HARD_REGNO_ADD_COST_MULTIPLIER cost += ((memory_move_cost [mode] [class] [0] - + memory_move_cost [mode] [class] [1]) * ALLOCNO_FREQ (p) + + memory_move_cost [mode] [class] [1]) + * ALLOCNO_FREQ (a) * IRA_HARD_REGNO_ADD_COST_MULTIPLIER (regno) / 2); #endif reg_costs [j] += cost; if (min_cost > reg_costs [j]) min_cost = reg_costs [j]; } - if (min_cost == INT_MAX) - continue; - ALLOCNO_COVER_CLASS_COST (p) = min_cost; + if (min_cost != INT_MAX) + ALLOCNO_COVER_CLASS_COST (a) = min_cost; } } diff --git a/gcc/ira-emit.c b/gcc/ira-emit.c index cc4088005ad..baba58dd59a 100644 --- a/gcc/ira-emit.c +++ b/gcc/ira-emit.c @@ -54,19 +54,22 @@ static int eq_move_lists_p (struct move *, struct move *); static int change_regs (rtx *); static void add_to_edge_list (edge, struct move *, int); static rtx create_new_reg (rtx); -static int subloop_tree_node_p (struct ira_loop_tree_node *, - struct ira_loop_tree_node *); +static int subloop_tree_node_p (loop_tree_node_t, loop_tree_node_t); static void set_allocno_reg (allocno_t, rtx); static int not_modified_p (allocno_t, allocno_t); static void generate_edge_moves (edge); -static void change_loop (struct ira_loop_tree_node *); +static void change_loop (loop_tree_node_t); static int eq_edge_move_lists_p (VEC(edge,gc) *); -static int can_move_through_p (rtx *, struct move *, int); static void unify_moves (basic_block, int); static void traverse_moves (struct move *); static struct move *modify_move_list (struct move *); static rtx emit_move_list (struct move *, int); static void emit_moves (void); +static void update_costs (allocno_t, int, int); +static void add_range_and_copies_from_move_list (struct move *, + loop_tree_node_t, + bitmap, int); +static void add_ranges_and_copies (void); /* The structure represents allocno shuffling. */ struct move @@ -82,6 +85,8 @@ struct move /* Moves on which given move depends on. Dependency can be cyclic. It means we need a temporary to generates the moves. */ struct move **deps; + /* First insn generated for the move. */ + rtx insn; }; /* Array of moves (indexed by BB index) which should be put at the @@ -105,6 +110,7 @@ create_move (allocno_t to, allocno_t from) move->to = to; move->from = from; move->next = NULL; + move->insn = NULL_RTX; move->visited_p = FALSE; return move; } @@ -221,8 +227,8 @@ create_new_reg (rtx original_reg) REG_USERVAR_P (new_reg) = REG_USERVAR_P (original_reg); REG_POINTER (new_reg) = REG_POINTER (original_reg); REG_ATTRS (new_reg) = REG_ATTRS (original_reg); - if (ira_dump_file) - fprintf (ira_dump_file, " Creating newreg=%i from oldreg=%i\n", + if (internal_flag_ira_verbose > 3 && ira_dump_file != NULL) + fprintf (ira_dump_file, " Creating newreg=%i from oldreg=%i\n", REGNO (new_reg), REGNO (original_reg)); return new_reg; } @@ -230,8 +236,7 @@ create_new_reg (rtx original_reg) /* The function returns non-zero if loop given by SUBNODE inside the loop given by NODE. */ static int -subloop_tree_node_p (struct ira_loop_tree_node *subnode, - struct ira_loop_tree_node *node) +subloop_tree_node_p (loop_tree_node_t subnode, loop_tree_node_t node) { for (; subnode != NULL; subnode = subnode->father) if (subnode == node) @@ -246,7 +251,7 @@ static void set_allocno_reg (allocno_t allocno, rtx reg) { allocno_t a; - struct ira_loop_tree_node *node; + loop_tree_node_t node; node = ALLOCNO_LOOP_TREE_NODE (allocno); for (a = regno_allocno_map [ALLOCNO_REGNO (allocno)]; @@ -263,7 +268,7 @@ not_modified_p (allocno_t src_allocno, allocno_t dest_allocno) { int regno, orig_regno; allocno_t a; - struct ira_loop_tree_node *node; + loop_tree_node_t node; orig_regno = ALLOCNO_REGNO (src_allocno); regno = REGNO (ALLOCNO_REG (dest_allocno)); @@ -285,7 +290,7 @@ not_modified_p (allocno_t src_allocno, allocno_t dest_allocno) static void generate_edge_moves (edge e) { - struct ira_loop_tree_node *src_loop_node, *dest_loop_node; + loop_tree_node_t src_loop_node, dest_loop_node; unsigned int regno; bitmap_iterator bi; allocno_t src_allocno, dest_allocno, *src_map, *dest_map; @@ -314,9 +319,12 @@ generate_edge_moves (edge e) && ALLOCNO_HARD_REGNO (src_allocno) >= 0 && not_modified_p (src_allocno, dest_allocno)) { - if (ira_dump_file != NULL) - fprintf (ira_dump_file, "Remove r%d:%d->%d\n", regno, - ALLOCNO_NUM (src_allocno), ALLOCNO_NUM (dest_allocno)); + ALLOCNO_MEM_OPTIMIZED_DEST (src_allocno) = dest_allocno; + ALLOCNO_MEM_OPTIMIZED_DEST_P (dest_allocno) = TRUE; + if (internal_flag_ira_verbose > 3 && ira_dump_file != NULL) + fprintf (ira_dump_file, " Remove r%d:a%d->a%d(mem)\n", + regno, ALLOCNO_NUM (src_allocno), + ALLOCNO_NUM (dest_allocno)); continue; } move = create_move (dest_allocno, src_allocno); @@ -335,7 +343,7 @@ static bitmap used_regno_bitmap; /* The following function changes (if necessary) pseudo-registers inside loop given by loop tree node NODE. */ static void -change_loop (struct ira_loop_tree_node *node) +change_loop (loop_tree_node_t node) { bitmap_iterator bi; unsigned int i; @@ -357,8 +365,9 @@ change_loop (struct ira_loop_tree_node *node) return; } - if (ira_dump_file != NULL) - fprintf (ira_dump_file, "Changing RTL for loop %d (header bb%d)\n", + if (internal_flag_ira_verbose > 3 && ira_dump_file != NULL) + fprintf (ira_dump_file, + " Changing RTL for loop %d (header bb%d)\n", node->loop->num, node->loop->header->index); map = ira_curr_loop_tree_node->father->regno_allocno_map; @@ -378,6 +387,9 @@ change_loop (struct ira_loop_tree_node *node) && (ALLOCNO_HARD_REGNO (allocno) == ALLOCNO_HARD_REGNO (father_allocno)) && (ALLOCNO_HARD_REGNO (allocno) < 0 + || TEST_HARD_REG_BIT (prohibited_mode_move_regs + [ALLOCNO_MODE (allocno)], + ALLOCNO_HARD_REGNO (allocno)) /* don't create copies because reload can spill a allocno set by copy although allocno will not get memory slot. */ @@ -388,7 +400,7 @@ change_loop (struct ira_loop_tree_node *node) if (father_allocno == NULL || REGNO (ALLOCNO_REG (father_allocno)) == REGNO (original_reg)) { - if (ira_dump_file) + if (internal_flag_ira_verbose > 3 && ira_dump_file) fprintf (ira_dump_file, " %i vs father %i:", ALLOCNO_HARD_REGNO (allocno), ALLOCNO_HARD_REGNO (father_allocno)); @@ -431,114 +443,6 @@ eq_edge_move_lists_p (VEC(edge,gc) *vec) return TRUE; } -/* Current regno allocno map used to check moves in function - `can_move_through_p'. */ -static allocno_t *curr_jump_map; - -/* This recursive function returns nonzero if list of moves LIST can - be moved through setting (if OUTPUT_P is nonzero) or reading - LOC. */ -static int -can_move_through_p (rtx *loc, struct move *list, int output_p) -{ - int i; - const char *fmt; - enum rtx_code code = GET_CODE (*loc); - - code = GET_CODE (*loc); - if (code == REG) - { - int hard_regno, regno; - HARD_REG_SET regs, move_regs; - - regno = ORIGINAL_REGNO (*loc); - hard_regno = REGNO (*loc); - if (hard_regno >= FIRST_PSEUDO_REGISTER) - hard_regno = ALLOCNO_HARD_REGNO (curr_jump_map [regno]); - if (hard_regno < 0) - CLEAR_HARD_REG_SET (regs); - else - COPY_HARD_REG_SET - (regs, reg_mode_hard_regset [hard_regno] [GET_MODE (*loc)]); - for (;list != NULL; list = list->next) - if (output_p - && regno == (int) ORIGINAL_REGNO (ALLOCNO_REG (list->from))) - return FALSE; - else - { - hard_regno = ALLOCNO_HARD_REGNO (list->to); - if (hard_regno < 0) - CLEAR_HARD_REG_SET (move_regs); - else - COPY_HARD_REG_SET - (move_regs, - reg_mode_hard_regset [hard_regno] [ALLOCNO_MODE (list->to)]); - if (output_p) - { - hard_regno = ALLOCNO_HARD_REGNO (list->from); - if (hard_regno >= 0) - IOR_HARD_REG_SET (move_regs, - reg_mode_hard_regset - [hard_regno] [ALLOCNO_MODE (list->from)]); - } - AND_HARD_REG_SET (move_regs, regs); - if (! hard_reg_set_equal_p (move_regs, zero_hard_reg_set)) - return FALSE; - } - return TRUE; - } - else if (code == SET) - { - if (! can_move_through_p (&SET_DEST (*loc), list, TRUE)) - return FALSE; - if (! can_move_through_p (&SET_SRC (*loc), list, FALSE)) - return FALSE; - return TRUE; - } - else if (code == CLOBBER) - { - if (! can_move_through_p (&XEXP (*loc, 0), list, TRUE)) - return FALSE; - return TRUE; - } - else if (code == MEM) - { - if (! can_move_through_p (&XEXP (*loc, 0), list, FALSE)) - return FALSE; - return TRUE; - } - else if (code == PRE_DEC || code == POST_DEC || code == PRE_INC || - code == POST_INC || code == POST_MODIFY || code == PRE_MODIFY) - { - if (! can_move_through_p (&XEXP (*loc, 0), list, TRUE)) - return FALSE; - if (! can_move_through_p (&XEXP (*loc, 0), list, FALSE)) - return FALSE; - return TRUE; - } - - fmt = GET_RTX_FORMAT (code); - for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) - { - if (fmt[i] == 'e') - { - if (! can_move_through_p (&XEXP (*loc, i), list, output_p)) - return FALSE; - } - else if (fmt[i] == 'E') - { - int j; - - for (j = XVECLEN (*loc, i) - 1; j >= 0; j--) - { - if (! can_move_through_p (&XVECEXP (*loc, i, j), list, output_p)) - return FALSE; - } - } - } - return TRUE; -} - /* The function looks at all enter edges (if START_P) or exit edges of basic block BB and puts move lists at the BB start or end if it is possible. In other words, it decreases code duplication of @@ -556,10 +460,7 @@ unify_moves (basic_block bb, int start_p) return; e = EDGE_I (vec, 0); list = e->aux; - curr_jump_map = IRA_BB_NODE (bb)->father->regno_allocno_map; - if (! start_p - && (control_flow_insn_p (BB_END (bb)) - && ! can_move_through_p (&PATTERN (BB_END (bb)), list, FALSE))) + if (! start_p && control_flow_insn_p (BB_END (bb))) return; e->aux = NULL; for (i = EDGE_COUNT (vec) - 1; i > 0; i--) @@ -621,7 +522,7 @@ traverse_moves (struct move *move) static struct move * modify_move_list (struct move *list) { - int i, n, nregs, hard_regno; + int i, n, nregs, hard_regno, hard_regs_num; allocno_t to, from, new_allocno; struct move *move, *new_move, *set_move, *first, *last; @@ -689,14 +590,35 @@ modify_move_list (struct move *list) for (i = 0; i < nregs; i++) if (hard_regno_last_set_check [hard_regno + i] == curr_tick && ALLOCNO_HARD_REGNO (hard_regno_last_set - [hard_regno + i]->to) >= 0) + [hard_regno + i]->to) >= 0) { set_move = hard_regno_last_set [hard_regno + i]; + /* It does not matter what loop_tree_node (of TO or + FROM) to use for the new allocno because of + subsequent IR flattening. */ new_allocno = create_allocno (ALLOCNO_REGNO (set_move->to), FALSE, - ALLOCNO_LOOP_TREE_NODE (set_move->to)); - /* That is a minimum to emit allocnos correctly and for - setting reg_renumber. */ + ALLOCNO_LOOP_TREE_NODE (set_move->to)); + ALLOCNO_MODE (new_allocno) = ALLOCNO_MODE (set_move->to); + ALLOCNO_COVER_CLASS (new_allocno) + = ALLOCNO_COVER_CLASS (set_move->to); + ALLOCNO_BEST_CLASS (new_allocno) + = ALLOCNO_COVER_CLASS (new_allocno); + hard_regs_num + = class_hard_regs_num [ALLOCNO_COVER_CLASS (new_allocno)]; + ALLOCNO_HARD_REG_COSTS (new_allocno) + = ira_allocate (hard_regs_num * sizeof (int)); + memset (ALLOCNO_HARD_REG_COSTS (new_allocno), 0, + hard_regs_num * sizeof (int)); + ALLOCNO_CONFLICT_HARD_REG_COSTS (new_allocno) + = ira_allocate (hard_regs_num * sizeof (int)); + memset (ALLOCNO_CONFLICT_HARD_REG_COSTS (new_allocno), 0, + hard_regs_num * sizeof (int)); + ALLOCNO_UPDATED_HARD_REG_COSTS (new_allocno) + = ira_allocate (hard_regs_num * sizeof (int)); + ALLOCNO_UPDATED_CONFLICT_HARD_REG_COSTS (new_allocno) + = ira_allocate (hard_regs_num * sizeof (int)); + ALLOCNO_ASSIGNED_P (new_allocno) = TRUE; ALLOCNO_HARD_REGNO (new_allocno) = -1; ALLOCNO_REG (new_allocno) = create_new_reg (ALLOCNO_REG (set_move->to)); @@ -704,6 +626,11 @@ modify_move_list (struct move *list) set_move->to = new_allocno; VARRAY_PUSH_GENERIC_PTR (move_varray, new_move); move_loops_num++; + if (internal_flag_ira_verbose > 2 && ira_dump_file != NULL) + fprintf (ira_dump_file, + " Creating temporary allocno a%dr%d\n", + ALLOCNO_NUM (new_allocno), + REGNO (ALLOCNO_REG (new_allocno))); } } if ((hard_regno = ALLOCNO_HARD_REGNO (to)) < 0) @@ -731,15 +658,24 @@ static rtx emit_move_list (struct move *list, int freq) { int cost; - rtx result; + rtx result, insn; enum machine_mode mode; enum reg_class cover_class; - list = modify_move_list (list); start_sequence (); for (; list != NULL; list = list->next) { + start_sequence (); emit_move_insn (ALLOCNO_REG (list->to), ALLOCNO_REG (list->from)); + list->insn = get_insns (); + end_sequence (); + /* The reload needs to have set up insn codes. If the reload + sets up insn codes by itself, it may fail because insns will + have hard registers instead of pseudos and there may be no + machine insn with given hard registers. */ + for (insn = list->insn; insn != NULL_RTX; insn = NEXT_INSN (insn)) + recog_memoized (insn); + emit_insn (list->insn); mode = ALLOCNO_MODE (list->to); cover_class = ALLOCNO_COVER_CLASS (list->to); cost = 0; @@ -785,6 +721,7 @@ emit_moves (void) { if (at_bb_start [bb->index] != NULL) { + at_bb_start [bb->index] = modify_move_list (at_bb_start [bb->index]); insns = emit_move_list (at_bb_start [bb->index], REG_FREQ_FROM_BB (bb)); tmp = BB_HEAD (bb); @@ -802,12 +739,11 @@ emit_moves (void) if (at_bb_end [bb->index] != NULL) { + at_bb_end [bb->index] = modify_move_list (at_bb_end [bb->index]); insns = emit_move_list (at_bb_end [bb->index], REG_FREQ_FROM_BB (bb)); - if (! control_flow_insn_p (BB_END (bb))) - emit_insn_after (insns, BB_END (bb)); - else - emit_insn_before (insns, BB_END (bb)); + ira_assert (! control_flow_insn_p (BB_END (bb))); + emit_insn_after (insns, BB_END (bb)); } FOR_EACH_EDGE (e, ei, bb->succs) @@ -816,6 +752,7 @@ emit_moves (void) continue; ira_assert ((e->flags & EDGE_ABNORMAL) == 0 || ! EDGE_CRITICAL_P (e)); + e->aux = modify_move_list (e->aux); insert_insn_on_edge (emit_move_list (e->aux, REG_FREQ_FROM_EDGE_FREQ (EDGE_FREQUENCY (e))), @@ -826,27 +763,186 @@ emit_moves (void) } } +/* Update costs of A and its parents from reading (if READ_P) or + writing A on an execution path with FREQ. */ +static void +update_costs (allocno_t a, int read_p, int freq) +{ + loop_tree_node_t father; + + for (;;) + { + ALLOCNO_NREFS (a)++; + ALLOCNO_FREQ (a) += freq; + ALLOCNO_MEMORY_COST (a) + += (memory_move_cost [ALLOCNO_MODE (a)] [ALLOCNO_COVER_CLASS (a)] + [read_p ? 1 : 0] * freq); + if ((father = ALLOCNO_LOOP_TREE_NODE (a)->father) == NULL + || (a = father->regno_allocno_map [ALLOCNO_REGNO (a)]) == NULL) + break; + } +} + +/* The function processes moves from LIST with execution FREQ to add + ranges, copies, and modify costs. All regnos living through the + list is in LIVE_THROUGH, and the loop tree node used to find + corresponding allocnos is NODE. */ +static void +add_range_and_copies_from_move_list (struct move *list, loop_tree_node_t node, + bitmap live_through, int freq) +{ + int start, n; + unsigned int regno; + struct move *move; + allocno_t to, from, a; + copy_t cp; + allocno_live_range_t r; + bitmap_iterator bi; + HARD_REG_SET hard_regs_live; + + if (list == NULL) + return; + n = 0; + EXECUTE_IF_SET_IN_BITMAP (live_through, FIRST_PSEUDO_REGISTER, regno, bi) + n++; + REG_SET_TO_HARD_REG_SET (hard_regs_live, live_through); + /* This is a trick to guarantee that new ranges is not merged with + the old ones. */ + max_point++; + start = max_point; + for (move = list; move != NULL; move = move->next) + { + from = move->from; + to = move->to; + if (ALLOCNO_CONFLICT_ALLOCNO_VEC (to) == NULL) + { + if (internal_flag_ira_verbose > 2 && ira_dump_file != NULL) + fprintf (ira_dump_file, + " Allocate conflict vector of size %d for a%dr%d\n", + n, ALLOCNO_NUM (to), REGNO (ALLOCNO_REG (to))); + allocate_allocno_conflicts (to, n); + } + bitmap_clear_bit (live_through, ALLOCNO_REGNO (from)); + bitmap_clear_bit (live_through, ALLOCNO_REGNO (to)); + IOR_HARD_REG_SET (ALLOCNO_CONFLICT_HARD_REGS (from), hard_regs_live); + IOR_HARD_REG_SET (ALLOCNO_CONFLICT_HARD_REGS (to), hard_regs_live); + IOR_HARD_REG_SET (ALLOCNO_TOTAL_CONFLICT_HARD_REGS (from), + hard_regs_live); + IOR_HARD_REG_SET (ALLOCNO_TOTAL_CONFLICT_HARD_REGS (to), hard_regs_live); + update_costs (from, TRUE, freq); + update_costs (to, FALSE, freq); + cp = add_allocno_copy (from, to, freq, move->insn, NULL); + if (internal_flag_ira_verbose > 2 && ira_dump_file != NULL) + fprintf (ira_dump_file, " Adding cp%d:a%dr%d-a%dr%d\n", + cp->num, ALLOCNO_NUM (cp->first), + REGNO (ALLOCNO_REG (cp->first)), ALLOCNO_NUM (cp->second), + REGNO (ALLOCNO_REG (cp->second))); + r = ALLOCNO_LIVE_RANGES (from); + if (r == NULL || r->finish >= 0) + { + ALLOCNO_LIVE_RANGES (from) + = create_allocno_live_range (from, start, max_point, r); + if (internal_flag_ira_verbose > 2 && ira_dump_file != NULL) + fprintf (ira_dump_file, + " Adding range [%d..%d] to allocno a%dr%d\n", + start, max_point, ALLOCNO_NUM (from), + REGNO (ALLOCNO_REG (from))); + } + else + r->finish = max_point; + max_point++; + ALLOCNO_LIVE_RANGES (to) + = create_allocno_live_range (to, max_point, -1, + ALLOCNO_LIVE_RANGES (to)); + max_point++; + } + for (move = list; move != NULL; move = move->next) + { + r = ALLOCNO_LIVE_RANGES (move->to); + if (r->finish < 0) + { + r->finish = max_point - 1; + if (internal_flag_ira_verbose > 2 && ira_dump_file != NULL) + fprintf (ira_dump_file, + " Adding range [%d..%d] to allocno a%dr%d\n", + r->start, r->finish, ALLOCNO_NUM (move->to), + REGNO (ALLOCNO_REG (move->to))); + } + } + EXECUTE_IF_SET_IN_BITMAP (live_through, FIRST_PSEUDO_REGISTER, regno, bi) + { + a = node->regno_allocno_map [regno]; + if (ALLOCNO_MEM_OPTIMIZED_DEST (a) == NULL) + { + ALLOCNO_LIVE_RANGES (a) + = create_allocno_live_range (a, start, max_point - 1, + ALLOCNO_LIVE_RANGES (a)); + if (internal_flag_ira_verbose > 2 && ira_dump_file != NULL) + fprintf + (ira_dump_file, + " Adding range [%d..%d] to live through allocno a%dr%d\n", + start, max_point - 1, ALLOCNO_NUM (a), REGNO (ALLOCNO_REG (a))); + } + } +} + +/* The function processes all move list to add ranges, conflicts, + copies, and modify costs. */ +static void +add_ranges_and_copies (void) +{ + basic_block bb; + edge_iterator ei; + edge e; + loop_tree_node_t node; + bitmap live_through; + + live_through = ira_allocate_bitmap (); + FOR_EACH_BB (bb) + { + /* It does not matter what loop_tree_node (of source or + destination block) to use for searching allocnos by their + regnos because of subsequent IR flattening. */ + node = IRA_BB_NODE (bb)->father; + bitmap_copy (live_through, DF_LR_IN (bb)); + add_range_and_copies_from_move_list + (at_bb_start [bb->index], node, live_through, REG_FREQ_FROM_BB (bb)); + bitmap_copy (live_through, DF_LR_OUT (bb)); + add_range_and_copies_from_move_list + (at_bb_end [bb->index], node, live_through, REG_FREQ_FROM_BB (bb)); + FOR_EACH_EDGE (e, ei, bb->succs) + { + bitmap_and (live_through, DF_LR_IN (e->dest), DF_LR_OUT (bb)); + add_range_and_copies_from_move_list + (e->aux, node, live_through, + REG_FREQ_FROM_EDGE_FREQ (EDGE_FREQUENCY (e))); + } + } + ira_free_bitmap (live_through); +} + /* Entry function changing code and generating allocno shuffling for - the regional register allocation. */ + the regional (LOOPS_P is TRUE in this case) register allocation. */ void -ira_emit (void) +ira_emit (int loops_p) { int i; basic_block bb; edge_iterator ei; edge e; + for (i = 0; i < allocnos_num; i++) + ALLOCNO_REG (allocnos [i]) = regno_reg_rtx [ALLOCNO_REGNO (allocnos [i])]; + if (! loops_p) + return; at_bb_start = ira_allocate (sizeof (struct move *) * last_basic_block); memset (at_bb_start, 0, sizeof (struct move *) * last_basic_block); at_bb_end = ira_allocate (sizeof (struct move *) * last_basic_block); memset (at_bb_end, 0, sizeof (struct move *) * last_basic_block); - for (i = 0; i < allocnos_num; i++) - if (ALLOCNO_CAP_MEMBER (allocnos [i]) == NULL) - ALLOCNO_REG (allocnos [i]) = regno_reg_rtx [ALLOCNO_REGNO (allocnos [i])]; local_allocno_bitmap = ira_allocate_bitmap (); used_regno_bitmap = ira_allocate_bitmap (); max_regno_before_changing = max_reg_num (); - traverse_loop_tree (ira_loop_tree_root, change_loop, NULL); + traverse_loop_tree (FALSE, ira_loop_tree_root, change_loop, NULL); ira_free_bitmap (used_regno_bitmap); ira_free_bitmap (local_allocno_bitmap); FOR_EACH_BB (bb) @@ -868,6 +964,7 @@ ira_emit (void) unify_moves (bb, FALSE); VARRAY_GENERIC_PTR_NOGC_INIT (move_varray, allocnos_num, "ordered moves"); emit_moves (); + add_ranges_and_copies (); /* Clean up: */ FOR_EACH_BB (bb) { diff --git a/gcc/ira-int.h b/gcc/ira-int.h index 20054b772b7..4030173c47b 100644 --- a/gcc/ira-int.h +++ b/gcc/ira-int.h @@ -22,6 +22,7 @@ Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA #include "cfgloop.h" #include "ira.h" +#include "alloc-pool.h" #ifdef ENABLE_CHECKING #define ENABLE_IRA_CHECKING @@ -46,10 +47,18 @@ Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA /* All natural loops. */ extern struct loops ira_loops; +/* The flag value used internally. */ +extern int internal_flag_ira_verbose; + /* Dump file of the allocator if it is not NULL. */ extern FILE *ira_dump_file; -/* Allocno and copy of allocnos. */ +/* Pools for allocnos, copies, allocno live ranges. */ +extern alloc_pool allocno_pool, copy_pool, allocno_live_range_pool; + +/* Allocno live range, allocno, and copy of allocnos. */ +typedef struct loop_tree_node *loop_tree_node_t; +typedef struct allocno_live_range *allocno_live_range_t; typedef struct allocno *allocno_t; typedef struct allocno_copy *copy_t; @@ -59,17 +68,20 @@ typedef struct allocno_copy *copy_t; part of the tree from cfgloop.h). We also use the nodes for storing additional information about basic blocks/loops for the register allocation. */ -struct ira_loop_tree_node +struct loop_tree_node { /* The node represents basic block if inner == NULL. */ basic_block bb; /* NULL for loop. */ struct loop *loop; /* NULL for BB. */ /* The next node on the same tree level. */ - struct ira_loop_tree_node *next; + loop_tree_node_t next; /* The first node immediately inside the node. */ - struct ira_loop_tree_node *inner; + loop_tree_node_t inner; /* The node containing given node. */ - struct ira_loop_tree_node *father; + loop_tree_node_t father; + + /* Loop level in range [0, ira_loop_tree_height). */ + int level; /* Allocnos in loop corresponding to regnos. If it is NULL the loop is not included in the loop tree (e.g. it has abnormal enter/exit @@ -94,17 +106,20 @@ struct ira_loop_tree_node }; /* The root of the loop tree corresponding to the all function. */ -extern struct ira_loop_tree_node *ira_loop_tree_root; +extern loop_tree_node_t ira_loop_tree_root; + +/* Height of the loop tree. */ +extern int ira_loop_tree_height; /* All basic block data are referred through the following array. We can not use member `aux' for this because it is used for insertion of insns on edges. */ -extern struct ira_loop_tree_node *ira_bb_nodes; +extern loop_tree_node_t ira_bb_nodes; /* Two access macros to the basic block data. */ #if defined ENABLE_IRA_CHECKING && (GCC_VERSION >= 2007) #define IRA_BB_NODE_BY_INDEX(index) __extension__ \ -(({ struct ira_loop_tree_node *const _node = (&ira_bb_nodes [index]); \ +(({ loop_tree_node_t _node = (&ira_bb_nodes [index]); \ if (_node->inner != NULL || _node->loop != NULL || _node->bb == NULL)\ { \ fprintf (stderr, \ @@ -120,12 +135,12 @@ extern struct ira_loop_tree_node *ira_bb_nodes; #define IRA_BB_NODE(bb) IRA_BB_NODE_BY_INDEX ((bb)->index) /* All loop data are referred through the following array. */ -extern struct ira_loop_tree_node *ira_loop_nodes; +extern loop_tree_node_t ira_loop_nodes; /* Two access macros to the loop data. */ #if defined ENABLE_IRA_CHECKING && (GCC_VERSION >= 2007) #define IRA_LOOP_NODE_BY_INDEX(index) __extension__ \ -(({ struct ira_loop_tree_node *const _node = (&ira_loop_nodes [index]);\ +(({ loop_tree_node_t const _node = (&ira_loop_nodes [index]);\ if (_node->inner == NULL || _node->bb != NULL || _node->loop == NULL)\ { \ fprintf (stderr, \ @@ -142,53 +157,119 @@ extern struct ira_loop_tree_node *ira_loop_nodes; -/* Node representing allocnos (register allocation entity). */ +/* The structure describes program points where a given allocno + lives. */ +struct allocno_live_range +{ + /* Allocno whose live range is described by given structure. */ + allocno_t allocno; + /* Program point range. */ + int start, finish; + /* Next structure describing program points where the allocno + lives. */ + allocno_live_range_t next; + /* Pointer to structures with the same start/finish. */ + allocno_live_range_t start_next, finish_next; +}; + +/* Program points are enumerated by number from range + 0..MAX_POINT-1. */ +extern int max_point; + +/* Arrays of size MAX_POINT mapping a program point to the allocno + live ranges with given start/finish point. */ +extern allocno_live_range_t *start_point_ranges, *finish_point_ranges; + +/* Node representing allocnos (allocation entity). */ struct allocno { /* The allocno order number starting with 0. */ int num; /* Regno for allocno or cap. */ int regno; + /* Mode of the allocno. */ + enum machine_mode mode; /* Final rtx representation of the allocno. */ rtx reg; + /* Hard register assigned to given allocno. Negative value means + that memory was allocated to the allocno. */ + int hard_regno; /* Allocnos with the same regno are linked by the following member. - allocnos corresponding to inner loops are first in the list. */ + Allocnos corresponding to inner loops are first in the list + (depth-first tarverse). */ allocno_t next_regno_allocno; /* There may be different allocnos with the same regno. They are bound to a loop tree node. */ - struct ira_loop_tree_node *loop_tree_node; + loop_tree_node_t loop_tree_node; + /* Accumulated usage references of the allocno. */ + int nrefs; + /* Accumulated frequency of usage of the allocno. */ + int freq; + /* Register class which should be used for allocation for given + allocno. NO_REGS means that we should use memory. */ + enum reg_class cover_class; + /* The biggest register class with minimal cost usage for given + allocno. */ + enum reg_class best_class; + /* Minimal accumulated cost of usage register of the cover class for + the allocno. */ + int cover_class_cost; + /* Minimal accumulated, and updated costs of memory for the + allocno. */ + int memory_cost, updated_memory_cost; + /* Copies to other non-conflicting allocnos. The copies can + represent move insn or potential move insn usually because of two + operand constraints. */ + copy_t allocno_copies; /* It is a allocno (cap) representing given allocno on upper loop tree level. */ allocno_t cap; /* It is a link to allocno (cap) on lower loop level represented by given cap. Null if it is not a cap. */ allocno_t cap_member; - /* Vector of conflicting allocnos with NULL end marker. */ + /* Coalesced allocnos form a cyclic list. One allocno given by + FIRST_COALESCED_ALLOCNO represents all coalesced allocnos. The + list is chained by NEXT_COALESCED_ALLOCNO. */ + allocno_t first_coalesced_allocno; + allocno_t next_coalesced_allocno; + /* Pointer to structures describing at what program point the + allocno lives. We always maintain the condition that *ranges in + the list are not intersected and ordered by decreasing their + program points*. */ + allocno_live_range_t live_ranges; + /* Vector of conflicting allocnos with NULL end marker (first + initial and then accumulated conflict allocnos). Only allocnos + with the same cover class are in the vector. */ allocno_t *conflict_allocno_vec; - /* Allocated and current size (both without NULL marker) of the + /* Allocated size of the previous array. */ + int conflict_allocno_vec_size; + /* Numbers of initial and total (with) accumulated conflicts in the previous array. */ - int conflict_allocno_vec_size, conflict_allocno_vec_active_size; - /* Hard registers conflicting with this allocno and as a consequences - can not be assigned to the allocno. */ - HARD_REG_SET conflict_hard_regs; - /* Frequency of usage of the allocno. */ - int freq; - /* Hard register assigned to given allocno. Negative value means - that memory was allocated to the allocno. */ - int hard_regno; - /* Frequency of calls which given allocno intersects. */ + int conflict_allocnos_num, total_conflict_allocnos_num; + /* Initial and accumulated hard registers conflicting with this + allocno and as a consequences can not be assigned to the + allocno. */ + HARD_REG_SET conflict_hard_regs, total_conflict_hard_regs; + /* Accumulated frequency of calls which given allocno + intersects. */ int call_freq; /* Start index of calls intersected by the allocno in regno_calls [regno]. */ int calls_crossed_start; - /* Length of the previous array (number of the intersected - calls). */ + /* Length of the previous array (number of the intersected calls). */ int calls_crossed_num; + /* Non NULL if we remove restoring value from given allocno to + MEM_OPTIMIZED_DEST at the end of loop because the value is not + changed in loop. */ + allocno_t mem_optimized_dest; + /* TRUE if the allocno was a destination of removed move at the end + of loop because the value is not changed in loop. */ + unsigned int mem_optimized_dest_p : 1; #ifdef STACK_REGS /* Set to TRUE if allocno can't be allocated in the stack register. */ - unsigned int no_stack_reg_p : 1; + unsigned int no_stack_reg_p : 1, total_no_stack_reg_p : 1; #endif /* TRUE value means than the allocno was not removed from the conflicting graph during colouring. */ @@ -199,35 +280,20 @@ struct allocno /* TRUE if it is put on the stack to make other allocnos colorable. */ unsigned int may_be_spilled_p : 1; - /* Mode of the allocno. */ - ENUM_BITFIELD(machine_mode) mode : 8; - /* Copies to other non-conflicting allocnos. The copies can - represent move insn or potential move insn usually because of two - operand constraints. */ - copy_t allocno_copies; - /* Array of additional costs (initial and current during coloring) - for hard regno of allocno cover class. If given allocno represents - a set of allocnos the current costs represents costs of the all - set. */ - int *hard_reg_costs, *curr_hard_reg_costs; - /* Array of decreasing costs (initial and current during coloring) - for conflicting allocnos for hard regno of the allocno cover class. - The member values can be NULL if all costs are the same. If - given allocno represents a set of allocnos the current costs - represents costs of the all set. */ - int *conflict_hard_reg_costs, *curr_conflict_hard_reg_costs; + /* Array of additional costs (accumulated and the one updated during + coloring) for hard regno of allocno cover class. If given + allocno represents a set of allocnos the current costs represents + costs of the all set. */ + int *hard_reg_costs, *updated_hard_reg_costs; + /* Array of decreasing costs (accumulated and the one updated during + coloring) for conflicting allocnos for hard regno of the allocno + cover class. The member values can be NULL if all costs are the + same. If given allocno represents a set of allocnos the current + costs represents costs of the all set. */ + int *conflict_hard_reg_costs, *updated_conflict_hard_reg_costs; /* Number of allocnos with TRUE in_graph_p value and conflicting with given allocno. */ int left_conflicts_num; - /* Register class which should be used for allocation for given - allocno. NO_REGS means that we should use memory. */ - enum reg_class cover_class; - /* The biggest register class with minimal cost usage for given - allocno. */ - enum reg_class best_class; - /* Minimal cost of usage register of the cover class and memory for - the allocno. */ - int cover_class_cost, memory_cost, original_memory_cost; /* Number of hard register of the allocno cover class really availiable for the allocno allocation. */ int available_regs_num; @@ -237,66 +303,69 @@ struct allocno allocno_t prev_bucket_allocno; /* Used for temporary purposes. */ int temp; - /* Coalesced allocnos form a cyclic list. One allocno given by - FIRST_COALESCED_ALLOCNO represents all coalesced allocnos. The - list is chained by NEXT_COALESCED_ALLOCNO. */ - allocno_t first_coalesced_allocno; - allocno_t next_coalesced_allocno; }; /* All members of the allocno node should be accessed only through the following macros. */ -#define ALLOCNO_NUM(P) ((P)->num) -#define ALLOCNO_REGNO(P) ((P)->regno) -#define ALLOCNO_REG(P) ((P)->reg) -#define ALLOCNO_NEXT_REGNO_ALLOCNO(P) ((P)->next_regno_allocno) -#define ALLOCNO_LOOP_TREE_NODE(P) ((P)->loop_tree_node) -#define ALLOCNO_CAP(P) ((P)->cap) -#define ALLOCNO_CAP_MEMBER(P) ((P)->cap_member) -#define ALLOCNO_CONFLICT_ALLOCNO_VEC(P) ((P)->conflict_allocno_vec) -#define ALLOCNO_CONFLICT_ALLOCNO_VEC_SIZE(P) ((P)->conflict_allocno_vec_size) -#define ALLOCNO_CONFLICT_ALLOCNO_VEC_ACTIVE_SIZE(P) \ - ((P)->conflict_allocno_vec_active_size) -#define ALLOCNO_CONFLICT_HARD_REGS(P) ((P)->conflict_hard_regs) -#define ALLOCNO_FREQ(P) ((P)->freq) -#define ALLOCNO_HARD_REGNO(P) ((P)->hard_regno) -#define ALLOCNO_CALL_FREQ(P) ((P)->call_freq) -#define ALLOCNO_CALLS_CROSSED_START(P) ((P)->calls_crossed_start) -#define ALLOCNO_CALLS_CROSSED_NUM(P) ((P)->calls_crossed_num) +#define ALLOCNO_NUM(A) ((A)->num) +#define ALLOCNO_REGNO(A) ((A)->regno) +#define ALLOCNO_REG(A) ((A)->reg) +#define ALLOCNO_NEXT_REGNO_ALLOCNO(A) ((A)->next_regno_allocno) +#define ALLOCNO_LOOP_TREE_NODE(A) ((A)->loop_tree_node) +#define ALLOCNO_CAP(A) ((A)->cap) +#define ALLOCNO_CAP_MEMBER(A) ((A)->cap_member) +#define ALLOCNO_CONFLICT_ALLOCNO_VEC(A) ((A)->conflict_allocno_vec) +#define ALLOCNO_CONFLICT_ALLOCNO_VEC_SIZE(A) ((A)->conflict_allocno_vec_size) +#define ALLOCNO_CONFLICT_ALLOCNOS_NUM(A) ((A)->conflict_allocnos_num) +#define ALLOCNO_TOTAL_CONFLICT_ALLOCNOS_NUM(A) \ + ((A)->total_conflict_allocnos_num) +#define ALLOCNO_CONFLICT_HARD_REGS(A) ((A)->conflict_hard_regs) +#define ALLOCNO_TOTAL_CONFLICT_HARD_REGS(A) ((A)->total_conflict_hard_regs) +#define ALLOCNO_NREFS(A) ((A)->nrefs) +#define ALLOCNO_FREQ(A) ((A)->freq) +#define ALLOCNO_HARD_REGNO(A) ((A)->hard_regno) +#define ALLOCNO_CALL_FREQ(A) ((A)->call_freq) +#define ALLOCNO_CALLS_CROSSED_START(A) ((A)->calls_crossed_start) +#define ALLOCNO_CALLS_CROSSED_NUM(A) ((A)->calls_crossed_num) +#define ALLOCNO_MEM_OPTIMIZED_DEST(A) ((A)->mem_optimized_dest) +#define ALLOCNO_MEM_OPTIMIZED_DEST_P(A) ((A)->mem_optimized_dest_p) #ifdef STACK_REGS -#define ALLOCNO_NO_STACK_REG_P(P) ((P)->no_stack_reg_p) +#define ALLOCNO_NO_STACK_REG_P(A) ((A)->no_stack_reg_p) +#define ALLOCNO_TOTAL_NO_STACK_REG_P(A) ((A)->total_no_stack_reg_p) #endif -#define ALLOCNO_IN_GRAPH_P(P) ((P)->in_graph_p) -#define ALLOCNO_ASSIGNED_P(P) ((P)->assigned_p) -#define ALLOCNO_MAY_BE_SPILLED_P(P) ((P)->may_be_spilled_p) -#define ALLOCNO_MODE(P) ((P)->mode) -#define ALLOCNO_COPIES(P) ((P)->allocno_copies) -#define ALLOCNO_HARD_REG_COSTS(P) ((P)->hard_reg_costs) -#define ALLOCNO_CURR_HARD_REG_COSTS(P) ((P)->curr_hard_reg_costs) -#define ALLOCNO_CONFLICT_HARD_REG_COSTS(P) ((P)->conflict_hard_reg_costs) -#define ALLOCNO_CURR_CONFLICT_HARD_REG_COSTS(P) \ - ((P)->curr_conflict_hard_reg_costs) -#define ALLOCNO_LEFT_CONFLICTS_NUM(P) ((P)->left_conflicts_num) -#define ALLOCNO_BEST_CLASS(P) ((P)->best_class) -#define ALLOCNO_COVER_CLASS(P) ((P)->cover_class) -#define ALLOCNO_COVER_CLASS_COST(P) ((P)->cover_class_cost) -#define ALLOCNO_MEMORY_COST(P) ((P)->memory_cost) -#define ALLOCNO_ORIGINAL_MEMORY_COST(P) ((P)->original_memory_cost) -#define ALLOCNO_AVAILABLE_REGS_NUM(P) ((P)->available_regs_num) -#define ALLOCNO_NEXT_BUCKET_ALLOCNO(P) ((P)->next_bucket_allocno) -#define ALLOCNO_PREV_BUCKET_ALLOCNO(P) ((P)->prev_bucket_allocno) -#define ALLOCNO_TEMP(P) ((P)->temp) -#define ALLOCNO_FIRST_COALESCED_ALLOCNO(P) ((P)->first_coalesced_allocno) -#define ALLOCNO_NEXT_COALESCED_ALLOCNO(P) ((P)->next_coalesced_allocno) - -/* Map regno -> allocno for the current loop tree node. */ +#define ALLOCNO_IN_GRAPH_P(A) ((A)->in_graph_p) +#define ALLOCNO_ASSIGNED_P(A) ((A)->assigned_p) +#define ALLOCNO_MAY_BE_SPILLED_P(A) ((A)->may_be_spilled_p) +#define ALLOCNO_MODE(A) ((A)->mode) +#define ALLOCNO_COPIES(A) ((A)->allocno_copies) +#define ALLOCNO_HARD_REG_COSTS(A) ((A)->hard_reg_costs) +#define ALLOCNO_UPDATED_HARD_REG_COSTS(A) ((A)->updated_hard_reg_costs) +#define ALLOCNO_CONFLICT_HARD_REG_COSTS(A) \ + ((A)->conflict_hard_reg_costs) +#define ALLOCNO_UPDATED_CONFLICT_HARD_REG_COSTS(A) \ + ((A)->updated_conflict_hard_reg_costs) +#define ALLOCNO_LEFT_CONFLICTS_NUM(A) ((A)->left_conflicts_num) +#define ALLOCNO_BEST_CLASS(A) ((A)->best_class) +#define ALLOCNO_COVER_CLASS(A) ((A)->cover_class) +#define ALLOCNO_COVER_CLASS_COST(A) ((A)->cover_class_cost) +#define ALLOCNO_MEMORY_COST(A) ((A)->memory_cost) +#define ALLOCNO_UPDATED_MEMORY_COST(A) ((A)->updated_memory_cost) +#define ALLOCNO_AVAILABLE_REGS_NUM(A) ((A)->available_regs_num) +#define ALLOCNO_NEXT_BUCKET_ALLOCNO(A) ((A)->next_bucket_allocno) +#define ALLOCNO_PREV_BUCKET_ALLOCNO(A) ((A)->prev_bucket_allocno) +#define ALLOCNO_TEMP(A) ((A)->temp) +#define ALLOCNO_FIRST_COALESCED_ALLOCNO(A) ((A)->first_coalesced_allocno) +#define ALLOCNO_NEXT_COALESCED_ALLOCNO(A) ((A)->next_coalesced_allocno) +#define ALLOCNO_LIVE_RANGES(A) ((A)->live_ranges) + +/* Map regno -> allocnos. */ extern allocno_t *regno_allocno_map; -/* Array of references to all allocnos. The order number of the allocno - corresponds to the index in the array. */ +/* Array of references to all allocnos. The order number of the + allocno corresponds to the index in the array. */ extern allocno_t *allocnos; -/* Size of the previous array. */ +/* Sizes of the previous array. */ extern int allocnos_num; /* The following structure represents a copy of given allocno to @@ -320,6 +389,8 @@ struct allocno_copy /* Copies with the same allocno as SECOND are linked by the two following members. */ copy_t prev_second_allocno_copy, next_second_allocno_copy; + /* Region from which given copy is originated. */ + loop_tree_node_t loop_tree_node; }; /* Array of references to copies. The order number of the copy @@ -365,6 +436,47 @@ extern int reg_class_nregs [N_REG_CLASSES] [MAX_MACHINE_MODE]; /* Maximal value of the previous array elements. */ extern int max_nregs; +/* The number of bits in each element of `allocnos_live' and what + type that element has. We use the largest integer format on the + host machine. */ +#define INT_BITS HOST_BITS_PER_WIDE_INT +#define INT_TYPE HOST_WIDE_INT + +/* Set, clear or test bit number I in R, a bit vector indexed by + allocno number. */ +#define SET_ALLOCNO_SET_BIT(R, I) \ + ((R)[(unsigned) (I) / INT_BITS] \ + |= ((INT_TYPE) 1 << ((unsigned) (I) % INT_BITS))) + +#define CLEAR_ALLOCNO_SET_BIT(R, I) \ + ((R) [(unsigned) (I) / INT_BITS] \ + &= ~((INT_TYPE) 1 << ((unsigned) (I) % INT_BITS))) + +#define TEST_ALLOCNO_SET_BIT(R, I) \ + ((R) [(unsigned) (I) / INT_BITS] \ + & ((INT_TYPE) 1 << ((unsigned) (I) % INT_BITS))) + +/* For each allocno set in ALLOCNO_SET, set ALLOCNO to that + allocno, and execute CODE. */ +#define EXECUTE_IF_SET_IN_ALLOCNO_SET(ALLOCNO_SET, ALLOCNO, CODE) \ +do { \ + int i_; \ + int allocno_; \ + INT_TYPE *p_ = (ALLOCNO_SET); \ + \ + for (i_ = allocno_set_words - 1, allocno_ = 0; i_ >= 0; \ + i_--, allocno_ += INT_BITS) \ + { \ + unsigned INT_TYPE word_ = (unsigned INT_TYPE) *p_++; \ + \ + for ((ALLOCNO) = allocno_; word_; word_ >>= 1, (ALLOCNO)++) \ + { \ + if (word_ & 1) \ + {CODE;} \ + } \ + } \ +} while (0) + /* ira.c: */ /* Hard regsets whose all bits are correspondingly zero or one. */ @@ -414,6 +526,11 @@ extern int available_class_regs [N_REG_CLASSES]; extern HARD_REG_SET prohibited_class_mode_regs [N_REG_CLASSES] [NUM_MACHINE_MODES]; +/* Array whose values are hard regset of hard registers for which + move of the hard register in given mode into itself is + prohibited. */ +extern HARD_REG_SET prohibited_mode_move_regs [NUM_MACHINE_MODES]; + /* Number of cover classes. */ extern int reg_class_cover_size; @@ -424,22 +541,26 @@ extern enum reg_class reg_class_cover [N_REG_CLASSES]; /* The value is number of elements in the subsequent array. */ extern int important_classes_num; -/* The array containing classes which are subclasses of a cover - class. */ +/* The array containing classes which are subclasses of cover + classes. */ extern enum reg_class important_classes [N_REG_CLASSES]; +/* The array containing order numbers of important classes (they are + subclasses of cover classes). */ +extern enum reg_class important_class_nums [N_REG_CLASSES]; + /* Map of register classes to corresponding cover class containing the given class. */ extern enum reg_class class_translate [N_REG_CLASSES]; extern void set_non_alloc_regs (int); extern void *ira_allocate (size_t); +extern void *ira_reallocate (void *, size_t); extern void ira_free (void *addr); extern bitmap ira_allocate_bitmap (void); extern void ira_free_bitmap (bitmap); extern regset ira_allocate_regset (void); extern void ira_free_regset (regset); -extern int hard_reg_in_set_p (int, enum machine_mode, HARD_REG_SET); extern int hard_reg_not_in_set_p (int, enum machine_mode, HARD_REG_SET); extern void print_disposition (FILE *); extern void debug_disposition (void); @@ -451,48 +572,66 @@ extern int *reg_equiv_invariant_p; /* Regno equivalent constants. */ extern rtx *reg_equiv_const; -extern char *original_regno_call_crossed_p; -extern int ira_max_regno_before; -extern int ira_max_regno_call_before; - /* ira-build.c */ /* The current loop tree node. */ -extern struct ira_loop_tree_node *ira_curr_loop_tree_node; +extern loop_tree_node_t ira_curr_loop_tree_node; +extern allocno_t *ira_curr_regno_allocno_map; extern VEC(rtx, heap) **regno_calls; extern int add_regno_call (int, rtx); -extern void traverse_loop_tree (struct ira_loop_tree_node *, - void (*) (struct ira_loop_tree_node *), - void (*) (struct ira_loop_tree_node *)); -extern allocno_t create_allocno (int, int, struct ira_loop_tree_node *); +extern void traverse_loop_tree (int, loop_tree_node_t, + void (*) (loop_tree_node_t), + void (*) (loop_tree_node_t)); +extern allocno_t create_allocno (int, int, loop_tree_node_t); extern void allocate_allocno_conflicts (allocno_t, int); +extern int allocno_conflict_index (allocno_t, allocno_t); +extern void add_allocno_conflict (allocno_t, allocno_t); extern void print_expanded_allocno (allocno_t); -extern copy_t create_copy (allocno_t, allocno_t, int, rtx); - +extern allocno_live_range_t create_allocno_live_range (allocno_t, int, int, + allocno_live_range_t); +extern void finish_allocno_live_range (allocno_live_range_t); +extern copy_t create_copy (allocno_t, allocno_t, int, rtx, loop_tree_node_t); +extern void add_allocno_copy_to_list (copy_t); +extern void swap_allocno_copy_ends_if_necessary (copy_t); +extern void remove_allocno_copy_from_list (copy_t); +extern copy_t add_allocno_copy (allocno_t, allocno_t, int, rtx, + loop_tree_node_t); + +extern void ira_flattening (int, int); extern int ira_build (int); extern void ira_destroy (void); /* ira-costs.c */ extern void init_ira_costs_once (void); +extern void finish_ira_costs_once (void); extern void ira_costs (void); extern void tune_allocno_costs_and_cover_classes (void); +/* ira-lives.c */ + +/* Number of ints required to hold allocnos_num bits. */ +extern int allocno_set_words; + +extern void rebuild_start_finish_chains (void); +extern void print_live_range_list (FILE *, allocno_live_range_t); +extern void debug_live_range_list (allocno_live_range_t); +extern void debug_allocno_live_ranges (allocno_t); +extern void debug_live_ranges (void); +extern void create_allocno_live_ranges (void); +extern void finish_allocno_live_ranges (void); + /* ira-conflicts.c */ -extern copy_t add_allocno_copy (allocno_t, allocno_t, int, rtx); +extern int allocno_conflict_p (allocno_t, allocno_t); extern int allocno_reg_conflict_p (int, int); -extern void debug_conflicts (void); +extern void debug_conflicts (int); extern void ira_build_conflicts (void); /* ira-color.c */ +extern int loop_edge_freq (loop_tree_node_t, int, int); extern void reassign_conflict_allocnos (int, int); extern void ira_color (void); /* ira-emit.c */ -extern void ira_emit (void); - -/* ira-call.c */ -extern void debug_ira_call_data (void); -extern int split_around_calls (void); -extern int get_around_calls_regno (int); +extern void ira_emit (int); diff --git a/gcc/ira-lives.c b/gcc/ira-lives.c new file mode 100644 index 00000000000..7199af5a6fa --- /dev/null +++ b/gcc/ira-lives.c @@ -0,0 +1,891 @@ +/* IRA processing allocno lives. + Copyright (C) 2006, 2007 + Free Software Foundation, Inc. + Contributed by Vladimir Makarov . + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 2, or (at your option) any later +version. + +GCC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING. If not, write to the Free +Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301, USA. */ + +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "tm.h" +#include "regs.h" +#include "rtl.h" +#include "tm_p.h" +#include "target.h" +#include "flags.h" +#include "hard-reg-set.h" +#include "basic-block.h" +#include "insn-config.h" +#include "recog.h" +#include "toplev.h" +#include "params.h" +#include "df.h" +#include "ira-int.h" + +/* The file contains code is analogous to one in global but the code + works on the allocno basis. */ + +static void set_allocno_live (allocno_t); +static void clear_allocno_live (allocno_t); +static void mark_reg_store (rtx, const_rtx, void *); +static void mark_reg_clobber (rtx, const_rtx, void *); +static void mark_reg_conflicts (rtx); +static void mark_reg_death (rtx); +static enum reg_class single_reg_class (const char *, rtx op, rtx); +static enum reg_class single_reg_operand_class (int); +static void process_single_reg_class_operands (int); +static void process_bb_node_lives (loop_tree_node_t); + +/* Program points are enumerated by number from range + 0..MAX_POINT-1. */ +int max_point; + +/* Arrays of size MAX_POINT mapping a program point to the allocno + live ranges with given start/finish point. */ +allocno_live_range_t *start_point_ranges, *finish_point_ranges; + +/* Number of the current program point. */ +static int curr_point; + +/* Number of ints required to hold allocnos_num bits. */ +int allocno_set_words; + +/* Set, clear or test bit number I in `allocnos_live', + a bit vector indexed by allocno number. */ +#define SET_ALLOCNO_LIVE(I) SET_ALLOCNO_SET_BIT (allocnos_live, I) +#define CLEAR_ALLOCNO_LIVE(I) CLEAR_ALLOCNO_SET_BIT (allocnos_live, I) +#define TEST_ALLOCNO_LIVE(I) TEST_ALLOCNO_SET_BIT (allocnos_live, I) + +/* Bit mask for allocnos live at current point in the scan. */ +static INT_TYPE *allocnos_live; + +/* The same as previous but as bitmap. */ +static bitmap allocnos_live_bitmap; + +/* Set of hard regs (except eliminable ones) currently live (during + scan of all insns). */ +static HARD_REG_SET hard_regs_live; + +/* Loop tree node corresponding to the current basic block. */ +static loop_tree_node_t curr_bb_node; + +/* The function processing birth of register REGNO. It updates living + hard regs and conflict hard regs for living allocnos or starts a + new live range for allocno corresponding to REGNO. */ +static void +make_regno_born (int regno) +{ + int i; + allocno_t a; + allocno_live_range_t p; + + if (regno < FIRST_PSEUDO_REGISTER) + { + SET_HARD_REG_BIT (hard_regs_live, regno); + EXECUTE_IF_SET_IN_ALLOCNO_SET (allocnos_live, i, + { + SET_HARD_REG_BIT (ALLOCNO_CONFLICT_HARD_REGS (allocnos [i]), regno); + SET_HARD_REG_BIT (ALLOCNO_TOTAL_CONFLICT_HARD_REGS (allocnos [i]), + regno); + }); + return; + } + a = ira_curr_regno_allocno_map [regno]; + if (a == NULL) + return; + if ((p = ALLOCNO_LIVE_RANGES (a)) == NULL + || (p->finish != curr_point && p->finish + 1 != curr_point)) + ALLOCNO_LIVE_RANGES (a) + = create_allocno_live_range (a, curr_point, -1, ALLOCNO_LIVE_RANGES (a)); +} + +/* The function processing death of register REGNO. It updates live + hard regs or finish the current live range for allocno + corresponding to REGNO. */ +static void +make_regno_dead (int regno) +{ + allocno_t a; + allocno_live_range_t p; + + if (regno < FIRST_PSEUDO_REGISTER) + { + CLEAR_HARD_REG_BIT (hard_regs_live, regno); + return; + } + a = ira_curr_regno_allocno_map [regno]; + if (a == NULL) + return; + p = ALLOCNO_LIVE_RANGES (a); + ira_assert (p != NULL); + p->finish = curr_point; +} + +/* The function processing birth and, right after then, death of + register REGNO. */ +static void +make_regno_born_and_died (int regno) +{ + make_regno_born (regno); + make_regno_dead (regno); +} + +/* The current pressure for the current basic block. */ +static int curr_reg_pressure [N_REG_CLASSES]; + +/* The function marks allocno A as currently living. */ +static void +set_allocno_live (allocno_t a) +{ + enum reg_class cover_class; + + if (TEST_ALLOCNO_LIVE (ALLOCNO_NUM (a))) + return; + SET_ALLOCNO_LIVE (ALLOCNO_NUM (a)); + IOR_HARD_REG_SET (ALLOCNO_CONFLICT_HARD_REGS (a), hard_regs_live); + IOR_HARD_REG_SET (ALLOCNO_TOTAL_CONFLICT_HARD_REGS (a), hard_regs_live); + bitmap_set_bit (allocnos_live_bitmap, ALLOCNO_NUM (a)); + cover_class = ALLOCNO_COVER_CLASS (a); + curr_reg_pressure [cover_class] + += reg_class_nregs [cover_class] [ALLOCNO_MODE (a)]; + if (curr_bb_node->reg_pressure [cover_class] + < curr_reg_pressure [cover_class]) + curr_bb_node->reg_pressure [cover_class] = curr_reg_pressure [cover_class]; +} + +/* The function marks allocno A as currently not living. */ +static void +clear_allocno_live (allocno_t a) +{ + enum reg_class cover_class; + + if (bitmap_bit_p (allocnos_live_bitmap, ALLOCNO_NUM (a))) + { + cover_class = ALLOCNO_COVER_CLASS (a); + curr_reg_pressure [cover_class] + -= reg_class_nregs [cover_class] [ALLOCNO_MODE (a)]; + ira_assert (curr_reg_pressure [cover_class] >= 0); + } + CLEAR_ALLOCNO_LIVE (ALLOCNO_NUM (a)); + bitmap_clear_bit (allocnos_live_bitmap, ALLOCNO_NUM (a)); +} + +/* Record all regs that are set in any one insn. Communication from + mark_reg_{store,clobber}. */ +static VEC(rtx, heap) *regs_set; + +/* Handle the case where REG is set by the insn being scanned, during + the scan to accumulate conflicts. Store a 1 in hard_regs_live or + allocnos_live for this register or the + corresponding allocno, record how many consecutive hardware + registers it actually needs, and record a conflict with all other + reg allocnos already live. + + Note that even if REG does not remain alive after this insn, we + must mark it here as live, to ensure a conflict between REG and any + other reg allocnos set in this insn that really do live. This is + because those other allocnos could be considered after this. + + REG might actually be something other than a register; if so, we do + nothing. + + SETTER is 0 if this register was modified by an auto-increment + (i.e., a REG_INC note was found for it). */ +static void +mark_reg_store (rtx reg, const_rtx setter ATTRIBUTE_UNUSED, + void *data ATTRIBUTE_UNUSED) +{ + int regno; + + if (GET_CODE (reg) == SUBREG) + reg = SUBREG_REG (reg); + + if (! REG_P (reg)) + return; + + VEC_safe_push (rtx, heap, regs_set, reg); + + regno = REGNO (reg); + + if (regno >= FIRST_PSEUDO_REGISTER) + { + allocno_t a = ira_curr_regno_allocno_map [regno]; + + ira_assert (a != NULL || REG_N_REFS (regno) == 0); + if (a != NULL) + { + if (bitmap_bit_p (allocnos_live_bitmap, ALLOCNO_NUM (a))) + return; + set_allocno_live (a); + } + make_regno_born (regno); + } + else if (! TEST_HARD_REG_BIT (no_alloc_regs, regno)) + { + int last = regno + hard_regno_nregs [regno] [GET_MODE (reg)]; + enum reg_class cover_class; + + while (regno < last) + { + if (! TEST_HARD_REG_BIT (hard_regs_live, regno) + && ! TEST_HARD_REG_BIT (eliminable_regset, regno)) + { + cover_class = class_translate [REGNO_REG_CLASS (regno)]; + curr_reg_pressure [cover_class]++; + make_regno_born (regno); + if (curr_bb_node->reg_pressure [cover_class] + < curr_reg_pressure [cover_class]) + curr_bb_node->reg_pressure [cover_class] + = curr_reg_pressure [cover_class]; + } + regno++; + } + } +} + +/* Like mark_reg_store except notice just CLOBBERs; ignore SETs. */ +static void +mark_reg_clobber (rtx reg, const_rtx setter, void *data) +{ + if (GET_CODE (setter) == CLOBBER) + mark_reg_store (reg, setter, data); +} + +/* Record that REG (or the corresponding allocno) has conflicts with + all the allocno currently live. Do not mark REG (or the allocno) + itself as live. */ +static void +mark_reg_conflicts (rtx reg) +{ + int regno; + + if (GET_CODE (reg) == SUBREG) + reg = SUBREG_REG (reg); + + if (! REG_P (reg)) + return; + + regno = REGNO (reg); + + if (regno >= FIRST_PSEUDO_REGISTER) + make_regno_born_and_died (regno); + else if (! TEST_HARD_REG_BIT (no_alloc_regs, regno)) + { + int last = regno + hard_regno_nregs [regno] [GET_MODE (reg)]; + + while (regno < last) + { + make_regno_born_and_died (regno); + regno++; + } + } +} + +/* Mark REG (or the corresponding allocno) as being dead (following + the insn being scanned now). Store a 0 in hard_regs_live or + allocnos_live for this register. */ +static void +mark_reg_death (rtx reg) +{ + int regno = REGNO (reg); + + if (regno >= FIRST_PSEUDO_REGISTER) + { + allocno_t a = ira_curr_regno_allocno_map [regno]; + + ira_assert (a != NULL || REG_N_REFS (regno) == 0); + if (a != NULL) + { + if (! bitmap_bit_p (allocnos_live_bitmap, ALLOCNO_NUM (a))) + return; + clear_allocno_live (a); + } + make_regno_dead (regno); + } + else if (! TEST_HARD_REG_BIT (no_alloc_regs, regno)) + { + int last = regno + hard_regno_nregs [regno] [GET_MODE (reg)]; + enum reg_class cover_class; + + while (regno < last) + { + if (TEST_HARD_REG_BIT (hard_regs_live, regno)) + { + cover_class = class_translate [REGNO_REG_CLASS (regno)]; + curr_reg_pressure [cover_class]--; + ira_assert (curr_reg_pressure [cover_class] >= 0); + make_regno_dead (regno); + } + regno++; + } + } +} + +/* The function checks that CONSTRAINTS permits to use only one hard + register. If it is so, the function returns the class of the hard + register. Otherwise it returns NO_REGS. + + EQUIV_COSNT ??? */ +static enum reg_class +single_reg_class (const char *constraints, rtx op, rtx equiv_const) +{ + int ignore_p; + enum reg_class cl, next_cl; + int c; + + cl = NO_REGS; + for (ignore_p = FALSE; + (c = *constraints); + constraints += CONSTRAINT_LEN (c, constraints)) + if (c == '#') + ignore_p = TRUE; + else if (c == ',') + ignore_p = FALSE; + else if (! ignore_p) + switch (c) + { + case ' ': + case '\t': + case '=': + case '+': + case '*': + case '&': + case '%': + case '!': + case '?': + break; + case 'i': + if (CONSTANT_P (op) + || (equiv_const != NULL_RTX && CONSTANT_P (equiv_const))) + return NO_REGS; + break; + + case 'n': + if (GET_CODE (op) == CONST_INT + || (GET_CODE (op) == CONST_DOUBLE && GET_MODE (op) == VOIDmode) + || (equiv_const != NULL_RTX + && (GET_CODE (equiv_const) == CONST_INT + || (GET_CODE (equiv_const) == CONST_DOUBLE + && GET_MODE (equiv_const) == VOIDmode)))) + return NO_REGS; + break; + + case 's': + if ((CONSTANT_P (op) && GET_CODE (op) != CONST_INT + && (GET_CODE (op) != CONST_DOUBLE || GET_MODE (op) != VOIDmode)) + || (equiv_const != NULL_RTX + && CONSTANT_P (equiv_const) + && GET_CODE (equiv_const) != CONST_INT + && (GET_CODE (equiv_const) != CONST_DOUBLE + || GET_MODE (equiv_const) != VOIDmode))) + return NO_REGS; + break; + + case 'I': + case 'J': + case 'K': + case 'L': + case 'M': + case 'N': + case 'O': + case 'P': + if ((GET_CODE (op) == CONST_INT + && CONST_OK_FOR_CONSTRAINT_P (INTVAL (op), c, constraints)) + || (equiv_const != NULL_RTX + && GET_CODE (equiv_const) == CONST_INT + && CONST_OK_FOR_CONSTRAINT_P (INTVAL (equiv_const), + c, constraints))) + return NO_REGS; + break; + + case 'E': + case 'F': + if (GET_CODE (op) == CONST_DOUBLE + || (GET_CODE (op) == CONST_VECTOR + && GET_MODE_CLASS (GET_MODE (op)) == MODE_VECTOR_FLOAT) + || (equiv_const != NULL_RTX + && (GET_CODE (equiv_const) == CONST_DOUBLE + || (GET_CODE (equiv_const) == CONST_VECTOR + && (GET_MODE_CLASS (GET_MODE (equiv_const)) + == MODE_VECTOR_FLOAT))))) + return NO_REGS; + break; + + case 'G': + case 'H': + if ((GET_CODE (op) == CONST_DOUBLE + && CONST_DOUBLE_OK_FOR_CONSTRAINT_P (op, c, constraints)) + || (equiv_const != NULL_RTX + && GET_CODE (equiv_const) == CONST_DOUBLE + && CONST_DOUBLE_OK_FOR_CONSTRAINT_P (equiv_const, + c, constraints))) + return NO_REGS; + /* ??? what about memory */ + case 'r': + case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': + case 'h': case 'j': case 'k': case 'l': + case 'q': case 't': case 'u': + case 'v': case 'w': case 'x': case 'y': case 'z': + case 'A': case 'B': case 'C': case 'D': + case 'Q': case 'R': case 'S': case 'T': case 'U': + case 'W': case 'Y': case 'Z': + next_cl = (c == 'r' + ? GENERAL_REGS + : REG_CLASS_FROM_CONSTRAINT (c, constraints)); + if ((cl != NO_REGS && next_cl != cl) + || available_class_regs [next_cl] > 1) + return NO_REGS; + cl = next_cl; + break; + + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + next_cl + = single_reg_class (recog_data.constraints [c - '0'], + recog_data.operand [c - '0'], NULL_RTX); + if ((cl != NO_REGS && next_cl != cl) || next_cl == NO_REGS + || available_class_regs [next_cl] > 1) + return NO_REGS; + cl = next_cl; + break; + + default: + return NO_REGS; + } + return cl; +} + +/* The function checks that operand OP_NUM of the current insn can use + only one hard register. If it is so, the function returns the + class of the hard register. Otherwise it returns NO_REGS. */ +static enum reg_class +single_reg_operand_class (int op_num) +{ + if (op_num < 0 || recog_data.n_alternatives == 0) + return NO_REGS; + return single_reg_class (recog_data.constraints [op_num], + recog_data.operand [op_num], NULL_RTX); +} + +/* The function processes input (if IN_P) or output operands to find + allocno which can use only one hard register and makes other + currently living reg allocnos conflicting with the hard register. */ +static void +process_single_reg_class_operands (int in_p) +{ + int i, regno, px, cost; + enum reg_class cl, cover_class; + rtx operand; + allocno_t operand_a, a; + + for (i = 0; i < recog_data.n_operands; i++) + { + operand = recog_data.operand [i]; + if (in_p && recog_data.operand_type [i] != OP_IN + && recog_data.operand_type [i] != OP_INOUT) + continue; + if (! in_p && recog_data.operand_type [i] != OP_OUT + && recog_data.operand_type [i] != OP_INOUT) + continue; + cl = single_reg_operand_class (i); + if (cl == NO_REGS) + continue; + + operand_a = NULL; + + if (GET_CODE (operand) == SUBREG) + operand = SUBREG_REG (operand); + + if (REG_P (operand) + && (regno = REGNO (operand)) >= FIRST_PSEUDO_REGISTER) + { + enum machine_mode mode; + enum reg_class cover_class; + + operand_a = ira_curr_regno_allocno_map [regno]; + mode = ALLOCNO_MODE (operand_a); + cover_class = ALLOCNO_MODE (operand_a); + if (class_subset_p [cl] [cover_class] + && (reg_class_size [cl] + <= (unsigned) CLASS_MAX_NREGS (cl, mode))) + { + cost = (in_p + ? register_move_cost [mode] [cover_class] [cl] + : register_move_cost [mode] [cl] [cover_class]); + ALLOCNO_CONFLICT_HARD_REG_COSTS (operand_a) + [class_hard_reg_index [cover_class] [class_hard_regs [cl] [0]]] + -= cost; + } + } + + EXECUTE_IF_SET_IN_ALLOCNO_SET (allocnos_live, px, + { + a = allocnos [px]; + cover_class = ALLOCNO_COVER_CLASS (a); + if (a != operand_a) + { + /* We could increase costs of A instead of making it + conflicting with the hard register. But it works worse + because it will be spilled in reload in anyway. */ + IOR_HARD_REG_SET (ALLOCNO_CONFLICT_HARD_REGS (a), + reg_class_contents [cl]); + IOR_HARD_REG_SET (ALLOCNO_TOTAL_CONFLICT_HARD_REGS (a), + reg_class_contents [cl]); + } + }); + } +} + +/* The function processes insns of the basic block given by its + LOOP_TREE_NODE to update allocno conflict table. */ +static void +process_bb_node_lives (loop_tree_node_t loop_tree_node) +{ + int i, index; + unsigned int j; + basic_block bb; + rtx insn; + edge e; + edge_iterator ei; + bitmap_iterator bi; + bitmap reg_live_in; + int px = 0; + + bb = loop_tree_node->bb; + if (bb != NULL) + { + for (i = 0; i < reg_class_cover_size; i++) + curr_reg_pressure [reg_class_cover [i]] = 0; + curr_bb_node = loop_tree_node; + reg_live_in = DF_LR_IN (bb); + memset (allocnos_live, 0, allocno_set_words * sizeof (INT_TYPE)); + REG_SET_TO_HARD_REG_SET (hard_regs_live, reg_live_in); + AND_COMPL_HARD_REG_SET (hard_regs_live, eliminable_regset); + for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) + if (TEST_HARD_REG_BIT (hard_regs_live, i)) + { + enum reg_class cover_class; + + cover_class = class_translate [REGNO_REG_CLASS (i)]; + curr_reg_pressure [cover_class]++; + if (curr_bb_node->reg_pressure [cover_class] + < curr_reg_pressure [cover_class]) + curr_bb_node->reg_pressure [cover_class] + = curr_reg_pressure [cover_class]; + } + bitmap_clear (allocnos_live_bitmap); + EXECUTE_IF_SET_IN_BITMAP (reg_live_in, FIRST_PSEUDO_REGISTER, j, bi) + { + allocno_t a = ira_curr_regno_allocno_map [j]; + + ira_assert (a != NULL || REG_N_REFS (j) == 0); + if (a == NULL) + continue; + ira_assert (! bitmap_bit_p (allocnos_live_bitmap, ALLOCNO_NUM (a))); + set_allocno_live (a); + make_regno_born (j); + } + +#ifdef EH_RETURN_DATA_REGNO + if (bb_has_eh_pred (bb)) + { + for (j = 0; ; ++j) + { + unsigned int regno = EH_RETURN_DATA_REGNO (j); + + if (regno == INVALID_REGNUM) + break; + make_regno_born_and_died (regno); + } + } +#endif + + /* Reg allocnos can't go in stack regs at the start of a basic block + that is reached by an abnormal edge. Likewise for call + clobbered regs, because caller-save, fixup_abnormal_edges and + possibly the table driven EH machinery are not quite ready to + handle such reg allocnos live across such edges. */ + FOR_EACH_EDGE (e, ei, bb->preds) + if (e->flags & EDGE_ABNORMAL) + break; + + if (e != NULL) + { +#ifdef STACK_REGS + EXECUTE_IF_SET_IN_ALLOCNO_SET (allocnos_live, px, + { + ALLOCNO_NO_STACK_REG_P (allocnos [px]) = TRUE; + ALLOCNO_TOTAL_NO_STACK_REG_P (allocnos [px]) = TRUE; + }); + for (px = FIRST_STACK_REG; px <= LAST_STACK_REG; px++) + make_regno_born_and_died (px); +#endif + /* No need to record conflicts for call clobbered regs if we + have nonlocal labels around, as we don't ever try to + allocate such regs in this case. */ + if (! current_function_has_nonlocal_label) + for (px = 0; px < FIRST_PSEUDO_REGISTER; px++) + if (call_used_regs [px]) + make_regno_born_and_died (px); + } + + /* Scan the code of this basic block, noting which allocnos and + hard regs are born or die. When one is born, record a + conflict with all others currently live. */ + FOR_BB_INSNS (bb, insn) + { + rtx link; + + if (! INSN_P (insn)) + continue; + + if (internal_flag_ira_verbose > 2 && ira_dump_file != NULL) + fprintf (ira_dump_file, " Insn %u(l%d): point = %d\n", + INSN_UID (insn), loop_tree_node->father->loop->num, + curr_point); + + /* Check regs_set is an empty set. */ + gcc_assert (VEC_empty (rtx, regs_set)); + + /* Mark any allocnos clobbered by INSN as live, so they + conflict with the inputs. */ + note_stores (PATTERN (insn), mark_reg_clobber, NULL); + + extract_insn (insn); + process_single_reg_class_operands (TRUE); + + /* Mark any allocnos dead after INSN as dead now. */ + for (link = REG_NOTES (insn); link; link = XEXP (link, 1)) + if (REG_NOTE_KIND (link) == REG_DEAD) + mark_reg_death (XEXP (link, 0)); + + curr_point++; + if (CALL_P (insn)) + { + HARD_REG_SET clobbered_regs; + + get_call_invalidated_used_regs (insn, &clobbered_regs, FALSE); + IOR_HARD_REG_SET (cfun->emit->call_used_regs, clobbered_regs); + EXECUTE_IF_SET_IN_ALLOCNO_SET (allocnos_live, i, + { + int freq; + allocno_t a = allocnos [i]; + + freq = REG_FREQ_FROM_BB (BLOCK_FOR_INSN (insn)); + if (freq == 0) + freq = 1; + ALLOCNO_CALL_FREQ (a) += freq; + index = add_regno_call (ALLOCNO_REGNO (a), insn); + if (ALLOCNO_CALLS_CROSSED_START (a) < 0) + ALLOCNO_CALLS_CROSSED_START (a) = index; + ALLOCNO_CALLS_CROSSED_NUM (a)++; + /* Don't allocate allocnos that cross calls, if this + function receives a nonlocal goto. */ + if (current_function_has_nonlocal_label) + { + SET_HARD_REG_SET (ALLOCNO_CONFLICT_HARD_REGS (a)); + SET_HARD_REG_SET (ALLOCNO_TOTAL_CONFLICT_HARD_REGS (a)); + } + }); + } + + /* Mark any allocnos set in INSN as live, and mark them as + conflicting with all other live reg allocnos. Clobbers are + processed again, so they conflict with the reg allocnos that + are set. */ + note_stores (PATTERN (insn), mark_reg_store, NULL); + +#ifdef AUTO_INC_DEC + for (link = REG_NOTES (insn); link; link = XEXP (link, 1)) + if (REG_NOTE_KIND (link) == REG_INC) + mark_reg_store (XEXP (link, 0), NULL_RTX, NULL); +#endif + + /* If INSN has multiple outputs, then any allocno that dies + here and is used inside of an output must conflict with + the other outputs. + + It is unsafe to use !single_set here since it will ignore + an unused output. Just because an output is unused does + not mean the compiler can assume the side effect will not + occur. Consider if ALLOCNO appears in the address of an + output and we reload the output. If we allocate ALLOCNO + to the same hard register as an unused output we could + set the hard register before the output reload insn. */ + if (GET_CODE (PATTERN (insn)) == PARALLEL && multiple_sets (insn)) + for (link = REG_NOTES (insn); link; link = XEXP (link, 1)) + if (REG_NOTE_KIND (link) == REG_DEAD) + { + int i; + int used_in_output = 0; + rtx reg = XEXP (link, 0); + + for (i = XVECLEN (PATTERN (insn), 0) - 1; i >= 0; i--) + { + rtx set = XVECEXP (PATTERN (insn), 0, i); + + if (GET_CODE (set) == SET + && ! REG_P (SET_DEST (set)) + && ! rtx_equal_p (reg, SET_DEST (set)) + && reg_overlap_mentioned_p (reg, SET_DEST (set))) + used_in_output = 1; + } + if (used_in_output) + mark_reg_conflicts (reg); + } + + process_single_reg_class_operands (FALSE); + + /* Mark any allocnos set in INSN and then never used. */ + while (! VEC_empty (rtx, regs_set)) + { + rtx reg = VEC_pop (rtx, regs_set); + rtx note = find_regno_note (insn, REG_UNUSED, REGNO (reg)); + + if (note) + mark_reg_death (XEXP (note, 0)); + } + curr_point++; + } + EXECUTE_IF_SET_IN_ALLOCNO_SET (allocnos_live, i, + { + make_regno_dead (ALLOCNO_REGNO (allocnos [i])); + }); + curr_point++; + } + /* Propagate register pressure: */ + if (loop_tree_node != ira_loop_tree_root) + for (i = 0; i < reg_class_cover_size; i++) + { + enum reg_class cover_class; + + cover_class = reg_class_cover [i]; + if (loop_tree_node->reg_pressure [cover_class] + > loop_tree_node->father->reg_pressure [cover_class]) + loop_tree_node->father->reg_pressure [cover_class] + = loop_tree_node->reg_pressure [cover_class]; + } +} + +static void +create_start_finish_chains (void) +{ + int i; + allocno_t a; + allocno_live_range_t r; + + start_point_ranges + = ira_allocate (max_point * sizeof (allocno_live_range_t)); + memset (start_point_ranges, 0, max_point * sizeof (allocno_live_range_t)); + finish_point_ranges + = ira_allocate (max_point * sizeof (allocno_live_range_t)); + memset (finish_point_ranges, 0, max_point * sizeof (allocno_live_range_t)); + for (i = 0; i < allocnos_num; i++) + { + a = allocnos [i]; + for (r = ALLOCNO_LIVE_RANGES (a); r != NULL; r = r->next) + { + r->start_next = start_point_ranges [r->start]; + start_point_ranges [r->start] = r; + r->finish_next = finish_point_ranges [r->finish]; + finish_point_ranges [r->finish] = r; + } + } +} + +void +rebuild_start_finish_chains (void) +{ + ira_free (finish_point_ranges); + ira_free (start_point_ranges); + create_start_finish_chains (); +} + +void +print_live_range_list (FILE *f, allocno_live_range_t r) +{ + for (; r != NULL; r = r->next) + fprintf (f, " [%d..%d]", r->start, r->finish); + fprintf (f, "\n"); +} + +void +debug_live_range_list (allocno_live_range_t r) +{ + print_live_range_list (stderr, r); +} + +static void +print_allocno_live_ranges (FILE *f, allocno_t a) +{ + fprintf (f, " a%d(r%d):", ALLOCNO_NUM (a), ALLOCNO_REGNO (a)); + print_live_range_list (f, ALLOCNO_LIVE_RANGES (a)); +} + +void +debug_allocno_live_ranges (allocno_t a) +{ + print_allocno_live_ranges (stderr, a); +} + +static void +print_live_ranges (FILE *f) +{ + int i; + + for (i = 0; i < allocnos_num; i++) + print_allocno_live_ranges (f, allocnos [i]); +} + +void +debug_live_ranges (void) +{ + print_live_ranges (stderr); +} + +/* The function creates live ranges, set up CONFLICT_HARD_REGS and + TOTAL_CONFLICT_HARD_REGS for allocnos. It also modifies hard reg + costs for allocnos whose hard register is actually fixed in an + insn. */ +void +create_allocno_live_ranges (void) +{ + allocno_set_words = (allocnos_num + INT_BITS - 1) / INT_BITS; + allocnos_live = ira_allocate (sizeof (INT_TYPE) * allocno_set_words); + allocnos_live_bitmap = ira_allocate_bitmap (); + /* Make a vector that mark_reg_{store,clobber} will store in. */ + if (!regs_set) + regs_set = VEC_alloc (rtx, heap, 10); + curr_point = 0; + traverse_loop_tree (FALSE, ira_loop_tree_root, NULL, process_bb_node_lives); + max_point = curr_point; + create_start_finish_chains (); + if (internal_flag_ira_verbose > 2 && ira_dump_file != NULL) + print_live_ranges (ira_dump_file); + /* Clean up. */ + ira_free_bitmap (allocnos_live_bitmap); + ira_free (allocnos_live); +} + +void +finish_allocno_live_ranges (void) +{ + ira_free (finish_point_ranges); + ira_free (start_point_ranges); +} diff --git a/gcc/ira.c b/gcc/ira.c index f60126ddf4c..a2522699d20 100644 --- a/gcc/ira.c +++ b/gcc/ira.c @@ -55,7 +55,7 @@ Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA Graph Coloring Register Allocation. o David Callahan, Brian Koblenz. Register allocation via - hierarchical graph coloring + hierarchical graph coloring. o Keith Cooper, Anshuman Dasgupta, Jason Eckhardt. Revisiting Graph Coloring Register Allocation: A Study of the Chaitin-Briggs and @@ -109,13 +109,14 @@ static void print_class_cover (FILE *); static void find_reg_class_closure (void); static void setup_reg_class_nregs (void); static void setup_prohibited_class_mode_regs (void); +static void setup_prohibited_mode_move_regs (void); static int insn_contains_asm_1 (rtx *, void *); static int insn_contains_asm (rtx); static void compute_regs_asm_clobbered (char *); static void setup_eliminable_regset (void); static void find_reg_equiv_invariant_const (void); -static void setup_reg_renumber (int, int); -static int setup_allocno_assignment_from_reg_renumber (void); +static void setup_reg_renumber (void); +static void setup_allocno_assignment_flags (void); static void calculate_allocation_cost (void); #ifdef ENABLE_IRA_CHECKING static void check_allocation (void); @@ -130,9 +131,15 @@ static void expand_reg_info (int); static bool gate_ira (void); static unsigned int rest_of_handle_ira (void); +/* The flag value used internally. */ +int internal_flag_ira_verbose; + /* Dump file for IRA. */ FILE *ira_dump_file; +/* Pools for allocnos, copies, allocno live ranges. */ +alloc_pool allocno_pool, copy_pool, allocno_live_range_pool; + /* The number of elements in the following array. */ int spilled_reg_stack_slots_num; @@ -365,7 +372,7 @@ setup_alloc_regs (int use_hard_frame_p) /* Define the following macro if allocation through malloc if preferable. */ -/*#define IRA_NO_OBSTACK*/ +#define IRA_NO_OBSTACK #ifndef IRA_NO_OBSTACK /* Obstack used for storing all dynamic data (except bitmaps) of the @@ -390,6 +397,20 @@ ira_allocate (size_t len) return res; } +/* The function reallocates memory PTR of size LEN for IRA data. */ +void * +ira_reallocate (void *ptr, size_t len) +{ + void *res; + +#ifndef IRA_NO_OBSTACK + res = obstack_alloc (&ira_obstack, len); +#else + res = xrealloc (ptr, len); +#endif + return res; +} + /* The function free memory ADDR allocated for IRA data. */ void ira_free (void *addr ATTRIBUTE_UNUSED) @@ -462,7 +483,9 @@ print_disposition (FILE *f) fprintf (f, "Disposition:"); max_regno = max_reg_num (); for (n = 0, i = FIRST_PSEUDO_REGISTER; i < max_regno; i++) - for (a = regno_allocno_map [i]; a != NULL; a = ALLOCNO_NEXT_REGNO_ALLOCNO (a)) + for (a = regno_allocno_map [i]; + a != NULL; + a = ALLOCNO_NEXT_REGNO_ALLOCNO (a)) { if (n % 4 == 0) fprintf (f, "\n"); @@ -543,10 +566,14 @@ enum reg_class reg_class_cover [N_REG_CLASSES]; /* The value is number of elements in the subsequent array. */ int important_classes_num; -/* The array containing classes which are subclasses of a cover - class. */ +/* The array containing classes which are subclasses of cover + classes. */ enum reg_class important_classes [N_REG_CLASSES]; +/* The array containing order numbers of important classes (they are + subclasses of cover classes). */ +enum reg_class important_class_nums [N_REG_CLASSES]; + #ifdef IRA_COVER_CLASSES /* The function checks IRA_COVER_CLASSES and sets the two global @@ -578,7 +605,10 @@ setup_cover_classes (void) for (j = 0; j < reg_class_cover_size; j++) if (hard_reg_set_subset_p (reg_class_contents [cl], reg_class_contents [reg_class_cover [j]])) - important_classes [important_classes_num++] = cl; + { + important_class_nums [cl] = important_classes_num; + important_classes [important_classes_num++] = cl; + } } } #endif @@ -777,6 +807,60 @@ init_ira_once (void) init_ira_costs_once (); } +/* Function called once at the end of compiler work. */ +void +finish_ira_once (void) +{ + finish_ira_costs_once (); +} + + + +/* Array whose values are hard regset of hard registers for which + move of the hard register in given mode into itself is + prohibited. */ +HARD_REG_SET prohibited_mode_move_regs [NUM_MACHINE_MODES]; + +/* Flag of that the above array has been initialized. */ +static int prohibited_mode_move_regs_initialized_p = FALSE; + +/* The function setting up PROHIBITED_MODE_MOVE_REGS. */ +static void +setup_prohibited_mode_move_regs (void) +{ + int i, j; + rtx test_reg1, test_reg2, move_pat, move_insn; + + if (prohibited_mode_move_regs_initialized_p) + return; + prohibited_mode_move_regs_initialized_p = TRUE; + test_reg1 = gen_rtx_REG (VOIDmode, 0); + test_reg2 = gen_rtx_REG (VOIDmode, 0); + move_pat = gen_rtx_SET (VOIDmode, test_reg1, test_reg2); + move_insn = gen_rtx_INSN (VOIDmode, 0, 0, 0, 0, 0, move_pat, -1, 0); + for (i = 0; i < NUM_MACHINE_MODES; i++) + { + SET_HARD_REG_SET (prohibited_mode_move_regs [i]); + for (j = 0; j < FIRST_PSEUDO_REGISTER; j++) + { + if (! HARD_REGNO_MODE_OK (j, i)) + continue; + SET_REGNO (test_reg1, j); + PUT_MODE (test_reg1, i); + SET_REGNO (test_reg2, j); + PUT_MODE (test_reg2, i); + INSN_CODE (move_insn) = -1; + recog_memoized (move_insn); + if (INSN_CODE (move_insn) < 0) + continue; + extract_insn (move_insn); + if (! constrain_operands (1)) + continue; + CLEAR_HARD_REG_BIT (prohibited_mode_move_regs [i], j); + } + } +} + /* Function specific hard registers excluded from the allocation. */ @@ -974,12 +1058,9 @@ find_reg_equiv_invariant_const (void) /* The function sets up REG_RENUMBER and CALLER_SAVE_NEEDED used by - reload from the allocation found by IRA. If AFTER_EMIT_P, the - function is called after emitting the move insns, otherwise if - AFTER_CALL_P, the function is called right after splitting allocnos - around calls. */ + reload from the allocation found by IRA. */ static void -setup_reg_renumber (int after_emit_p, int after_call_p) +setup_reg_renumber (void) { int i, regno, hard_regno; allocno_t a; @@ -988,52 +1069,50 @@ setup_reg_renumber (int after_emit_p, int after_call_p) for (i = 0; i < allocnos_num; i++) { a = allocnos [i]; - if (ALLOCNO_CAP_MEMBER (a) != NULL) - /* It is a cap. */ - continue; + /* There are no caps at this point. */ + ira_assert (ALLOCNO_CAP_MEMBER (a) == NULL); if (! ALLOCNO_ASSIGNED_P (a)) ALLOCNO_ASSIGNED_P (a) = TRUE; ira_assert (ALLOCNO_ASSIGNED_P (a)); hard_regno = ALLOCNO_HARD_REGNO (a); - regno = after_emit_p ? (int) REGNO (ALLOCNO_REG (a)) : ALLOCNO_REGNO (a); + regno = (int) REGNO (ALLOCNO_REG (a)); reg_renumber [regno] = (hard_regno < 0 ? -1 : hard_regno); if (hard_regno >= 0 && ALLOCNO_CALLS_CROSSED_NUM (a) != 0 && ! hard_reg_not_in_set_p (hard_regno, ALLOCNO_MODE (a), call_used_reg_set)) { - ira_assert - ((! after_call_p && flag_caller_saves) - || (flag_caller_saves && ! flag_ira_split_around_calls) - || reg_equiv_const [regno] || reg_equiv_invariant_p [regno]); + ira_assert (flag_caller_saves || reg_equiv_const [regno] + || reg_equiv_invariant_p [regno]); caller_save_needed = 1; } } } -/* The function sets up allocno assignment from reg_renumber. If the - cover class of a allocno does not correspond to the hard register, - return TRUE and mark the allocno as unassigned. */ -static int -setup_allocno_assignment_from_reg_renumber (void) +/* The function sets up allocno assignment flags for further + allocation improvements. */ +static void +setup_allocno_assignment_flags (void) { int i, hard_regno; allocno_t a; - int result = FALSE; for (i = 0; i < allocnos_num; i++) { a = allocnos [i]; - hard_regno = ALLOCNO_HARD_REGNO (a) = reg_renumber [ALLOCNO_REGNO (a)]; - ira_assert (! ALLOCNO_ASSIGNED_P (a)); - if (hard_regno >= 0 - && hard_reg_not_in_set_p (hard_regno, ALLOCNO_MODE (a), - reg_class_contents - [ALLOCNO_COVER_CLASS (a)])) - result = TRUE; - else - ALLOCNO_ASSIGNED_P (a) = TRUE; + hard_regno = ALLOCNO_HARD_REGNO (a); + /* Don't assign hard registers to allocnos which are destination + of removed store at the end of loop. It has a few sense to + keep the same value in different hard registers. It is also + impossible to assign hard registers correctly to such + allocnos because the cost info and info about intersected + calls are incorrect for them. */ + ALLOCNO_ASSIGNED_P (a) = (hard_regno >= 0 + || ALLOCNO_MEM_OPTIMIZED_DEST_P (a)); + ira_assert (hard_regno < 0 + || ! hard_reg_not_in_set_p (hard_regno, ALLOCNO_MODE (a), + reg_class_contents + [ALLOCNO_COVER_CLASS (a)])); } - return result; } /* The function evaluates overall allocation cost and costs for using @@ -1056,7 +1135,7 @@ calculate_allocation_cost (void) reg_class_contents [ALLOCNO_COVER_CLASS (a)])); if (hard_regno < 0) { - cost = ALLOCNO_MEMORY_COST (a); + cost = ALLOCNO_UPDATED_MEMORY_COST (a); mem_cost += cost; } else @@ -1069,7 +1148,7 @@ calculate_allocation_cost (void) overall_cost += cost; } - if (ira_dump_file != NULL) + if (internal_flag_ira_verbose > 0 && ira_dump_file != NULL) { fprintf (ira_dump_file, "+++Costs: overall %d, reg %d, mem %d, ld %d, st %d, move %d\n", @@ -1189,9 +1268,11 @@ print_redundant_copies (void) else { next_cp = cp->next_second_allocno_copy; - if (ira_dump_file != NULL && cp->move_insn != NULL_RTX + if (internal_flag_ira_verbose > 4 && ira_dump_file != NULL + && cp->move_insn != NULL_RTX && ALLOCNO_HARD_REGNO (cp->first) == hard_regno) - fprintf (ira_dump_file, "move %d(freq %d):%d\n", + fprintf (ira_dump_file, + " Redundant move %d(freq %d):%d\n", INSN_UID (cp->move_insn), cp->freq, hard_regno); } } @@ -1235,24 +1316,27 @@ expand_reg_info (int old_size) -/* The value of max_regn_num () correspondingly before the allocator - and before splitting allocnos around calls. */ -int ira_max_regno_before; -int ira_max_regno_call_before; - -/* Flags for each regno (existing before the splitting allocnos around - calls) about that the corresponding register crossed a call. */ -char *original_regno_call_crossed_p; /* This is the main entry of IRA. */ void ira (FILE *f) { - int i, overall_cost_before, loops_p, allocated_size; + int overall_cost_before, loops_p, allocated_reg_info_size; + int max_regno_before_ira, max_point_before_emit; int rebuild_p; - allocno_t a; - ira_dump_file = f; + if (flag_ira_verbose < 10) + { + internal_flag_ira_verbose = flag_ira_verbose; + ira_dump_file = f; + } + else + { + internal_flag_ira_verbose = flag_ira_verbose - 10; + ira_dump_file = stderr; + } + + setup_prohibited_mode_move_regs (); df_note_add_problem (); @@ -1266,6 +1350,11 @@ ira (FILE *f) df_clear_flags (DF_NO_INSN_RESCAN); + allocno_pool = create_alloc_pool ("allocnos", sizeof (struct allocno), 100); + copy_pool = create_alloc_pool ("copies", sizeof (struct allocno_copy), 100); + allocno_live_range_pool + = create_alloc_pool ("allocno live ranges", + sizeof (struct allocno_live_range), 100); regstat_init_n_sets_and_refs (); regstat_compute_ri (); rebuild_p = update_equiv_regs (); @@ -1277,11 +1366,11 @@ ira (FILE *f) #endif bitmap_obstack_initialize (&ira_bitmap_obstack); - ira_max_regno_call_before = ira_max_regno_before = max_reg_num (); - reg_equiv_invariant_p = ira_allocate (max_reg_num () * sizeof (int)); - memset (reg_equiv_invariant_p, 0, max_reg_num () * sizeof (int)); - reg_equiv_const = ira_allocate (max_reg_num () * sizeof (rtx)); - memset (reg_equiv_const, 0, max_reg_num () * sizeof (rtx)); + max_regno = max_reg_num (); + reg_equiv_invariant_p = ira_allocate (max_regno * sizeof (int)); + memset (reg_equiv_invariant_p, 0, max_regno * sizeof (int)); + reg_equiv_const = ira_allocate (max_regno * sizeof (rtx)); + memset (reg_equiv_const, 0, max_regno * sizeof (rtx)); find_reg_equiv_invariant_const (); if (rebuild_p) { @@ -1290,7 +1379,7 @@ ira (FILE *f) purge_all_dead_edges (); timevar_pop (TV_JUMP); } - allocated_size = max_reg_num (); + max_regno_before_ira = allocated_reg_info_size = max_reg_num (); allocate_reg_info (); setup_eliminable_regset (); @@ -1300,61 +1389,42 @@ ira (FILE *f) overall_cost = reg_cost = mem_cost = 0; load_cost = store_cost = shuffle_cost = 0; move_loops_num = additional_jumps_num = 0; + if (internal_flag_ira_verbose > 0 && ira_dump_file != NULL) + fprintf (ira_dump_file, "Building IRA IR\n"); loops_p = ira_build (flag_ira_algorithm == IRA_ALGORITHM_REGIONAL || flag_ira_algorithm == IRA_ALGORITHM_MIXED); ira_color (); - ira_emit (); + max_point_before_emit = max_point; + + ira_emit (loops_p); max_regno = max_reg_num (); - expand_reg_info (allocated_size); - allocated_size = max_regno; - - setup_reg_renumber (TRUE, FALSE); - if (loops_p) { - /* Even if new registers are not created rebuild IRA internal - representation to use correct regno allocno map. */ - ira_destroy (); - ira_build (FALSE); - if (setup_allocno_assignment_from_reg_renumber ()) - { - reassign_conflict_allocnos (max_regno, FALSE); - setup_reg_renumber (FALSE, FALSE); - } - } - - original_regno_call_crossed_p = ira_allocate (max_regno * sizeof (char)); + expand_reg_info (allocated_reg_info_size); + allocated_reg_info_size = max_regno; + + if (internal_flag_ira_verbose > 0 && ira_dump_file != NULL) + fprintf (ira_dump_file, "Flattening IR\n"); + ira_flattening (max_regno_before_ira, max_point_before_emit); + /* New insns were generated: add notes and recaclulate live + info. */ + df_analyze (); + { + basic_block bb; - for (i = 0; i < allocnos_num; i++) - { - a = allocnos [i]; - ira_assert (ALLOCNO_CAP_MEMBER (a) == NULL); - original_regno_call_crossed_p [ALLOCNO_REGNO (a)] - = ALLOCNO_CALLS_CROSSED_NUM (a) != 0; - } - ira_max_regno_call_before = max_reg_num (); - if (flag_caller_saves && flag_ira_split_around_calls) - { - if (split_around_calls ()) - { - ira_destroy (); - max_regno = max_reg_num (); - expand_reg_info (allocated_size); - allocated_size = max_regno; - for (i = ira_max_regno_call_before; i < max_regno; i++) - reg_renumber [i] = -1; - ira_build (FALSE); - setup_allocno_assignment_from_reg_renumber (); - reassign_conflict_allocnos ((flag_ira_assign_after_call_split - ? ira_max_regno_call_before - : max_reg_num ()), TRUE); - setup_reg_renumber (FALSE, TRUE); - } + FOR_ALL_BB (bb) + bb->loop_father = NULL; + current_loops = NULL; + } + setup_allocno_assignment_flags (); + reassign_conflict_allocnos (max_regno, FALSE); } + setup_reg_renumber (); + calculate_allocation_cost (); #ifdef ENABLE_IRA_CHECKING @@ -1363,8 +1433,7 @@ ira (FILE *f) setup_preferred_alternate_classes (); - max_regno = max_reg_num (); - delete_trivially_dead_insns (get_insns (), max_regno); + delete_trivially_dead_insns (get_insns (), max_reg_num ()); max_regno = max_reg_num (); /* Determine if the current function is a leaf before running IRA @@ -1401,7 +1470,8 @@ ira (FILE *f) ira_free (spilled_reg_stack_slots); - if (ira_dump_file != NULL && overall_cost_before != overall_cost) + if (internal_flag_ira_verbose > 0 && ira_dump_file != NULL + && overall_cost_before != overall_cost) fprintf (ira_dump_file, "+++Overall after reload %d\n", overall_cost); ira_destroy (); @@ -1411,10 +1481,13 @@ ira (FILE *f) regstat_free_ri (); regstat_free_n_sets_and_refs (); - ira_free (original_regno_call_crossed_p); ira_free (reg_equiv_invariant_p); ira_free (reg_equiv_const); + free_alloc_pool (allocno_live_range_pool); + free_alloc_pool (copy_pool); + free_alloc_pool (allocno_pool); + bitmap_obstack_release (&ira_bitmap_obstack); #ifndef IRA_NO_OBSTACK obstack_free (&ira_obstack, NULL); diff --git a/gcc/ira.h b/gcc/ira.h index 8a8cc56773f..394a590210d 100644 --- a/gcc/ira.h +++ b/gcc/ira.h @@ -54,6 +54,7 @@ extern rtx *reg_equiv_address; extern rtx *reg_equiv_mem; extern void init_ira_once (void); +extern void finish_ira_once (void); extern rtx ira_eliminate_regs (rtx, enum machine_mode); extern void ira (FILE *); diff --git a/gcc/opts.c b/gcc/opts.c index c6e37e008f5..0900b02dd7e 100644 --- a/gcc/opts.c +++ b/gcc/opts.c @@ -1736,6 +1736,10 @@ common_handle_option (size_t scode, const char *arg, int value, warning (0, "unknown ira algorithm \"%s\"", arg); break; + case OPT_fira_verbose_: + flag_ira_verbose = value; + break; + case OPT_ftracer: flag_tracer_set = true; break; diff --git a/gcc/print-rtl.c b/gcc/print-rtl.c index 2891967390e..1485d0f6853 100644 --- a/gcc/print-rtl.c +++ b/gcc/print-rtl.c @@ -60,8 +60,10 @@ const char *print_rtx_head = ""; This must be defined here so that programs like gencodes can be linked. */ int flag_dump_unnumbered = 0; +#ifdef GENERATOR_FILE /* Nonzero means use simplified format without flags, modes, etc. */ -int flag_simple = 0; +int flag_dump_simple_rtl = 0; +#endif /* Nonzero if we are dumping graphical description. */ int dump_for_graph; @@ -136,7 +138,7 @@ print_rtx (const_rtx in_rtx) if (sawclose) { - if (flag_simple) + if (flag_dump_simple_rtl) fputc (' ', outfile); else fprintf (outfile, "\n%s%*s", print_rtx_head, indent * 2, ""); @@ -170,12 +172,12 @@ print_rtx (const_rtx in_rtx) else { /* Print name of expression code. */ - if (flag_simple && GET_CODE (in_rtx) == CONST_INT) + if (flag_dump_simple_rtl && GET_CODE (in_rtx) == CONST_INT) fputc ('(', outfile); else fprintf (outfile, "(%s", GET_RTX_NAME (GET_CODE (in_rtx))); - if (! flag_simple) + if (! flag_dump_simple_rtl) { if (RTX_FLAG (in_rtx, in_struct)) fputs ("/s", outfile); @@ -376,10 +378,10 @@ print_rtx (const_rtx in_rtx) break; case 'w': - if (! flag_simple) + if (! flag_dump_simple_rtl) fprintf (outfile, " "); fprintf (outfile, HOST_WIDE_INT_PRINT_DEC, XWINT (in_rtx, i)); - if (! flag_simple) + if (! flag_dump_simple_rtl) fprintf (outfile, " [" HOST_WIDE_INT_PRINT_HEX "]", XWINT (in_rtx, i)); break; @@ -766,7 +768,9 @@ print_rtl_single (FILE *outf, const_rtx x) void print_simple_rtl (FILE *outf, const_rtx x) { - flag_simple = 1; + int save = flag_dump_simple_rtl; + + flag_dump_simple_rtl = 1; print_rtl (outf, x); - flag_simple = 0; + flag_dump_simple_rtl = save; } diff --git a/gcc/toplev.c b/gcc/toplev.c index 2b005bd18c1..004119753ff 100644 --- a/gcc/toplev.c +++ b/gcc/toplev.c @@ -308,6 +308,10 @@ enum tls_model flag_tls_default = TLS_MODEL_GLOBAL_DYNAMIC; enum ira_algorithm flag_ira_algorithm = IRA_ALGORITHM_REGIONAL; +/* Set the default value for -fira-verbose. */ + +unsigned int flag_ira_verbose = 5; + /* Nonzero means change certain warnings into errors. Usually these are warnings about failure to conform to some standard. */ @@ -2198,6 +2202,8 @@ finalize (void) finish_optimization_passes (); + finish_ira_once (); + if (mem_report) dump_memory_report (true); diff --git a/gcc/toplev.h b/gcc/toplev.h index dbd5e55f187..3a1d32e0e54 100644 --- a/gcc/toplev.h +++ b/gcc/toplev.h @@ -136,15 +136,12 @@ extern int flag_unswitch_loops; extern int flag_cprop_registers; extern int time_report; extern int flag_ira; -extern int flag_ira_assign_after_call_split; -extern int flag_ira_biased_coloring; extern int flag_ira_coalesce; extern int flag_ira_ipra; extern int flag_ira_move_spills; extern int flag_ira_propagate_cost; extern int flag_ira_share_save_slots; extern int flag_ira_share_spill_slots; -extern int flag_ira_split_around_calls; /* Things to do with target switches. */ extern void print_version (FILE *, const char *); -- 2.11.4.GIT