blob: 1f8c651cc0318bacb74264e25393f18676eb1489 [file]
/* Dispatch scheduling hooks for AArch64.
Copyright The GNU Toolchain Authors.
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/>. */
#include "config.h"
#include "system.h"
#include "coretypes.h"
#include "backend.h"
#include "rtl.h"
#include "insn-attr.h"
#include "insn-attr-common.h"
#include "aarch64-protos.h"
#include "aarch64-sched-dispatch.h"
#include "regset.h"
#include "sched-int.h"
#include "dumpfile.h"
#include "print-rtl.h"
/* This file implements the target hooks for dispatch scheduling for AArch64.
Instructions are scheduled into the current dispatch window according to
dispatch constraints provided by the tuning model.
To enable dispatch scheduling for a core (see Neoverse V2 for an example):
- in the tuning model, add the AARCH64_EXTRA_TUNE_DISPATCH_SCHED tune flag
- in the tuning model, add an array of max slot counts for each constraint
and add its reference and length to the tune_params struct
- in the tuning model, add a callback function to determine slot requirements
- optionally, create a new instruction attribute to classify instructions
into dispatch groups (e.g. neoversev2_dispatch) */
static dispatch_window *current_dispatch_window;
/* Constructor for class dispatch_window. */
dispatch_window::dispatch_window (const dispatch_constraint_info &constraint_info)
: m_max_slots (constraint_info.max_slots),
m_num_constraints (constraint_info.num_constraints),
m_callback (constraint_info.callback),
m_violation (false)
{
m_free_slots = XNEWVEC (int, m_num_constraints);
reset_constraints ();
}
/* Destructor for class dispatch_window. */
dispatch_window::~dispatch_window ()
{
XDELETEVEC (m_free_slots);
}
/* Return TRUE iff the given constraints fit into the dispatch window. */
bool
dispatch_window::fits_window (rtx_insn *insn,
const vec<std::pair<int, int>> &constraints) const
{
if (INSN_CODE (insn) < 0)
return true;
if (dump_file)
{
fprintf (dump_file, "Checking if insn fits into dispatch window:\n");
print_rtl_single (dump_file, insn);
}
for (const auto &constraint : constraints)
{
if (constraint.second > m_free_slots[constraint.first])
{
if (dump_file && (dump_flags & TDF_DETAILS))
{
fprintf (dump_file, "Constraint %d needs %d slot(s), "
"only %d free.\n",
constraint.first, constraint.second,
m_free_slots[constraint.first]);
}
return false;
}
}
return true;
}
/* Get the constraints for an instruction. */
vec<std::pair<int, int>>
dispatch_window::get_constraints (rtx_insn *insn) const
{
return m_callback (insn);
}
/* Add INSN to the dispatch window and set the violation flag
if there is a constraint violation. */
void
dispatch_window::add_insn_to_window (rtx_insn *insn)
{
if (INSN_CODE (insn) < 0)
return;
auto constraints = m_callback (insn);
if (!fits_window (insn, constraints))
{
if (dump_file)
fprintf (dump_file, "Window full. Starting new dispatch window.\n");
reset_constraints ();
}
for (const auto &constraint : constraints)
{
m_free_slots[constraint.first] -= constraint.second;
if (m_free_slots[constraint.first] < 0)
m_violation = true;
}
if (dump_file)
{
fprintf (dump_file, "Insn added to dispatch window.\n");
if (dump_flags & TDF_DETAILS)
print_window (dump_file);
}
}
/* Return TRUE iff there is a dispatch violation, i.e. one of the dispatch
constraints has a negative number of free slots. */
bool
dispatch_window::has_violation () const
{
return m_violation;
}
/* Print information about the dispatch window to the given FILE. */
void
dispatch_window::print_window (FILE *file) const
{
fprintf (file, "==== Current dispatch window ====\n");
fprintf (file, "Violation: %s\n", m_violation ? "true" : "false");
for (int i = 0; i < m_num_constraints; i++)
{
fprintf (file, "Constraint %d: %d of %d slots free\n",
i, m_free_slots[i], m_max_slots[i]);
}
fprintf (file, "\n");
}
/* For all dispatch constraints, reset the number of free slots to the
maximum number of slots.
This is called when the next dispatch window is started. */
void
dispatch_window::reset_constraints ()
{
for (int i = 0; i < m_num_constraints; i++)
m_free_slots[i] = m_max_slots[i];
m_violation = false;
}
/* Initialize the dispatch window using the constraints from the tuning model.
This is called once at the beginning of scheduling. */
void
init_dispatch_window (void)
{
const struct dispatch_constraint_info *dispatch_constraints
= aarch64_tune_params.dispatch_constraints;
current_dispatch_window = new dispatch_window (*dispatch_constraints);
if (dump_file)
{
fprintf (dump_file, "DISPATCH WINDOW INITIALIZED\n");
if (dump_flags & TDF_DETAILS)
current_dispatch_window->print_window (dump_file);
}
}
/* The next two functions implement the dispatch-scheduling target hooks
for aarch64 and are the drivers of the dispatch scheduler. */
void
aarch64_sched_dispatch_do (rtx_insn *insn, int mode)
{
if (mode == DISPATCH_INIT)
init_dispatch_window ();
else if (mode == ADD_TO_DISPATCH_WINDOW && current_dispatch_window)
current_dispatch_window->add_insn_to_window (insn);
}
bool
aarch64_sched_dispatch (rtx_insn *insn, int action)
{
/* We only want dispatch scheduling to be enabled during the last
scheduling pass, i.e. after reload and sched_fusion. */
if ((aarch64_tune_params.extra_tuning_flags
& AARCH64_EXTRA_TUNE_DISPATCH_SCHED)
&& reload_completed && !sched_fusion)
switch (action)
{
case IS_DISPATCH_ON:
return true;
/* IS_CMP may be used to delay scheduling of flag setting instructions
to keep them close to their consumers, e.g. branches at the end of a BB.
However, we don't want to delay scheduling of flag setting instructions,
because many consumers are not branches. */
case IS_CMP:
return false;
case DISPATCH_VIOLATION:
return current_dispatch_window->has_violation ();
case FITS_DISPATCH_WINDOW:
{
auto constraints = current_dispatch_window->get_constraints (insn);
return current_dispatch_window->fits_window (insn, constraints);
}
default:
return false;
}
return false;
}