From patchwork Mon Jul 25 18:49:51 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Aldy Hernandez X-Patchwork-Id: 171 Return-Path: Delivered-To: ouuuleilei@gmail.com Received: by 2002:a05:6a10:b5d6:b0:2b9:3548:2db5 with SMTP id v22csp1820597pxt; Mon, 25 Jul 2022 11:51:02 -0700 (PDT) X-Google-Smtp-Source: AGRyM1uz7V4tmSQaI7uv6+2O7pfGiBsWuZm6GdJINXJ3BJ2u9u8h7+AxNMLxQbhfsl9IDoAEdjm+ X-Received: by 2002:aa7:d9d3:0:b0:43b:c457:fb06 with SMTP id v19-20020aa7d9d3000000b0043bc457fb06mr14218269eds.5.1658775062750; Mon, 25 Jul 2022 11:51:02 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1658775062; cv=none; d=google.com; s=arc-20160816; b=McbT/hgP1KiCdykI1byGvYZ3F7SFkymzXmk4QIqWAnOjVDjwogIn9DMgd08/vWEQp6 UMinYBjyoyPRGp2Rc9K7zoNlHGkSIJjiguFix0SejchiKgcPndLUqeL4Fu+ipExvXOYw RSZ1Ul/G7VHXCcb0pN8ahduisiNnHeoxzMwlW5wfnE6Rj56QI5dn+Rpss2+nhnr4gt5l BgIP+7j1pFiNylozG5DXdnpUBxpiEH6JcP7iv+yP2yOh2B/sW2BBLW3d78UAPrPrEFm9 8bmpPSJ59coPkP9ML6X941nEaWqg7hm+hGItP5jjaW4TfhR+xGAhdOD2y0FJ59q/11pw JF/w== 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=iR9yAvh53KxD4Evn7eY6VWaUwmCaN+/pF+ruBYsxLHM=; b=dQIYJxxs2w8RgV55wmBlYEj4RFoTVmZ8h5OvVPC0rm66SaluapwXTHnM9k0PlwEIdn l6GiaOLhc2dRwcDONntnRbZAkoCuDWIglY2PSCH2mg/itL/YtPVn+17Z0M1dzze0BDWl quRBX+et8AzCACb/qZgyWCOSMhPOWZkS2T6gpE/GOjoVNJ53FuEhpxk5WPWqk3EsRm54 9OIvOIkeWVZHCGPUHBJ53Z6D8j7lHLeaxv2kX5s/e9VacbifbgjtSHH943itvzfggKM7 +mvmXqwFB2ReunPMU3U782oL9AhAcRFHJlzi9KJYdZKuaoBiuF+9eIjdQSNAM22e3Knj wEUA== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@gcc.gnu.org header.s=default header.b=atbMkT7T; spf=pass (google.com: domain of gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org designates 2620:52:3:1:0:246e:9693:128c 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 (server2.sourceware.org. [2620:52:3:1:0:246e:9693:128c]) by mx.google.com with ESMTPS id rh16-20020a17090720f000b006fe98e19840si11899418ejb.733.2022.07.25.11.51.02 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 25 Jul 2022 11:51:02 -0700 (PDT) Received-SPF: pass (google.com: domain of gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org designates 2620:52:3:1:0:246e:9693:128c as permitted sender) client-ip=2620:52:3:1:0:246e:9693:128c; Authentication-Results: mx.google.com; dkim=pass header.i=@gcc.gnu.org header.s=default header.b=atbMkT7T; spf=pass (google.com: domain of gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org designates 2620:52:3:1:0:246e:9693:128c 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 474373858286 for ; Mon, 25 Jul 2022 18:51:01 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 474373858286 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gcc.gnu.org; s=default; t=1658775061; bh=iR9yAvh53KxD4Evn7eY6VWaUwmCaN+/pF+ruBYsxLHM=; h=To:Subject:Date:List-Id:List-Unsubscribe:List-Archive:List-Post: List-Help:List-Subscribe:From:Reply-To:Cc:From; b=atbMkT7TumfXUy5y8ibiddam+3jR00ltgHOzi4pH9XI6OhWKEKwSEjHiGlmtDee3p C9oprAYKZbfUwFkpbNpToCxCi4GvAJgjc8eGbD8fF+ItgpQ0TPq0ytu3KGExTen1cO pE3vsdNIDTjJerM9MJtQpzK2hbeYmt3RPIx2dkvs= 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.133.124]) by sourceware.org (Postfix) with ESMTPS id 970663858413 for ; Mon, 25 Jul 2022 18:50:17 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org 970663858413 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-20-2e8HH7uVNg68jZ-5QANjOw-1; Mon, 25 Jul 2022 14:50:12 -0400 X-MC-Unique: 2e8HH7uVNg68jZ-5QANjOw-1 Received: from smtp.corp.redhat.com (int-mx01.intmail.prod.int.rdu2.redhat.com [10.11.54.1]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id CD334101A54E; Mon, 25 Jul 2022 18:50:11 +0000 (UTC) Received: from abulafia.quesejoda.com (unknown [10.39.195.31]) by smtp.corp.redhat.com (Postfix) with ESMTPS id 2D1A4404C324; Mon, 25 Jul 2022 18:50:10 +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 26PIo8c42202403 (version=TLSv1.3 cipher=TLS_AES_256_GCM_SHA384 bits=256 verify=NOT); Mon, 25 Jul 2022 20:50:08 +0200 Received: (from aldyh@localhost) by abulafia.quesejoda.com (8.17.1/8.17.1/Submit) id 26PIo6h92202402; Mon, 25 Jul 2022 20:50:06 +0200 To: GCC patches Subject: [RFA] Implement basic range operators to enable floating point VRP. Date: Mon, 25 Jul 2022 20:49:51 +0200 Message-Id: <20220725184951.2202379-1-aldyh@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.84 on 10.11.54.1 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, RCVD_IN_DNSWL_NONE, 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-Patchwork-Original-From: Aldy Hernandez via Gcc-patches From: Aldy Hernandez Reply-To: Aldy Hernandez Cc: Jakub Jelinek , roger@nextmovesoftware.com 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?1739351720333522648?= X-GMAIL-MSGID: =?utf-8?q?1739351720333522648?= 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;