From patchwork Thu Sep 1 11:18:50 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Aldy Hernandez X-Patchwork-Id: 895 Return-Path: Delivered-To: ouuuleilei@gmail.com Received: by 2002:adf:ecc5:0:0:0:0:0 with SMTP id s5csp200660wro; Thu, 1 Sep 2022 04:20:43 -0700 (PDT) X-Google-Smtp-Source: AA6agR63iGx7WwAZYj/JKM/XcZjH0ZMAVR/53M5eZW20nUrKMZADePDgOaHcyXSxWYIAVFYpXt3c X-Received: by 2002:a17:907:7ba0:b0:73d:bc94:3c20 with SMTP id ne32-20020a1709077ba000b0073dbc943c20mr22887310ejc.126.1662031243766; Thu, 01 Sep 2022 04:20:43 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1662031243; cv=none; d=google.com; s=arc-20160816; b=T5BMv9q1wpQW2Dj+6m87W31xAa019P3ZA7Zp3WX4eofGsIshRhaG3BczRc5SUkaBXd T4tOgms537LS37SomfWG7EfYIumzfVCNYjCYgx+tgz6rhaqLG0AjIEos4T4Go9noIqIP 3cYFnXYoVZNoQm+bIEKYViOfrpwJptSyeh2Su3QjSYWhil+5NzgzRieLY4pjmV1roSOP 1PcB41rGbnoqxyNjN+QL+FP25xjk/0xzZ+jtEjeJ4nxzNObKDR0/AYhs+hdQHbtiuAcA FgDOnKn6g/w04Pf5l0lUdonuF2LBYoCAmoxHbzHW5jxlDlZQ+pMmLQqZXmFVxQo0XKgd TxKQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:cc:reply-to:from:list-subscribe:list-help :list-post:list-archive:list-unsubscribe:list-id:precedence :content-transfer-encoding:mime-version:message-id:date:subject:to :dmarc-filter:delivered-to:dkim-signature:dkim-filter; bh=jKUWQODyLcCw39uHgiiLEfdRdy7yGNViO5XA2wciQHg=; b=s8+N8uesIRMwEsyx48rxK7cVUSikA8/m9zHGbo/M1EbnGSxwTkkYK58dcASWyY9HZ1 HYu+TRE1b4qd9eIJQ8SUKPIrlVns5TcIUNGo1fTbMryf/4JLCErqB8sWwy2l/8jyZkb/ PlEOf3W6N6IaWtpD3bSAkH8CxhL6uQSo6/s6VXl3KVOh8H9g9ZLOJNNiYcrrVWKo6E1F aigXwiOZrEEKIFKkTLXYDanuj/c1ML3AqmcCBaiQRq7GRcn4VUroG69IKqGQCW42uX0e 4xUDzGdTTCSlFQPCAyRY9p0JPguCBhAtsKATqqMZPluW2kfkmDTCxlYEbcoC6KdulJLD PrXw== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@gcc.gnu.org header.s=default header.b="F4/AO/5D"; spf=pass (google.com: domain of gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org designates 8.43.85.97 as permitted sender) smtp.mailfrom="gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org"; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=gnu.org Received: from sourceware.org (ip-8-43-85-97.sourceware.org. [8.43.85.97]) by mx.google.com with ESMTPS id p5-20020a056402500500b00446881b5329si1557281eda.332.2022.09.01.04.20.43 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 01 Sep 2022 04:20:43 -0700 (PDT) Received-SPF: pass (google.com: domain of gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org designates 8.43.85.97 as permitted sender) client-ip=8.43.85.97; Authentication-Results: mx.google.com; dkim=pass header.i=@gcc.gnu.org header.s=default header.b="F4/AO/5D"; spf=pass (google.com: domain of gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org designates 8.43.85.97 as permitted sender) smtp.mailfrom="gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org"; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=gnu.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 78F21382DA40 for ; Thu, 1 Sep 2022 11:20:42 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 78F21382DA40 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gcc.gnu.org; s=default; t=1662031242; bh=jKUWQODyLcCw39uHgiiLEfdRdy7yGNViO5XA2wciQHg=; h=To:Subject:Date:List-Id:List-Unsubscribe:List-Archive:List-Post: List-Help:List-Subscribe:From:Reply-To:Cc:From; b=F4/AO/5D2tR5K6PVWPX6rVmgMj4xRHqz8efZavebOp42GElzC5iNNe/TJkHaheRKN jqYXlnUkhSl3La/wq7OFkGiMrppceZV4KtWhnqMO+62K1DhAFSbG/4yKN0Nry5HF6m HkGMaB3yquAz21cQDVzbDCOekCB4MGdt/Ln5D4vc= X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) by sourceware.org (Postfix) with ESMTPS id 78F05385829C for ; Thu, 1 Sep 2022 11:19:02 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org 78F05385829C Received: from mimecast-mx02.redhat.com (mimecast-mx02.redhat.com [66.187.233.88]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-245-YWVKeIBUNpW8hQKfYA9L1A-1; Thu, 01 Sep 2022 07:18:59 -0400 X-MC-Unique: YWVKeIBUNpW8hQKfYA9L1A-1 Received: from smtp.corp.redhat.com (int-mx05.intmail.prod.int.rdu2.redhat.com [10.11.54.5]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id D1578801231; Thu, 1 Sep 2022 11:18:58 +0000 (UTC) Received: from abulafia.quesejoda.com (unknown [10.39.192.149]) by smtp.corp.redhat.com (Postfix) with ESMTPS id 3A07718ECC; Thu, 1 Sep 2022 11:18:58 +0000 (UTC) Received: from abulafia.quesejoda.com (localhost [127.0.0.1]) by abulafia.quesejoda.com (8.17.1/8.17.1) with ESMTPS id 281BIt2P2596635 (version=TLSv1.3 cipher=TLS_AES_256_GCM_SHA384 bits=256 verify=NOT); Thu, 1 Sep 2022 13:18:55 +0200 Received: (from aldyh@localhost) by abulafia.quesejoda.com (8.17.1/8.17.1/Submit) id 281BIsoQ2596634; Thu, 1 Sep 2022 13:18:54 +0200 To: GCC patches Subject: [COMMITTED] Add signbit property to frange to better model signed zeros. Date: Thu, 1 Sep 2022 13:18:50 +0200 Message-Id: <20220901111851.2595874-1-aldyh@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.79 on 10.11.54.5 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com X-Spam-Status: No, score=-11.7 required=5.0 tests=BAYES_00, DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, KAM_SHORT, RCVD_IN_DNSWL_LOW, SPF_HELO_NONE, SPF_NONE, TXREP, T_SCC_BODY_TEXT_LINE autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org X-BeenThere: gcc-patches@gcc.gnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Gcc-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-Patchwork-Original-From: Aldy Hernandez via Gcc-patches From: Aldy Hernandez Reply-To: Aldy Hernandez Cc: Jakub Jelinek Errors-To: gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org Sender: "Gcc-patches" X-getmail-retrieved-from-mailbox: =?utf-8?q?INBOX?= X-GMAIL-THRID: =?utf-8?q?1742766073472085683?= X-GMAIL-MSGID: =?utf-8?q?1742766073472085683?= As discussed here: https://gcc.gnu.org/pipermail/gcc-patches/2022-August/600656.html This adds an frange property to keep track of the sign bit. We keep it updated at all times, but we don't use it make any decisions when !HONOR_SIGNED_ZEROS. With this property we can now query the range for the appropriate sign with frange::get_signbit (). Possible values are yes, no, and unknown. We had some old notes in PR24021 that indicated what was important in the FP world: finite, signed zeros, normalized, in the range [-1,1] for trig functions, etc. I think frange now has enough to model everything we care about. gcc/ChangeLog: * range-op-float.cc (foperator_equal::op1_range): Do not copy sign bit. (foperator_not_equal::op1_range): Same. * value-query.cc (range_query::get_tree_range): Set sign bit. * value-range-pretty-print.cc (vrange_printer::visit): Dump sign bit. * value-range.cc (frange::set_signbit): New. (frange::set): Adjust for sign bit. (frange::normalize_kind): Same. (frange::union_): Remove useless comment. (frange::intersect): Same. (frange::contains_p): Adjust for sign bit. (frange::singleton_p): Same. (frange::verify_range): Same. (range_tests_signbit): New tests. (range_tests_floats): Call range_tests_signbit. * value-range.h (class frange_props): Add signbit (class frange): Same. --- gcc/range-op-float.cc | 6 + gcc/value-query.cc | 27 +++-- gcc/value-range-pretty-print.cc | 1 + gcc/value-range.cc | 200 +++++++++++++++++++++++++++----- gcc/value-range.h | 4 + 5 files changed, 204 insertions(+), 34 deletions(-) diff --git a/gcc/range-op-float.cc b/gcc/range-op-float.cc index c30f2af391c..2f1af4055c3 100644 --- a/gcc/range-op-float.cc +++ b/gcc/range-op-float.cc @@ -361,6 +361,9 @@ foperator_equal::op1_range (frange &r, tree type, case BRS_TRUE: // If it's true, the result is the same as OP2. r = op2; + // Make sure we don't copy the sign bit if we may have a zero. + if (HONOR_SIGNED_ZEROS (type) && r.contains_p (build_zero_cst (type))) + r.set_signbit (fp_prop::VARYING); // The TRUE side of op1 == op2 implies op1 is !NAN. r.set_nan (fp_prop::NO); break; @@ -462,6 +465,9 @@ foperator_not_equal::op1_range (frange &r, tree type, case BRS_FALSE: // If it's false, the result is the same as OP2. r = op2; + // Make sure we don't copy the sign bit if we may have a zero. + if (HONOR_SIGNED_ZEROS (type) && r.contains_p (build_zero_cst (type))) + r.set_signbit (fp_prop::VARYING); // The FALSE side of op1 != op2 implies op1 is !NAN. r.set_nan (fp_prop::NO); break; diff --git a/gcc/value-query.cc b/gcc/value-query.cc index 4637fb409e5..201f679a36e 100644 --- a/gcc/value-query.cc +++ b/gcc/value-query.cc @@ -217,14 +217,25 @@ range_query::get_tree_range (vrange &r, tree expr, gimple *stmt) return true; case REAL_CST: - if (TREE_OVERFLOW_P (expr)) - expr = drop_tree_overflow (expr); - r.set (expr, expr); - if (real_isnan (TREE_REAL_CST_PTR (expr))) - as_a (r).set_nan (fp_prop::YES); - else - as_a (r).set_nan (fp_prop::NO); - return true; + { + if (TREE_OVERFLOW_P (expr)) + expr = drop_tree_overflow (expr); + + frange &f = as_a (r); + f.set (expr, expr); + + // Singletons from the tree world have known properties. + REAL_VALUE_TYPE *rv = TREE_REAL_CST_PTR (expr); + if (real_isnan (rv)) + f.set_nan (fp_prop::YES); + else + f.set_nan (fp_prop::NO); + if (real_isneg (rv)) + f.set_signbit (fp_prop::YES); + else + f.set_signbit (fp_prop::NO); + return true; + } case SSA_NAME: gimple_range_global (r, expr); diff --git a/gcc/value-range-pretty-print.cc b/gcc/value-range-pretty-print.cc index e66d56da29d..93e18d3c1b6 100644 --- a/gcc/value-range-pretty-print.cc +++ b/gcc/value-range-pretty-print.cc @@ -146,6 +146,7 @@ vrange_printer::visit (const frange &r) const pp_string (pp, "] "); print_frange_prop ("NAN", r.get_nan ()); + print_frange_prop ("SIGN", r.get_signbit ()); } // Print the FP properties in an frange. diff --git a/gcc/value-range.cc b/gcc/value-range.cc index 3c7d4cb84b9..71581b2c54d 100644 --- a/gcc/value-range.cc +++ b/gcc/value-range.cc @@ -288,6 +288,58 @@ frange::set_nan (fp_prop::kind k) m_props.set_nan (k); normalize_kind (); + if (flag_checking) + verify_range (); +} + +// Set the SIGNBIT property. Adjust the range if appropriate. + +void +frange::set_signbit (fp_prop::kind k) +{ + gcc_checking_assert (m_type); + + // No additional adjustments are needed for a NAN. + if (get_nan ().yes_p ()) + { + m_props.set_signbit (k); + return; + } + // Ignore sign changes when they're set correctly. + if (real_less (&m_max, &dconst0)) + { + gcc_checking_assert (get_signbit ().yes_p ()); + return; + } + if (real_less (&dconst0, &m_min)) + { + gcc_checking_assert (get_signbit ().no_p ()); + return; + } + // Adjust the range depending on the sign bit. + if (k == fp_prop::YES) + { + // Crop the range to [-INF, 0]. + REAL_VALUE_TYPE min; + real_inf (&min, 1); + frange crop (m_type, min, dconst0); + intersect (crop); + m_props.set_signbit (fp_prop::YES); + } + else if (k == fp_prop::NO) + { + // Crop the range to [0, +INF]. + REAL_VALUE_TYPE max; + real_inf (&max, 0); + frange crop (m_type, dconst0, max); + intersect (crop); + m_props.set_signbit (fp_prop::NO); + } + else + m_props.set_signbit (fp_prop::VARYING); + + if (flag_checking) + verify_range (); } // Setter for franges. @@ -329,6 +381,12 @@ frange::set (tree min, tree max, value_range_kind kind) else if (!HONOR_NANS (m_type)) m_props.nan_set_no (); + // Set SIGNBIT property for positive and negative ranges. + if (real_less (&m_max, &dconst0)) + m_props.signbit_set_yes (); + else if (real_less (&dconst0, &m_min)) + m_props.signbit_set_no (); + // Check for swapped ranges. gcc_checking_assert (is_nan || tree_compare (LE_EXPR, min, max)); @@ -360,7 +418,7 @@ bool frange::normalize_kind () { // Undefined is viral. - if (m_props.nan_undefined_p ()) + if (m_props.nan_undefined_p () || m_props.signbit_undefined_p ()) { set_undefined (); return true; @@ -438,13 +496,6 @@ frange::union_ (const vrange &v) } bool changed = m_props.union_ (r.m_props); - - // Note: for the combination of zeros that differ in sign we keep - // the endpoints untouched. This means that for -0.0 U +0.0, the - // result will be -0.0. Ultimately this doesn't matter, because we - // keep singleton zeros ambiguous throughout to avoid propagating - // them. See frange::singleton_p(). - if (real_less (&r.m_min, &m_min)) { m_min = r.m_min; @@ -484,12 +535,6 @@ frange::intersect (const vrange &v) bool changed = m_props.intersect (r.m_props); - // Note: for the combination of zeros that differ in sign we keep - // the endpoints untouched. This means that for -0.0 ^ +0.0, the - // result will be -0.0. Ultimately this doesn't matter, because we - // keep singleton zeros ambiguous throughout to avoid propagating - // them. See frange::singleton_p(). - if (real_less (&m_min, &r.m_min)) { m_min = r.m_min; @@ -563,26 +608,51 @@ frange::contains_p (tree cst) const gcc_checking_assert (m_kind == VR_RANGE); - return (real_compare (GE_EXPR, TREE_REAL_CST_PTR (cst), &m_min) - && real_compare (LE_EXPR, TREE_REAL_CST_PTR (cst), &m_max)); + const REAL_VALUE_TYPE *rv = TREE_REAL_CST_PTR (cst); + if (real_compare (GE_EXPR, rv, &m_min) + && real_compare (LE_EXPR, rv, &m_max)) + { + if (HONOR_SIGNED_ZEROS (m_type) && real_iszero (rv)) + { + if (get_signbit ().yes_p ()) + return real_isneg (rv); + else if (get_signbit ().no_p ()) + return !real_isneg (rv); + else + return true; + } + return true; + } + return false; } // If range is a singleton, place it in RESULT and return TRUE. If // RESULT is NULL, just return TRUE. // -// A NAN can never be a singleton, and neither can a 0.0 if we're -// honoring signed zeros because we have no way of telling which zero -// it is. +// A NAN can never be a singleton. bool frange::singleton_p (tree *result) const { if (m_kind == VR_RANGE && real_identical (&m_min, &m_max)) { - // If we're honoring signed zeros, fail because we don't know - // which zero we have. This avoids propagating the wrong zero. + // Return the appropriate zero if known. if (HONOR_SIGNED_ZEROS (m_type) && zero_p ()) - return false; + { + if (get_signbit ().no_p ()) + { + if (result) + *result = build_real (m_type, dconst0); + return true; + } + if (get_signbit ().yes_p ()) + { + if (result) + *result = build_real (m_type, real_value_negate (&dconst0)); + return true; + } + return false; + } // Return false for any singleton that may be a NAN. if (HONOR_NANS (m_type) && !get_nan ().no_p ()) @@ -639,6 +709,14 @@ frange::verify_range () gcc_checking_assert (real_isnan (&m_min)); gcc_checking_assert (real_isnan (&m_max)); } + else + { + // Make sure the signbit and range agree. + if (m_props.get_signbit ().yes_p ()) + gcc_checking_assert (real_compare (LE_EXPR, &m_max, &dconst0)); + else if (m_props.get_signbit ().no_p ()) + gcc_checking_assert (real_compare (GE_EXPR, &m_min, &dconst0)); + } // If all the properties are clear, we better not span the entire // domain, because that would make us varying. @@ -3605,16 +3683,85 @@ range_tests_signed_zeros () tree neg_zero = fold_build1 (NEGATE_EXPR, float_type_node, zero); frange r0, r1; - // ?? If -0.0 == +0.0, then a range of [-0.0, -0.0] should - // contain +0.0 and vice versa. We probably need a property to - // track signed zeros in the frange like we do for NAN, to - // properly model all this. + // Since -0.0 == +0.0, a range of [-0.0, -0.0] should contain +0.0 + // and vice versa. r0 = frange (zero, zero); r1 = frange (neg_zero, neg_zero); ASSERT_TRUE (r0.contains_p (zero)); ASSERT_TRUE (r0.contains_p (neg_zero)); ASSERT_TRUE (r1.contains_p (zero)); ASSERT_TRUE (r1.contains_p (neg_zero)); + + // Test contains_p() when we know the sign of the zero. + r0 = frange(zero, zero); + r0.set_signbit (fp_prop::NO); + ASSERT_TRUE (r0.contains_p (zero)); + ASSERT_FALSE (r0.contains_p (neg_zero)); + r0.set_signbit (fp_prop::YES); + ASSERT_TRUE (r0.contains_p (neg_zero)); + ASSERT_FALSE (r0.contains_p (zero)); + + // The intersection of zeros that differ in sign is the empty set. + r0 = frange (zero, zero); + r0.set_signbit (fp_prop::YES); + r1 = frange (zero, zero); + r1.set_signbit (fp_prop::NO); + r0.intersect (r1); + ASSERT_TRUE (r0.undefined_p ()); + + // The union of zeros that differ in sign is a zero with unknown sign. + r0 = frange (zero, zero); + r0.set_signbit (fp_prop::NO); + r1 = frange (zero, zero); + r1.set_signbit (fp_prop::YES); + r0.union_ (r1); + ASSERT_TRUE (r0.zero_p () && r0.get_signbit ().varying_p ()); +} + +static void +range_tests_signbit () +{ + frange r0, r1; + + // Setting the signbit drops the range to [-INF, 0]. + r0.set_varying (float_type_node); + r0.set_signbit (fp_prop::YES); + ASSERT_TRUE (real_isinf (&r0.lower_bound (), 1)); + ASSERT_TRUE (real_iszero (&r0.upper_bound ())); + + // Setting the signbit for [-5, 10] crops the range to [-5, 0] with + // the signbit property set. + r0 = frange_float ("-5", "10"); + r0.set_signbit (fp_prop::YES); + ASSERT_TRUE (r0.get_signbit ().yes_p ()); + r1 = frange_float ("-5", "0"); + ASSERT_TRUE (real_identical (&r0.lower_bound (), &r1.lower_bound ())); + ASSERT_TRUE (real_identical (&r0.upper_bound (), &r1.upper_bound ())); + + // Negative numbers should have the SIGNBIT set. + r0 = frange_float ("-5", "-1"); + ASSERT_TRUE (r0.get_signbit ().yes_p ()); + // Positive numbers should have the SIGNBIT clear. + r0 = frange_float ("1", "10"); + ASSERT_TRUE (r0.get_signbit ().no_p ()); + // Numbers containing zero should have an unknown SIGNBIT. + r0 = frange_float ("0", "10"); + ASSERT_TRUE (r0.get_signbit ().varying_p ()); + // Numbers spanning both positive and negative should have an + // unknown SIGNBIT. + r0 = frange_float ("-10", "10"); + ASSERT_TRUE (r0.get_signbit ().varying_p ()); + r0.set_varying (float_type_node); + ASSERT_TRUE (r0.get_signbit ().varying_p ()); + + // Ignore signbit changes when the sign bit is obviously known from + // the range. + r0 = frange_float ("5", "10"); + r0.set_signbit (fp_prop::VARYING); + ASSERT_TRUE (r0.get_signbit ().no_p ()); + r0 = frange_float ("-5", "-1"); + r0.set_signbit (fp_prop::NO); + ASSERT_TRUE (r0.get_signbit ().yes_p ()); } static void @@ -3623,6 +3770,7 @@ range_tests_floats () frange r0, r1; range_tests_nan (); + range_tests_signbit (); if (HONOR_SIGNED_ZEROS (float_type_node)) range_tests_signed_zeros (); diff --git a/gcc/value-range.h b/gcc/value-range.h index f7f3665be07..3767bd17314 100644 --- a/gcc/value-range.h +++ b/gcc/value-range.h @@ -314,10 +314,12 @@ public: bool intersect (const frange_props &other); bool operator== (const frange_props &other) const; FP_PROP_ACCESSOR(nan) + FP_PROP_ACCESSOR(signbit) private: union { struct { unsigned char nan : 2; + unsigned char signbit : 2; } bits; unsigned char bytes; } u; @@ -364,6 +366,8 @@ public: // Accessors for FP properties. fp_prop get_nan () const { return m_props.get_nan (); } void set_nan (fp_prop::kind f); + fp_prop get_signbit () const { return m_props.get_signbit (); } + void set_signbit (fp_prop::kind); private: void verify_range (); bool normalize_kind ();