mirror of
https://github.com/autc04/Retro68.git
synced 2024-06-03 00:29:47 +00:00
138 lines
4.8 KiB
C++
138 lines
4.8 KiB
C++
// RTL SSA utility functions for changing instructions -*- C++ -*-
|
|
// Copyright (C) 2020-2022 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 {
|
|
|
|
// Return true if INSN is one of the instructions being changed by CHANGES.
|
|
inline bool
|
|
insn_is_changing (array_slice<insn_change *const> changes,
|
|
const insn_info *insn)
|
|
{
|
|
for (const insn_change *change : changes)
|
|
if (change->insn () == insn)
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
// Return a closure of insn_is_changing, for use as a predicate.
|
|
// This could be done using local lambdas instead, but the predicate is
|
|
// used often enough that having a class should be more convenient and allow
|
|
// reuse of template instantiations.
|
|
//
|
|
// We don't use std::bind because it would involve an indirect function call,
|
|
// whereas this function is used in relatively performance-critical code.
|
|
inline insn_is_changing_closure
|
|
insn_is_changing (array_slice<insn_change *const> changes)
|
|
{
|
|
return insn_is_changing_closure (changes);
|
|
}
|
|
|
|
// Restrict CHANGE.move_range so that the changed instruction can perform
|
|
// all its definitions and uses. Assume that if:
|
|
//
|
|
// - CHANGE contains an access A1 of resource R;
|
|
// - an instruction I2 contains another access A2 to R; and
|
|
// - IGNORE (I2) is true
|
|
//
|
|
// then either:
|
|
//
|
|
// - A2 will be removed; or
|
|
// - something will ensure that A1 and A2 maintain their current order,
|
|
// without this having to be enforced by CHANGE's move range.
|
|
//
|
|
// IGNORE should return true for CHANGE.insn ().
|
|
//
|
|
// Return true on success, otherwise leave CHANGE.move_range in an invalid
|
|
// state.
|
|
//
|
|
// This function only works correctly for instructions that remain within
|
|
// the same extended basic block.
|
|
template<typename IgnorePredicate>
|
|
bool
|
|
restrict_movement_ignoring (insn_change &change, IgnorePredicate ignore)
|
|
{
|
|
// Uses generally lead to failure quicker, so test those first.
|
|
return (restrict_movement_for_uses_ignoring (change.move_range,
|
|
change.new_uses, ignore)
|
|
&& restrict_movement_for_defs_ignoring (change.move_range,
|
|
change.new_defs, ignore)
|
|
&& canonicalize_move_range (change.move_range, change.insn ()));
|
|
}
|
|
|
|
// Like restrict_movement_ignoring, but ignore only the instruction
|
|
// that is being changed.
|
|
inline bool
|
|
restrict_movement (insn_change &change)
|
|
{
|
|
return restrict_movement_ignoring (change, insn_is (change.insn ()));
|
|
}
|
|
|
|
using add_regno_clobber_fn = std::function<bool (insn_change &,
|
|
unsigned int)>;
|
|
bool recog_internal (insn_change &, add_regno_clobber_fn);
|
|
|
|
// Try to recognize the new instruction pattern for CHANGE, potentially
|
|
// tweaking the pattern or adding extra clobbers in order to make it match.
|
|
//
|
|
// When adding an extra clobber for register R, restrict CHANGE.move_range
|
|
// to a range of instructions for which R is not live. When determining
|
|
// whether R is live, ignore accesses made by an instruction I if
|
|
// IGNORE (I) is true. The caller then assumes the responsibility
|
|
// of ensuring that CHANGE and I are placed in a valid order.
|
|
//
|
|
// IGNORE should return true for CHANGE.insn ().
|
|
//
|
|
// Return true on success. Leave CHANGE unmodified on failure.
|
|
template<typename IgnorePredicate>
|
|
inline bool
|
|
recog_ignoring (obstack_watermark &watermark, insn_change &change,
|
|
IgnorePredicate ignore)
|
|
{
|
|
auto add_regno_clobber = [&](insn_change &change, unsigned int regno)
|
|
{
|
|
return crtl->ssa->add_regno_clobber (watermark, change, regno, ignore);
|
|
};
|
|
return recog_internal (change, add_regno_clobber);
|
|
}
|
|
|
|
// As for recog_ignoring, but ignore only the instruction that is being
|
|
// changed.
|
|
inline bool
|
|
recog (obstack_watermark &watermark, insn_change &change)
|
|
{
|
|
return recog_ignoring (watermark, change, insn_is (change.insn ()));
|
|
}
|
|
|
|
// Check whether insn costs indicate that the net effect of the changes
|
|
// in CHANGES is worthwhile. Require a strict improvement if STRICT_P,
|
|
// otherwise allow the new instructions to be the same cost as the old
|
|
// instructions.
|
|
bool changes_are_worthwhile (array_slice<insn_change *const> changes,
|
|
bool strict_p = false);
|
|
|
|
// Like changes_are_worthwhile, but for a single change.
|
|
inline bool
|
|
change_is_worthwhile (insn_change &change, bool strict_p = false)
|
|
{
|
|
insn_change *changes[] = { &change };
|
|
return changes_are_worthwhile (changes, strict_p);
|
|
}
|
|
|
|
}
|