From patchwork Sun Mar 9 20:35:43 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Zoran Jovanovic X-Patchwork-Id: 328396 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from sourceware.org (server1.sourceware.org [209.132.180.131]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 4C0BA2C00D1 for ; Mon, 10 Mar 2014 07:36:04 +1100 (EST) DomainKey-Signature: a=rsa-sha1; c=nofws; d=gcc.gnu.org; h=list-id :list-unsubscribe:list-archive:list-post:list-help:sender:from :to:cc:subject:date:message-id:references:in-reply-to :content-type:content-transfer-encoding:mime-version; q=dns; s= default; b=TewQ5yRVSORNVr9mntb+jyaFdc8lRe8CQDDygZtRQpQbLt+iP4wLt xGYlQQ6ybMDkQllGu7X+Ck6RXtZoZRVnRIIKJiqGabu1OZc9fRaBYP5f5OObqGZW eXZHkFv3BMECuCPTZ28JRnM/IJtSzWSgy3qiYu/2Y9vWP3O6waSWXo= DKIM-Signature: v=1; a=rsa-sha1; c=relaxed; d=gcc.gnu.org; h=list-id :list-unsubscribe:list-archive:list-post:list-help:sender:from :to:cc:subject:date:message-id:references:in-reply-to :content-type:content-transfer-encoding:mime-version; s=default; bh=RYzqnQz+3s6k8i+zfw6Xi9iBcHY=; b=DMFdTMBIt+0Y+j+voXi+CdnDHnuQ glFeoL8nBDpE1J4IeWuB7spmwRw1kpQBGBBjndVCx+R2WcT2JUgE7p9kgpuNMBHL QzppNcXe5Ffkbkl7abgxEaGmZeN0PgW6l/Dl1DheZGyHv2RdRU2FVnp/Fy2ubebI aRI+6CkRN3UPMbs= Received: (qmail 26309 invoked by alias); 9 Mar 2014 20:35:55 -0000 Mailing-List: contact gcc-patches-help@gcc.gnu.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Archive: List-Post: List-Help: Sender: gcc-patches-owner@gcc.gnu.org Delivered-To: mailing list gcc-patches@gcc.gnu.org Received: (qmail 26297 invoked by uid 89); 9 Mar 2014 20:35:55 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=1.6 required=5.0 tests=AWL, BAYES_00, T_RP_MATCHES_RCVD, UNSUBSCRIBE_BODY autolearn=no version=3.3.2 X-HELO: mailapp01.imgtec.com Received: from mailapp01.imgtec.com (HELO mailapp01.imgtec.com) (195.89.28.115) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with ESMTP; Sun, 09 Mar 2014 20:35:51 +0000 Received: from KLMAIL01.kl.imgtec.org (unknown [192.168.5.35]) by Websense Email Security Gateway with ESMTPS id C3DFBA2055450; Sun, 9 Mar 2014 20:35:43 +0000 (GMT) Received: from KLMAIL02.kl.imgtec.org (192.168.5.97) by KLMAIL01.kl.imgtec.org (192.168.5.35) with Microsoft SMTP Server (TLS) id 14.3.174.1; Sun, 9 Mar 2014 20:35:47 +0000 Received: from BADAG03.ba.imgtec.org (192.168.146.115) by klmail02.kl.imgtec.org (192.168.5.97) with Microsoft SMTP Server (TLS) id 14.3.174.1; Sun, 9 Mar 2014 20:35:46 +0000 Received: from BADAG02.ba.imgtec.org ([fe80::612d:e977:c603:32d6]) by badag03.ba.imgtec.org ([::1]) with mapi id 14.03.0123.003; Sun, 9 Mar 2014 13:35:44 -0700 From: Zoran Jovanovic To: Richard Biener CC: "gcc-patches@gcc.gnu.org" Subject: RE: [PATCH] Add a new option "-ftree-bitfield-merge" (patch / doc inside) Date: Sun, 9 Mar 2014 20:35:43 +0000 Message-ID: <386B40EC5E8DBF459FD11A754D868AD96DA5A28A@BADAG02.ba.imgtec.org> References: <386B40EC5E8DBF459FD11A754D868AD922E31112@BADAG02.ba.imgtec.org> <386B40EC5E8DBF459FD11A754D868AD922E333D4@BADAG02.ba.imgtec.org> <386B40EC5E8DBF459FD11A754D868AD922E34DA7@BADAG02.ba.imgtec.org> , In-Reply-To: MIME-Version: 1.0 Hello, This is new patch version. Approach suggested by Richard Biener with lowering bit-field accesses instead of modifying gimple trees is implemented. Although, algorithm still detects adjacent bit field accesses, which copy values from one to another bit-field of same type. If those accesses are detected field size used during lowering is equal to sum of sizes of all adjacent fields that can be merged. Idea is to let dse and cse to remove unnecessary instructions afterwards. I wanted to preserve this behavior because it was one of original goals of this work. Example: Original code: : _2 = pr1.f1; pr2.f1 = _2; _4 = pr1.f2; pr2.f2 = _4; _6 = pr1.f3; pr2.f3 = _6; return; Optimized code: : _8 = pr1.D.1364; _9 = BIT_FIELD_REF <_8, 13, 0>; _10 = pr2.D.1364; _11 = _10 & 4294959104; _12 = (unsigned int) _9; _13 = _11 | _12; pr2.D.1364 = _13; _14 = pr1.D.1364; _15 = BIT_FIELD_REF <_14, 13, 0>; _16 = pr2.D.1364; _17 = _16 & 4294959104; _18 = (unsigned int) _15; _19 = _17 | _18; pr2.D.1364 = _19; _20 = pr1.D.1364; _21 = BIT_FIELD_REF <_20, 13, 0>; _22 = pr2.D.1364; _23 = _22 & 4294959104; _24 = (unsigned int) _21; _25 = _23 | _24; pr2.D.1364 = _25; return; Algorithm works on basic block level and consists of following 3 major steps: 1. Go through basic block statements list. If there are statement pairs that implement copy of bit field content from one memory location to another record statements pointers and other necessary data in corresponding data structure. 2. Identify records that represent adjacent bit field accesses and mark them as merged. 3. Lower bit-field accesses by using new field size for those that can be merged. New command line option "-fmerge-bitfields" is introduced. Tested - passed gcc regression tests. Changelog - gcc/ChangeLog: 2014-03-09 Zoran Jovanovic (zoran.jovanovic@imgtec.com) * common.opt (fmerge-bitfields): New option. * doc/invoke.texi: Added reference to "-fmerge-bitfields". * tree-sra.c (lower_bitfields): New function. Entry for (-fmerge-bitfields). (bfaccess::hash): New function. (bfaccess::equal): New function. (bfaccess::remove): New function. (bitfield_access_p): New function. (lower_bitfield_read): New function. (lower_bitfield_write): New function. (bitfield_stmt_access_pair_htab_hash): New function. (bitfield_stmt_access_pair_htab_eq): New function. (create_and_insert_access): New function. (get_bit_offset): New function. (get_merged_bit_field_size): New function. (add_stmt_access_pair): New function. (cmp_access): New function. * dwarf2out.c (simple_type_size_in_bits): moved to tree.c. (field_byte_offset): declaration moved to tree.h, static removed. * testsuite/gcc.dg/tree-ssa/bitfldmrg1.c: New test. * testsuite/gcc.dg/tree-ssa/bitfldmrg2.c: New test. * tree-ssa-sccvn.c (expressions_equal_p): moved to tree.c. * tree-ssa-sccvn.h (expressions_equal_p): declaration moved to tree.h. * tree.c (expressions_equal_p): moved from tree-ssa-sccvn.c. (simple_type_size_in_bits): moved from dwarf2out.c. * tree.h (expressions_equal_p): declaration added. (field_byte_offset): declaration added. Patch - Regards, Zoran Jovanovic diff --git a/gcc/common.opt b/gcc/common.opt index 661516d..3331d03 100644 --- a/gcc/common.opt +++ b/gcc/common.opt @@ -2193,6 +2193,10 @@ ftree-sra Common Report Var(flag_tree_sra) Optimization Perform scalar replacement of aggregates +fmerge-bitfields +Common Report Var(flag_tree_bitfield_merge) Init(0) Optimization +Merge loads and stores of consecutive bitfields + ftree-ter Common Report Var(flag_tree_ter) Optimization Replace temporary expressions in the SSA->normal pass diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi index 24bd76e..54bae56 100644 --- a/gcc/doc/invoke.texi +++ b/gcc/doc/invoke.texi @@ -411,7 +411,7 @@ Objective-C and Objective-C++ Dialects}. -fsplit-ivs-in-unroller -fsplit-wide-types -fstack-protector @gol -fstack-protector-all -fstack-protector-strong -fstrict-aliasing @gol -fstrict-overflow -fthread-jumps -ftracer -ftree-bit-ccp @gol --ftree-builtin-call-dce -ftree-ccp -ftree-ch @gol +-fmerge-bitfields -ftree-builtin-call-dce -ftree-ccp -ftree-ch @gol -ftree-coalesce-inline-vars -ftree-coalesce-vars -ftree-copy-prop @gol -ftree-copyrename -ftree-dce -ftree-dominator-opts -ftree-dse @gol -ftree-forwprop -ftree-fre -ftree-loop-if-convert @gol @@ -7807,6 +7807,11 @@ pointer alignment information. This pass only operates on local scalar variables and is enabled by default at @option{-O} and higher. It requires that @option{-ftree-ccp} is enabled. +@item -fbitfield-merge +@opindex fmerge-bitfields +Combines several adjacent bit-field accesses that copy values +from one memory location to another into one single bit-field access. + @item -ftree-ccp @opindex ftree-ccp Perform sparse conditional constant propagation (CCP) on trees. This diff --git a/gcc/dwarf2out.c b/gcc/dwarf2out.c index 2b584a5..5150d40 100644 --- a/gcc/dwarf2out.c +++ b/gcc/dwarf2out.c @@ -3119,8 +3119,6 @@ static HOST_WIDE_INT ceiling (HOST_WIDE_INT, unsigned int); static tree field_type (const_tree); static unsigned int simple_type_align_in_bits (const_tree); static unsigned int simple_decl_align_in_bits (const_tree); -static unsigned HOST_WIDE_INT simple_type_size_in_bits (const_tree); -static HOST_WIDE_INT field_byte_offset (const_tree); static void add_AT_location_description (dw_die_ref, enum dwarf_attribute, dw_loc_list_ref); static void add_data_member_location_attribute (dw_die_ref, tree); @@ -10281,25 +10279,6 @@ is_base_type (tree type) return 0; } -/* Given a pointer to a tree node, assumed to be some kind of a ..._TYPE - node, return the size in bits for the type if it is a constant, or else - return the alignment for the type if the type's size is not constant, or - else return BITS_PER_WORD if the type actually turns out to be an - ERROR_MARK node. */ - -static inline unsigned HOST_WIDE_INT -simple_type_size_in_bits (const_tree type) -{ - if (TREE_CODE (type) == ERROR_MARK) - return BITS_PER_WORD; - else if (TYPE_SIZE (type) == NULL_TREE) - return 0; - else if (tree_fits_uhwi_p (TYPE_SIZE (type))) - return tree_to_uhwi (TYPE_SIZE (type)); - else - return TYPE_ALIGN (type); -} - /* Similarly, but return a double_int instead of UHWI. */ static inline double_int @@ -14667,7 +14646,7 @@ round_up_to_align (double_int t, unsigned int align) because the offset is actually variable. (We can't handle the latter case just yet). */ -static HOST_WIDE_INT +HOST_WIDE_INT field_byte_offset (const_tree decl) { double_int object_offset_in_bits; diff --git a/gcc/tree-sra.c b/gcc/tree-sra.c index 284d544..c6a19b2 100644 --- a/gcc/tree-sra.c +++ b/gcc/tree-sra.c @@ -3462,10 +3462,608 @@ perform_intra_sra (void) return ret; } +/* Bitfield access and hashtable support commoning same base and + representative. */ + +struct bfaccess +{ + bfaccess (tree r):ref (r), r_count (1), w_count (1), merged (false), + modified (false), is_barrier (false), next (0), head_access (0) + { + } + + tree ref; + unsigned r_count; /* Read counter. */ + unsigned w_count; /* Write counter. */ + + /* hash_table support. */ + typedef bfaccess value_type; + typedef bfaccess compare_type; + static inline hashval_t hash (const bfaccess *); + static inline int equal (const bfaccess *, const bfaccess *); + static inline void remove (bfaccess *); + + gimple load_stmt; /* Bit-field load statement. */ + gimple store_stmt; /* Bit-field store statement. */ + unsigned src_offset_words; /* Bit-field offset at src in words. */ + unsigned src_bit_offset; /* Bit-field offset inside source word. */ + unsigned src_bit_size; /* Size of bit-field in source word. */ + unsigned dst_offset_words; /* Bit-field offset at dst in words. */ + unsigned dst_bit_offset; /* Bit-field offset inside destination + word. */ + unsigned src_field_offset; /* Source field offset. */ + unsigned dst_bit_size; /* Size of bit-field in destination word. */ + tree src_addr; /* Address of source memory access. */ + tree dst_addr; /* Address of destination memory access. */ + bool merged; /* True if access is merged with another + one. */ + bool modified; /* True if bit-field size is modified. */ + bool is_barrier; /* True if access is barrier (call or mem + access). */ + struct bfaccess *next; /* Access with which this one is merged. */ + tree bitfield_representative; /* Bit field representative of original + declaration. */ + struct bfaccess *head_access; /* Head of access list where this one is + merged. */ +}; + +hashval_t bfaccess::hash (const bfaccess * a) +{ + return iterative_hash_hashval_t + (iterative_hash_expr (TREE_OPERAND (a->ref, 0), 0), + DECL_UID (DECL_BIT_FIELD_REPRESENTATIVE (TREE_OPERAND (a->ref, 1)))); +} + +int +bfaccess::equal (const bfaccess * a, const bfaccess * b) +{ + return ((DECL_BIT_FIELD_REPRESENTATIVE (TREE_OPERAND (a->ref, 1)) + == DECL_BIT_FIELD_REPRESENTATIVE (TREE_OPERAND (b->ref, 1))) + && operand_equal_p (TREE_OPERAND (a->ref, 0), + TREE_OPERAND (b->ref, 0), 0)); +} + +void +bfaccess::remove (bfaccess * a) +{ + delete a; +} + +/* Return whether REF is a bitfield access the bit offset of the bitfield + within the representative in *OFF if that is not NULL. */ + +static bool +bitfield_access_p (tree ref, unsigned HOST_WIDE_INT * off) +{ + if (TREE_CODE (ref) != COMPONENT_REF) + return false; + + tree field = TREE_OPERAND (ref, 1); + if (!DECL_BIT_FIELD_TYPE (field)) + return false; + + tree rep = DECL_BIT_FIELD_REPRESENTATIVE (field); + if (!rep) + return false; + + if (!off) + return true; + + if (tree_fits_uhwi_p (DECL_FIELD_OFFSET (field)) + && tree_fits_uhwi_p (DECL_FIELD_OFFSET (rep))) + *off = (tree_to_uhwi (DECL_FIELD_OFFSET (field)) + - tree_to_uhwi (DECL_FIELD_OFFSET (rep))) * BITS_PER_UNIT; + else + *off = 0; + *off += (tree_to_uhwi (DECL_FIELD_BIT_OFFSET (field)) + - tree_to_uhwi (DECL_FIELD_BIT_OFFSET (rep))); + + return true; +} + + +/* Lower the bitfield read at *GSI, the offset of the bitfield + relative to the bitfield representative is OFF bits. */ +#include "gimple-pretty-print.h" +static void +lower_bitfield_read (gimple_stmt_iterator * gsi, unsigned HOST_WIDE_INT off, + tree size, tree type) +{ + //debug_tree (size); + gimple stmt = gsi_stmt (*gsi); + tree ref = gimple_assign_rhs1 (stmt); + tree field = TREE_OPERAND (ref, 1); + tree rep = DECL_BIT_FIELD_REPRESENTATIVE (field); + + tree loadres = make_ssa_name (TREE_TYPE (rep), NULL); + gimple load = gimple_build_assign (loadres, + build3 (COMPONENT_REF, TREE_TYPE (rep), + TREE_OPERAND (ref, 0), rep, + NULL_TREE)); + gimple_set_vuse (load, gimple_vuse (stmt)); + gsi_insert_before (gsi, load, GSI_SAME_STMT); + if (!type) + type = TREE_TYPE (ref); + gimple_assign_set_rhs1 (stmt, + build3 (BIT_FIELD_REF, type, + loadres, size, bitsize_int (off))); + update_stmt (stmt); +} + +/* Lower the bitfield write at *GSI, the offset of the bitfield + relative to the bitfield representative is OFF bits. */ + +static void +lower_bitfield_write (gimple_stmt_iterator * gsi, unsigned HOST_WIDE_INT off, + tree size) +{ + gimple stmt = gsi_stmt (*gsi); + tree ref = gimple_assign_lhs (stmt); + tree field = TREE_OPERAND (ref, 1); + tree rep = DECL_BIT_FIELD_REPRESENTATIVE (field); + + tree loadres = make_ssa_name (TREE_TYPE (rep), NULL); + gimple load = gimple_build_assign (loadres, + build3 (COMPONENT_REF, TREE_TYPE (rep), + unshare_expr + (TREE_OPERAND (ref, 0)), + rep, + NULL_TREE)); + gimple_set_vuse (load, gimple_vuse (stmt)); + gsi_insert_before (gsi, load, GSI_SAME_STMT); + /* FIXME: BIT_FIELD_EXPR. */ + /* Mask out bits. */ + tree masked = make_ssa_name (TREE_TYPE (rep), NULL); + tree mask = double_int_to_tree (TREE_TYPE (rep), + ~double_int::mask + (TREE_INT_CST_LOW (size)).lshift (off)); + gimple tems = gimple_build_assign_with_ops (BIT_AND_EXPR, + masked, loadres, mask); + gsi_insert_before (gsi, tems, GSI_SAME_STMT); + /* Zero-extend the value to representative size. */ + tree tem2; + if (!TYPE_UNSIGNED (TREE_TYPE (field))) + { + tem2 = make_ssa_name (unsigned_type_for (TREE_TYPE (field)), NULL); + tems = gimple_build_assign_with_ops (NOP_EXPR, tem2, + gimple_assign_rhs1 (stmt), + NULL_TREE); + gsi_insert_before (gsi, tems, GSI_SAME_STMT); + } + else + tem2 = gimple_assign_rhs1 (stmt); + tree tem = make_ssa_name (TREE_TYPE (rep), NULL); + tems = gimple_build_assign_with_ops (NOP_EXPR, tem, tem2, NULL_TREE); + gsi_insert_before (gsi, tems, GSI_SAME_STMT); + /* Shift the value into place. */ + if (off != 0) + { + tem2 = make_ssa_name (TREE_TYPE (rep), NULL); + tems = gimple_build_assign_with_ops (LSHIFT_EXPR, tem2, tem, + size_int (off)); + gsi_insert_before (gsi, tems, GSI_SAME_STMT); + } + else + tem2 = tem; + /* Merge masked loaded value and value. */ + tree modres = make_ssa_name (TREE_TYPE (rep), NULL); + gimple mod = gimple_build_assign_with_ops (BIT_IOR_EXPR, modres, + masked, tem2); + gsi_insert_before (gsi, mod, GSI_SAME_STMT); + /* Finally adjust the store. */ + gimple_assign_set_rhs1 (stmt, modres); + gimple_assign_set_lhs (stmt, + build3 (COMPONENT_REF, TREE_TYPE (rep), + TREE_OPERAND (ref, 0), rep, NULL_TREE)); + update_stmt (stmt); +} + +/* Connecting register with bit-field access sequence that defines value in + that register. */ +struct bitfield_stmt_access_pair +{ + gimple stmt; + bfaccess *access; + bitfield_stmt_access_pair (gimple s, bfaccess *a):stmt (s), access (a) {}; + /* hash_table support. */ + typedef bitfield_stmt_access_pair value_type; + typedef bitfield_stmt_access_pair compare_type; + static inline hashval_t hash (const bitfield_stmt_access_pair *); + static inline int equal (const bitfield_stmt_access_pair *, + const bitfield_stmt_access_pair *); + static inline void remove (bitfield_stmt_access_pair *); +}; + +hashval_t bitfield_stmt_access_pair::hash (const bitfield_stmt_access_pair *a) +{ + return hashval_t (gimple_uid (a->stmt)); +} + +int bitfield_stmt_access_pair::equal (const bitfield_stmt_access_pair *a, + const bitfield_stmt_access_pair *b) +{ + return a->stmt == b->stmt; +} + +void bitfield_stmt_access_pair::remove (bitfield_stmt_access_pair *a) +{ + delete a; +} + +/* Create new bit-field access structure and add it to given bitfield_accesses + htab. */ + +static struct bfaccess * +create_and_insert_access (vec < struct bfaccess *>*bitfield_accesses, + struct bfaccess *access) +{ + if (!access) + access = new bfaccess (NULL); + bitfield_accesses->safe_push (access); + return access; + +} + +static inline HOST_WIDE_INT +get_bit_offset (tree decl) +{ + tree type = DECL_BIT_FIELD_TYPE (decl); + HOST_WIDE_INT bitpos_int; + + /* Must be a field and a bit-field. */ + gcc_assert (type && TREE_CODE (decl) == FIELD_DECL); + /* Bit position and decl size should be integer constants that can be + represented in a signle HOST_WIDE_INT. */ + if (!tree_fits_uhwi_p (bit_position (decl)) + || !tree_fits_uhwi_p (DECL_SIZE (decl))) + return -1; + + bitpos_int = int_bit_position (decl); + return bitpos_int; +} + +static bool +add_stmt_access_pair (hash_table < bitfield_stmt_access_pair > &bf_stmnt_acc, + bfaccess *access, gimple stmt) +{ + bitfield_stmt_access_pair p(stmt, access); + bitfield_stmt_access_pair **slot = bf_stmnt_acc.find_slot (&p, INSERT); + if (!*slot) + { + *slot = new bitfield_stmt_access_pair (stmt, access); + return true; + } + return false; +} + +/* Compare two bit-field access records. */ + +static int +cmp_access (const void *p1, const void *p2) +{ + const struct bfaccess *a1 = *((const struct bfaccess **) p1); + const struct bfaccess *a2 = *((const struct bfaccess **) p2); + + if (DECL_UID (a1->bitfield_representative) - + DECL_UID (a2->bitfield_representative)) + return DECL_UID (a1->bitfield_representative) - + DECL_UID (a2->bitfield_representative); + + if (!expressions_equal_p (a1->src_addr, a2->src_addr)) + return a1 - a2; + if (!expressions_equal_p (a1->dst_addr, a2->dst_addr)) + return a1 - a2; + if (a1->src_offset_words - a2->src_offset_words) + return a1->src_offset_words - a2->src_offset_words; + return a1->src_bit_offset - a2->src_bit_offset; +} + +/* Returns size of combined bitfields. Size cannot be larger than size + of largest directly accessible memory unit. */ + +static int +get_merged_bit_field_size (bfaccess * access) +{ + bfaccess *tmp_access = access; + int size = 0; + + while (tmp_access) + { + size += tmp_access->src_bit_size; + tmp_access = tmp_access->next; + } + return size; +} + +/* Lower bitfield accesses to accesses of their + DECL_BIT_FIELD_REPRESENTATIVE. */ + +static void +lower_bitfields (bool all) +{ + basic_block bb; + + hash_table < bfaccess > bf; + bf.create (1); + hash_table < bitfield_stmt_access_pair > bf_stmnt_acc; + bf_stmnt_acc.create (1); + + vec < struct bfaccess *>bitfield_accesses; + struct bfaccess *access; + + FOR_EACH_BB_FN (bb, cfun) + { + bool any = false; + bf.empty (); + bf_stmnt_acc.empty (); + tree prev_representative = NULL_TREE; + bitfield_accesses.create (0); + + /* We do two passes, the first one identifying interesting + bitfield accesses and the second one actually lowering them. */ + if (!all) + for (gimple_stmt_iterator gsi = gsi_start_bb (bb); + !gsi_end_p (gsi); gsi_next (&gsi)) + { + gimple stmt = gsi_stmt (gsi); + + if (!gimple_assign_single_p (stmt) + || gimple_has_volatile_ops (stmt)) + continue; + + tree ref = gimple_assign_rhs1 (stmt); + if (bitfield_access_p (ref, NULL)) + { + bfaccess a (ref); + bfaccess **slot = bf.find_slot (&a, INSERT); + gimple use_stmt; + use_operand_p use; + tree op0 = TREE_OPERAND (ref, 0); + tree op1 = TREE_OPERAND (ref, 1); + + if (TREE_CODE (DECL_CONTEXT (op1)) == UNION_TYPE + || TREE_CODE (DECL_CONTEXT (op1)) == QUAL_UNION_TYPE) + continue; + + if (*slot) + (*slot)->r_count++; + else + *slot = new bfaccess (a); + + if ((*slot)->r_count > 1) + any = true; + + if (single_imm_use (gimple_assign_lhs (stmt), &use, &use_stmt) + && is_gimple_assign (use_stmt)) + { + tree uses_stmt_lhs = gimple_assign_lhs (use_stmt); + if (bitfield_access_p (uses_stmt_lhs, NULL)) + { + tree use_op0 = TREE_OPERAND (uses_stmt_lhs, 0); + tree use_op1 = TREE_OPERAND (uses_stmt_lhs, 1); + tree use_repr = DECL_BIT_FIELD_REPRESENTATIVE (use_op1); + + if (prev_representative + && (prev_representative != use_repr)) + { + /* If previous access has different + representative then barrier is needed + between it and new access. */ + access = create_and_insert_access + (&bitfield_accesses, NULL); + access->is_barrier = true; + } + prev_representative = use_repr; + /* Create new bit-field access structure. */ + access = create_and_insert_access + (&bitfield_accesses, NULL); + /* Collect access data - load instruction. */ + access->src_bit_size = tree_to_uhwi (DECL_SIZE (op1)); + access->src_bit_offset = get_bit_offset (op1); + access->src_offset_words = + field_byte_offset (op1) / UNITS_PER_WORD; + access->src_field_offset = + tree_to_uhwi (DECL_FIELD_OFFSET (op1)); + access->src_addr = op0; + access->load_stmt = gsi_stmt (gsi); + /* Collect access data - store instruction. */ + access->dst_bit_size = + tree_to_uhwi (DECL_SIZE (use_op1)); + access->dst_bit_offset = get_bit_offset (use_op1); + access->dst_offset_words = + field_byte_offset (use_op1) / UNITS_PER_WORD; + access->dst_addr = use_op0; + access->store_stmt = use_stmt; + add_stmt_access_pair (bf_stmnt_acc, access, stmt); + add_stmt_access_pair (bf_stmnt_acc, access, use_stmt); + access->bitfield_representative = use_repr; + } + } + } + + ref = gimple_assign_lhs (stmt); + if (bitfield_access_p (ref, NULL)) + { + bfaccess a (ref); + bfaccess **slot = bf.find_slot (&a, INSERT); + if (*slot) + (*slot)->w_count++; + else + *slot = new bfaccess (a); + if ((*slot)->w_count > 1) + any = true; + } + /* Insert barrier for merging if statement is function call or memory + access. */ + bitfield_stmt_access_pair asdata (stmt, NULL); + if (!bf_stmnt_acc.find (&asdata) + && ((gimple_code (stmt) == GIMPLE_CALL) + || (gimple_has_mem_ops (stmt)))) + { + /* Create new bit-field access structure. */ + access = create_and_insert_access (&bitfield_accesses, NULL); + /* Mark it as barrier. */ + access->is_barrier = true; + } + } + + if (!all && !any) + continue; + + /* If there are no at least two accesses go to the next basic block. */ + if (bitfield_accesses.length () <= 1) + { + bitfield_accesses.release (); + continue; + } + vec < struct bfaccess *>bitfield_accesses_merge = vNULL; + /* Find bit-field accesses that can be merged. */ + for (int ix = 0; bitfield_accesses.iterate (ix, &access); ix++) + { + struct bfaccess *head_access; + struct bfaccess *mrg_access; + struct bfaccess *prev_access; + + if (!bitfield_accesses_merge.exists ()) + bitfield_accesses_merge.create (0); + + if (!access->is_barrier) + bitfield_accesses_merge.safe_push (access); + + if (!access->is_barrier + && !(access == bitfield_accesses.last () + && !bitfield_accesses_merge.is_empty ())) + continue; + + bitfield_accesses_merge.qsort (cmp_access); + head_access = prev_access = NULL; + int iy; + for (iy = 0; bitfield_accesses_merge.iterate (iy, &mrg_access); iy++) + { + if (head_access + && expressions_equal_p (head_access->src_addr, + mrg_access->src_addr) + && expressions_equal_p (head_access->dst_addr, + mrg_access->dst_addr) + && prev_access->src_offset_words + == mrg_access->src_offset_words + && prev_access->dst_offset_words + == mrg_access->dst_offset_words + && prev_access->src_bit_offset + prev_access->src_bit_size + == mrg_access->src_bit_offset + && prev_access->dst_bit_offset + prev_access->dst_bit_size + == mrg_access->dst_bit_offset + && prev_access->bitfield_representative + == mrg_access->bitfield_representative) + { + /* Merge conditions are satisfied - merge accesses. */ + mrg_access->merged = true; + prev_access->next = mrg_access; + head_access->modified = true; + prev_access = mrg_access; + mrg_access->head_access = head_access; + } + else + head_access = prev_access = mrg_access; + } + bitfield_accesses_merge.release (); + bitfield_accesses_merge = vNULL; + } + + for (gimple_stmt_iterator gsi = gsi_start_bb (bb); + !gsi_end_p (gsi); gsi_next (&gsi)) + { + gimple stmt = gsi_stmt (gsi); + if (!gimple_assign_single_p (stmt) || gimple_has_volatile_ops (stmt)) + continue; + + tree ref; + unsigned HOST_WIDE_INT off; + tree size; + + /* Lower a bitfield read. */ + ref = gimple_assign_rhs1 (stmt); + if (bitfield_access_p (ref, &off)) + { + bfaccess a (ref); + bfaccess *aa = NULL; + bitfield_stmt_access_pair st_acc (stmt, NULL); + bitfield_stmt_access_pair *p_st_acc; + p_st_acc = bf_stmnt_acc.find (&st_acc); + if (p_st_acc) + aa = p_st_acc->access; + if (!aa) + aa = bf.find (&a); + + if (aa->merged || aa->modified) + size = + build_int_cst (unsigned_type_node, + get_merged_bit_field_size (aa-> + head_access ? aa-> + head_access : aa)); + else + size = DECL_SIZE (TREE_OPERAND (ref, 1)); + if (aa->merged) + off = aa->head_access->src_bit_offset; + + if (aa->merged || aa->modified) + { + tree tmp_ssa; + tree itype = make_node (INTEGER_TYPE); + TYPE_PRECISION (itype) = TREE_INT_CST_LOW (size); + fixup_unsigned_type (itype); + lower_bitfield_read (&gsi, off, size, itype); + tmp_ssa = + make_ssa_name (create_tmp_var (itype, NULL), NULL); + gimple_assign_set_lhs (aa->load_stmt, tmp_ssa); + update_stmt (aa->load_stmt); + gimple_assign_set_rhs1 (aa->store_stmt, tmp_ssa); + } + else if (all || (aa->r_count > 1)) + lower_bitfield_read (&gsi, off, size, NULL); + } + /* Lower a bitfield write to a read-modify-write cycle. */ + ref = gimple_assign_lhs (stmt); + if (bitfield_access_p (ref, &off)) + { + bfaccess a (ref); + bfaccess *aa = NULL; + bitfield_stmt_access_pair st_acc (stmt, NULL); + bitfield_stmt_access_pair *p_st_acc; + p_st_acc = bf_stmnt_acc.find (&st_acc); + if (p_st_acc) + aa = p_st_acc->access; + if (!aa) + aa = bf.find (&a); + + if (aa->merged || aa->modified) + size = + build_int_cst (unsigned_type_node, + get_merged_bit_field_size (aa-> + head_access ? aa-> + head_access : aa)); + else + size = DECL_SIZE (TREE_OPERAND (ref, 1)); + if (aa->merged) + off = aa->head_access->dst_bit_offset; + + if (all || (aa->w_count > 1) || aa->merged || aa->modified) + lower_bitfield_write (&gsi, off, size); + } + } + } + + bf.dispose (); + bf_stmnt_acc.dispose (); +} + /* Perform early intraprocedural SRA. */ static unsigned int early_intra_sra (void) { + + if (flag_tree_bitfield_merge) + lower_bitfields (false); sra_mode = SRA_MODE_EARLY_INTRA; return perform_intra_sra (); } diff --git a/gcc/tree-ssa-sccvn.c b/gcc/tree-ssa-sccvn.c index f7ec8b6..cde6ce6 100644 --- a/gcc/tree-ssa-sccvn.c +++ b/gcc/tree-ssa-sccvn.c @@ -4193,29 +4193,6 @@ get_next_value_id (void) return next_value_id++; } - -/* Compare two expressions E1 and E2 and return true if they are equal. */ - -bool -expressions_equal_p (tree e1, tree e2) -{ - /* The obvious case. */ - if (e1 == e2) - return true; - - /* If only one of them is null, they cannot be equal. */ - if (!e1 || !e2) - return false; - - /* Now perform the actual comparison. */ - if (TREE_CODE (e1) == TREE_CODE (e2) - && operand_equal_p (e1, e2, OEP_PURE_SAME)) - return true; - - return false; -} - - /* Return true if the nary operation NARY may trap. This is a copy of stmt_could_throw_1_p adjusted to the SCCVN IL. */ diff --git a/gcc/tree-ssa-sccvn.h b/gcc/tree-ssa-sccvn.h index f52783a..0aa5537 100644 --- a/gcc/tree-ssa-sccvn.h +++ b/gcc/tree-ssa-sccvn.h @@ -21,10 +21,6 @@ #ifndef TREE_SSA_SCCVN_H #define TREE_SSA_SCCVN_H -/* In tree-ssa-sccvn.c */ -bool expressions_equal_p (tree, tree); - - /* TOP of the VN lattice. */ extern tree VN_TOP; diff --git a/gcc/tree.c b/gcc/tree.c index d102d07..78355cc 100644 --- a/gcc/tree.c +++ b/gcc/tree.c @@ -12369,4 +12369,44 @@ get_base_address (tree t) return t; } +/* Compare two expressions E1 and E2 and return true if they are equal. */ + +bool +expressions_equal_p (tree e1, tree e2) +{ + /* The obvious case. */ + if (e1 == e2) + return true; + + /* If only one of them is null, they cannot be equal. */ + if (!e1 || !e2) + return false; + + /* Now perform the actual comparison. */ + if (TREE_CODE (e1) == TREE_CODE (e2) + && operand_equal_p (e1, e2, OEP_PURE_SAME)) + return true; + + return false; +} + +/* Given a pointer to a tree node, assumed to be some kind of a ..._TYPE + node, return the size in bits for the type if it is a constant, or else + return the alignment for the type if the type's size is not constant, or + else return BITS_PER_WORD if the type actually turns out to be an + ERROR_MARK node. */ + +unsigned HOST_WIDE_INT +simple_type_size_in_bits (const_tree type) +{ + if (TREE_CODE (type) == ERROR_MARK) + return BITS_PER_WORD; + else if (TYPE_SIZE (type) == NULL_TREE) + return 0; + else if (tree_fits_uhwi_p (TYPE_SIZE (type))) + return tree_to_uhwi (TYPE_SIZE (type)); + else + return TYPE_ALIGN (type); +} + #include "gt-tree.h" diff --git a/gcc/tree.h b/gcc/tree.h index 0dc8d0d..4a5b930 100644 --- a/gcc/tree.h +++ b/gcc/tree.h @@ -3984,6 +3984,7 @@ extern tree substitute_placeholder_in_expr (tree, tree); ((EXP) == 0 || TREE_CONSTANT (EXP) ? (EXP) \ : substitute_placeholder_in_expr (EXP, OBJ)) +extern unsigned HOST_WIDE_INT simple_type_size_in_bits (const_tree type); /* stabilize_reference (EXP) returns a reference equivalent to EXP but it can be used multiple times @@ -4100,6 +4101,11 @@ inlined_function_outer_scope_p (const_tree block) (TREE = function_args_iter_cond (&(ITER))) != NULL_TREE; \ function_args_iter_next (&(ITER))) + +/* In dwarf2out.c. */ +HOST_WIDE_INT +field_byte_offset (const_tree decl); + /* In tree.c */ extern unsigned crc32_string (unsigned, const char *); extern unsigned crc32_byte (unsigned, char); @@ -4244,6 +4250,7 @@ extern tree obj_type_ref_class (tree ref); extern bool types_same_for_odr (tree type1, tree type2); extern bool contains_bitfld_component_ref_p (const_tree); extern bool type_in_anonymous_namespace_p (tree); +extern bool expressions_equal_p (tree e1, tree e2); extern bool block_may_fallthru (const_tree); extern void using_eh_for_cleanups (void); extern bool using_eh_for_cleanups_p (void);