#include "config.h"
#include "system.h"
#include "coretypes.h"
#include "backend.h"
#include "target.h"
#include "rtl.h"
#include "tree.h"
#include "predict.h"
#include "df.h"
#include "insn-config.h"
#include "memmodel.h"
#include "emit-rtl.h"
#include "recog.h"
#include "cfgrtl.h"
#include "expr.h"
#include "tree-pass.h"
#include "dbgcnt.h"
#include "print-rtl.h"
#include "valtrack.h"
Data Structures | |
struct | inc_insn |
struct | mem_insn |
Enumerations | |
enum | form { FORM_PRE_ADD , FORM_PRE_INC , FORM_POST_ADD , FORM_POST_INC , FORM_last } |
enum | inc_state { INC_ZERO , INC_NEG_SIZE , INC_POS_SIZE , INC_NEG_ANY , INC_POS_ANY , INC_REG , INC_last } |
enum | gen_form { NOTHING , SIMPLE_PRE_INC , SIMPLE_POST_INC , SIMPLE_PRE_DEC , SIMPLE_POST_DEC , DISP_PRE , DISP_POST , REG_PRE , REG_POST } |
Functions | |
static enum inc_state | set_inc_state (HOST_WIDE_INT val, poly_int64 size) |
static void | init_decision_table (void) |
static void | dump_inc_insn (FILE *file) |
static void | dump_mem_insn (FILE *file) |
static void | move_dead_notes (rtx_insn *to_insn, rtx_insn *from_insn, rtx pattern) |
static bool | attempt_change (rtx new_addr, rtx inc_reg) |
static bool | try_merge (void) |
static rtx_insn * | get_next_ref (int regno, basic_block bb, rtx_insn **next_array) |
static bool | parse_add_or_inc (rtx_insn *insn, bool before_mem) |
static int | find_address (rtx *address_of_x, rtx findreg) |
static bool | find_inc (bool first_try) |
static bool | find_mem (rtx *address_of_x) |
static void | merge_in_block (int max_reg, basic_block bb) |
rtl_opt_pass * | make_pass_inc_dec (gcc::context *ctxt) |
Variables | |
static rtx | mem_tmp |
static bool | initialized = false |
static enum gen_form | decision_table [INC_last][INC_last][FORM_last] |
static struct inc_insn | inc_insn |
static struct mem_insn | mem_insn |
static rtx_insn ** | reg_next_debug_use = NULL |
static rtx_insn ** | reg_next_use = NULL |
static rtx_insn ** | reg_next_inc_use = NULL |
static rtx_insn ** | reg_next_def = NULL |
enum form |
Discovery of auto-inc and auto-dec instructions. Copyright (C) 2006-2025 Free Software Foundation, Inc. Contributed by Kenneth Zadeck <zadeck@naturalbridge.com> 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 3, 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 COPYING3. If not see <http://www.gnu.org/licenses/>.
This pass was originally removed from flow.c. However there is almost nothing that remains of that code. There are (4) basic forms that are matched: (1) FORM_PRE_ADD a <- b + c ... *a becomes a <- b ... *(a += c) pre or, alternately, a <- b + c ... *b becomes a <- b ... *(a += c) post This uses a post-add, but it's handled as FORM_PRE_ADD because the "increment" insn appears before the memory access. (2) FORM_PRE_INC a += c ... *a becomes ... *(a += c) pre (3) FORM_POST_ADD *a ... b <- a + c (For this case to be true, b must not be assigned or used between the *a and the assignment to b. B must also be a Pmode reg.) becomes b <- a *(b += c) post ... (4) FORM_POST_INC *a ... a <- a + c becomes *(a += c) post ... There are three types of values of c. 1) c is a constant equal to the width of the value being accessed by the pointer. This is useful for machines that have HAVE_PRE_INCREMENT, HAVE_POST_INCREMENT, HAVE_PRE_DECREMENT or HAVE_POST_DECREMENT defined. 2) c is a constant not equal to the width of the value being accessed by the pointer. This is useful for machines that have HAVE_PRE_MODIFY_DISP, HAVE_POST_MODIFY_DISP defined. 3) c is a register. This is useful for machines that have HAVE_PRE_MODIFY_REG, HAVE_POST_MODIFY_REG The is one special case: if a already had an offset equal to it +- its width and that offset is equal to -c when the increment was before the ref or +c if the increment was after the ref, then if we can do the combination but switch the pre/post bit.
Enumerator | |
---|---|
FORM_PRE_ADD | |
FORM_PRE_INC | |
FORM_POST_ADD | |
FORM_POST_INC | |
FORM_last |
enum gen_form |
enum inc_state |
The states of the second operands of mem refs and inc insns. If no second operand of the mem_ref was found, it is assumed to just be ZERO. SIZE is the size of the mode accessed in the memref. The ANY is used for constants that are not +-size or 0. REG is used if the forms are reg1 + reg2.
Enumerator | |
---|---|
INC_ZERO | |
INC_NEG_SIZE | |
INC_POS_SIZE | |
INC_NEG_ANY | |
INC_POS_ANY | |
INC_REG | |
INC_last |
Change mem_insn.mem_loc so that uses NEW_ADDR which has an increment of INC_REG. To have reached this point, the change is a legitimate one from a dataflow point of view. The only questions are is this a valid change to the instruction and is this a profitable change to the instruction.
References add_reg_note(), BLOCK_FOR_INSN(), delete_insn(), DF_INSN_LUID, df_recompute_luids(), dump_file, dump_insn_slim(), emit_insn_before(), emit_move_insn(), end_sequence(), find_reg_note(), inc_insn::form, FORM_last, FORM_POST_ADD, FORM_POST_INC, FORM_PRE_ADD, FORM_PRE_INC, gcc_assert, gcc_unreachable, get_insns(), GET_MODE, inc_insn::insn, mem_insn::insn, MEM_ALIGN, mem_insn::mem_loc, mem_tmp, move_dead_notes(), NULL, optimize_bb_for_speed_p(), PATTERN(), PREV_INSN(), propagate_for_debug(), PUT_MODE(), inc_insn::reg0, inc_insn::reg1, inc_insn::reg1_is_const, reg_next_debug_use, reg_next_def, reg_next_inc_use, reg_next_use, inc_insn::reg_res, REGNO, replace_equiv_address_nv(), seq_cost(), set_mem_align(), set_rtx_cost(), set_src_cost(), start_sequence(), validate_change(), and XEXP.
Referenced by try_merge().
|
static |
Dump the parsed inc insn to FILE.
References dump_insn_slim(), inc_insn::form, FORM_POST_ADD, FORM_POST_INC, FORM_PRE_ADD, FORM_PRE_INC, inc_insn::insn, INSN_UID(), inc_insn::reg0, inc_insn::reg1, inc_insn::reg1_is_const, inc_insn::reg1_val, inc_insn::reg_res, and REGNO.
Referenced by find_inc(), and merge_in_block().
|
static |
Dump the parsed mem insn to FILE.
References dump_insn_slim(), mem_insn::insn, INSN_UID(), mem_insn::reg0, mem_insn::reg1, mem_insn::reg1_is_const, mem_insn::reg1_val, and REGNO.
Referenced by find_inc(), and merge_in_block().
A recursive function that checks all of the mem uses in ADDRESS_OF_X to see if any single one of them is compatible with what has been found in inc_insn. To avoid accidental matches, we will only find MEMs with FINDREG, be it inc_insn.reg_res, be it inc_insn.reg0. -1 is returned for success. 0 is returned if nothing was found and 1 is returned for failure.
References b, CONST_INT_P, find_address(), GEN_INT, GET_CODE, GET_RTX_FORMAT, GET_RTX_LENGTH, i, INTVAL, mem_insn::mem_loc, inc_insn::reg0, mem_insn::reg0, inc_insn::reg1, mem_insn::reg1, inc_insn::reg1_is_const, mem_insn::reg1_is_const, inc_insn::reg1_val, mem_insn::reg1_val, inc_insn::reg_res, rtx_equal_p(), XEXP, XVECEXP, and XVECLEN.
Referenced by find_address(), and merge_in_block().
Once a suitable mem reference has been found and the MEM_INSN structure has been filled in, FIND_INC is called to see if there is a suitable add or inc insn that follows the mem reference and determine if it is suitable to merge. In the case where the MEM_INSN has two registers in the reference, this function may be called recursively. The first time looking for an add of the first register, and if that fails, looking for an add of the second register. The FIRST_TRY parameter is used to only allow the parameters to be reversed once.
References BLOCK_FOR_INSN(), count_occurrences(), DF_INSN_LUID, DF_REF_REGNO, dump_file, dump_inc_insn(), dump_mem_insn(), find_inc(), FOR_EACH_INSN_DEF, inc_insn::form, FORM_POST_ADD, FORM_POST_INC, GET_MODE, get_next_ref(), inc_insn::insn, mem_insn::insn, MEM_ADDR_SPACE, mem_insn::mem_loc, parse_add_or_inc(), PATTERN(), inc_insn::reg0, mem_insn::reg0, inc_insn::reg1, mem_insn::reg1, inc_insn::reg1_is_const, mem_insn::reg1_is_const, inc_insn::reg1_val, mem_insn::reg1_val, reg_next_def, reg_next_inc_use, reg_next_use, reg_overlap_mentioned_p(), inc_insn::reg_res, REGNO, rtx_equal_p(), targetm, and try_merge().
Referenced by find_inc(), and find_mem().
A recursive function that walks ADDRESS_OF_X to find all of the mem uses in pat that could be used as an auto inc or dec. It then calls FIND_INC for each one.
References CONST_INT_P, find_inc(), find_mem(), GEN_INT, GET_CODE, GET_RTX_FORMAT, GET_RTX_LENGTH, i, INTVAL, mem_insn::mem_loc, mem_insn::reg0, mem_insn::reg1, mem_insn::reg1_is_const, mem_insn::reg1_val, REG_P, XEXP, XVECEXP, and XVECLEN.
Referenced by find_mem(), and merge_in_block().
|
static |
Return the next insn that uses (if reg_next_use is passed in NEXT_ARRAY) or defines (if reg_next_def is passed in NEXT_ARRAY) REGNO in BB.
References BLOCK_FOR_INSN(), and NULL.
Referenced by find_inc(), and merge_in_block().
|
static |
References decision_table, DISP_POST, DISP_PRE, FORM_POST_ADD, FORM_POST_INC, FORM_PRE_ADD, FORM_PRE_INC, HAVE_POST_DECREMENT, HAVE_POST_INCREMENT, HAVE_POST_MODIFY_DISP, HAVE_POST_MODIFY_REG, HAVE_PRE_DECREMENT, HAVE_PRE_INCREMENT, HAVE_PRE_MODIFY_DISP, HAVE_PRE_MODIFY_REG, INC_NEG_ANY, INC_NEG_SIZE, INC_POS_ANY, INC_POS_SIZE, INC_REG, INC_ZERO, initialized, REG_POST, REG_PRE, SIMPLE_POST_DEC, SIMPLE_POST_INC, SIMPLE_PRE_DEC, and SIMPLE_PRE_INC.
rtl_opt_pass * make_pass_inc_dec | ( | gcc::context * | ctxt | ) |
|
static |
Try to combine all incs and decs by constant values with memory references in BB.
References DEBUG_BIND_INSN_P, DF_INSN_INFO_GET, DF_INSN_LUID, df_recompute_luids(), DF_REF_REGNO, dump_file, dump_inc_insn(), dump_insn_slim(), dump_mem_insn(), find_address(), find_mem(), FOR_BB_INSNS_REVERSE_SAFE, FOR_EACH_INSN_INFO_DEF, FOR_EACH_INSN_INFO_USE, inc_insn::form, FORM_PRE_INC, GET_CODE, get_next_ref(), basic_block_def::index, mem_insn::insn, INSN_UID(), JUMP_P, merge_in_block(), NONDEBUG_INSN_P, NULL, parse_add_or_inc(), PATTERN(), inc_insn::reg0, inc_insn::reg1, inc_insn::reg1_is_const, reg_next_debug_use, reg_next_def, reg_next_inc_use, reg_next_use, inc_insn::reg_res, REGNO, targetm, and try_merge().
Referenced by merge_in_block().
Move dead note that match PATTERN to TO_INSN from FROM_INSN. We do not really care about moving any other notes from the inc or add insn. Moving the REG_EQUAL and REG_EQUIV is clearly wrong and it does not appear that there are any other kinds of relevant notes.
References NULL, REG_NOTE_KIND, REG_NOTES, and XEXP.
Referenced by attempt_change().
Return true if INSN is of a form "a = b op c" where a and b are regs. op is + if c is a reg and +|- if c is a const. Fill in INC_INSN with what is found. This function is called in two contexts, if BEFORE_MEM is true, this is called for each insn in the basic block. If BEFORE_MEM is false, it is called for the instruction in the block that uses the index register for some memory reference that is currently being processed.
References CONST_INT_P, inc_insn::form, FORM_POST_ADD, FORM_POST_INC, FORM_PRE_ADD, FORM_PRE_INC, frame_pointer_rtx, GEN_INT, GET_CODE, HAVE_POST_MODIFY_REG, HAVE_PRE_MODIFY_REG, inc_insn::insn, INTVAL, inc_insn::pat, inc_insn::reg0, inc_insn::reg1, inc_insn::reg1_is_const, inc_insn::reg1_val, REG_P, inc_insn::reg_res, rtx_equal_p(), SET_DEST, SET_SRC, single_set(), and XEXP.
Referenced by find_inc(), and merge_in_block().
|
static |
References INC_NEG_ANY, INC_NEG_SIZE, INC_POS_ANY, INC_POS_SIZE, INC_ZERO, and known_eq.
Referenced by try_merge().
|
static |
Try to combine the instruction in INC_INSN with the instruction in MEM_INSN. First the form is determined using the DECISION_TABLE and the results of parsing the INC_INSN and the MEM_INSN. Assuming the form is ok, a prototype new address is built which is passed to ATTEMPT_CHANGE for final processing.
References attempt_change(), dbg_cnt(), decision_table, DISP_POST, DISP_PRE, dump_file, find_regno_note(), inc_insn::form, FORM_last, FORM_POST_ADD, FORM_POST_INC, FORM_PRE_ADD, FORM_PRE_INC, gcc_unreachable, GET_MODE, GET_MODE_SIZE(), INC_REG, inc_insn::insn, mem_insn::insn, mem_insn::mem_loc, NOTHING, NULL, mem_insn::reg0, inc_insn::reg1, inc_insn::reg1_is_const, mem_insn::reg1_is_const, inc_insn::reg1_state, mem_insn::reg1_state, inc_insn::reg1_val, mem_insn::reg1_val, reg_mode, REG_POST, REG_PRE, inc_insn::reg_res, REGNO, set_inc_state(), SIMPLE_POST_DEC, SIMPLE_POST_INC, SIMPLE_PRE_DEC, SIMPLE_PRE_INC, and stack_pointer_rtx.
Referenced by find_inc(), and merge_in_block().
Referenced by init_decision_table(), and try_merge().
|
static |
The DECISION_TABLE that describes what form, if any, the increment or decrement will take. It is a three dimensional table. The first index is the type of constant or register found as the second operand of the inc insn. The second index is the type of constant or register found as the second operand of the memory reference (if no second operand exists, 0 is used). The third index is the form and location (relative to the mem reference) of inc insn.
Referenced by alloc_aux_for_blocks(), alloc_aux_for_edges(), based_loc_descr(), concat_loc_descriptor(), concatn_loc_descriptor(), df_set_blocks(), dw_loc_list(), dw_loc_list_1(), dw_sra_loc_expr(), emit_note_insn_var_location(), init_decision_table(), loc_descriptor(), mem_loc_descriptor(), multiple_reg_loc_descriptor(), one_reg_loc_descriptor(), one_time_initialization(), reg_loc_descriptor(), set_slot_part(), set_variable_part(), unshare_variable(), var_mem_decl_set(), var_mem_delete_and_set(), var_mem_set(), var_reg_decl_set(), var_reg_delete_and_set(), and var_reg_set().
|
static |
|
static |
Tmp mem rtx for use in cost modeling.
Referenced by attempt_change().
The following three arrays contain pointers to instructions. They are indexed by REGNO. At any point in the basic block where we are looking these three arrays contain, respectively, the next insn that uses REGNO, the next inc or add insn that uses REGNO and the next insn that sets REGNO. The arrays are not cleared when we move from block to block so whenever an insn is retrieved from these arrays, it's block number must be compared with the current block.
Referenced by attempt_change(), and merge_in_block().
Referenced by attempt_change(), find_inc(), and merge_in_block().
Referenced by attempt_change(), find_inc(), and merge_in_block().
Referenced by attempt_change(), find_inc(), and merge_in_block().