/* Shorten memrefs pass for RISC-V. Copyright (C) 2018-2023 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 . */ #define IN_TARGET_CODE 1 #include "config.h" #include "system.h" #include "coretypes.h" #include "tm.h" #include "rtl.h" #include "backend.h" #include "regs.h" #include "target.h" #include "memmodel.h" #include "emit-rtl.h" #include "df.h" #include "predict.h" #include "tree-pass.h" /* Try to make more use of compressed load and store instructions by replacing a load/store at address BASE + LARGE_OFFSET with a new load/store at address NEW BASE + SMALL OFFSET. If NEW BASE is stored in a compressed register, the load/store can be compressed. Since creating NEW BASE incurs an overhead, the change is only attempted when BASE is referenced by at least four load/stores in the same basic block. */ namespace { const pass_data pass_data_shorten_memrefs = { RTL_PASS, /* type */ "shorten_memrefs", /* name */ OPTGROUP_NONE, /* optinfo_flags */ TV_NONE, /* tv_id */ 0, /* properties_required */ 0, /* properties_provided */ 0, /* properties_destroyed */ 0, /* todo_flags_start */ 0, /* todo_flags_finish */ }; class pass_shorten_memrefs : public rtl_opt_pass { public: pass_shorten_memrefs (gcc::context *ctxt) : rtl_opt_pass (pass_data_shorten_memrefs, ctxt) {} /* opt_pass methods: */ virtual bool gate (function *) { return TARGET_RVC && riscv_mshorten_memrefs && optimize > 0; } virtual unsigned int execute (function *); private: typedef int_hash regno_hash; typedef hash_map regno_map; regno_map * analyze (basic_block bb); void transform (regno_map *m, basic_block bb); bool get_si_mem_base_reg (rtx mem, rtx *addr, bool *extend); }; // class pass_shorten_memrefs bool pass_shorten_memrefs::get_si_mem_base_reg (rtx mem, rtx *addr, bool *extend) { /* Whether it's sign/zero extended. */ if (GET_CODE (mem) == ZERO_EXTEND || GET_CODE (mem) == SIGN_EXTEND) { *extend = true; mem = XEXP (mem, 0); } if (!MEM_P (mem) || GET_MODE (mem) != SImode) return false; *addr = XEXP (mem, 0); return GET_CODE (*addr) == PLUS && REG_P (XEXP (*addr, 0)); } /* Count how many times each regno is referenced as base address for a memory access. */ pass_shorten_memrefs::regno_map * pass_shorten_memrefs::analyze (basic_block bb) { regno_map *m = hash_map::create_ggc (10); rtx_insn *insn; regstat_init_n_sets_and_refs (); FOR_BB_INSNS (bb, insn) { if (!NONJUMP_INSN_P (insn)) continue; rtx pat = PATTERN (insn); if (GET_CODE (pat) != SET) continue; /* Analyze stores first then loads. */ for (int i = 0; i < 2; i++) { rtx mem = XEXP (pat, i); rtx addr; bool extend = false; if (get_si_mem_base_reg (mem, &addr, &extend)) { HOST_WIDE_INT regno = REGNO (XEXP (addr, 0)); /* Do not count store zero as these cannot be compressed. */ if (i == 0) { if (XEXP (pat, 1) == CONST0_RTX (GET_MODE (XEXP (pat, 1)))) continue; } if (REG_N_REFS (regno) < 4) continue; m->get_or_insert (regno)++; } } } regstat_free_n_sets_and_refs (); return m; } /* Convert BASE + LARGE_OFFSET to NEW_BASE + SMALL_OFFSET for each load/store with a base reg referenced at least 4 times. */ void pass_shorten_memrefs::transform (regno_map *m, basic_block bb) { rtx_insn *insn; FOR_BB_INSNS (bb, insn) { if (!NONJUMP_INSN_P (insn)) continue; rtx pat = PATTERN (insn); if (GET_CODE (pat) != SET) continue; start_sequence (); /* Transform stores first then loads. */ for (int i = 0; i < 2; i++) { rtx mem = XEXP (pat, i); rtx addr; bool extend = false; if (get_si_mem_base_reg (mem, &addr, &extend)) { HOST_WIDE_INT regno = REGNO (XEXP (addr, 0)); /* Do not transform store zero as these cannot be compressed. */ if (i == 0) { if (XEXP (pat, 1) == CONST0_RTX (GET_MODE (XEXP (pat, 1)))) continue; } if (m->get_or_insert (regno) > 3) { if (extend) { addr = targetm.legitimize_address (addr, addr, GET_MODE (XEXP (mem, 0))); XEXP (XEXP (pat, i), 0) = replace_equiv_address (XEXP (mem, 0), addr); } else { addr = targetm.legitimize_address (addr, addr, GET_MODE (mem)); XEXP (pat, i) = replace_equiv_address (mem, addr); } df_insn_rescan (insn); } } } rtx_insn *seq = get_insns (); end_sequence (); emit_insn_before (seq, insn); } } unsigned int pass_shorten_memrefs::execute (function *fn) { basic_block bb; FOR_ALL_BB_FN (bb, fn) { regno_map *m; if (optimize_bb_for_speed_p (bb)) continue; m = analyze (bb); transform (m, bb); } return 0; } } // anon namespace rtl_opt_pass * make_pass_shorten_memrefs (gcc::context *ctxt) { return new pass_shorten_memrefs (ctxt); }