From patchwork Thu Jun 1 20:12:21 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Andrew MacLeod X-Patchwork-Id: 1789326 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=gcc.gnu.org (client-ip=8.43.85.97; helo=sourceware.org; envelope-from=gcc-patches-bounces+incoming=patchwork.ozlabs.org@gcc.gnu.org; receiver=) Authentication-Results: legolas.ozlabs.org; dkim=pass (1024-bit key; unprotected) header.d=gcc.gnu.org header.i=@gcc.gnu.org header.a=rsa-sha256 header.s=default header.b=BnNTexxf; dkim-atps=neutral Received: from sourceware.org (server2.sourceware.org [8.43.85.97]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (P-384) server-digest SHA384) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4QXHPb2JfGz20Py for ; Fri, 2 Jun 2023 06:13:06 +1000 (AEST) Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 17B5E385703F for ; Thu, 1 Jun 2023 20:13:03 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 17B5E385703F DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gcc.gnu.org; s=default; t=1685650383; bh=bLZRmUXTzkYeL8Bn7wzv4yZNXEb+4FgvnEn1xmYqT0E=; h=Date:Subject:To:Cc:List-Id:List-Unsubscribe:List-Archive: List-Post:List-Help:List-Subscribe:From:Reply-To:From; b=BnNTexxf+Ezz+QtAhrs24JR3C1VUtUxUZ8cdqMrf+YWk5zK9ae+PkrJb7Ume7r/dh MP/G4tVKg6DAsT5fq60WNh41eAhfzmfMNSxNhjhQthNiovz61HT8LczTRVgX1XH7Ww RdkeMG1V2K0JjBDkZIDO+2UIaP5oycvdLgxW8bCA= 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 4A7233858C50 for ; Thu, 1 Jun 2023 20:12:32 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 4A7233858C50 Received: from mail-oi1-f198.google.com (mail-oi1-f198.google.com [209.85.167.198]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-528-iwtkt2ArN3Caprybv1ioyQ-1; Thu, 01 Jun 2023 16:12:25 -0400 X-MC-Unique: iwtkt2ArN3Caprybv1ioyQ-1 Received: by mail-oi1-f198.google.com with SMTP id 5614622812f47-38e4f308c49so901983b6e.1 for ; Thu, 01 Jun 2023 13:12:25 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1685650344; x=1688242344; h=cc:to:subject:from:content-language:user-agent:mime-version:date :message-id:x-gm-message-state:from:to:cc:subject:date:message-id :reply-to; bh=sGqPJgmErC+zd2HdjsAenZzjVh7Q+QfidDLTk6f8OKo=; b=hnJ/uMDUw51ZlBvYZrsu0CzPtcQmGFbU1VtSrW6x7YCwljbzJKdXDRYyiKucveqLZy 2kxm1rWHUZEQ46eWwNrC+GpIzQVsHvgrpmpek5HLOR3B8X9QJ1AcWqMH7oog/5qcKIem Q+s8Q+Lo5iorNlycAU8uEFxOUSajiOL8bBSbUZMZWOQUwgeYLzTjuX3I0aKg/HOJ28T8 /l2kJbnsgPF4gl40I1r0ERHmvlhdEGthmrQP3/vAXgNS+DWsxhjzO0kB0+hv6/HaNmIm r8kEAiJzgG72SyiqsEdXEaRmLzovAbOdOoClNdh9tocwcSTc0TMNt9gV0wPPErrow+/s +BeA== X-Gm-Message-State: AC+VfDzXIjVCleF+/yWtIz/CRyq+oNNm56CFtBZMOLsku50KtGIHpyXT 60z0Hp1IWG1R4hRU4tHqCtr76YeY4vUCfFADenj23ICnICySKwVNe6wQu7Jbt2OiNETIXwRXsfg DeoyfnSV6aa9aWC/Yan0z0E8GEoxyz2LNld82tzOg9Z/G9hEVkPJo6yTJJHQihNh01/Rgef9GdL eHyA== X-Received: by 2002:a05:6808:10:b0:398:30af:53b8 with SMTP id u16-20020a056808001000b0039830af53b8mr386033oic.6.1685650344537; Thu, 01 Jun 2023 13:12:24 -0700 (PDT) X-Google-Smtp-Source: ACHHUZ4O7ol7QUUo7S4bbynpQFZvwqcxdn8C0asT5z5Ig4lu8mvJomE7I9WDlAuRh6p+o3X0eo2Vjw== X-Received: by 2002:a05:6808:10:b0:398:30af:53b8 with SMTP id u16-20020a056808001000b0039830af53b8mr386005oic.6.1685650343902; Thu, 01 Jun 2023 13:12:23 -0700 (PDT) Received: from ?IPV6:2607:fea8:51df:4200::ca58? ([2607:fea8:51df:4200::ca58]) by smtp.gmail.com with ESMTPSA id mz22-20020a0562142d1600b00621430707f7sm632336qvb.83.2023.06.01.13.12.22 (version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128); Thu, 01 Jun 2023 13:12:23 -0700 (PDT) Message-ID: Date: Thu, 1 Jun 2023 16:12:21 -0400 MIME-Version: 1.0 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:102.0) Gecko/20100101 Thunderbird/102.11.0 Subject: [RFC] range-op restructuring To: gcc-patches Cc: "hernandez, aldy" X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Content-Language: en-US X-Spam-Status: No, score=-12.0 required=5.0 tests=BAYES_00, DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, KAM_ASCII_DIVIDERS, KAM_SHORT, RCVD_IN_DNSWL_NONE, 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: Andrew MacLeod via Gcc-patches From: Andrew MacLeod Reply-To: Andrew MacLeod Errors-To: gcc-patches-bounces+incoming=patchwork.ozlabs.org@gcc.gnu.org Sender: "Gcc-patches" With the addition of floating point ranges, we did a lot of additional class abstraction, then added a bunch more routines for floating point. We didn't know how it would look in the end, so we just marched forward and got it working. Now that has settled down a bit, and before we go and add more kinds of ranges, I want to visit restructuring the files and provide a better dispatch to the range operators from a vrange. We currently dispatch based on the type of the statement.. int or float.  the line is blurred heavily when we have statements that have more than one kid of range.. ie int_value = (int) float_value   vs float_value = (float) int_value Under the current regime, both kinds of casts have to go into the float table.. and this is going to get more complicated if we add more distinct kinds of ranges. With the current implementation, the floating point range operators don't even inherit from range_operator, they are their own kind of operator.   The ideal situation is to have a single unified range-operator class which has all the combinations, and they rest in a single table.  This simplifies numerous things, and avoid us having to classify anything in some arbitrary way. It also moves us back in the direction of the original vision I had for range-ops. Ive done an initial rough conversion so you can see what it looks like. I've attached a new range-op.h which shows class range-operator will all the virtual function combinations. The new dispatch mechniasm buys us about 1% speedup in both VRP and in jump_threading.  The new mechanism also handles unsupported combinations of operands smoothly, simply returning false if its an unsupported combination of aprameters that is invoked, which is what a default routine would do.   As for conversion, lets take operator_not_equal as an example.    The end result in range-operator.h is: class operator_not_equal : public range_operator { public:   bool fold_range (irange &r, tree type, const irange &op1, const irange &op2, relation_trio = TRIO_VARYING) const final override;   bool fold_range (irange &r, tree type, const frange &op1, const frange &op2, relation_trio = TRIO_VARYING) const final override;   bool op1_range (irange &r, tree type, const irange &lhs, const irange &op2, relation_trio = TRIO_VARYING) const final override;   bool op1_range (frange &r, tree type, const irange &lhs, const frange &op2, relation_trio = TRIO_VARYING) const final override;   bool op2_range (frange &r, tree type, const irange &lhs, const frange &op1, relation_trio rel = TRIO_VARYING) const final override;   bool op2_range (irange &r, tree type, const irange &lhs, const irange &op1, relation_trio = TRIO_VARYING) const final override;   relation_kind op1_op2_relation (const irange &lhs) const;   void update_bitmask (irange &r, const irange &lh, const irange &rh) const; }; extern operator_not_equal rop_NE_EXPR; When we add a new range type, such as pointers, you simply add the required prototypes, add new dispatch codes, and implement them. This is going  to cause some churn but I am trying to keep it to a minimum.  I've been mucking about with it for a couple of weeks, and I was thinking to structure it something like this: range-op.h and range-op.cc  will have the base range_operator class, along with the range_op_handler class  we move all the class headers, like the above operator_not_equal class into "range-operator.h" Where the code goes is the biggest struggle. Initially I was going to put it all in one file. This would be best as it allows us to co-locate all the code for various classes of routines. But that would already be about 8000 lines for int and float combined, and will only get larger with new range types. I also considered just including all the range-op-int.cc and range-op-float.cc files into range-op.cc when it compiles, but you still end up with  a big compilation unit.  So this is what I'm think now: We leave all the existing floating point code in range-op-float.cc, and then move all the existing integer code into a range-op-int.cc file (or I suppose we could even leave it in range-op.cc to avoid extra churn).  The classes must move to  a header to be accessible from the various files which implement them.  This provides for minimal churn. a few deletes and renames, and thats it.  I've attached the diff which moves operator_not_equal to this form (once some other structuring is in place). The plus is you can see in the header file range-operator.h exactly what is available for any opcode.  Whats less than ideal is that some of the routines are in range-op.cc and some are in range-op-float.cc,   Ultimately, I dont think thats such a big deal as those floating point routines often require common infrastructure, such as nan querying that integer things dont need.   When we add say a pointer range class,  we would create range-op-pointer.cc and all the new stuff required for prange would go in that file. That is the direction I am headed to clean this up, are there any objections or other suggestions? I hope to start with some of the early infrastructure next wee., then move on to each individual opcode. Andrew PS For the curious, I also decided to preview the first 3 patches which move to a unified range_operator class and implements the new dispatch system (which is in patch 0003*).   Whats missing is the follow on transition to a single unified table which I would do a single opcode at a time to ensure that if a problem comes up, we can easily isolate it to a specific change.  Once all the opcodes have been converted, and everything is in the new unified tree table, then we can simply switch to using just that.  Much of the speedup comes from lack of conditions when loading an operator.... it is a much more streamlined mechanism. From d5e2d6eb39d0e1c74b1d0e8c389f62f5e22a0640 Mon Sep 17 00:00:00 2001 From: Andrew MacLeod Date: Wed, 31 May 2023 13:10:31 -0400 Subject: [PATCH 03/11] Provide a new dispatch mechanism for range-ops. Simplify range_op_handler to have a single range-operator pointer and provide a more flexible dispatch mechanism for calls via generic vrange classes. This is more extensible for adding new classes of range support. * gimple-range-op.cc (gimple_range_op_handler::gimple_range_op_handler): Adjust. (gimple_range_op_handler::maybe_builtin_call): Adjust. * gimple-range-op.h (operand1, operand2): Use m_operator. * range-op.cc (integral_table, pointer_table): Relocate. (get_op_handler): Rename from get_handler and handle all types. (range_op_handler::range_op_handler): Relocate. (range_op_handler::set_op_handler): Relocate and adjust. (range_op_handler::range_op_handler): Relocate. (dispatch_trio): New. (RO_III, RO_IFI, RO_IFF, RO_FFF, RO_FIF, RO_FII): New consts. (range_op_handler::dispatch_kind): New. (range_op_handler::fold_range): Relocate and Use new dispatch value. (range_op_handler::op1_range): Ditto. (range_op_handler::op2_range): Ditto. (range_op_handler::lhs_op1_relation): Ditto. (range_op_handler::lhs_op2_relation): Ditto. (range_op_handler::op1_op2_relation): Ditto. (range_op_handler::set_op_handler): Use m_operator member. * range-op.h (range_op_handler::operator bool): Use m_operator. (range_op_handler::dispatch_kind): New. (range_op_handler::m_valid): Delete. (range_op_handler::m_int): Delete (range_op_handler::m_float): Delete (range_op_handler::m_operator): New. (range_op_table::operator[]): Relocate from .cc file. (range_op_table::set): Ditto. * value-range.h (class vrange): Make range_op_handler a friend. --- gcc/gimple-range-op.cc | 84 +++----- gcc/gimple-range-op.h | 4 +- gcc/range-op.cc | 474 ++++++++++++++++++++++------------------- gcc/range-op.h | 27 ++- gcc/value-range.h | 1 + 5 files changed, 310 insertions(+), 280 deletions(-) diff --git a/gcc/gimple-range-op.cc b/gcc/gimple-range-op.cc index 0c32a1d2859..727cc2817c7 100644 --- a/gcc/gimple-range-op.cc +++ b/gcc/gimple-range-op.cc @@ -144,7 +144,7 @@ gimple_range_op_handler::gimple_range_op_handler (gimple *s) if (type) set_op_handler (code, type); - if (m_valid) + if (m_operator) switch (gimple_code (m_stmt)) { case GIMPLE_COND: @@ -152,7 +152,7 @@ gimple_range_op_handler::gimple_range_op_handler (gimple *s) m_op2 = gimple_cond_rhs (m_stmt); // Check that operands are supported types. One check is enough. if (!Value_Range::supports_type_p (TREE_TYPE (m_op1))) - m_valid = false; + m_operator = NULL; return; case GIMPLE_ASSIGN: m_op1 = gimple_range_base_of_assignment (m_stmt); @@ -171,7 +171,7 @@ gimple_range_op_handler::gimple_range_op_handler (gimple *s) m_op2 = gimple_assign_rhs2 (m_stmt); // Check that operands are supported types. One check is enough. if ((m_op1 && !Value_Range::supports_type_p (TREE_TYPE (m_op1)))) - m_valid = false; + m_operator = NULL; return; default: gcc_unreachable (); @@ -1199,7 +1199,6 @@ gimple_range_op_handler::maybe_non_standard () gcc_fallthrough (); case WIDEN_MULT_EXPR: { - m_valid = false; m_op1 = gimple_assign_rhs1 (m_stmt); m_op2 = gimple_assign_rhs2 (m_stmt); tree ret = gimple_assign_lhs (m_stmt); @@ -1216,14 +1215,13 @@ gimple_range_op_handler::maybe_non_standard () if ((signed1 ^ signed2) && signed_ret) return; - m_valid = true; if (signed2 && !signed1) std::swap (m_op1, m_op2); if (signed1 || signed2) - m_int = signed_op; + m_operator = signed_op; else - m_int = unsigned_op; + m_operator = unsigned_op; break; } default: @@ -1252,47 +1250,41 @@ gimple_range_op_handler::maybe_builtin_call () { case CFN_BUILT_IN_CONSTANT_P: m_op1 = gimple_call_arg (call, 0); - m_valid = true; if (irange::supports_p (TREE_TYPE (m_op1))) - m_int = &op_cfn_constant_p; + m_operator = &op_cfn_constant_p; else if (frange::supports_p (TREE_TYPE (m_op1))) - m_float = &op_cfn_constant_float_p; + m_operator = &op_cfn_constant_float_p; else - m_valid = false; + m_operator = NULL; break; CASE_FLT_FN (CFN_BUILT_IN_SIGNBIT): m_op1 = gimple_call_arg (call, 0); - m_float = &op_cfn_signbit; - m_valid = true; + m_operator = &op_cfn_signbit; break; CASE_CFN_COPYSIGN_ALL: m_op1 = gimple_call_arg (call, 0); m_op2 = gimple_call_arg (call, 1); - m_float = &op_cfn_copysign; - m_valid = true; + m_operator = &op_cfn_copysign; break; CASE_CFN_SQRT: CASE_CFN_SQRT_FN: m_op1 = gimple_call_arg (call, 0); - m_float = &op_cfn_sqrt; - m_valid = true; + m_operator = &op_cfn_sqrt; break; CASE_CFN_SIN: CASE_CFN_SIN_FN: m_op1 = gimple_call_arg (call, 0); - m_float = &op_cfn_sin; - m_valid = true; + m_operator = &op_cfn_sin; break; CASE_CFN_COS: CASE_CFN_COS_FN: m_op1 = gimple_call_arg (call, 0); - m_float = &op_cfn_cos; - m_valid = true; + m_operator = &op_cfn_cos; break; case CFN_BUILT_IN_TOUPPER: @@ -1300,68 +1292,57 @@ gimple_range_op_handler::maybe_builtin_call () // Only proceed If the argument is compatible with the LHS. m_op1 = gimple_call_arg (call, 0); if (range_compatible_p (type, TREE_TYPE (m_op1))) - { - m_valid = true; - m_int = (func == CFN_BUILT_IN_TOLOWER) ? &op_cfn_tolower - : &op_cfn_toupper; - } + m_operator = (func == CFN_BUILT_IN_TOLOWER) ? &op_cfn_tolower + : &op_cfn_toupper; break; CASE_CFN_FFS: m_op1 = gimple_call_arg (call, 0); - m_int = &op_cfn_ffs; - m_valid = true; + m_operator = &op_cfn_ffs; break; CASE_CFN_POPCOUNT: m_op1 = gimple_call_arg (call, 0); - m_int = &op_cfn_popcount; - m_valid = true; + m_operator = &op_cfn_popcount; break; CASE_CFN_CLZ: m_op1 = gimple_call_arg (call, 0); - m_valid = true; if (gimple_call_internal_p (call)) - m_int = &op_cfn_clz_internal; + m_operator = &op_cfn_clz_internal; else - m_int = &op_cfn_clz; + m_operator = &op_cfn_clz; break; CASE_CFN_CTZ: m_op1 = gimple_call_arg (call, 0); - m_valid = true; if (gimple_call_internal_p (call)) - m_int = &op_cfn_ctz_internal; + m_operator = &op_cfn_ctz_internal; else - m_int = &op_cfn_ctz; + m_operator = &op_cfn_ctz; break; CASE_CFN_CLRSB: m_op1 = gimple_call_arg (call, 0); - m_valid = true; - m_int = &op_cfn_clrsb; + m_operator = &op_cfn_clrsb; break; case CFN_UBSAN_CHECK_ADD: m_op1 = gimple_call_arg (call, 0); m_op2 = gimple_call_arg (call, 1); - m_valid = true; - m_int = &op_cfn_ubsan_add; + m_operator = &op_cfn_ubsan_add; break; case CFN_UBSAN_CHECK_SUB: m_op1 = gimple_call_arg (call, 0); m_op2 = gimple_call_arg (call, 1); - m_valid = true; - m_int = &op_cfn_ubsan_sub; + m_operator = &op_cfn_ubsan_sub; break; case CFN_UBSAN_CHECK_MUL: m_op1 = gimple_call_arg (call, 0); m_op2 = gimple_call_arg (call, 1); - m_valid = true; - m_int = &op_cfn_ubsan_mul; + m_operator = &op_cfn_ubsan_mul; break; case CFN_BUILT_IN_STRLEN: @@ -1371,8 +1352,7 @@ gimple_range_op_handler::maybe_builtin_call () == TYPE_PRECISION (TREE_TYPE (lhs)))) { m_op1 = gimple_call_arg (call, 0); - m_valid = true; - m_int = &op_cfn_strlen; + m_operator = &op_cfn_strlen; } break; } @@ -1384,21 +1364,18 @@ gimple_range_op_handler::maybe_builtin_call () // This call will ensure all the asserts are triggered. oacc_get_ifn_dim_arg (call); m_op1 = gimple_call_arg (call, 0); - m_valid = true; - m_int = &op_cfn_goacc_dim_size; + m_operator = &op_cfn_goacc_dim_size; break; case CFN_GOACC_DIM_POS: // This call will ensure all the asserts are triggered. oacc_get_ifn_dim_arg (call); m_op1 = gimple_call_arg (call, 0); - m_valid = true; - m_int = &op_cfn_goacc_dim_pos; + m_operator = &op_cfn_goacc_dim_pos; break; CASE_CFN_PARITY: - m_valid = true; - m_int = &op_cfn_parity; + m_operator = &op_cfn_parity; break; default: @@ -1406,9 +1383,8 @@ gimple_range_op_handler::maybe_builtin_call () unsigned arg; if (gimple_call_fnspec (call).returns_arg (&arg) && arg == 0) { - m_valid = true; m_op1 = gimple_call_arg (call, 0); - m_int = &op_cfn_pass_through_arg1; + m_operator = &op_cfn_pass_through_arg1; } break; } diff --git a/gcc/gimple-range-op.h b/gcc/gimple-range-op.h index 1bf63c5ce6f..e7bb0095440 100644 --- a/gcc/gimple-range-op.h +++ b/gcc/gimple-range-op.h @@ -32,8 +32,8 @@ public: gimple_range_op_handler (gimple *s); inline gimple *stmt () const { return m_stmt; } inline tree lhs () const { return gimple_get_lhs (m_stmt); } - tree operand1 () const { gcc_checking_assert (m_valid); return m_op1; } - tree operand2 () const { gcc_checking_assert (m_valid); return m_op2; } + tree operand1 () const { gcc_checking_assert (m_operator); return m_op1; } + tree operand2 () const { gcc_checking_assert (m_operator); return m_op2; } bool calc_op1 (vrange &r, const vrange &lhs_range); bool calc_op1 (vrange &r, const vrange &lhs_range, const vrange &op2_range, relation_trio = TRIO_VARYING); diff --git a/gcc/range-op.cc b/gcc/range-op.cc index 203c30f6e94..b24832922f0 100644 --- a/gcc/range-op.cc +++ b/gcc/range-op.cc @@ -48,6 +48,260 @@ along with GCC; see the file COPYING3. If not see #include "range-op.h" #include "tree-ssa-ccp.h" +// Instantiate a range op table for integral operations. + +class integral_table : public range_op_table +{ +public: + integral_table (); +} integral_tree_table; + +// Instantiate a range op table for pointer operations. + +class pointer_table : public range_op_table +{ +public: + pointer_table (); +} pointer_tree_table; + + +// The tables are hidden and accessed via a simple extern function. + +range_operator * +get_op_handler (enum tree_code code, tree type) +{ + // First check if there is a pointer specialization. + if (POINTER_TYPE_P (type)) + return pointer_tree_table[code]; + if (INTEGRAL_TYPE_P (type)) + return integral_tree_table[code]; + if (frange::supports_p (type)) + return (*floating_tree_table)[code]; + return NULL; +} + +range_op_handler::range_op_handler () +{ + m_operator = NULL; +} + +void +range_op_handler::set_op_handler (tree_code code, tree type) +{ + m_operator = get_op_handler (code, type); +} + +range_op_handler::range_op_handler (tree_code code, tree type) +{ + set_op_handler (code, type); +} + +// Create a dispatch pattern for value range discriminators LHS, OP1, and OP2. +// This is used to produce a unique value for each dispatch pattern. The +// denser the values, the more efficient dispatch can be created via +// jump tables. +// Ensure the shifts are appropriate for the number of range types supported. +// Current only INT and FLOAT are supported, so a shift of 1 is sufficient +// producing a value of 0-7 value for dispatching. + +constexpr unsigned +dispatch_trio (unsigned lhs, unsigned op1, unsigned op2) +{ + return ((lhs << 2) + (op1 << 1) + (op2)); +} + +// These are the supported dispatch patterns. These map to the parameter list +// of the routines in range_operator. Note the last 3 characters are +// shorthand for the LHS, OP1, and OP2 range discriminator class. + +const unsigned RO_III = dispatch_trio (VR_IRANGE, VR_IRANGE, VR_IRANGE); +const unsigned RO_IFI = dispatch_trio (VR_IRANGE, VR_FRANGE, VR_IRANGE); +const unsigned RO_IFF = dispatch_trio (VR_IRANGE, VR_FRANGE, VR_FRANGE); +const unsigned RO_FFF = dispatch_trio (VR_FRANGE, VR_FRANGE, VR_FRANGE); +const unsigned RO_FIF = dispatch_trio (VR_FRANGE, VR_IRANGE, VR_FRANGE); +const unsigned RO_FII = dispatch_trio (VR_FRANGE, VR_IRANGE, VR_IRANGE); + +// Return a dispatch value for parameter types LHS, OP1 and OP2. + +unsigned +range_op_handler::dispatch_kind (const vrange &lhs, const vrange &op1, + const vrange& op2) const +{ + return dispatch_trio (lhs.m_discriminator, op1.m_discriminator, + op2.m_discriminator); +} + +// Dispatch a call to fold_range based on the types of R, LH and RH. + +bool +range_op_handler::fold_range (vrange &r, tree type, + const vrange &lh, + const vrange &rh, + relation_trio rel) const +{ + gcc_checking_assert (m_operator); + switch (dispatch_kind (r, lh, rh)) + { + case RO_III: + return m_operator->fold_range (as_a (r), type, + as_a (lh), + as_a (rh), rel); + case RO_IFI: + return m_operator->fold_range (as_a (r), type, + as_a (lh), + as_a (rh), rel); + case RO_IFF: + return m_operator->fold_range (as_a (r), type, + as_a (lh), + as_a (rh), rel); + case RO_FFF: + return m_operator->fold_range (as_a (r), type, + as_a (lh), + as_a (rh), rel); + default: + return false; + } +} + +// Dispatch a call to op1_range based on the types of R, LHS and OP2. + +bool +range_op_handler::op1_range (vrange &r, tree type, + const vrange &lhs, + const vrange &op2, + relation_trio rel) const +{ + gcc_checking_assert (m_operator); + + if (lhs.undefined_p ()) + return false; + switch (dispatch_kind (r, lhs, op2)) + { + case RO_III: + return m_operator->op1_range (as_a (r), type, + as_a (lhs), + as_a (op2), rel); + case RO_FIF: + return m_operator->op1_range (as_a (r), type, + as_a (lhs), + as_a (op2), rel); + case RO_FFF: + return m_operator->op1_range (as_a (r), type, + as_a (lhs), + as_a (op2), rel); + default: + return false; + } +} + +// Dispatch a call to op2_range based on the types of R, LHS and OP1. + +bool +range_op_handler::op2_range (vrange &r, tree type, + const vrange &lhs, + const vrange &op1, + relation_trio rel) const +{ + gcc_checking_assert (m_operator); + if (lhs.undefined_p ()) + return false; + + switch (dispatch_kind (r, lhs, op1)) + { + case RO_III: + return m_operator->op2_range (as_a (r), type, + as_a (lhs), + as_a (op1), rel); + case RO_FIF: + return m_operator->op2_range (as_a (r), type, + as_a (lhs), + as_a (op1), rel); + case RO_FFF: + return m_operator->op2_range (as_a (r), type, + as_a (lhs), + as_a (op1), rel); + default: + return false; + } +} + +// Dispatch a call to lhs_op1_relation based on the types of LHS, OP1 and OP2. + +relation_kind +range_op_handler::lhs_op1_relation (const vrange &lhs, + const vrange &op1, + const vrange &op2, + relation_kind rel) const +{ + gcc_checking_assert (m_operator); + + switch (dispatch_kind (lhs, op1, op2)) + { + case RO_III: + return m_operator->lhs_op1_relation (as_a (lhs), + as_a (op1), + as_a (op2), rel); + case RO_IFF: + return m_operator->lhs_op1_relation (as_a (lhs), + as_a (op1), + as_a (op2), rel); + case RO_FFF: + return m_operator->lhs_op1_relation (as_a (lhs), + as_a (op1), + as_a (op2), rel); + default: + return VREL_VARYING; + } +} + +// Dispatch a call to lhs_op2_relation based on the types of LHS, OP1 and OP2. + +relation_kind +range_op_handler::lhs_op2_relation (const vrange &lhs, + const vrange &op1, + const vrange &op2, + relation_kind rel) const +{ + gcc_checking_assert (m_operator); + switch (dispatch_kind (lhs, op1, op2)) + { + case RO_III: + return m_operator->lhs_op2_relation (as_a (lhs), + as_a (op1), + as_a (op2), rel); + case RO_IFF: + return m_operator->lhs_op2_relation (as_a (lhs), + as_a (op1), + as_a (op2), rel); + case RO_FFF: + return m_operator->lhs_op2_relation (as_a (lhs), + as_a (op1), + as_a (op2), rel); + default: + return VREL_VARYING; + } +} + +// Dispatch a call to op1_op2_relation based on the type of LHS. + +relation_kind +range_op_handler::op1_op2_relation (const vrange &lhs) const +{ + gcc_checking_assert (m_operator); + switch (dispatch_kind (lhs, lhs, lhs)) + { + case RO_III: + return m_operator->op1_op2_relation (as_a (lhs)); + + case RO_FFF: + return m_operator->op1_op2_relation (as_a (lhs)); + + default: + return VREL_VARYING; + } +} + + // Convert irange bitmasks into a VALUE MASK pair suitable for calling CCP. static void @@ -4612,33 +4866,6 @@ pointer_or_operator::wi_fold (irange &r, tree type, r.set_varying (type); } -// Return a pointer to the range_operator instance, if there is one -// associated with tree_code CODE. - -range_operator * -range_op_table::operator[] (enum tree_code code) -{ - gcc_checking_assert (code > 0 && code < MAX_TREE_CODES); - return m_range_tree[code]; -} - -// Add OP to the handler table for CODE. - -void -range_op_table::set (enum tree_code code, range_operator &op) -{ - gcc_checking_assert (m_range_tree[code] == NULL); - m_range_tree[code] = &op; -} - -// Instantiate a range op table for integral operations. - -class integral_table : public range_op_table -{ -public: - integral_table (); -} integral_tree_table; - integral_table::integral_table () { set (EQ_EXPR, op_equal); @@ -4682,14 +4909,6 @@ integral_table::integral_table () set (ADDR_EXPR, op_addr); } -// Instantiate a range op table for pointer operations. - -class pointer_table : public range_op_table -{ -public: - pointer_table (); -} pointer_tree_table; - pointer_table::pointer_table () { set (BIT_AND_EXPR, op_pointer_and); @@ -4714,191 +4933,6 @@ pointer_table::pointer_table () set (BIT_XOR_EXPR, op_bitwise_xor); } -// The tables are hidden and accessed via a simple extern function. - -static inline range_operator * -get_handler (enum tree_code code, tree type) -{ - // First check if there is a pointer specialization. - if (POINTER_TYPE_P (type)) - return pointer_tree_table[code]; - if (INTEGRAL_TYPE_P (type)) - return integral_tree_table[code]; - return NULL; -} - -// Return the floating point operator for CODE or NULL if none available. - -static inline range_operator * -get_float_handler (enum tree_code code, tree) -{ - return (*floating_tree_table)[code]; -} - -void -range_op_handler::set_op_handler (tree_code code, tree type) -{ - if (irange::supports_p (type)) - { - m_float = NULL; - m_int = get_handler (code, type); - m_valid = m_int != NULL; - } - else if (frange::supports_p (type)) - { - m_int = NULL; - m_float = get_float_handler (code, type); - m_valid = m_float != NULL; - } - else - { - m_int = NULL; - m_float = NULL; - m_valid = false; - } -} - -range_op_handler::range_op_handler () -{ - m_int = NULL; - m_float = NULL; - m_valid = false; -} - -range_op_handler::range_op_handler (tree_code code, tree type) -{ - set_op_handler (code, type); -} - - -bool -range_op_handler::fold_range (vrange &r, tree type, - const vrange &lh, - const vrange &rh, - relation_trio rel) const -{ - gcc_checking_assert (m_valid); - if (m_int) - return m_int->fold_range (as_a (r), type, - as_a (lh), - as_a (rh), rel); - - if (is_a (r)) - { - if (is_a (rh)) - return m_float->fold_range (as_a (r), type, - as_a (lh), - as_a (rh), rel); - else - return m_float->fold_range (as_a (r), type, - as_a (lh), - as_a (rh), rel); - } - return m_float->fold_range (as_a (r), type, - as_a (lh), - as_a (rh), rel); -} - -bool -range_op_handler::op1_range (vrange &r, tree type, - const vrange &lhs, - const vrange &op2, - relation_trio rel) const -{ - gcc_checking_assert (m_valid); - - if (lhs.undefined_p ()) - return false; - if (m_int) - return m_int->op1_range (as_a (r), type, - as_a (lhs), - as_a (op2), rel); - - if (is_a (lhs)) - return m_float->op1_range (as_a (r), type, - as_a (lhs), - as_a (op2), rel); - return m_float->op1_range (as_a (r), type, - as_a (lhs), - as_a (op2), rel); -} - -bool -range_op_handler::op2_range (vrange &r, tree type, - const vrange &lhs, - const vrange &op1, - relation_trio rel) const -{ - gcc_checking_assert (m_valid); - if (lhs.undefined_p ()) - return false; - if (m_int) - return m_int->op2_range (as_a (r), type, - as_a (lhs), - as_a (op1), rel); - - if (is_a (lhs)) - return m_float->op2_range (as_a (r), type, - as_a (lhs), - as_a (op1), rel); - return m_float->op2_range (as_a (r), type, - as_a (lhs), - as_a (op1), rel); -} - -relation_kind -range_op_handler::lhs_op1_relation (const vrange &lhs, - const vrange &op1, - const vrange &op2, - relation_kind rel) const -{ - gcc_checking_assert (m_valid); - if (m_int) - return m_int->lhs_op1_relation (as_a (lhs), - as_a (op1), - as_a (op2), rel); - - if (is_a (lhs)) - return m_float->lhs_op1_relation (as_a (lhs), - as_a (op1), - as_a (op2), rel); - return m_float->lhs_op1_relation (as_a (lhs), - as_a (op1), - as_a (op2), rel); -} - -relation_kind -range_op_handler::lhs_op2_relation (const vrange &lhs, - const vrange &op1, - const vrange &op2, - relation_kind rel) const -{ - gcc_checking_assert (m_valid); - if (m_int) - return m_int->lhs_op2_relation (as_a (lhs), - as_a (op1), - as_a (op2), rel); - - if (is_a (lhs)) - return m_float->lhs_op2_relation (as_a (lhs), - as_a (op1), - as_a (op2), rel); - return m_float->lhs_op2_relation (as_a (lhs), - as_a (op1), - as_a (op2), rel); -} - -relation_kind -range_op_handler::op1_op2_relation (const vrange &lhs) const -{ - gcc_checking_assert (m_valid); - if (m_int) - return m_int->op1_op2_relation (as_a (lhs)); - if (is_a (lhs)) - return m_float->op1_op2_relation (as_a (lhs)); - return m_float->op1_op2_relation (as_a (lhs)); -} - // Cast the range in R to TYPE. bool diff --git a/gcc/range-op.h b/gcc/range-op.h index cad16f4cd20..7af58736c3f 100644 --- a/gcc/range-op.h +++ b/gcc/range-op.h @@ -186,7 +186,7 @@ class range_op_handler public: range_op_handler (); range_op_handler (enum tree_code code, tree type); - inline operator bool () const { return m_valid; } + inline operator bool () const { return m_operator != NULL; } bool fold_range (vrange &r, tree type, const vrange &lh, @@ -210,10 +210,10 @@ public: relation_kind = VREL_VARYING) const; relation_kind op1_op2_relation (const vrange &lhs) const; protected: + unsigned dispatch_kind (const vrange &lhs, const vrange &op1, + const vrange& op2) const; void set_op_handler (enum tree_code code, tree type); - bool m_valid; - range_operator *m_int; - range_operator *m_float; + range_operator *m_operator; }; extern bool range_cast (vrange &, tree type); @@ -296,6 +296,25 @@ private: }; +// Return a pointer to the range_operator instance, if there is one +// associated with tree_code CODE. + +inline range_operator * +range_op_table::operator[] (enum tree_code code) +{ + gcc_checking_assert (code > 0 && code < MAX_TREE_CODES); + return m_range_tree[code]; +} + +// Add OP to the handler table for CODE. + +inline void +range_op_table::set (enum tree_code code, range_operator &op) +{ + gcc_checking_assert (m_range_tree[code] == NULL); + m_range_tree[code] = &op; +} + // This holds the range op table for floating point operations. extern range_op_table *floating_tree_table; diff --git a/gcc/value-range.h b/gcc/value-range.h index 2b4ebabe7c8..9103e9c41c7 100644 --- a/gcc/value-range.h +++ b/gcc/value-range.h @@ -77,6 +77,7 @@ class GTY((user)) vrange template friend bool is_a (vrange &); friend class Value_Range; friend void streamer_write_vrange (struct output_block *, const vrange &); + friend class range_op_handler; public: virtual void accept (const class vrange_visitor &v) const = 0; virtual void set (tree, tree, value_range_kind = VR_RANGE); -- 2.40.1