blob: b57701f0ea102bf1d9e4bb792681d0d176c9ec8b [file] [log] [blame]
/* Support routines for vrange storage.
Copyright (C) 2022 Free Software Foundation, Inc.
Contributed by Aldy Hernandez <aldyh@redhat.com>.
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 "tree.h"
#include "gimple.h"
#include "ssa.h"
#include "tree-pretty-print.h"
#include "fold-const.h"
#include "gimple-range.h"
#include "value-range-storage.h"
// Return a newly allocated slot holding R, or NULL if storing a range
// of R's type is not supported.
void *
vrange_storage::alloc_slot (const vrange &r)
{
gcc_checking_assert (m_alloc);
if (is_a <irange> (r))
return irange_storage_slot::alloc_slot (*m_alloc, as_a <irange> (r));
if (is_a <frange> (r))
return frange_storage_slot::alloc_slot (*m_alloc, as_a <frange> (r));
return NULL;
}
// Set SLOT to R.
void
vrange_storage::set_vrange (void *slot, const vrange &r)
{
if (is_a <irange> (r))
{
irange_storage_slot *s = static_cast <irange_storage_slot *> (slot);
gcc_checking_assert (s->fits_p (as_a <irange> (r)));
s->set_irange (as_a <irange> (r));
}
else if (is_a <frange> (r))
{
frange_storage_slot *s = static_cast <frange_storage_slot *> (slot);
gcc_checking_assert (s->fits_p (as_a <frange> (r)));
s->set_frange (as_a <frange> (r));
}
else
gcc_unreachable ();
}
// Restore R from SLOT. TYPE is the type of R.
void
vrange_storage::get_vrange (const void *slot, vrange &r, tree type)
{
if (is_a <irange> (r))
{
const irange_storage_slot *s
= static_cast <const irange_storage_slot *> (slot);
s->get_irange (as_a <irange> (r), type);
}
else if (is_a <frange> (r))
{
const frange_storage_slot *s
= static_cast <const frange_storage_slot *> (slot);
s->get_frange (as_a <frange> (r), type);
}
else
gcc_unreachable ();
}
// Return TRUE if SLOT can fit R.
bool
vrange_storage::fits_p (const void *slot, const vrange &r)
{
if (is_a <irange> (r))
{
const irange_storage_slot *s
= static_cast <const irange_storage_slot *> (slot);
return s->fits_p (as_a <irange> (r));
}
if (is_a <frange> (r))
{
const frange_storage_slot *s
= static_cast <const frange_storage_slot *> (slot);
return s->fits_p (as_a <frange> (r));
}
gcc_unreachable ();
return false;
}
// Factory that creates a new irange_storage_slot object containing R.
// This is the only way to construct an irange slot as stack creation
// is disallowed.
irange_storage_slot *
irange_storage_slot::alloc_slot (vrange_allocator &allocator, const irange &r)
{
size_t size = irange_storage_slot::size (r);
irange_storage_slot *p
= static_cast <irange_storage_slot *> (allocator.alloc (size));
new (p) irange_storage_slot (r);
return p;
}
// Initialize the current slot with R.
irange_storage_slot::irange_storage_slot (const irange &r)
{
gcc_checking_assert (!r.undefined_p ());
unsigned prec = TYPE_PRECISION (r.type ());
unsigned n = num_wide_ints_needed (r);
if (n > MAX_INTS)
{
int_range<MAX_PAIRS> squash (r);
m_ints.set_precision (prec, num_wide_ints_needed (squash));
set_irange (squash);
}
else
{
m_ints.set_precision (prec, n);
set_irange (r);
}
}
// Store R into the current slot.
void
irange_storage_slot::set_irange (const irange &r)
{
gcc_checking_assert (fits_p (r));
m_ints[0] = r.get_nonzero_bits ();
unsigned pairs = r.num_pairs ();
for (unsigned i = 0; i < pairs; ++i)
{
m_ints[i*2 + 1] = r.lower_bound (i);
m_ints[i*2 + 2] = r.upper_bound (i);
}
}
// Restore a range of TYPE from the current slot into R.
void
irange_storage_slot::get_irange (irange &r, tree type) const
{
gcc_checking_assert (TYPE_PRECISION (type) == m_ints.get_precision ());
r.set_undefined ();
unsigned nelements = m_ints.num_elements ();
for (unsigned i = 1; i < nelements; i += 2)
{
int_range<2> tmp (type, m_ints[i], m_ints[i + 1]);
r.union_ (tmp);
}
r.set_nonzero_bits (get_nonzero_bits ());
}
// Return the size in bytes to allocate a slot that can hold R.
size_t
irange_storage_slot::size (const irange &r)
{
gcc_checking_assert (!r.undefined_p ());
unsigned prec = TYPE_PRECISION (r.type ());
unsigned n = num_wide_ints_needed (r);
if (n > MAX_INTS)
n = MAX_INTS;
return (sizeof (irange_storage_slot)
+ trailing_wide_ints<MAX_INTS>::extra_size (prec, n));
}
// Return the number of wide ints needed to represent R.
unsigned int
irange_storage_slot::num_wide_ints_needed (const irange &r)
{
return r.num_pairs () * 2 + 1;
}
// Return TRUE if R fits in the current slot.
bool
irange_storage_slot::fits_p (const irange &r) const
{
return m_ints.num_elements () >= num_wide_ints_needed (r);
}
// Dump the current slot.
void
irange_storage_slot::dump () const
{
fprintf (stderr, "raw irange_storage_slot:\n");
for (unsigned i = 1; i < m_ints.num_elements (); i += 2)
{
m_ints[i].dump ();
m_ints[i + 1].dump ();
}
fprintf (stderr, "NONZERO ");
wide_int nz = get_nonzero_bits ();
nz.dump ();
}
DEBUG_FUNCTION void
debug (const irange_storage_slot &storage)
{
storage.dump ();
fprintf (stderr, "\n");
}
// Implementation of frange_storage_slot.
frange_storage_slot *
frange_storage_slot::alloc_slot (vrange_allocator &allocator, const frange &r)
{
size_t size = sizeof (frange_storage_slot);
frange_storage_slot *p
= static_cast <frange_storage_slot *> (allocator.alloc (size));
new (p) frange_storage_slot (r);
return p;
}
void
frange_storage_slot::set_frange (const frange &r)
{
gcc_checking_assert (fits_p (r));
gcc_checking_assert (!r.undefined_p ());
m_kind = r.m_kind;
m_min = r.m_min;
m_max = r.m_max;
m_pos_nan = r.m_pos_nan;
m_neg_nan = r.m_neg_nan;
}
void
frange_storage_slot::get_frange (frange &r, tree type) const
{
gcc_checking_assert (r.supports_type_p (type));
// Handle explicit NANs.
if (m_kind == VR_NAN)
{
if (HONOR_NANS (type))
{
if (m_pos_nan && m_neg_nan)
r.set_nan (type);
else
r.set_nan (type, m_neg_nan);
}
else
r.set_undefined ();
return;
}
// We use the constructor to create the new range instead of writing
// out the bits into the frange directly, because the global range
// being read may be being inlined into a function with different
// restrictions as when it was originally written. We want to make
// sure the resulting range is canonicalized correctly for the new
// consumer.
r = frange (type, m_min, m_max, m_kind);
// The constructor will set the NAN bits for HONOR_NANS, but we must
// make sure to set the NAN sign if known.
if (HONOR_NANS (type) && (m_pos_nan ^ m_neg_nan) == 1)
r.update_nan (m_neg_nan);
else if (!m_pos_nan && !m_neg_nan)
r.clear_nan ();
}
bool
frange_storage_slot::fits_p (const frange &) const
{
return true;
}