aboutsummaryrefslogtreecommitdiff
path: root/gcc
diff options
context:
space:
mode:
Diffstat (limited to 'gcc')
-rw-r--r--gcc/gimple-range.cc30
-rw-r--r--gcc/tree-core.h13
-rw-r--r--gcc/tree-ssa-dom.cc2
-rw-r--r--gcc/tree-ssanames.cc240
-rw-r--r--gcc/tree-ssanames.h12
-rw-r--r--gcc/tree-vrp.cc22
-rw-r--r--gcc/tree.h8
-rw-r--r--gcc/value-query.cc54
-rw-r--r--gcc/value-query.h1
-rw-r--r--gcc/value-range-storage.cc4
10 files changed, 149 insertions, 237 deletions
diff --git a/gcc/gimple-range.cc b/gcc/gimple-range.cc
index 3a9f0b0..7ac4830 100644
--- a/gcc/gimple-range.cc
+++ b/gcc/gimple-range.cc
@@ -468,22 +468,12 @@ gimple_ranger::register_inferred_ranges (gimple *s)
{
Value_Range tmp (TREE_TYPE (lhs));
if (range_of_stmt (tmp, s, lhs) && !tmp.varying_p ()
- && update_global_range (tmp, lhs) && dump_file)
+ && set_range_info (lhs, tmp) && dump_file)
{
- // ?? This section should be adjusted when non-iranges can
- // be exported. For now, the only way update_global_range
- // above can succeed is with an irange so this is safe.
- value_range vr = as_a <irange> (tmp);
fprintf (dump_file, "Global Exported: ");
print_generic_expr (dump_file, lhs, TDF_SLIM);
fprintf (dump_file, " = ");
- vr.dump (dump_file);
- int_range_max same = vr;
- if (same != as_a <irange> (tmp))
- {
- fprintf (dump_file, " ... irange was : ");
- tmp.dump (dump_file);
- }
+ tmp.dump (dump_file);
fputc ('\n', dump_file);
}
}
@@ -509,7 +499,7 @@ gimple_ranger::export_global_ranges ()
&& m_cache.get_global_range (r, name)
&& !r.varying_p())
{
- bool updated = update_global_range (r, name);
+ bool updated = set_range_info (name, r);
if (!updated || !dump_file)
continue;
@@ -522,22 +512,10 @@ gimple_ranger::export_global_ranges ()
print_header = false;
}
- if (!irange::supports_p (TREE_TYPE (name)))
- continue;
-
- vrange &v = r;
- value_range vr = as_a <irange> (v);
print_generic_expr (dump_file, name , TDF_SLIM);
fprintf (dump_file, " : ");
- vr.dump (dump_file);
+ r.dump (dump_file);
fprintf (dump_file, "\n");
- int_range_max same = vr;
- if (same != as_a <irange> (v))
- {
- fprintf (dump_file, " irange : ");
- r.dump (dump_file);
- fprintf (dump_file, "\n");
- }
}
}
}
diff --git a/gcc/tree-core.h b/gcc/tree-core.h
index ab5fa01..ea9f281 100644
--- a/gcc/tree-core.h
+++ b/gcc/tree-core.h
@@ -33,7 +33,7 @@ struct function;
struct real_value;
struct fixed_value;
struct ptr_info_def;
-struct range_info_def;
+struct irange_storage_slot;
struct die_struct;
@@ -1194,9 +1194,6 @@ struct GTY(()) tree_base {
TRANSACTION_EXPR_OUTER in
TRANSACTION_EXPR
- SSA_NAME_ANTI_RANGE_P in
- SSA_NAME
-
MUST_TAIL_CALL in
CALL_EXPR
@@ -1594,8 +1591,12 @@ struct GTY(()) tree_ssa_name {
union ssa_name_info_type {
/* Pointer attributes used for alias analysis. */
struct GTY ((tag ("0"))) ptr_info_def *ptr_info;
- /* Value range attributes used for zero/sign extension elimination. */
- struct GTY ((tag ("1"))) range_info_def *range_info;
+ /* This holds any range info supported by ranger (except ptr_info
+ above) and is managed by vrange_storage. */
+ void * GTY ((skip)) range_info;
+ /* GTY tag when the range in the range_info slot above satisfies
+ irange::supports_type_p. */
+ struct GTY ((tag ("1"))) irange_storage_slot *irange_info;
} GTY ((desc ("%1.typed.type ?" \
"!POINTER_TYPE_P (TREE_TYPE ((tree)&%1)) : 2"))) info;
diff --git a/gcc/tree-ssa-dom.cc b/gcc/tree-ssa-dom.cc
index 2bc2c3d..43acc75 100644
--- a/gcc/tree-ssa-dom.cc
+++ b/gcc/tree-ssa-dom.cc
@@ -1255,7 +1255,7 @@ dom_opt_dom_walker::set_global_ranges_from_unreachable_edges (basic_block bb)
&& !r.varying_p ()
&& !r.undefined_p ())
{
- update_global_range (r, name);
+ set_range_info (name, r);
maybe_set_nonzero_bits (pred_e, name);
}
}
diff --git a/gcc/tree-ssanames.cc b/gcc/tree-ssanames.cc
index bc22ece..9389454 100644
--- a/gcc/tree-ssanames.cc
+++ b/gcc/tree-ssanames.cc
@@ -32,6 +32,7 @@ along with GCC; see the file COPYING3. If not see
#include "cfgloop.h"
#include "tree-scalar-evolution.h"
#include "value-query.h"
+#include "value-range-storage.h"
/* Rewriting a function into SSA form can create a huge number of SSA_NAMEs,
many of which may be thrown away shortly after their creation if jumps
@@ -71,6 +72,74 @@ unsigned int ssa_name_nodes_created;
#define FREE_SSANAMES(fun) (fun)->gimple_df->free_ssanames
#define FREE_SSANAMES_QUEUE(fun) (fun)->gimple_df->free_ssanames_queue
+static ggc_vrange_allocator ggc_allocator;
+static vrange_storage vstore (&ggc_allocator);
+
+/* Return TRUE if NAME has global range info. */
+
+inline bool
+range_info_p (const_tree name)
+{
+ return SSA_NAME_RANGE_INFO (name);
+}
+
+/* Return TRUE if R fits in the global range of NAME. */
+
+inline bool
+range_info_fits_p (tree name, const vrange &r)
+{
+ gcc_checking_assert (range_info_p (name));
+ void *mem = SSA_NAME_RANGE_INFO (name);
+ return vrange_storage::fits_p (mem, r);
+}
+
+/* Allocate a new global range for NAME and set it to R. Return the
+ allocation slot. */
+
+inline void *
+range_info_alloc (tree name, const vrange &r)
+{
+ void *mem = vstore.alloc_slot (r);
+ SSA_NAME_RANGE_INFO (name) = mem;
+ return mem;
+}
+
+/* Free storage allocated for the global range for NAME. */
+
+inline void
+range_info_free (tree name)
+{
+ void *mem = SSA_NAME_RANGE_INFO (name);
+ vstore.free (mem);
+}
+
+/* Return the global range for NAME in R. */
+
+inline void
+range_info_get_range (tree name, vrange &r)
+{
+ vstore.get_vrange (SSA_NAME_RANGE_INFO (name), r, TREE_TYPE (name));
+}
+
+/* Set the global range for NAME from R. Return TRUE if successfull,
+ or FALSE if we can't set a range of NAME's type. */
+
+inline bool
+range_info_set_range (tree name, const vrange &r)
+{
+ if (!range_info_p (name) || !range_info_fits_p (name, r))
+ {
+ if (range_info_p (name))
+ range_info_free (name);
+
+ return range_info_alloc (name, r);
+ }
+ else
+ {
+ vstore.set_vrange (SSA_NAME_RANGE_INFO (name), r);
+ return true;
+ }
+}
/* Initialize management of SSA_NAMEs to default SIZE. If SIZE is
zero use default. */
@@ -343,94 +412,38 @@ make_ssa_name_fn (struct function *fn, tree var, gimple *stmt,
return t;
}
-/* Helper function for set_range_info.
-
- Store range information RANGE_TYPE, MIN, and MAX to tree ssa_name
- NAME. */
-
-void
-set_range_info_raw (tree name, enum value_range_kind range_type,
- const wide_int_ref &min, const wide_int_ref &max)
-{
- gcc_assert (!POINTER_TYPE_P (TREE_TYPE (name)));
- gcc_assert (range_type == VR_RANGE || range_type == VR_ANTI_RANGE);
- range_info_def *ri = SSA_NAME_RANGE_INFO (name);
- unsigned int precision = TYPE_PRECISION (TREE_TYPE (name));
-
- /* Allocate if not available. */
- if (ri == NULL)
- {
- size_t size = (sizeof (range_info_def)
- + trailing_wide_ints <3>::extra_size (precision));
- ri = static_cast<range_info_def *> (ggc_internal_alloc (size));
- ri->ints.set_precision (precision);
- SSA_NAME_RANGE_INFO (name) = ri;
- ri->set_nonzero_bits (wi::shwi (-1, precision));
- }
-
- /* Record the range type. */
- if (SSA_NAME_RANGE_TYPE (name) != range_type)
- SSA_NAME_ANTI_RANGE_P (name) = (range_type == VR_ANTI_RANGE);
+/* Update the range information for NAME, intersecting into an existing
+ range if applicable. Return TRUE if the range was updated. */
- /* Set the values. */
- ri->set_min (min);
- ri->set_max (max);
-
- /* If it is a range, try to improve nonzero_bits from the min/max. */
- if (range_type == VR_RANGE)
- {
- wide_int xorv = ri->get_min () ^ ri->get_max ();
- if (xorv != 0)
- xorv = wi::mask (precision - wi::clz (xorv), false, precision);
- ri->set_nonzero_bits (ri->get_nonzero_bits () & (ri->get_min () | xorv));
- }
-}
-
-/* Store range information RANGE_TYPE, MIN, and MAX to tree ssa_name
- NAME while making sure we don't store useless range info. */
-
-static void
-set_range_info (tree name, enum value_range_kind range_type,
- const wide_int_ref &min, const wide_int_ref &max)
+bool
+set_range_info (tree name, const vrange &r)
{
- gcc_assert (!POINTER_TYPE_P (TREE_TYPE (name)));
+ if (r.undefined_p () || r.varying_p ())
+ return false;
tree type = TREE_TYPE (name);
- if (range_type == VR_VARYING)
+ if (POINTER_TYPE_P (type))
{
- /* SSA_NAME_RANGE_TYPE can only hold a VR_RANGE or
- VR_ANTI_RANGE. Denormalize VR_VARYING to VR_RANGE. */
- range_type = VR_RANGE;
- gcc_checking_assert (min == wi::min_value (type));
- gcc_checking_assert (max == wi::max_value (type));
- }
-
- /* A range of the entire domain is really no range at all. */
- if (min == wi::min_value (TYPE_PRECISION (type), TYPE_SIGN (type))
- && max == wi::max_value (TYPE_PRECISION (type), TYPE_SIGN (type)))
- {
- range_info_def *ri = SSA_NAME_RANGE_INFO (name);
- if (ri == NULL)
- return;
- if (ri->get_nonzero_bits () == -1)
+ if (r.nonzero_p ())
{
- ggc_free (ri);
- SSA_NAME_RANGE_INFO (name) = NULL;
- return;
+ set_ptr_nonnull (name);
+ return true;
}
+ return false;
}
- set_range_info_raw (name, range_type, min, max);
-}
-
-/* Store range information for NAME from a value_range. */
+ /* If a global range already exists, incorporate it. */
+ if (range_info_p (name))
+ {
+ Value_Range tmp (type);
+ range_info_get_range (name, tmp);
+ tmp.intersect (r);
+ if (tmp.undefined_p ())
+ return false;
-void
-set_range_info (tree name, const value_range &vr)
-{
- wide_int min = wi::to_wide (vr.min ());
- wide_int max = wi::to_wide (vr.max ());
- set_range_info (name, vr.kind (), min, max);
+ return range_info_set_range (name, tmp);
+ }
+ return range_info_set_range (name, r);
}
/* Set nonnull attribute to pointer NAME. */
@@ -443,22 +456,16 @@ set_ptr_nonnull (tree name)
pi->pt.null = 0;
}
-/* Change non-zero bits bitmask of NAME. */
+/* Update the non-zero bits bitmask of NAME. */
void
set_nonzero_bits (tree name, const wide_int_ref &mask)
{
gcc_assert (!POINTER_TYPE_P (TREE_TYPE (name)));
- if (SSA_NAME_RANGE_INFO (name) == NULL)
- {
- if (mask == -1)
- return;
- set_range_info_raw (name, VR_RANGE,
- wi::to_wide (TYPE_MIN_VALUE (TREE_TYPE (name))),
- wi::to_wide (TYPE_MAX_VALUE (TREE_TYPE (name))));
- }
- range_info_def *ri = SSA_NAME_RANGE_INFO (name);
- ri->set_nonzero_bits (mask);
+
+ int_range<2> r (TREE_TYPE (name));
+ r.set_nonzero_bits (mask);
+ set_range_info (name, r);
}
/* Return a widest_int with potentially non-zero bits in SSA_NAME
@@ -482,10 +489,15 @@ get_nonzero_bits (const_tree name)
return wi::shwi (-1, precision);
}
- range_info_def *ri = SSA_NAME_RANGE_INFO (name);
- if (!ri)
+ if (!range_info_p (name))
return wi::shwi (-1, precision);
+ /* Optimization to get at the nonzero bits because we know the
+ storage type. This saves us measurable time compared to going
+ through vrange_storage. */
+ gcc_checking_assert (irange::supports_p (TREE_TYPE (name)));
+ irange_storage_slot *ri
+ = static_cast <irange_storage_slot *> (SSA_NAME_RANGE_INFO (name));
return ri->get_nonzero_bits ();
}
@@ -727,38 +739,18 @@ duplicate_ssa_name_ptr_info (tree name, struct ptr_info_def *ptr_info)
SSA_NAME_PTR_INFO (name) = new_ptr_info;
}
-/* Creates a duplicate of the range_info_def at RANGE_INFO of type
- RANGE_TYPE for use by the SSA name NAME. */
-static void
-duplicate_ssa_name_range_info (tree name, enum value_range_kind range_type,
- struct range_info_def *range_info)
-{
- struct range_info_def *new_range_info;
-
- gcc_assert (!POINTER_TYPE_P (TREE_TYPE (name)));
- gcc_assert (!SSA_NAME_RANGE_INFO (name));
-
- if (!range_info)
- return;
-
- unsigned int precision = TYPE_PRECISION (TREE_TYPE (name));
- size_t size = (sizeof (range_info_def)
- + trailing_wide_ints <3>::extra_size (precision));
- new_range_info = static_cast<range_info_def *> (ggc_internal_alloc (size));
- memcpy (new_range_info, range_info, size);
-
- gcc_assert (range_type == VR_RANGE || range_type == VR_ANTI_RANGE);
- SSA_NAME_ANTI_RANGE_P (name) = (range_type == VR_ANTI_RANGE);
- SSA_NAME_RANGE_INFO (name) = new_range_info;
-}
-
void
duplicate_ssa_name_range_info (tree name, tree src)
{
gcc_checking_assert (!POINTER_TYPE_P (TREE_TYPE (src)));
- duplicate_ssa_name_range_info (name,
- SSA_NAME_RANGE_TYPE (src),
- SSA_NAME_RANGE_INFO (src));
+ gcc_checking_assert (!range_info_p (name));
+
+ if (range_info_p (src))
+ {
+ Value_Range src_range (TREE_TYPE (src));
+ range_info_get_range (src, src_range);
+ range_info_set_range (name, src_range);
+ }
}
@@ -776,14 +768,8 @@ duplicate_ssa_name_fn (struct function *fn, tree name, gimple *stmt)
if (old_ptr_info)
duplicate_ssa_name_ptr_info (new_name, old_ptr_info);
}
- else
- {
- struct range_info_def *old_range_info = SSA_NAME_RANGE_INFO (name);
-
- if (old_range_info)
- duplicate_ssa_name_range_info (new_name, SSA_NAME_RANGE_TYPE (name),
- old_range_info);
- }
+ else if (range_info_p (name))
+ duplicate_ssa_name_range_info (new_name, name);
return new_name;
}
diff --git a/gcc/tree-ssanames.h b/gcc/tree-ssanames.h
index 8c419b1..ce10af9 100644
--- a/gcc/tree-ssanames.h
+++ b/gcc/tree-ssanames.h
@@ -45,16 +45,6 @@ struct GTY(()) ptr_info_def
unsigned int misalign;
};
-/* Value range information for SSA_NAMEs representing non-pointer variables. */
-
-struct GTY ((variable_size)) range_info_def {
- /* Minimum, maximum and nonzero bits. */
- TRAILING_WIDE_INT_ACCESSOR (min, ints, 0)
- TRAILING_WIDE_INT_ACCESSOR (max, ints, 1)
- TRAILING_WIDE_INT_ACCESSOR (nonzero_bits, ints, 2)
- trailing_wide_ints <3> ints;
-};
-
#define SSANAMES(fun) (fun)->gimple_df->ssa_names
#define DEFAULT_DEFS(fun) (fun)->gimple_df->default_defs
@@ -67,7 +57,7 @@ struct GTY ((variable_size)) range_info_def {
if (VAR)
/* Sets the value range to SSA. */
-extern void set_range_info (tree, const value_range &);
+extern bool set_range_info (tree, const vrange &);
extern void set_nonzero_bits (tree, const wide_int_ref &);
extern wide_int get_nonzero_bits (const_tree);
extern bool ssa_name_has_boolean_range (tree);
diff --git a/gcc/tree-vrp.cc b/gcc/tree-vrp.cc
index ed881be..c3030a1 100644
--- a/gcc/tree-vrp.cc
+++ b/gcc/tree-vrp.cc
@@ -3739,16 +3739,18 @@ vrp_asserts::remove_range_assertions ()
&& all_imm_uses_in_stmt_or_feed_cond (var, stmt,
single_pred (bb)))
{
- /* We could use duplicate_ssa_name_range_info here
- instead of peeking inside SSA_NAME_RANGE_INFO,
- but the aforementioned asserts that the
- destination has no global range. This is
- slated for removal anyhow. */
- value_range r (TREE_TYPE (lhs),
- SSA_NAME_RANGE_INFO (lhs)->get_min (),
- SSA_NAME_RANGE_INFO (lhs)->get_max (),
- SSA_NAME_RANGE_TYPE (lhs));
- set_range_info (var, r);
+ if (SSA_NAME_RANGE_INFO (var))
+ {
+ /* ?? This is a minor wart exposing the
+ internals of SSA_NAME_RANGE_INFO in order
+ to maintain existing behavior. This is
+ because duplicate_ssa_name_range_info below
+ needs a NULL destination range. This is
+ all slated for removal... */
+ ggc_free (SSA_NAME_RANGE_INFO (var));
+ SSA_NAME_RANGE_INFO (var) = NULL;
+ }
+ duplicate_ssa_name_range_info (var, lhs);
maybe_set_nonzero_bits (single_pred_edge (bb), var);
}
}
diff --git a/gcc/tree.h b/gcc/tree.h
index 6f6ad5a..e6564aa 100644
--- a/gcc/tree.h
+++ b/gcc/tree.h
@@ -2030,14 +2030,6 @@ class auto_suppress_location_wrappers
#define SSA_NAME_PTR_INFO(N) \
SSA_NAME_CHECK (N)->ssa_name.info.ptr_info
-/* True if SSA_NAME_RANGE_INFO describes an anti-range. */
-#define SSA_NAME_ANTI_RANGE_P(N) \
- SSA_NAME_CHECK (N)->base.static_flag
-
-/* The type of range described by SSA_NAME_RANGE_INFO. */
-#define SSA_NAME_RANGE_TYPE(N) \
- (SSA_NAME_ANTI_RANGE_P (N) ? VR_ANTI_RANGE : VR_RANGE)
-
/* Value range info attributes for SSA_NAMEs of non pointer-type variables. */
#define SSA_NAME_RANGE_INFO(N) \
SSA_NAME_CHECK (N)->ssa_name.info.range_info
diff --git a/gcc/value-query.cc b/gcc/value-query.cc
index 1d7541c..51911bd 100644
--- a/gcc/value-query.cc
+++ b/gcc/value-query.cc
@@ -32,6 +32,7 @@ along with GCC; see the file COPYING3. If not see
#include "value-query.h"
#include "alloc-pool.h"
#include "gimple-range.h"
+#include "value-range-storage.h"
// value_query default methods.
@@ -271,13 +272,13 @@ range_query::get_tree_range (vrange &r, tree expr, gimple *stmt)
// Return the range for NAME from SSA_NAME_RANGE_INFO.
static inline void
-get_ssa_name_range_info (irange &r, const_tree name)
+get_ssa_name_range_info (vrange &r, const_tree name)
{
tree type = TREE_TYPE (name);
gcc_checking_assert (!POINTER_TYPE_P (type));
gcc_checking_assert (TREE_CODE (name) == SSA_NAME);
- range_info_def *ri = SSA_NAME_RANGE_INFO (name);
+ void *ri = SSA_NAME_RANGE_INFO (name);
// Return VR_VARYING for SSA_NAMEs with NULL RANGE_INFO or SSA_NAMEs
// with integral types width > 2 * HOST_BITS_PER_WIDE_INT precision.
@@ -285,9 +286,10 @@ get_ssa_name_range_info (irange &r, const_tree name)
> 2 * HOST_BITS_PER_WIDE_INT))
r.set_varying (type);
else
- r.set (wide_int_to_tree (type, ri->get_min ()),
- wide_int_to_tree (type, ri->get_max ()),
- SSA_NAME_RANGE_TYPE (name));
+ {
+ vrange_storage vstore (NULL);
+ vstore.get_vrange (ri, r, TREE_TYPE (name));
+ }
}
// Return nonnull attribute of pointer NAME from SSA_NAME_PTR_INFO.
@@ -311,43 +313,6 @@ get_ssa_name_ptr_info_nonnull (const_tree name)
}
// Update the global range for NAME into the SSA_RANGE_NAME_INFO and
-// SSA_NAME_PTR_INFO fields. Return TRUE if the range for NAME was
-// updated.
-
-bool
-update_global_range (vrange &r, tree name)
-{
- tree type = TREE_TYPE (name);
-
- if (r.undefined_p () || r.varying_p ())
- return false;
-
- if (INTEGRAL_TYPE_P (type))
- {
- // If a global range already exists, incorporate it.
- if (SSA_NAME_RANGE_INFO (name))
- {
- value_range glob;
- get_ssa_name_range_info (glob, name);
- r.intersect (glob);
- }
- if (r.undefined_p ())
- return false;
-
- set_range_info (name, as_a <irange> (r));
- return true;
- }
- else if (POINTER_TYPE_P (type))
- {
- if (r.nonzero_p ())
- {
- set_ptr_nonnull (name);
- return true;
- }
- }
- return false;
-}
-
// Return the legacy global range for NAME if it has one, otherwise
// return VARYING.
@@ -372,7 +337,7 @@ get_range_global (vrange &r, tree name)
r.set_nonzero (type);
else if (INTEGRAL_TYPE_P (type))
{
- get_ssa_name_range_info (as_a <irange> (r), name);
+ get_ssa_name_range_info (r, name);
if (r.undefined_p ())
r.set_varying (type);
}
@@ -387,8 +352,7 @@ get_range_global (vrange &r, tree name)
}
else if (!POINTER_TYPE_P (type) && SSA_NAME_RANGE_INFO (name))
{
- gcc_checking_assert (irange::supports_p (TREE_TYPE (name)));
- get_ssa_name_range_info (as_a <irange> (r), name);
+ get_ssa_name_range_info (r, name);
if (r.undefined_p ())
r.set_varying (type);
}
diff --git a/gcc/value-query.h b/gcc/value-query.h
index 280e47e..fc638eb 100644
--- a/gcc/value-query.h
+++ b/gcc/value-query.h
@@ -144,6 +144,5 @@ get_range_query (const struct function *fun)
}
extern void gimple_range_global (vrange &v, tree name);
-extern bool update_global_range (vrange &v, tree name);
#endif // GCC_QUERY_H
diff --git a/gcc/value-range-storage.cc b/gcc/value-range-storage.cc
index ac62bfa..8b5ab54 100644
--- a/gcc/value-range-storage.cc
+++ b/gcc/value-range-storage.cc
@@ -30,7 +30,8 @@ along with GCC; see the file COPYING3. If not see
#include "gimple-range.h"
#include "value-range-storage.h"
-// Return a newly allocated slot holding R.
+// 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)
@@ -40,7 +41,6 @@ vrange_storage::alloc_slot (const vrange &r)
if (is_a <irange> (r))
return irange_storage_slot::alloc_slot (*m_alloc, as_a <irange> (r));
- gcc_unreachable ();
return NULL;
}