mirror of
https://forge.sourceware.org/marek/gcc.git
synced 2026-02-21 19:35:36 -05:00
196 lines
7.4 KiB
C++
196 lines
7.4 KiB
C++
// Pass to fuse adjacent loads/stores into paired memory accesses.
|
|
//
|
|
// This file contains the definition of the virtual base class which is
|
|
// overriden by targets that make use of the pass.
|
|
//
|
|
// Copyright (C) 2023-2026 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/>.
|
|
|
|
namespace rtl_ssa {
|
|
class def_info;
|
|
class insn_info;
|
|
class insn_range_info;
|
|
class bb_info;
|
|
}
|
|
|
|
// Information about a potential base candidate, used in try_fuse_pair.
|
|
// There may be zero, one, or two viable RTL bases for a given pair.
|
|
struct base_cand
|
|
{
|
|
// DEF is the def of the base register to be used by the pair.
|
|
rtl_ssa::def_info *def;
|
|
|
|
// FROM_INSN is -1 if the base candidate is already shared by both
|
|
// candidate insns. Otherwise it holds the index of the insn from
|
|
// which the base originated.
|
|
//
|
|
// In the case that the base is shared, either DEF is already used
|
|
// by both candidate accesses, or both accesses see different versions
|
|
// of the same regno, in which case DEF is the def consumed by the
|
|
// first candidate access.
|
|
int from_insn;
|
|
|
|
// To form a pair, we do so by moving the first access down and the second
|
|
// access up. To determine where to form the pair, and whether or not
|
|
// it is safe to form the pair, we track instructions which cannot be
|
|
// re-ordered past due to either dataflow or alias hazards.
|
|
//
|
|
// Since we allow changing the base used by an access, the choice of
|
|
// base can change which instructions act as re-ordering hazards for
|
|
// this pair (due to different dataflow). We store the initial
|
|
// dataflow hazards for this choice of base candidate in HAZARDS.
|
|
//
|
|
// These hazards act as re-ordering barriers to each candidate insn
|
|
// respectively, in program order.
|
|
//
|
|
// Later on, when we take alias analysis into account, we narrow
|
|
// HAZARDS accordingly.
|
|
rtl_ssa::insn_info *hazards[2];
|
|
|
|
base_cand (rtl_ssa::def_info *def, int insn)
|
|
: def (def), from_insn (insn), hazards {nullptr, nullptr} {}
|
|
|
|
base_cand (rtl_ssa::def_info *def) : base_cand (def, -1) {}
|
|
|
|
// Test if this base candidate is viable according to HAZARDS.
|
|
bool viable () const;
|
|
};
|
|
|
|
struct alias_walker;
|
|
|
|
// When querying should_handle_writeback, this enum is used to
|
|
// qualify which opportunities we are asking about.
|
|
enum class writeback_type {
|
|
// Only those writeback opportunities that arise from existing
|
|
// auto-increment accesses.
|
|
EXISTING,
|
|
|
|
// All writeback opportunities, including those that involve folding
|
|
// base register updates into a non-writeback pair.
|
|
ALL
|
|
};
|
|
|
|
// This class can be overriden by targets to give a pass that fuses
|
|
// adjacent loads and stores into load/store pair instructions.
|
|
//
|
|
// The target can override the various virtual functions to customize
|
|
// the behaviour of the pass as appropriate for the target.
|
|
struct pair_fusion {
|
|
pair_fusion ();
|
|
|
|
// Given:
|
|
// - an rtx REG_OP, the non-memory operand in a load/store insn,
|
|
// - a machine_mode MEM_MODE, the mode of the MEM in that insn, and
|
|
// - a boolean LOAD_P (true iff the insn is a load), then:
|
|
// return true if the access should be considered an FP/SIMD access.
|
|
// Such accesses are segregated from GPR accesses, since we only want
|
|
// to form pairs for accesses that use the same register file.
|
|
virtual bool fpsimd_op_p (rtx, machine_mode, bool)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Return true if we should consider forming pairs from memory
|
|
// accesses with operand mode MODE at this stage in compilation.
|
|
virtual bool pair_operand_mode_ok_p (machine_mode mode) = 0;
|
|
|
|
// Return true iff REG_OP is a suitable register operand for a paired
|
|
// memory access, where LOAD_P is true if we're asking about loads and
|
|
// false for stores. MODE gives the mode of the operand.
|
|
virtual bool pair_reg_operand_ok_p (bool load_p, rtx reg_op,
|
|
machine_mode mode) = 0;
|
|
|
|
// Return alias check limit.
|
|
// This is needed to avoid unbounded quadratic behaviour when
|
|
// performing alias analysis.
|
|
virtual int pair_mem_alias_check_limit () = 0;
|
|
|
|
// Return true if we should try to handle writeback opportunities.
|
|
// WHICH determines the kinds of writeback opportunities the caller
|
|
// is asking about.
|
|
virtual bool should_handle_writeback (writeback_type which) = 0;
|
|
|
|
// Given BASE_MEM, the mem from the lower candidate access for a pair,
|
|
// and LOAD_P (true if the access is a load), check if we should proceed
|
|
// to form the pair given the target's code generation policy on
|
|
// paired accesses.
|
|
virtual bool pair_mem_ok_with_policy (rtx base_mem, bool load_p) = 0;
|
|
|
|
// Generate the pattern for a paired access. PATS gives the patterns
|
|
// for the individual memory accesses (which by this point must share a
|
|
// common base register). If WRITEBACK is non-NULL, then this rtx
|
|
// describes the update to the base register that should be performed by
|
|
// the resulting insn. LOAD_P is true iff the accesses are loads.
|
|
virtual rtx gen_pair (rtx *pats, rtx writeback, bool load_p) = 0;
|
|
|
|
// Return true if INSN is a paired memory access. If so, set LOAD_P to
|
|
// true iff INSN is a load pair.
|
|
virtual bool pair_mem_insn_p (rtx_insn *insn, bool &load_p) = 0;
|
|
|
|
// Return true if we should track loads.
|
|
virtual bool track_loads_p ()
|
|
{
|
|
return true;
|
|
}
|
|
|
|
// Return true if we should track stores.
|
|
virtual bool track_stores_p ()
|
|
{
|
|
return true;
|
|
}
|
|
|
|
// Return true if OFFSET is in range for a paired memory access.
|
|
virtual bool pair_mem_in_range_p (HOST_WIDE_INT offset) = 0;
|
|
|
|
// Given a load/store pair insn in PATTERN, unpack the insn, storing
|
|
// the register operands in REGS, and returning the mem. LOAD_P is
|
|
// true for loads and false for stores.
|
|
virtual rtx destructure_pair (rtx regs[2], rtx pattern, bool load_p) = 0;
|
|
|
|
// Given a pair mem in MEM, register operands in REGS, and an rtx
|
|
// representing the effect of writeback on the base register in WB_EFFECT,
|
|
// return an insn representing a writeback variant of this pair.
|
|
// LOAD_P is true iff the pair is a load.
|
|
// This is used when promoting existing non-writeback pairs to writeback
|
|
// variants.
|
|
virtual rtx gen_promote_writeback_pair (rtx wb_effect, rtx mem,
|
|
rtx regs[2], bool load_p) = 0;
|
|
|
|
void process_block (rtl_ssa::bb_info *bb);
|
|
rtl_ssa::insn_info *find_trailing_add (rtl_ssa::insn_info *insns[2],
|
|
const rtl_ssa::insn_range_info
|
|
&pair_range,
|
|
int initial_writeback,
|
|
rtx *writeback_effect,
|
|
rtl_ssa::def_info **add_def,
|
|
rtl_ssa::def_info *base_def,
|
|
poly_int64 initial_offset,
|
|
unsigned access_size);
|
|
int get_viable_bases (rtl_ssa::insn_info *insns[2],
|
|
vec<base_cand> &base_cands,
|
|
rtx cand_mems[2],
|
|
unsigned access_size,
|
|
bool reversed);
|
|
void do_alias_analysis (rtl_ssa::insn_info *alias_hazards[4],
|
|
alias_walker *walkers[4],
|
|
bool load_p);
|
|
void try_promote_writeback (rtl_ssa::insn_info *insn, bool load_p);
|
|
void run ();
|
|
~pair_fusion ();
|
|
};
|