From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: 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 DCCE03856242 for ; Tue, 2 Aug 2022 12:57:24 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org DCCE03856242 Received: from mail-oo1-f69.google.com (mail-oo1-f69.google.com [209.85.161.69]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-177-1wyl86ykN-WH55xQeT-ZlA-1; Tue, 02 Aug 2022 08:57:23 -0400 X-MC-Unique: 1wyl86ykN-WH55xQeT-ZlA-1 Received: by mail-oo1-f69.google.com with SMTP id a3-20020a4a9b03000000b004284ce89ac2so6513673ook.7 for ; Tue, 02 Aug 2022 05:57:23 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=cc:to:subject:message-id:date:from:in-reply-to:references :mime-version:x-gm-message-state:from:to:cc; bh=2u0mQ9yup9fh9lGsWAm4w5luGhRMiN5yezxp2beLK+Y=; b=I16IcujByt100a5sOnpIcOQBWbB/FTMx6yk8k70Ca/WXSI8uL7Brd2htR7/xEOygaj uCgJxbqJqm1+ysuvp/62JDpt687+B2sIaRayeutUsfR7yNj7Z6IX2/vqCDzFqiLxAYlJ as4wNFCzuagoG83G2hWEfL3dwhiROcjn5bPD2qly8+gVhR4Q/a2BZU83VgwGf15uNCbL xAihoPJDF2bNcNUeX2mP7RVDNS+LMkVpjcNXTWF9wgPXCri0pkyezLzXbvBmJzHHo/5X uDQixaZimYfjODBYMWX2txULe2K2i4ChIF4VONBJgVeU5ZJj1WN59grK9+7U7Gokx7yR V19Q== X-Gm-Message-State: AJIora8K5VO/wVNm14HwHx57KsYhgdHAlAhgTwX5nwiQ/2oeu04IxI/G iCCtGhsm1ZJZZCsax/dyDSuOmWlk1RQ+eZx4xg67S1e7MLaHNSkAwdJGvXeoJGb9eXC1IgV6Ka0 TageLIovLbsrXVN7MPySRMaah5CacVzqq9A== X-Received: by 2002:a05:6808:1a23:b0:33a:7888:9c3b with SMTP id bk35-20020a0568081a2300b0033a78889c3bmr8564219oib.265.1659445042071; Tue, 02 Aug 2022 05:57:22 -0700 (PDT) X-Google-Smtp-Source: AGRyM1s/YRq/a76629eyvPq7W4NegU8aWZKzt7fQRbCEvmu/Di7ZrMi5CNkMQchdQAbrAT8RejsyUhz7rk1u2nfvw7w= X-Received: by 2002:a05:6808:1a23:b0:33a:7888:9c3b with SMTP id bk35-20020a0568081a2300b0033a78889c3bmr8564205oib.265.1659445041529; Tue, 02 Aug 2022 05:57:21 -0700 (PDT) MIME-Version: 1.0 References: <20220725184951.2202379-1-aldyh@redhat.com> In-Reply-To: From: Aldy Hernandez Date: Tue, 2 Aug 2022 14:57:10 +0200 Message-ID: Subject: Re: [RFA] Implement basic range operators to enable floating point VRP. To: GCC patches , "MacLeod, Andrew" Cc: Jeff Law , Richard Biener , Jakub Jelinek , Roger Sayle X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Content-Type: text/plain; charset="UTF-8" X-Spam-Status: No, score=-12.1 required=5.0 tests=BAYES_00, DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, RCVD_IN_DNSWL_LOW, SPF_HELO_NONE, SPF_NONE, TXREP 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-List-Received-Date: Tue, 02 Aug 2022 12:57:29 -0000 Alright, I know everyon'es pretty busy and I am blocking myself here, so I hereby approve my own patch, since I am the maintainer ;-). After all, I'd like to have 5-6 days to field any possible fallout before I disappear off to a tropical island to drink mojitos and beer. Ok, not really-- just changing diapers, but without a laptop. Feedback still welcome; I'll just address it as a follow-up. Pushed. Aldy On Sun, Jul 31, 2022 at 8:11 PM Aldy Hernandez wrote: > > PING > > Andrew, anyone, would you mind giving this a once over? I realize > reviewing ranger's range-op code is not on anyone's list of > priorities, but I could use a sanity check. > > The patch is sufficiently self-contained to easily catch anything > caused by it, and I'd like to commit earlier in the week to have > enough time to field any possible fallout before I take a few days off > next week. > > Updated patch attached. > > Thanks. > Aldy > > On Mon, Jul 25, 2022 at 8:50 PM Aldy Hernandez wrote: > > > > Without further ado, here is the implementation for floating point > > range operators, plus the switch to enable all ranger clients to > > handle floats. > > > > These are bare bone implementations good enough for relation operators > > to work, while keeping the NAN bits up to date in the frange. There > > is also minimal support for keeping track of +-INF when it is obvious. > > > > I have included some basic tests to help get a feel of what is > > ultimately handled. > > > > Since range-ops is the domain specific core of ranger, I think its > > best if a global maintainer or an FP expert could review this. > > > > OK for trunk? > > > > Tested on x86-64 Linux. > > > > p.s. I haven't done extensive testing in DOM, but with this we're mighty > > close for the forward threader there to be replaceable with the backward > > threader, thus removing the last use of the forward threader. > > > > gcc/ChangeLog: > > > > * range-op-float.cc (finite_operands_p): New. > > (frelop_early_resolve): New. > > (default_frelop_fold_range): New. > > (class foperator_equal): New. > > (class foperator_not_equal): New. > > (class foperator_lt): New. > > (class foperator_le): New. > > (class foperator_gt): New. > > (class foperator_ge): New. > > (class foperator_unordered): New. > > (class foperator_ordered): New. > > (class foperator_relop_unknown): New. > > (floating_op_table::floating_op_table): Add above classes to > > floating op table. > > * value-range.h (frange::supports_p): Enable. > > > > gcc/testsuite/ChangeLog: > > > > * g++.dg/opt/pr94589-2.C: Add notes. > > * gcc.dg/tree-ssa/vrp-float-1.c: New test. > > * gcc.dg/tree-ssa/vrp-float-11.c: New test. > > * gcc.dg/tree-ssa/vrp-float-3.c: New test. > > * gcc.dg/tree-ssa/vrp-float-4.c: New test. > > * gcc.dg/tree-ssa/vrp-float-6.c: New test. > > * gcc.dg/tree-ssa/vrp-float-7.c: New test. > > * gcc.dg/tree-ssa/vrp-float-8.c: New test. > > --- > > gcc/range-op-float.cc | 564 +++++++++++++++++++ > > gcc/testsuite/g++.dg/opt/pr94589-2.C | 25 + > > gcc/testsuite/gcc.dg/tree-ssa/vrp-float-1.c | 19 + > > gcc/testsuite/gcc.dg/tree-ssa/vrp-float-11.c | 26 + > > gcc/testsuite/gcc.dg/tree-ssa/vrp-float-3.c | 18 + > > gcc/testsuite/gcc.dg/tree-ssa/vrp-float-4.c | 16 + > > gcc/testsuite/gcc.dg/tree-ssa/vrp-float-6.c | 20 + > > gcc/testsuite/gcc.dg/tree-ssa/vrp-float-7.c | 14 + > > gcc/testsuite/gcc.dg/tree-ssa/vrp-float-8.c | 26 + > > gcc/value-range.h | 3 +- > > 10 files changed, 729 insertions(+), 2 deletions(-) > > create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/vrp-float-1.c > > create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/vrp-float-11.c > > create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/vrp-float-3.c > > create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/vrp-float-4.c > > create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/vrp-float-6.c > > create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/vrp-float-7.c > > create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/vrp-float-8.c > > > > diff --git a/gcc/range-op-float.cc b/gcc/range-op-float.cc > > index 8e9d83e3827..d94ff6f915a 100644 > > --- a/gcc/range-op-float.cc > > +++ b/gcc/range-op-float.cc > > @@ -150,6 +150,50 @@ range_operator_float::op1_op2_relation (const irange &lhs ATTRIBUTE_UNUSED) cons > > return VREL_VARYING; > > } > > > > +// Return TRUE if OP1 and OP2 are known to be free of NANs. > > + > > +static inline bool > > +finite_operands_p (const frange &op1, const frange &op2) > > +{ > > + return (flag_finite_math_only > > + || (op1.get_nan ().no_p () > > + && op2.get_nan ().no_p ())); > > +} > > + > > +// Floating version of relop_early_resolve that takes into account NAN > > +// and -ffinite-math-only. > > + > > +inline bool > > +frelop_early_resolve (irange &r, tree type, > > + const frange &op1, const frange &op2, > > + relation_kind rel, relation_kind my_rel) > > +{ > > + // If either operand is undefined, return VARYING. > > + if (empty_range_varying (r, type, op1, op2)) > > + return true; > > + > > + // We can fold relations from the oracle when we know both operands > > + // are free of NANs, or when -ffinite-math-only. > > + return (finite_operands_p (op1, op2) > > + && relop_early_resolve (r, type, op1, op2, rel, my_rel)); > > +} > > + > > +// Default implementation of fold_range for relational operators. > > +// This amounts to passing on any known relations from the oracle, iff > > +// we know the operands are not NAN or -ffinite-math-only holds. > > + > > +static inline bool > > +default_frelop_fold_range (irange &r, tree type, > > + const frange &op1, const frange &op2, > > + relation_kind rel, relation_kind my_rel) > > +{ > > + if (frelop_early_resolve (r, type, op1, op2, rel, my_rel)) > > + return true; > > + > > + r.set_varying (type); > > + return true; > > +} > > + > > class foperator_identity : public range_operator_float > > { > > using range_operator_float::fold_range; > > @@ -172,6 +216,509 @@ class foperator_identity : public range_operator_float > > public: > > } fop_identity; > > > > +class foperator_equal : public range_operator_float > > +{ > > + using range_operator_float::fold_range; > > + using range_operator_float::op1_range; > > + using range_operator_float::op2_range; > > + > > + bool fold_range (irange &r, tree type, > > + const frange &op1, const frange &op2, > > + relation_kind rel) const final override > > + { > > + return default_frelop_fold_range (r, type, op1, op2, rel, VREL_EQ); > > + } > > + relation_kind op1_op2_relation (const irange &lhs) const final override > > + { > > + return equal_op1_op2_relation (lhs); > > + } > > + bool op1_range (frange &r, tree type, > > + const irange &lhs, const frange &op2, > > + relation_kind rel) const final override; > > + bool op2_range (frange &r, tree type, > > + const irange &lhs, const frange &op1, > > + relation_kind rel) const final override > > + { > > + return op1_range (r, type, lhs, op1, rel); > > + } > > +} fop_equal; > > + > > +bool > > +foperator_equal::op1_range (frange &r, tree type, > > + const irange &lhs, > > + const frange &op2 ATTRIBUTE_UNUSED, > > + relation_kind rel) const > > +{ > > + switch (get_bool_state (r, lhs, type)) > > + { > > + case BRS_TRUE: > > + r.set_varying (type); > > + // The TRUE side of op1 == op2 implies op1 is !NAN. > > + r.set_nan (fp_prop::NO); > > + break; > > + > > + case BRS_FALSE: > > + r.set_varying (type); > > + // The FALSE side of op1 ORDERED op1 implies op1 is a NAN. > > + if (rel == VREL_EQ) > > + r.set_nan (fp_prop::YES); > > + break; > > + > > + default: > > + break; > > + } > > + return true; > > +} > > + > > +class foperator_not_equal : public range_operator_float > > +{ > > + using range_operator_float::fold_range; > > + using range_operator_float::op1_range; > > + > > + bool fold_range (irange &r, tree type, > > + const frange &op1, const frange &op2, > > + relation_kind rel) const final override > > + { > > + return default_frelop_fold_range (r, type, op1, op2, rel, VREL_NE); > > + } > > + relation_kind op1_op2_relation (const irange &lhs) const final override > > + { > > + return not_equal_op1_op2_relation (lhs); > > + } > > + bool op1_range (frange &r, tree type, > > + const irange &lhs, const frange &op2, > > + relation_kind rel) const final override; > > +} fop_not_equal; > > + > > +bool > > +foperator_not_equal::op1_range (frange &r, tree type, > > + const irange &lhs, > > + const frange &op2 ATTRIBUTE_UNUSED, > > + relation_kind) const > > +{ > > + switch (get_bool_state (r, lhs, type)) > > + { > > + case BRS_TRUE: > > + r.set_varying (type); > > + break; > > + > > + case BRS_FALSE: > > + r.set_varying (type); > > + // The FALSE side of op1 != op2 implies op1 is !NAN. > > + r.set_nan (fp_prop::NO); > > + break; > > + > > + default: > > + break; > > + } > > + return true; > > +} > > + > > +class foperator_lt : public range_operator_float > > +{ > > + using range_operator_float::fold_range; > > + using range_operator_float::op1_range; > > + using range_operator_float::op2_range; > > + > > + bool fold_range (irange &r, tree type, > > + const frange &op1, const frange &op2, > > + relation_kind rel) const final override > > + { > > + return default_frelop_fold_range (r, type, op1, op2, rel, VREL_LT); > > + } > > + relation_kind op1_op2_relation (const irange &lhs) const final override > > + { > > + return lt_op1_op2_relation (lhs); > > + } > > + bool op1_range (frange &r, tree type, > > + const irange &lhs, const frange &op2, > > + relation_kind rel) const final override; > > + bool op2_range (frange &r, tree type, > > + const irange &lhs, const frange &op1, > > + relation_kind rel) const final override; > > +} fop_lt; > > + > > +bool > > +foperator_lt::op1_range (frange &r, > > + tree type, > > + const irange &lhs, > > + const frange &op2 ATTRIBUTE_UNUSED, > > + relation_kind) const > > +{ > > + switch (get_bool_state (r, lhs, type)) > > + { > > + case BRS_TRUE: > > + r.set_varying (type); > > + // The TRUE side of op1 < op2 implies op1 is !NAN and !INF. > > + r.set_nan (fp_prop::NO); > > + r.set_inf (fp_prop::NO); > > + break; > > + > > + case BRS_FALSE: > > + r.set_varying (type); > > + break; > > + > > + default: > > + break; > > + } > > + return true; > > +} > > + > > +bool > > +foperator_lt::op2_range (frange &r, > > + tree type, > > + const irange &lhs, > > + const frange &op1 ATTRIBUTE_UNUSED, > > + relation_kind) const > > +{ > > + switch (get_bool_state (r, lhs, type)) > > + { > > + case BRS_TRUE: > > + r.set_varying (type); > > + // The TRUE side of op1 < op2 implies op2 is !NAN and !NINF. > > + r.set_nan (fp_prop::NO); > > + r.set_ninf (fp_prop::NO); > > + break; > > + > > + case BRS_FALSE: > > + r.set_varying (type); > > + break; > > + > > + default: > > + break; > > + } > > + return true; > > +} > > + > > +class foperator_le : public range_operator_float > > +{ > > + using range_operator_float::fold_range; > > + using range_operator_float::op1_range; > > + using range_operator_float::op2_range; > > + > > + bool fold_range (irange &r, tree type, > > + const frange &op1, const frange &op2, > > + relation_kind rel) const final override > > + { > > + return default_frelop_fold_range (r, type, op1, op2, rel, VREL_LE); > > + } > > + relation_kind op1_op2_relation (const irange &lhs) const final override > > + { > > + return le_op1_op2_relation (lhs); > > + } > > + bool op1_range (frange &r, tree type, > > + const irange &lhs, const frange &op2, > > + relation_kind rel) const final override; > > + bool op2_range (frange &r, tree type, > > + const irange &lhs, const frange &op1, > > + relation_kind rel) const final override > > + { > > + return op1_range (r, type, lhs, op1, rel); > > + } > > +} fop_le; > > + > > +bool > > +foperator_le::op1_range (frange &r, > > + tree type, > > + const irange &lhs, > > + const frange &op2 ATTRIBUTE_UNUSED, > > + relation_kind) const > > +{ > > + switch (get_bool_state (r, lhs, type)) > > + { > > + case BRS_TRUE: > > + r.set_varying (type); > > + // The TRUE side of op1 <= op2 implies op1 is !NAN. > > + r.set_nan (fp_prop::NO); > > + break; > > + > > + case BRS_FALSE: > > + r.set_varying (type); > > + break; > > + > > + default: > > + break; > > + } > > + return true; > > +} > > + > > +class foperator_gt : public range_operator_float > > +{ > > + using range_operator_float::fold_range; > > + using range_operator_float::op1_range; > > + using range_operator_float::op2_range; > > + > > + bool fold_range (irange &r, tree type, > > + const frange &op1, const frange &op2, > > + relation_kind rel) const final override > > + { > > + return default_frelop_fold_range (r, type, op1, op2, rel, VREL_GT); > > + } > > + relation_kind op1_op2_relation (const irange &lhs) const final override > > + { > > + return gt_op1_op2_relation (lhs); > > + } > > + bool op1_range (frange &r, tree type, > > + const irange &lhs, const frange &op2, > > + relation_kind rel) const final override; > > + bool op2_range (frange &r, tree type, > > + const irange &lhs, const frange &op1, > > + relation_kind rel) const final override; > > +} fop_gt; > > + > > +bool > > +foperator_gt::op1_range (frange &r, > > + tree type, > > + const irange &lhs, > > + const frange &op2 ATTRIBUTE_UNUSED, > > + relation_kind) const > > +{ > > + switch (get_bool_state (r, lhs, type)) > > + { > > + case BRS_TRUE: > > + r.set_varying (type); > > + // The TRUE side of op1 > op2 implies op1 is !NAN and !NINF. > > + r.set_nan (fp_prop::NO); > > + r.set_ninf (fp_prop::NO); > > + break; > > + > > + case BRS_FALSE: > > + r.set_varying (type); > > + break; > > + > > + default: > > + break; > > + } > > + return true; > > +} > > + > > +bool > > +foperator_gt::op2_range (frange &r, > > + tree type, > > + const irange &lhs, > > + const frange &op1 ATTRIBUTE_UNUSED, > > + relation_kind) const > > +{ > > + switch (get_bool_state (r, lhs, type)) > > + { > > + case BRS_TRUE: > > + r.set_varying (type); > > + // The TRUE side of op1 > op2 implies op2 is !NAN and !INF. > > + r.set_nan (fp_prop::NO); > > + r.set_inf (fp_prop::NO); > > + break; > > + > > + case BRS_FALSE: > > + r.set_varying (type); > > + break; > > + > > + default: > > + break; > > + } > > + return true; > > +} > > + > > +class foperator_ge : public range_operator_float > > +{ > > + using range_operator_float::fold_range; > > + using range_operator_float::op1_range; > > + using range_operator_float::op2_range; > > + > > + bool fold_range (irange &r, tree type, > > + const frange &op1, const frange &op2, > > + relation_kind rel) const final override > > + { > > + return default_frelop_fold_range (r, type, op1, op2, rel, VREL_GE); > > + } > > + relation_kind op1_op2_relation (const irange &lhs) const final override > > + { > > + return ge_op1_op2_relation (lhs); > > + } > > + bool op1_range (frange &r, tree type, > > + const irange &lhs, const frange &op2, > > + relation_kind rel) const final override; > > + bool op2_range (frange &r, tree type, > > + const irange &lhs, const frange &op1, > > + relation_kind rel) const final override > > + { > > + return op1_range (r, type, lhs, op1, rel); > > + } > > +} fop_ge; > > + > > +bool > > +foperator_ge::op1_range (frange &r, > > + tree type, > > + const irange &lhs, > > + const frange &op2 ATTRIBUTE_UNUSED, > > + relation_kind) const > > +{ > > + switch (get_bool_state (r, lhs, type)) > > + { > > + case BRS_TRUE: > > + r.set_varying (type); > > + // The TRUE side of op1 >= op2 implies op1 is !NAN. > > + r.set_nan (fp_prop::NO); > > + break; > > + > > + case BRS_FALSE: > > + r.set_varying (type); > > + break; > > + > > + default: > > + break; > > + } > > + return true; > > +} > > + > > +// UNORDERED_EXPR comparison. > > + > > +class foperator_unordered : public range_operator_float > > +{ > > + using range_operator_float::fold_range; > > + using range_operator_float::op1_range; > > + using range_operator_float::op2_range; > > + > > +public: > > + bool fold_range (irange &r, tree type, > > + const frange &op1, const frange &op2, > > + relation_kind rel) const final override; > > + bool op1_range (frange &r, tree type, > > + const irange &lhs, const frange &op2, > > + relation_kind rel) const final override; > > + bool op2_range (frange &r, tree type, > > + const irange &lhs, const frange &op1, > > + relation_kind rel) const final override > > + { > > + return op1_range (r, type, lhs, op1, rel); > > + } > > +} fop_unordered; > > + > > +bool > > +foperator_unordered::fold_range (irange &r, tree type, > > + const frange &op1, const frange &op2, > > + relation_kind) const > > +{ > > + // UNORDERED is TRUE if either operand is a NAN. > > + if (op1.get_nan ().yes_p () || op2.get_nan ().yes_p ()) > > + r = range_true (type); > > + // UNORDERED is FALSE if neither operand is a NAN. > > + else if (op1.get_nan ().no_p () && op2.get_nan ().no_p ()) > > + r = range_false (type); > > + else > > + r = range_true_and_false (type); > > + return true; > > +} > > + > > +bool > > +foperator_unordered::op1_range (frange &r, tree type, > > + const irange &lhs, > > + const frange &op2 ATTRIBUTE_UNUSED, > > + relation_kind) const > > +{ > > + switch (get_bool_state (r, lhs, type)) > > + { > > + case BRS_TRUE: > > + r.set_varying (type); > > + // Since at least one operand must be NAN, if one of them is > > + // not, the other must be. > > + if (op2.get_nan ().no_p ()) > > + r.set_nan (fp_prop::YES); > > + break; > > + > > + case BRS_FALSE: > > + r.set_varying (type); > > + // A false UNORDERED means both operands are !NAN. > > + r.set_nan (fp_prop::NO); > > + break; > > + > > + default: > > + break; > > + } > > + return true; > > +} > > + > > +// ORDERED_EXPR comparison. > > + > > +class foperator_ordered : public range_operator_float > > +{ > > + using range_operator_float::fold_range; > > + using range_operator_float::op1_range; > > + using range_operator_float::op2_range; > > + > > +public: > > + bool fold_range (irange &r, tree type, > > + const frange &op1, const frange &op2, > > + relation_kind rel) const final override; > > + bool op1_range (frange &r, tree type, > > + const irange &lhs, const frange &op2, > > + relation_kind rel) const final override; > > + bool op2_range (frange &r, tree type, > > + const irange &lhs, const frange &op1, > > + relation_kind rel) const final override > > + { > > + return op1_range (r, type, lhs, op1, rel); > > + } > > +} fop_ordered; > > + > > +bool > > +foperator_ordered::fold_range (irange &r, tree type, > > + const frange &op1, const frange &op2, > > + relation_kind) const > > +{ > > + // ORDERED is TRUE if neither operand is a NAN. > > + if (op1.get_nan ().no_p () && op2.get_nan ().no_p ()) > > + r = range_true (type); > > + // ORDERED is FALSE if either operand is a NAN. > > + else if (op1.get_nan ().yes_p () || op2.get_nan ().yes_p ()) > > + r = range_false (type); > > + else > > + r = range_true_and_false (type); > > + return true; > > +} > > + > > +bool > > +foperator_ordered::op1_range (frange &r, tree type, > > + const irange &lhs, > > + const frange &op2 ATTRIBUTE_UNUSED, > > + relation_kind rel) const > > +{ > > + switch (get_bool_state (r, lhs, type)) > > + { > > + case BRS_TRUE: > > + r.set_varying (type); > > + // The TRUE side of op1 UNORDERED op2 implies op1 is !NAN. > > + r.set_nan (fp_prop::NO); > > + break; > > + > > + case BRS_FALSE: > > + r.set_varying (type); > > + // The FALSE side of op1 UNORDERED op1 implies op1 is !NAN. > > + if (rel == VREL_EQ) > > + r.set_nan (fp_prop::NO); > > + break; > > + > > + default: > > + break; > > + } > > + return true; > > +} > > + > > +// Placeholder for unimplemented relational operators. > > + > > +class foperator_relop_unknown : public range_operator_float > > +{ > > + using range_operator_float::fold_range; > > + > > +public: > > + bool fold_range (irange &r, tree type, > > + const frange &, const frange &, > > + relation_kind) const final override > > + { > > + r.set_varying (type); > > + return true; > > + } > > +} fop_relop_unknown; > > + > > > > // Instantiate a range_op_table for floating point operations. > > static floating_op_table global_floating_table; > > @@ -185,6 +732,23 @@ floating_op_table::floating_op_table () > > set (PAREN_EXPR, fop_identity); > > set (OBJ_TYPE_REF, fop_identity); > > set (REAL_CST, fop_identity); > > + > > + // All the relational operators are expected to work, because the > > + // calculation of ranges on outgoing edges expect the handlers to be > > + // present. > > + set (EQ_EXPR, fop_equal); > > + set (NE_EXPR, fop_not_equal); > > + set (LT_EXPR, fop_lt); > > + set (LE_EXPR, fop_le); > > + set (GT_EXPR, fop_gt); > > + set (GE_EXPR, fop_ge); > > + set (UNLE_EXPR, fop_relop_unknown); > > + set (UNLT_EXPR, fop_relop_unknown); > > + set (UNGE_EXPR, fop_relop_unknown); > > + set (UNGT_EXPR, fop_relop_unknown); > > + set (UNEQ_EXPR, fop_relop_unknown); > > + set (ORDERED_EXPR, fop_ordered); > > + set (UNORDERED_EXPR, fop_unordered); > > } > > > > // Return a pointer to the range_operator_float instance, if there is > > diff --git a/gcc/testsuite/g++.dg/opt/pr94589-2.C b/gcc/testsuite/g++.dg/opt/pr94589-2.C > > index e9ef84b1912..1caa725061e 100644 > > --- a/gcc/testsuite/g++.dg/opt/pr94589-2.C > > +++ b/gcc/testsuite/g++.dg/opt/pr94589-2.C > > @@ -4,6 +4,31 @@ > > // { dg-final { scan-tree-dump-times "\[ij]_\[0-9]+\\(D\\) (?:<|<=|==|!=|>|>=) \[ij]_\[0-9]+\\(D\\)" 12 "optimized" } } > > // { dg-final { scan-tree-dump-times "i_\[0-9]+\\(D\\) (?:<|<=|==|!=|>|>=) 5\\.0" 12 "optimized" } } > > > > +/* This is failing on f5() because the spaceship operator is no longer > > + folded. What happens is that evrp folds away the final condition > > + here as always true. > > + > > + : > > + if (i_2(D) != j_4(D)) > > + goto ; [INV] > > + else > > + goto ; [INV] > > + > > + : > > + if (i_2(D) >= j_4(D)) > > + goto ; [INV] > > + else > > + goto ; [INV] > > + > > + : > > + if (i_2(D) > j_4(D)) <<== ALWAYS TRUE > > + goto ; [INV] > > + else > > + goto ; [INV] > > + > > + This causes phiopt to no longer be able to fold the total sequence > > + into i_2 >= j_4. */ > > + > > #include > > > > #define A __attribute__((noipa)) > > diff --git a/gcc/testsuite/gcc.dg/tree-ssa/vrp-float-1.c b/gcc/testsuite/gcc.dg/tree-ssa/vrp-float-1.c > > new file mode 100644 > > index 00000000000..88faf72ac42 > > --- /dev/null > > +++ b/gcc/testsuite/gcc.dg/tree-ssa/vrp-float-1.c > > @@ -0,0 +1,19 @@ > > +// { dg-do compile } > > +// { dg-options "-O2 -fdisable-tree-ethread -fdisable-tree-fre1 -fdump-tree-evrp" } > > + > > +void bar (); > > +void george (); > > + > > +float > > +foo (float x, float y) > > +{ > > + if (x == x) > > + { > > + if (x > y) > > + bar(); > > + if (x == x) > > + george(); > > + } > > +} > > + > > +// { dg-final { scan-tree-dump-times "Folding predicate x_*to 1" "evrp" 1 } } > > diff --git a/gcc/testsuite/gcc.dg/tree-ssa/vrp-float-11.c b/gcc/testsuite/gcc.dg/tree-ssa/vrp-float-11.c > > new file mode 100644 > > index 00000000000..2f4dc8757a3 > > --- /dev/null > > +++ b/gcc/testsuite/gcc.dg/tree-ssa/vrp-float-11.c > > @@ -0,0 +1,26 @@ > > +// { dg-do compile } > > +// { dg-options "-O2 -fno-thread-jumps -fdump-tree-evrp" } > > + > > +extern void link_error (); > > + > > +void fast_sqrt (float); > > + > > +float test (float x) > > +{ > > + float y = x*x; > > + if (y >= 0.f) > > + { > > + if (__builtin_isnan (y)) > > + link_error (); > > + else > > + fast_sqrt (y); > > + > > + if (!__builtin_isnan (y)) > > + fast_sqrt (y); > > + else > > + link_error (); > > + } > > +} > > + > > +// { dg-final { scan-tree-dump-times "fast_sqrt" 2 "evrp" } } > > +// { dg-final { scan-tree-dump-not "link_error" "evrp" } } > > diff --git a/gcc/testsuite/gcc.dg/tree-ssa/vrp-float-3.c b/gcc/testsuite/gcc.dg/tree-ssa/vrp-float-3.c > > new file mode 100644 > > index 00000000000..c659abb6cc0 > > --- /dev/null > > +++ b/gcc/testsuite/gcc.dg/tree-ssa/vrp-float-3.c > > @@ -0,0 +1,18 @@ > > +// { dg-do compile } > > +// { dg-options "-O2 -fdisable-tree-ethread -fdump-tree-evrp" } > > + > > +void link_error (); > > + > > +void > > +foo (double x, double y) > > +{ > > + if (x == y) > > + { > > + if (__builtin_isnan (x)) > > + link_error (); > > + if (__builtin_isnan (y)) > > + link_error (); > > + } > > +} > > + > > +// { dg-final { scan-tree-dump-not "link_error" "evrp" } } > > diff --git a/gcc/testsuite/gcc.dg/tree-ssa/vrp-float-4.c b/gcc/testsuite/gcc.dg/tree-ssa/vrp-float-4.c > > new file mode 100644 > > index 00000000000..86436742e0a > > --- /dev/null > > +++ b/gcc/testsuite/gcc.dg/tree-ssa/vrp-float-4.c > > @@ -0,0 +1,16 @@ > > +// { dg-do compile } > > +// { dg-options "-O2 -fdisable-tree-ethread -fdump-tree-evrp" } > > + > > +void link_error (); > > + > > +void > > +foo (double x, double y) > > +{ > > + if (x > y) > > + { > > + if (__builtin_isnan (x) || __builtin_isnan (y)) > > + link_error (); > > + } > > +} > > + > > +// { dg-final { scan-tree-dump-not "link_error" "evrp" } } > > diff --git a/gcc/testsuite/gcc.dg/tree-ssa/vrp-float-6.c b/gcc/testsuite/gcc.dg/tree-ssa/vrp-float-6.c > > new file mode 100644 > > index 00000000000..145d1861804 > > --- /dev/null > > +++ b/gcc/testsuite/gcc.dg/tree-ssa/vrp-float-6.c > > @@ -0,0 +1,20 @@ > > +// { dg-do compile } > > +// { dg-options "-O2 -fdisable-tree-ethread -fdump-tree-evrp" } > > + > > +void bar (); > > + > > +void > > +foo (double x, double y) > > +{ > > + if (x > y) > > + ; > > + else if (!__builtin_isnan (x) && !__builtin_isnan (y)) > > + { > > + // If x and y are not NAN, the x <= y relationship holds, and the > > + // following conditional can be folded away. > > + if (x <= y) > > + bar (); > > + } > > +} > > + > > +// { dg-final { scan-tree-dump-times "Folding predicate x_.* <= y_.* to 1" 1 "evrp" } } > > diff --git a/gcc/testsuite/gcc.dg/tree-ssa/vrp-float-7.c b/gcc/testsuite/gcc.dg/tree-ssa/vrp-float-7.c > > new file mode 100644 > > index 00000000000..92af87091a8 > > --- /dev/null > > +++ b/gcc/testsuite/gcc.dg/tree-ssa/vrp-float-7.c > > @@ -0,0 +1,14 @@ > > +// { dg-do compile } > > +// { dg-options "-O2 -fno-tree-forwprop -fno-tree-ccp -fno-tree-fre -fdump-tree-evrp" } > > + > > +extern void link_error (); > > + > > +void > > +foo () > > +{ > > + float z = 0.0; > > + if (__builtin_isnan (z)) > > + link_error (); > > +} > > + > > +// { dg-final { scan-tree-dump-not "link_error" "evrp" } } > > diff --git a/gcc/testsuite/gcc.dg/tree-ssa/vrp-float-8.c b/gcc/testsuite/gcc.dg/tree-ssa/vrp-float-8.c > > new file mode 100644 > > index 00000000000..9170150d453 > > --- /dev/null > > +++ b/gcc/testsuite/gcc.dg/tree-ssa/vrp-float-8.c > > @@ -0,0 +1,26 @@ > > +// { dg-do compile } > > +// { dg-options "-O2 -fno-thread-jumps -fdump-tree-evrp" } > > + > > +extern void link_error (); > > + > > +void fast_sqrt (float); > > + > > +float test (float x) > > +{ > > + float y = x*x; > > + if (y >= 0.f) > > + { > > + if (__builtin_isnan (y)) > > + link_error (); > > + else > > + fast_sqrt (y); > > + > > + if (!__builtin_isnan (y)) > > + fast_sqrt (y); > > + else > > + link_error (); > > + } > > +} > > + > > +// { dg-final { scan-tree-dump-times "fast_sqrt" 2 "evrp" } } > > +// { dg-final { scan-tree-dump-not "link_error" "evrp" } } > > diff --git a/gcc/value-range.h b/gcc/value-range.h > > index e43fbe30f27..478f165e02b 100644 > > --- a/gcc/value-range.h > > +++ b/gcc/value-range.h > > @@ -338,8 +338,7 @@ public: > > frange (const frange &); > > static bool supports_p (tree type) > > { > > - // Disabled until floating point range-ops come live. > > - return 0 && SCALAR_FLOAT_TYPE_P (type); > > + return SCALAR_FLOAT_TYPE_P (type); > > } > > virtual tree type () const override; > > virtual void set (tree, tree, value_range_kind = VR_RANGE) override; > > -- > > 2.36.1 > >