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 90256382EF24 for ; Mon, 5 Dec 2022 09:20:59 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org 90256382EF24 Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=redhat.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=redhat.com DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1670232059; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=2SJpke8BWumXp9JRrCh+QlDgulujCIg333a56f3x8Fw=; b=PLGPP6BBxfyeizSJvf/xLGRHMCWhdJhclQN84emBi735vxwuP8kDV4FiduLPW+YNDqQhyU IVebKjrXYOjwblHpnri1LjBm7wDv1eQXoXde9McUoLX6L/JCSMhu+ptwBWrrkXJpqlmcvo n/+LV+aOWaVrlWYQv2uf3eMroB0DT/o= Received: from mail-wm1-f71.google.com (mail-wm1-f71.google.com [209.85.128.71]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_128_GCM_SHA256) id us-mta-597-AdOLeQUBPGmOlwMottirtQ-1; Mon, 05 Dec 2022 04:20:57 -0500 X-MC-Unique: AdOLeQUBPGmOlwMottirtQ-1 Received: by mail-wm1-f71.google.com with SMTP id h81-20020a1c2154000000b003d1c8e519fbso680436wmh.2 for ; Mon, 05 Dec 2022 01:20:57 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=content-transfer-encoding:in-reply-to:from:content-language :references:cc:to:subject:user-agent:mime-version:date:message-id :x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=2SJpke8BWumXp9JRrCh+QlDgulujCIg333a56f3x8Fw=; b=OMUYekO3kQyMyXI11JCjhb2QIzURFVncLRxQmzlbRwu3yFj4Jvb1M1zs93zLoi+Jr2 DmoEnQ+85wXHWJRzJop7X8T+Kl312nBD10L4fYMbXTnWilmr162pWD8rVrfKMLx6rpM5 iICQZNfU3cO3DSh03Gg2NAlJ2cMZTtoQDXfOVfQEUb9k1fNIijQNEEzgbqnlrRba8pux RNRBt6YHdGSH1ulkUVBzmTE3eVtcOuN82Oy+qcFhYTmrwCeLPC8Aj/RDEFu05LzM82eN ttRRpJ4V/gnVQt64mEi3XNaQikAzvJnOX/SOhHLhtN/fL8lzdSLfraSWXrlLdNSbfvYv ofMQ== X-Gm-Message-State: ANoB5pnZMl4rm3xksbLnK5ejhI8dOu7SxNYaes3cwUVkyxh8Rtv2zmCo nMnA7l1GE/cXdy5gG+fbngjNYQbJg/1obBLTmmcdK3FO3epMeIR0PNa8IjfX5PqH7vszCh0Emo0 Fx66fyFttZo+I+tSytg== X-Received: by 2002:adf:fe0a:0:b0:242:30f5:a947 with SMTP id n10-20020adffe0a000000b0024230f5a947mr13374283wrr.597.1670232055966; Mon, 05 Dec 2022 01:20:55 -0800 (PST) X-Google-Smtp-Source: AA0mqf4lpcAMPhLjkVYm4unrbdZ0mOd2O4E06scrFOEoRVVAJNhfOvX8TYQT1kQyB23ig55u1/gZiQ== X-Received: by 2002:adf:fe0a:0:b0:242:30f5:a947 with SMTP id n10-20020adffe0a000000b0024230f5a947mr13374266wrr.597.1670232055562; Mon, 05 Dec 2022 01:20:55 -0800 (PST) Received: from [192.168.86.37] (128.red-81-33-16.staticip.rima-tde.net. [81.33.16.128]) by smtp.gmail.com with ESMTPSA id d15-20020a5d4f8f000000b0024246991121sm8211349wru.116.2022.12.05.01.20.54 (version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128); Mon, 05 Dec 2022 01:20:55 -0800 (PST) Message-ID: <1f2b50a8-8f3c-690a-182b-c636fc2f86ed@redhat.com> Date: Mon, 5 Dec 2022 10:20:53 +0100 MIME-Version: 1.0 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:102.0) Gecko/20100101 Thunderbird/102.5.0 Subject: Re: [PATCH] range-op-float: Fix up multiplication and division reverse operation [PR107879] To: Jakub Jelinek Cc: gcc-patches@gcc.gnu.org References: From: Aldy Hernandez In-Reply-To: X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Content-Language: en-US Content-Type: text/plain; charset=UTF-8; format=flowed Content-Transfer-Encoding: 7bit X-Spam-Status: No, score=-4.9 required=5.0 tests=BAYES_00,DKIMWL_WL_HIGH,DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,NICE_REPLY_A,RCVD_IN_DNSWL_NONE,RCVD_IN_MSPIKE_H2,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 List-Id: On 11/29/22 10:43, Jakub Jelinek wrote: > Hi! > > While for the normal cases it seems to be correct to implement > reverse multiplication (op1_range/op2_range) through division > with float_binary_op_range_finish, reverse division (op1_range) > through multiplication with float_binary_op_range_finish or > (op2_range) through division with float_binary_op_range_finish, > as e.g. following testcase shows for the corner cases it is > incorrect. > Say on the testcase we are doing reverse multiplication, we > have [-0., 0.] range (no NAN) on lhs and VARYING on op1 (or op2). > We implement that through division, because x from > lhs = x * op2 > is > x = lhs / op2 > For the division, [-0., 0.] / VARYING is computed (IMHO correctly) > as [-0., 0.] +-NAN, because 0 / anything but 0 or NAN is still > 0 and 0 / 0 is NAN and ditto 0 / NAN. And then we just > float_binary_op_range_finish, which figures out that because lhs > can't be NAN, neither operand can be NAN. So, the end range is > [-0., 0.]. But that is not correct for the reverse multiplication. > When the result is 0, if op2 can be zero, then x can be anything > (VARYING), to be precise anything but INF (unless result can be NAN), Not an objection, just an observation... If we know it can't be INF, could we drop INF from the range? We have frange_drop_{inf,ninf} for this. > because anything * 0 is 0 (or NAN for INF). While if op2 must be > non-zero, then x must be 0. Of course the sign logic > (signbit(x) = signbit(lhs) ^ signbit(op2)) still holds, so it actually > isn't full VARYING if both lhs and op2 have known sign bits. > And going through other corner cases one by one shows other differences > between what we compute for the corresponding forward operation and > what we should compute for the reverse operations. > The following patch is slightly conservative and includes INF > (in case of result including 0 and not NAN) in the ranges or > 0 in the ranges (in case of result including INF and not NAN). > The latter is what happens anyway because we flush denormals to 0, > and the former just not to deal with all the corner cases. > So, the end test is that for reverse multiplication and division > op2_range the cases we need to adjust to VARYING or VARYING positive > or VARYING negative are if lhs and op? ranges both contain 0, > or both contain some infinity, while for division op1_range the > corner case is if lhs range contains 0 and op2 range contains INF or vice > versa. Otherwise I believe ranges from the corresponding operation > are ok, or could be slightly more conservative (e.g. for > reverse multiplication, if op? range is singleton INF and lhs > range doesn't include any INF, then x's range should be UNDEFINED or > known NAN (depending on if lhs can be NAN), while the division computes > [-0., 0.] +-NAN; or similarly if op? range is only 0 and lhs range > doesn't include 0, division would compute +INF +-NAN, or -INF +-NAN, > or (for lack of multipart franges -INF +INF +-NAN just VARYING +-NAN), > while again it is UNDEFINED or known NAN. > > Oh, and I found by code inspection wrong condition for the division's > known NAN result, due to thinko it would trigger not just when > both operands are known to be 0 or both are known to be INF, but > when either both are known to be 0, or at least one is known to be INF. > > Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk? > > 2022-11-29 Jakub Jelinek > > PR tree-optimization/107879 > * range-op-float.cc (foperator_mult::op1_range): If both > lhs and op2 ranges contain zero or both ranges contain > some infinity, set r range to zero_to_inf_range depending on > signbit_known_p. > (foperator_div::op2_range): Similarly for lhs and op1 ranges. > (foperator_div::op1_range): If lhs range contains zero and op2 > range contains some infinity or vice versa, set r range to > zero_to_inf_range depending on signbit_known_p. > (foperator_div::rv_fold): Fix up condition for returning known NAN. > > --- gcc/range-op-float.cc.jj 2022-11-18 09:00:44.371322999 +0100 > +++ gcc/range-op-float.cc 2022-11-28 19:45:50.347869350 +0100 > @@ -2143,8 +2143,30 @@ public: > range_op_handler rdiv (RDIV_EXPR, type); > if (!rdiv) > return false; > - return float_binary_op_range_finish (rdiv.fold_range (r, type, lhs, op2), > - r, type, lhs); > + bool ret = rdiv.fold_range (r, type, lhs, op2); > + if (ret == false) > + return false; > + const REAL_VALUE_TYPE &lhs_lb = lhs.lower_bound (); > + const REAL_VALUE_TYPE &lhs_ub = lhs.upper_bound (); > + const REAL_VALUE_TYPE &op2_lb = op2.lower_bound (); > + const REAL_VALUE_TYPE &op2_ub = op2.upper_bound (); > + if ((contains_zero_p (lhs_lb, lhs_ub) && contains_zero_p (op2_lb, op2_ub)) > + || ((real_isinf (&lhs_lb) || real_isinf (&lhs_ub)) > + && (real_isinf (&op2_lb) || real_isinf (&op2_ub)))) > + { > + // If both lhs and op2 could be zeros or both could be infinities, > + // we don't know anything about op1 except maybe for the sign > + // and perhaps if it can be NAN or not. > + REAL_VALUE_TYPE lb, ub; > + int signbit_known = signbit_known_p (lhs_lb, lhs_ub, op2_lb, op2_ub); > + zero_to_inf_range (lb, ub, signbit_known); > + r.set (type, lb, ub); I assume we don't know anything about the sign of the NAN because of all the weird IEEE rules? > + } > + // Otherwise, if op2 is a singleton INF and lhs doesn't include INF, > + // or if lhs must be zero and op2 doesn't include zero, it would be > + // UNDEFINED, while rdiv.fold_range computes a zero or singleton INF > + // range. Those are supersets of UNDEFINED, so let's keep that way. > + return float_binary_op_range_finish (ret, r, type, lhs); > } > virtual bool op2_range (frange &r, tree type, > const frange &lhs, > @@ -2271,9 +2293,27 @@ public: > { > if (lhs.undefined_p ()) > return false; > - return float_binary_op_range_finish (fop_mult.fold_range (r, type, lhs, > - op2), > - r, type, lhs); > + bool ret = fop_mult.fold_range (r, type, lhs, op2); > + if (!ret) > + return ret; > + const REAL_VALUE_TYPE &lhs_lb = lhs.lower_bound (); > + const REAL_VALUE_TYPE &lhs_ub = lhs.upper_bound (); > + const REAL_VALUE_TYPE &op2_lb = op2.lower_bound (); > + const REAL_VALUE_TYPE &op2_ub = op2.upper_bound (); > + if ((contains_zero_p (lhs_lb, lhs_ub) > + && (real_isinf (&op2_lb) || real_isinf (&op2_ub))) > + || ((contains_zero_p (op2_lb, op2_ub)) > + && (real_isinf (&lhs_lb) || real_isinf (&lhs_ub)))) > + { > + // If both lhs could be zero and op2 infinity or vice versa, > + // we don't know anything about op1 except maybe for the sign > + // and perhaps if it can be NAN or not. > + REAL_VALUE_TYPE lb, ub; > + int signbit_known = signbit_known_p (lhs_lb, lhs_ub, op2_lb, op2_ub); > + zero_to_inf_range (lb, ub, signbit_known); > + r.set (type, lb, ub); > + } > + return float_binary_op_range_finish (ret, r, type, lhs); > } > virtual bool op2_range (frange &r, tree type, > const frange &lhs, > @@ -2282,8 +2322,26 @@ public: > { > if (lhs.undefined_p ()) > return false; > - return float_binary_op_range_finish (fold_range (r, type, op1, lhs), > - r, type, lhs); > + bool ret = fold_range (r, type, op1, lhs); > + if (!ret) > + return ret; > + const REAL_VALUE_TYPE &lhs_lb = lhs.lower_bound (); > + const REAL_VALUE_TYPE &lhs_ub = lhs.upper_bound (); > + const REAL_VALUE_TYPE &op1_lb = op1.lower_bound (); > + const REAL_VALUE_TYPE &op1_ub = op1.upper_bound (); > + if ((contains_zero_p (lhs_lb, lhs_ub) && contains_zero_p (op1_lb, op1_ub)) > + || ((real_isinf (&lhs_lb) || real_isinf (&lhs_ub)) > + && (real_isinf (&op1_lb) || real_isinf (&op1_ub)))) > + { > + // If both lhs and op1 could be zeros or both could be infinities, > + // we don't know anything about op2 except maybe for the sign > + // and perhaps if it can be NAN or not. > + REAL_VALUE_TYPE lb, ub; > + int signbit_known = signbit_known_p (lhs_lb, lhs_ub, op1_lb, op1_ub); > + zero_to_inf_range (lb, ub, signbit_known); > + r.set (type, lb, ub); > + } > + return float_binary_op_range_finish (ret, r, type, lhs); > } > private: > void rv_fold (REAL_VALUE_TYPE &lb, REAL_VALUE_TYPE &ub, bool &maybe_nan, > @@ -2296,7 +2354,7 @@ private: > { > // +-0.0 / +-0.0 or +-INF / +-INF is a known NAN. > if ((zero_p (lh_lb, lh_ub) && zero_p (rh_lb, rh_ub)) > - || (singleton_inf_p (lh_lb, lh_ub) || singleton_inf_p (rh_lb, rh_ub))) > + || (singleton_inf_p (lh_lb, lh_ub) && singleton_inf_p (rh_lb, rh_ub))) > { > real_nan (&lb, "", 0, TYPE_MODE (type)); > ub = lb; > --- gcc/testsuite/gcc.c-torture/execute/pr107879.c.jj 2022-11-28 19:53:06.720570324 +0100 > +++ gcc/testsuite/gcc.c-torture/execute/pr107879.c 2022-11-28 19:51:57.281572677 +0100 > @@ -0,0 +1,25 @@ > +/* PR tree-optimization/107879 */ > + > +__attribute__((noipa)) static double > +foo (double *y) > +{ > + volatile int ph = 0; > + volatile double vf = 1.0; > + double factor = vf; > + double x = - (double) ph * factor; > + if (x == 0) > + *y = 1.0; > + else > + *y = 1.0 / x; > + double w = 2.0 * x / factor; > + double omww = 1 - w; > + return omww > 0.0 ? omww : 0.0; > +} > + > +int > +main () > +{ > + double y = 42.0; > + if (foo (&y) != 1.0) > + __builtin_abort (); > +} > > Jakub > Thanks, OK. Aldy