// RTL SSA utility functions for changing instructions -*- C++ -*-
// Copyright (C) 2020-2024 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
// .
namespace rtl_ssa {
// Return true if INSN is one of the instructions being changed by CHANGES.
inline bool
insn_is_changing (array_slice changes,
const insn_info *insn)
{
for (const insn_change *change : changes)
if (change->insn () == insn)
return true;
return false;
}
// Restrict CHANGE.move_range so that the changed instruction can perform
// all its definitions and uses.
//
// IGNORE is an object that provides the same interface as ignore_nothing.
// Assume that if:
//
// - CHANGE contains an access A1 of resource R;
// - an instruction I2 contains another access A2 to R; and
// - IGNORE says that I2 should be ignored
//
// 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.
//
// Assume the same thing about a definition D of R, and about all uses of D,
// if IGNORE says that D should be ignored.
//
// IGNORE should ignore 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
bool
restrict_movement (insn_change &change, IgnorePredicates ignore)
{
// Uses generally lead to failure quicker, so test those first.
return (restrict_movement_for_uses (change.move_range,
change.new_uses, ignore)
&& restrict_movement_for_defs (change.move_range,
change.new_defs, ignore)
&& canonicalize_move_range (change.move_range, change.insn ()));
}
// As above, but ignore only the instruction that is being changed.
inline bool
restrict_movement (insn_change &change)
{
return restrict_movement (change, ignore_insn (change.insn ()));
}
using add_regno_clobber_fn = std::function;
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. Use IGNORE to guide
// this process, where IGNORE is an object that provides the same interface
// as ignore_nothing. When determining whether R is live, ignore accesses
// made by an instruction I if IGNORE says that I should be ignored.
// The caller then assumes the responsibility of ensuring that CHANGE
// and I are placed in a valid order. Similarly, ignore live ranges
// associated with a definition of R if IGNORE says that that definition
// should be ignored.
//
// IGNORE should ignore CHANGE.insn ().
//
// Return true on success. Leave CHANGE unmodified on failure.
template
inline bool
recog (obstack_watermark &watermark, insn_change &change,
IgnorePredicates 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 above, but ignore only the instruction that is being changed.
inline bool
recog (obstack_watermark &watermark, insn_change &change)
{
return recog (watermark, change, ignore_insn (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 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);
}
}