| /* Shared code for before and after reload gcse implementations. |
| Copyright (C) 1997-2021 Free Software Foundation, Inc. |
| |
| 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/>. |
| |
| It is expected that more hunks of gcse.c and postreload-gcse.c should |
| migrate into this file. */ |
| |
| #include "config.h" |
| #include "system.h" |
| #include "coretypes.h" |
| #include "backend.h" |
| #include "rtl.h" |
| #include "df.h" |
| #include "gcse-common.h" |
| |
| |
| /* Record all of the canonicalized MEMs of record_last_mem_set_info's insn. |
| Note we store a pair of elements in the list, so they have to be |
| taken off pairwise. */ |
| |
| void |
| canon_list_insert (rtx dest, const_rtx x ATTRIBUTE_UNUSED, void *data) |
| { |
| rtx dest_addr; |
| int bb; |
| modify_pair pair; |
| |
| while (GET_CODE (dest) == SUBREG |
| || GET_CODE (dest) == ZERO_EXTRACT |
| || GET_CODE (dest) == STRICT_LOW_PART) |
| dest = XEXP (dest, 0); |
| |
| /* If DEST is not a MEM, then it will not conflict with a load. Note |
| that function calls are assumed to clobber memory, but are handled |
| elsewhere. */ |
| |
| if (! MEM_P (dest)) |
| return; |
| |
| dest_addr = get_addr (XEXP (dest, 0)); |
| dest_addr = canon_rtx (dest_addr); |
| rtx_insn *insn = ((struct gcse_note_stores_info *)data)->insn; |
| bb = BLOCK_FOR_INSN (insn)->index; |
| |
| pair.dest = dest; |
| pair.dest_addr = dest_addr; |
| vec<modify_pair> *canon_mem_list |
| = ((struct gcse_note_stores_info *)data)->canon_mem_list; |
| canon_mem_list[bb].safe_push (pair); |
| } |
| |
| /* Record memory modification information for INSN. We do not actually care |
| about the memory location(s) that are set, or even how they are set (consider |
| a CALL_INSN). We merely need to record which insns modify memory. */ |
| |
| void |
| record_last_mem_set_info_common (rtx_insn *insn, |
| vec<rtx_insn *> *modify_mem_list, |
| vec<modify_pair> *canon_modify_mem_list, |
| bitmap modify_mem_list_set, |
| bitmap blocks_with_calls) |
| |
| { |
| int bb; |
| |
| bb = BLOCK_FOR_INSN (insn)->index; |
| modify_mem_list[bb].safe_push (insn); |
| bitmap_set_bit (modify_mem_list_set, bb); |
| |
| if (CALL_P (insn)) |
| bitmap_set_bit (blocks_with_calls, bb); |
| else |
| { |
| struct gcse_note_stores_info data; |
| data.insn = insn; |
| data.canon_mem_list = canon_modify_mem_list; |
| note_stores (insn, canon_list_insert, (void*) &data); |
| } |
| } |
| |
| |
| /* For each block, compute whether X is transparent. X is either an |
| expression or an assignment [though we don't care which, for this context |
| an assignment is treated as an expression]. For each block where an |
| element of X is modified, reset the INDX bit in BMAP. |
| |
| BLOCKS_WITH_CALLS indicates which blocks contain CALL_INSNs which kill |
| memory. |
| |
| MODIFY_MEM_LIST_SET indicates which blocks have memory stores which might |
| kill a particular memory location. |
| |
| CANON_MODIFY_MEM_LIST is the canonicalized list of memory locations modified |
| for each block. */ |
| |
| void |
| compute_transp (const_rtx x, int indx, sbitmap *bmap, |
| bitmap blocks_with_calls, |
| bitmap modify_mem_list_set, |
| vec<modify_pair> *canon_modify_mem_list) |
| { |
| int i, j; |
| enum rtx_code code; |
| const char *fmt; |
| |
| /* repeat is used to turn tail-recursion into iteration since GCC |
| can't do it when there's no return value. */ |
| repeat: |
| |
| if (x == 0) |
| return; |
| |
| code = GET_CODE (x); |
| switch (code) |
| { |
| case REG: |
| { |
| df_ref def; |
| for (def = DF_REG_DEF_CHAIN (REGNO (x)); |
| def; |
| def = DF_REF_NEXT_REG (def)) |
| bitmap_clear_bit (bmap[DF_REF_BB (def)->index], indx); |
| } |
| |
| return; |
| |
| case MEM: |
| if (! MEM_READONLY_P (x)) |
| { |
| bitmap_iterator bi; |
| unsigned bb_index; |
| rtx x_addr; |
| |
| x_addr = get_addr (XEXP (x, 0)); |
| x_addr = canon_rtx (x_addr); |
| |
| /* First handle all the blocks with calls. We don't need to |
| do any list walking for them. */ |
| EXECUTE_IF_SET_IN_BITMAP (blocks_with_calls, 0, bb_index, bi) |
| { |
| bitmap_clear_bit (bmap[bb_index], indx); |
| } |
| |
| /* Now iterate over the blocks which have memory modifications |
| but which do not have any calls. */ |
| EXECUTE_IF_AND_COMPL_IN_BITMAP (modify_mem_list_set, |
| blocks_with_calls, |
| 0, bb_index, bi) |
| { |
| vec<modify_pair> list |
| = canon_modify_mem_list[bb_index]; |
| modify_pair *pair; |
| unsigned ix; |
| |
| FOR_EACH_VEC_ELT_REVERSE (list, ix, pair) |
| { |
| rtx dest = pair->dest; |
| rtx dest_addr = pair->dest_addr; |
| |
| if (canon_true_dependence (dest, GET_MODE (dest), |
| dest_addr, x, x_addr)) |
| { |
| bitmap_clear_bit (bmap[bb_index], indx); |
| break; |
| } |
| } |
| } |
| } |
| |
| x = XEXP (x, 0); |
| goto repeat; |
| |
| case PC: |
| case CC0: /*FIXME*/ |
| case CONST: |
| CASE_CONST_ANY: |
| case SYMBOL_REF: |
| case LABEL_REF: |
| case ADDR_VEC: |
| case ADDR_DIFF_VEC: |
| return; |
| |
| default: |
| break; |
| } |
| |
| for (i = GET_RTX_LENGTH (code) - 1, fmt = GET_RTX_FORMAT (code); i >= 0; i--) |
| { |
| if (fmt[i] == 'e') |
| { |
| /* If we are about to do the last recursive call |
| needed at this level, change it into iteration. |
| This function is called enough to be worth it. */ |
| if (i == 0) |
| { |
| x = XEXP (x, i); |
| goto repeat; |
| } |
| |
| compute_transp (XEXP (x, i), indx, bmap, blocks_with_calls, |
| modify_mem_list_set, canon_modify_mem_list); |
| } |
| else if (fmt[i] == 'E') |
| for (j = 0; j < XVECLEN (x, i); j++) |
| compute_transp (XVECEXP (x, i, j), indx, bmap, blocks_with_calls, |
| modify_mem_list_set, canon_modify_mem_list); |
| } |
| } |