+
+static enum ssa_ranges
+union_ranges(enum ssa_ranges a, enum ssa_ranges b)
+{
+ static const enum ssa_ranges union_table[last_range + 1][last_range + 1] = {
+ /* left\right unknown lt_zero le_zero gt_zero ge_zero ne_zero eq_zero */
+ /* unknown */ { _______, _______, _______, _______, _______, _______, _______ },
+ /* lt_zero */ { _______, lt_zero, le_zero, ne_zero, _______, ne_zero, le_zero },
+ /* le_zero */ { _______, le_zero, le_zero, _______, _______, _______, le_zero },
+ /* gt_zero */ { _______, ne_zero, _______, gt_zero, ge_zero, ne_zero, ge_zero },
+ /* ge_zero */ { _______, _______, _______, ge_zero, ge_zero, _______, ge_zero },
+ /* ne_zero */ { _______, ne_zero, _______, ne_zero, _______, ne_zero, _______ },
+ /* eq_zero */ { _______, le_zero, le_zero, ge_zero, ge_zero, _______, eq_zero },
+ };
+
+ ASSERT_TABLE_IS_COMMUTATIVE(union_table);
+ ASSERT_TABLE_IS_DIAGONAL(union_table);
+
+ return union_table[a][b];
+}
+
+/* Verify that the 'unknown' entry in each row (or column) of the table is the
+ * union of all the other values in the row (or column).
+ */
+#define ASSERT_UNION_OF_OTHERS_MATCHES_UNKNOWN_2_SOURCE(t) \
+ do { \
+ for (unsigned i = 0; i < last_range; i++) { \
+ enum ssa_ranges col_range = t[i][unknown + 1]; \
+ enum ssa_ranges row_range = t[unknown + 1][i]; \
+ \
+ for (unsigned j = unknown + 2; j < last_range; j++) { \
+ col_range = union_ranges(col_range, t[i][j]); \
+ row_range = union_ranges(row_range, t[j][i]); \
+ } \
+ \
+ assert(col_range == t[i][unknown]); \
+ assert(row_range == t[unknown][i]); \
+ } \
+ } while (false)
+
+/* For most operations, the union of ranges for a strict inequality and
+ * equality should be the range of the non-strict inequality (e.g.,
+ * union_ranges(range(op(lt_zero), range(op(eq_zero))) == range(op(le_zero)).
+ *
+ * Does not apply to selection-like opcodes (bcsel, fmin, fmax, etc.).
+ */
+#define ASSERT_UNION_OF_EQ_AND_STRICT_INEQ_MATCHES_NONSTRICT_1_SOURCE(t) \
+ do { \
+ assert(union_ranges(t[lt_zero], t[eq_zero]) == t[le_zero]); \
+ assert(union_ranges(t[gt_zero], t[eq_zero]) == t[ge_zero]); \
+ } while (false)
+
+#define ASSERT_UNION_OF_EQ_AND_STRICT_INEQ_MATCHES_NONSTRICT_2_SOURCE(t) \
+ do { \
+ for (unsigned i = 0; i < last_range; i++) { \
+ assert(union_ranges(t[i][lt_zero], t[i][eq_zero]) == t[i][le_zero]); \
+ assert(union_ranges(t[i][gt_zero], t[i][eq_zero]) == t[i][ge_zero]); \
+ assert(union_ranges(t[lt_zero][i], t[eq_zero][i]) == t[le_zero][i]); \
+ assert(union_ranges(t[gt_zero][i], t[eq_zero][i]) == t[ge_zero][i]); \
+ } \
+ } while (false)
+
+/* Several other unordered tuples span the range of "everything." Each should
+ * have the same value as unknown: (lt_zero, ge_zero), (le_zero, gt_zero), and
+ * (eq_zero, ne_zero). union_ranges is already commutative, so only one
+ * ordering needs to be checked.
+ *
+ * Does not apply to selection-like opcodes (bcsel, fmin, fmax, etc.).
+ *
+ * In cases where this can be used, it is unnecessary to also use
+ * ASSERT_UNION_OF_OTHERS_MATCHES_UNKNOWN_*_SOURCE. For any range X,
+ * union_ranges(X, X) == X. The disjoint ranges cover all of the non-unknown
+ * possibilities, so the union of all the unions of disjoint ranges is
+ * equivalent to the union of "others."
+ */
+#define ASSERT_UNION_OF_DISJOINT_MATCHES_UNKNOWN_1_SOURCE(t) \
+ do { \
+ assert(union_ranges(t[lt_zero], t[ge_zero]) == t[unknown]); \
+ assert(union_ranges(t[le_zero], t[gt_zero]) == t[unknown]); \
+ assert(union_ranges(t[eq_zero], t[ne_zero]) == t[unknown]); \
+ } while (false)
+
+#define ASSERT_UNION_OF_DISJOINT_MATCHES_UNKNOWN_2_SOURCE(t) \
+ do { \
+ for (unsigned i = 0; i < last_range; i++) { \
+ assert(union_ranges(t[i][lt_zero], t[i][ge_zero]) == \
+ t[i][unknown]); \
+ assert(union_ranges(t[i][le_zero], t[i][gt_zero]) == \
+ t[i][unknown]); \
+ assert(union_ranges(t[i][eq_zero], t[i][ne_zero]) == \
+ t[i][unknown]); \
+ \
+ assert(union_ranges(t[lt_zero][i], t[ge_zero][i]) == \
+ t[unknown][i]); \
+ assert(union_ranges(t[le_zero][i], t[gt_zero][i]) == \
+ t[unknown][i]); \
+ assert(union_ranges(t[eq_zero][i], t[ne_zero][i]) == \
+ t[unknown][i]); \
+ } \
+ } while (false)
+