aboutsummaryrefslogtreecommitdiffstats
path: root/gcc-4.2.1-5666.3/gcc/see.c
diff options
context:
space:
mode:
Diffstat (limited to 'gcc-4.2.1-5666.3/gcc/see.c')
-rw-r--r--gcc-4.2.1-5666.3/gcc/see.c3782
1 files changed, 3782 insertions, 0 deletions
diff --git a/gcc-4.2.1-5666.3/gcc/see.c b/gcc-4.2.1-5666.3/gcc/see.c
new file mode 100644
index 000000000..d20cdf4be
--- /dev/null
+++ b/gcc-4.2.1-5666.3/gcc/see.c
@@ -0,0 +1,3782 @@
+/* Sign extension elimination optimization for GNU compiler.
+ Copyright (C) 2005 Free Software Foundation, Inc.
+ Contributed by Leehod Baruch <leehod@il.ibm.com>
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+-Software Foundation; either version 2, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING. If not, write to the Free
+Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+02111-1307, USA.
+
+Problem description:
+--------------------
+In order to support 32bit computations on a 64bit machine, sign
+extension instructions are generated to ensure the correctness of
+the computation.
+A possible policy (as currently implemented) is to generate a sign
+extension right after each 32bit computation.
+Depending on the instruction set of the architecture, some of these
+sign extension instructions may be redundant.
+There are two cases in which the extension may be redundant:
+
+Case1:
+The instruction that uses the 64bit operands that are sign
+extended has a dual mode that works with 32bit operands.
+For example:
+
+ int32 a, b;
+
+ a = .... --> a = ....
+ a = sign extend a -->
+ b = .... --> b = ....
+ b = sign extend a -->
+ -->
+ cmpd a, b --> cmpw a, b //half word compare
+
+Case2:
+The instruction that defines the 64bit operand (which is later sign
+extended) has a dual mode that defines and sign-extends simultaneously
+a 32bit operand. For example:
+
+ int32 a;
+
+ ld a --> lwa a // load half word and sign extend
+ a = sign extend a -->
+ -->
+ return a --> return a
+
+
+General idea for solution:
+--------------------------
+First, try to merge the sign extension with the instruction that
+defines the source of the extension and (separately) with the
+instructions that uses the extended result. By doing this, both cases
+of redundancies (as described above) will be eliminated.
+
+Then, use partial redundancy elimination to place the non redundant
+ones at optimal placements.
+
+
+Implementation by example:
+--------------------------
+Note: The instruction stream is not changed till the last phase.
+
+Phase 0: Initial code, as currently generated by gcc.
+
+ def1 def3
+ se1 def2 se3
+ | \ | / |
+ | \ | / |
+ | \ | / |
+ | \ | / |
+ | \ | / |
+ | \|/ |
+ use1 use2 use3
+ use4
+def1 + se1:
+set ((reg:SI 10) (..def1rhs..))
+set ((reg:DI 100) (sign_extend:DI (reg:SI 10)))
+
+def2:
+set ((reg:DI 100) (const_int 7))
+
+def3 + se3:
+set ((reg:SI 20) (..def3rhs..))
+set ((reg:DI 100) (sign_extend:DI (reg:SI 20)))
+
+use1:
+set ((reg:CC...) (compare:CC (reg:DI 100) (...)))
+
+use2, use3, use4:
+set ((...) (reg:DI 100))
+
+Phase 1: Propagate extensions to uses.
+
+ def1 def3
+ se1 def2 se3
+ | \ | / |
+ | \ | / |
+ | \ | / |
+ | \ | / |
+ | \ | / |
+ | \|/ |
+ se se se
+ use1 use2 use3
+ se
+ use4
+
+From here, all of the subregs are lowpart !
+
+def1, def2, def3: No change.
+
+use1:
+set ((reg:DI 100) (sign_extend:DI ((subreg:SI (reg:DI 100)))))
+set ((reg:CC...) (compare:CC (reg:DI 100) (...)))
+
+use2, use3, use4:
+set ((reg:DI 100) (sign_extend:DI ((subreg:SI (reg:DI 100)))))
+set ((...) (reg:DI 100))
+
+
+Phase 2: Merge and eliminate locally redundant extensions.
+
+
+ *def1 def2 *def3
+ [se removed] se se3
+ | \ | / |
+ | \ | / |
+ | \ | / |
+ | \ | / |
+ | \ | / |
+ | \|/ |
+ [se removed] se se
+ *use1 use2 use3
+ [se removed]
+ use4
+
+The instructions that were changed at this phase are marked with
+asterisk.
+
+*def1: Merge failed.
+ Remove the sign extension instruction, modify def1 and
+ insert a move instruction to assure to correctness of the code.
+set ((subreg:SI (reg:DI 100)) (..def1rhs..))
+set ((reg:SI 10) (subreg:SI (reg:DI 100)))
+
+def2 + se: There is no need for merge.
+ Def2 is not changed but a sign extension instruction is
+ created.
+set ((reg:DI 100) (const_int 7))
+set ((reg:DI 100) (sign_extend:DI ((subreg:SI (reg:DI 100)))))
+
+*def3 + se3: Merge succeeded.
+set ((reg:DI 100) (sign_extend:DI (..def3rhs..)))
+set ((reg:SI 20) (reg:DI 100))
+set ((reg:DI 100) (sign_extend:DI (reg:SI 20)))
+(The extension instruction is the original one).
+
+*use1: Merge succeeded. Remove the sign extension instruction.
+set ((reg:CC...)
+ (compare:CC (subreg:SI (reg:DI 100)) (...)))
+
+use2, use3: Merge failed. No change.
+
+use4: The extension is locally redundant, therefore it is eliminated
+ at this point.
+
+
+Phase 3: Eliminate globally redundant extensions.
+
+Following the LCM output:
+
+ def1 def2 def3
+ se se3
+ | \ | / |
+ | \ | / |
+ | se | / |
+ | \ | / |
+ | \ | / |
+ | \|/ |
+ [ses removed]
+ use1 use2 use3
+ use4
+
+se:
+set ((reg:DI 100) (sign_extend:DI ((subreg:SI (reg:DI 100)))))
+
+se3:
+set ((reg:DI 100) (sign_extend:DI (reg:SI 20)))
+
+
+Phase 4: Commit changes to the insn stream.
+
+
+ def1 def3 *def1 def2 *def3
+ se1 def2 se3 [se removed] [se removed]
+ | \ | / | | \ | / |
+ | \ | / | ------> | \ | / |
+ | \ | / | ------> | se | / |
+ | \ | / | | \ | / |
+ | \ | / | | \ | / |
+ | \|/ | | \|/ |
+ use1 use2 use3 *use1 use2 use3
+ use4 use4
+
+The instructions that were changed during the whole optimization are
+marked with asterisk.
+
+The result:
+
+def1 + se1:
+[ set ((reg:SI 10) (..def1rhs..)) ] - Deleted
+[ set ((reg:DI 100) (sign_extend:DI (reg:SI 10))) ] - Deleted
+set ((subreg:SI (reg:DI 100)) (..def3rhs..)) - Inserted
+set ((reg:SI 10) (subreg:SI (reg:DI 100))) - Inserted
+
+def2:
+set ((reg:DI 100) (const_int 7)) - No change
+
+def3 + se3:
+[ set ((reg:SI 20) (..def3rhs..)) ] - Deleted
+[ set ((reg:DI 100) (sign_extend:DI (reg:SI 20))) ] - Deleted
+set ((reg:DI 100) (sign_extend:DI (..def3rhs..))) - Inserted
+set ((reg:SI 20) (reg:DI 100)) - Inserted
+
+use1:
+[ set ((reg:CC...) (compare:CC (reg:DI 100) (...))) ] - Deleted
+set ((reg:CC...) - Inserted
+ (compare:CC (subreg:SI (reg:DI 100)) (...)))
+
+use2, use3, use4:
+set ((...) (reg:DI 100)) - No change
+
+se: - Inserted
+set ((reg:DI 100) (sign_extend:DI ((subreg:SI (reg:DI 100)))))
+
+Note: Most of the simple move instructions that were inserted will be
+ trivially dead and therefore eliminated.
+
+The implementation outline:
+---------------------------
+Some definitions:
+ A web is RELEVANT if at the end of phase 1, his leader's
+ relevancy is {ZERO, SIGN}_EXTENDED_DEF. The source_mode of
+ the web is the source_mode of his leader.
+ A definition is a candidate for the optimization if it is part
+ of a RELEVANT web and his local source_mode is not narrower
+ then the source_mode of its web.
+ A use is a candidate for the optimization if it is part of a
+ RELEVANT web.
+ A simple explicit extension is a single set instruction that
+ extends a register (or a subregister) to a register (or
+ subregister).
+ A complex explicit extension is an explicit extension instruction
+ that is not simple.
+ A def extension is a simple explicit extension that is
+ also a candidate for the optimization. This extension is part
+ of the instruction stream, it is not generated by this
+ optimization.
+ A use extension is a simple explicit extension that is generated
+ and stored for candidate use during this optimization. It is
+ not emitted to the instruction stream till the last phase of
+ the optimization.
+ A reference is an instruction that satisfy at least on of these
+ criteria:
+ - It contains a definition with EXTENDED_DEF relevancy in a RELEVANT web.
+ - It is followed by a def extension.
+ - It contains a candidate use.
+
+Phase 1: Propagate extensions to uses.
+ In this phase, we find candidate extensions for the optimization
+ and we generate (but not emit) proper extensions "right before the
+ uses".
+
+ a. Build a DF object.
+ b. Traverse over all the instructions that contains a definition
+ and set their local relevancy and local source_mode like this:
+ - If the instruction is a simple explicit extension instruction,
+ mark it as {ZERO, SIGN}_EXTENDED_DEF according to the extension
+ type and mark its source_mode to be the mode of the quantity
+ that is been extended.
+ - Otherwise, If the instruction has an implicit extension,
+ which means that its high part is an extension of its low part,
+ or if it is a complicated explicit extension, mark it as
+ EXTENDED_DEF and set its source_mode to be the narrowest
+ mode that is been extended in the instruction.
+ c. Traverse over all the instructions that contains a use and set
+ their local relevancy to RELEVANT_USE (except for few corner
+ cases).
+ d. Produce the web. During union of two entries, update the
+ relevancy and source_mode of the leader. There are two major
+ guide lines for this update:
+ - If one of the entries is NOT_RELEVANT, mark the leader
+ NOT_RELEVANT.
+ - If one is ZERO_EXTENDED_DEF and the other is SIGN_EXTENDED_DEF
+ (or vice versa) mark the leader as NOT_RELEVANT. We don't
+ handle this kind of mixed webs.
+ (For more details about this update process,
+ see see_update_leader_extra_info ()).
+ e. Generate uses extensions according to the relevancy and
+ source_mode of the webs.
+
+Phase 2: Merge and eliminate locally redundant extensions.
+ In this phase, we try to merge def extensions and use
+ extensions with their references, and eliminate redundant extensions
+ in the same basic block.
+
+ Traverse over all the references. Do this in basic block number and
+ luid number forward order.
+ For each reference do:
+ a. Peephole optimization - try to merge it with all its
+ def extensions and use extensions in the following
+ order:
+ - Try to merge only the def extensions, one by one.
+ - Try to merge only the use extensions, one by one.
+ - Try to merge any couple of use extensions simultaneously.
+ - Try to merge any def extension with one or two uses
+ extensions simultaneously.
+ b. Handle each EXTENDED_DEF in it as if it was already merged with
+ an extension.
+
+ During the merge process we save the following data for each
+ register in each basic block:
+ a. The first instruction that defines the register in the basic
+ block.
+ b. The last instruction that defines the register in the basic
+ block.
+ c. The first extension of this register before the first
+ instruction that defines it in the basic block.
+ c. The first extension of this register after the last
+ instruction that defines it in the basic block.
+ This data will help us eliminate (or more precisely, not generate)
+ locally redundant extensions, and will be useful in the next stage.
+
+ While merging extensions with their reference there are 4 possible
+ situations:
+ a. A use extension was merged with the reference:
+ Delete the extension instruction and save the merged reference
+ for phase 4. (For details, see see_use_extension_merged ())
+ b. A use extension failed to be merged with the reference:
+ If there is already such an extension in the same basic block
+ and it is not dead at this point, delete the unmerged extension
+ (it is locally redundant), otherwise properly update the above
+ basic block data.
+ (For details, see see_merge_one_use_extension ())
+ c. A def extension was merged with the reference:
+ Mark this extension as a merged_def extension and properly
+ update the above basic block data.
+ (For details, see see_merge_one_def_extension ())
+ d. A def extension failed to be merged with the reference:
+ Replace the definition of the NARROWmode register in the
+ reference with the proper subreg of WIDEmode register and save
+ the result as a merged reference. Also, properly update the
+ the above basic block data.
+ (For details, see see_def_extension_not_merged ())
+
+Phase 3: Eliminate globally redundant extensions.
+In this phase, we set the bit vectors input of the edge based LCM
+using the recorded data on the registers in each basic block.
+We also save pointers for all the anticipatable and available
+occurrences of the relevant extensions. Then we run the LCM.
+
+ a. Initialize the comp, antloc, kill bit vectors to zero and the
+ transp bit vector to ones.
+
+ b. Traverse over all the references. Do this in basic block number
+ and luid number forward order. For each reference:
+ - Go over all its use extensions. For each such extension -
+ If it is not dead from the beginning of the basic block SET
+ the antloc bit of the current extension in the current
+ basic block bits.
+ If it is not dead till the end of the basic block SET the
+ comp bit of the current extension in the current basic
+ block bits.
+ - Go over all its def extensions that were merged with
+ it. For each such extension -
+ If it is not dead till the end of the basic block SET the
+ comp bit of the current extension in the current basic
+ block bits.
+ RESET the proper transp and kill bits.
+ - Go over all its def extensions that were not merged
+ with it. For each such extension -
+ RESET the transp bit and SET the kill bit of the current
+ extension in the current basic block bits.
+
+ c. Run the edge based LCM.
+
+Phase 4: Commit changes to the insn stream.
+This is the only phase that actually changes the instruction stream.
+Up to this point the optimization could be aborted at any time.
+Here we insert extensions at their best placements and delete the
+redundant ones according to the output of the LCM. We also replace
+some of the instructions according to the second phase merges results.
+
+ a. Use the pre_delete_map (from the output of the LCM) in order to
+ delete redundant extensions. This will prevent them from been
+ emitted in the first place.
+
+ b. Insert extensions on edges where needed according to
+ pre_insert_map and edge_list (from the output of the LCM).
+
+ c. For each reference do-
+ - Emit all the uses extensions that were not deleted until now,
+ right before the reference.
+ - Delete all the merged and unmerged def extensions from
+ the instruction stream.
+ - Replace the reference with the merged one, if exist.
+
+The implementation consists of four data structures:
+- Data structure I
+ Purpose: To handle the relevancy of the uses, definitions and webs.
+ Relevant structures: web_entry (from df.h), see_entry_extra_info.
+ Details: This is a disjoint-set data structure. Most of its functions are
+ implemented in web.c. Each definition and use in the code are
+ elements. A web_entry structure is allocated for each element to
+ hold the element's relevancy and source_mode. The union rules are
+ defined in see_update_leader_extra_info ().
+- Data structure II
+ Purpose: To store references and their extensions (uses and defs)
+ and to enable traverse over these references according to basic
+ block order.
+ Relevant structure: see_ref_s.
+ Details: This data structure consists of an array of splay trees. One splay
+ tree for each basic block. The splay tree nodes are references and
+ the keys are the luids of the references.
+ A see_ref_s structure is allocated for each reference. It holds the
+ reference itself, its def and uses extensions and later the merged
+ version of the reference.
+ Using this data structure we can traverse over all the references of
+ a basic block and their extensions in forward order.
+- Data structure III.
+ Purpose: To store local properties of registers for each basic block.
+ This data will later help us build the LCM sbitmap_vectors
+ input.
+ Relevant structure: see_register_properties.
+ Details: This data structure consists of an array of hash tables. One hash
+ for each basic block. The hash node are a register properties
+ and the keys are the numbers of the registers.
+ A see_register_properties structure is allocated for each register
+ that we might be interested in its properties.
+ Using this data structure we can easily find the properties of a
+ register in a specific basic block. This is necessary for locally
+ redundancy elimination and for setting up the LCM input.
+- Data structure IV.
+ Purpose: To store the extensions that are candidate for PRE and their
+ anticipatable and available occurrences.
+ Relevant structure: see_occr, see_pre_extension_expr.
+ Details: This data structure is a hash tables. Its nodes are the extensions
+ that are candidate for PRE.
+ A see_pre_extension_expr structure is allocated for each candidate
+ extension. It holds a copy of the extension and a linked list of all
+ the anticipatable and available occurrences of it.
+ We use this data structure when we read the output of the LCM. */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "tm.h"
+
+#include "obstack.h"
+#include "rtl.h"
+#include "output.h"
+#include "df.h"
+#include "insn-config.h"
+#include "recog.h"
+#include "expr.h"
+#include "splay-tree.h"
+#include "hashtab.h"
+#include "regs.h"
+#include "timevar.h"
+#include "tree-pass.h"
+
+/* Used to classify defs and uses according to relevancy. */
+enum entry_type {
+ NOT_RELEVANT,
+ SIGN_EXTENDED_DEF,
+ ZERO_EXTENDED_DEF,
+ EXTENDED_DEF,
+ RELEVANT_USE
+};
+
+/* Used to classify extensions in relevant webs. */
+enum extension_type {
+ DEF_EXTENSION,
+ EXPLICIT_DEF_EXTENSION,
+ IMPLICIT_DEF_EXTENSION,
+ USE_EXTENSION
+};
+
+/* Global data structures and flags. */
+
+/* This structure will be assigned for each web_entry structure (defined
+ in df.h). It is placed in the extra_info field of a web_entry and holds the
+ relevancy and source mode of the web_entry. */
+
+struct see_entry_extra_info
+{
+ /* The relevancy of the ref. */
+ enum entry_type relevancy;
+ /* The relevancy of the ref.
+ This field is updated only once - when this structure is created. */
+ enum entry_type local_relevancy;
+ /* The source register mode. */
+ enum machine_mode source_mode;
+ /* This field is used only if the relevancy is ZERO/SIGN_EXTENDED_DEF.
+ It is updated only once when this structure is created. */
+ enum machine_mode local_source_mode;
+ /* This field is used only if the relevancy is EXTENDED_DEF.
+ It holds the narrowest mode that is sign extended. */
+ enum machine_mode source_mode_signed;
+ /* This field is used only if the relevancy is EXTENDED_DEF.
+ It holds the narrowest mode that is zero extended. */
+ enum machine_mode source_mode_unsigned;
+};
+
+/* There is one such structure for every reference. It stores the reference
+ itself as well as its extensions (uses and definitions).
+ Used as the value in splay_tree see_bb_splay_ar[]. */
+struct see_ref_s
+{
+ /* The luid of the insn. */
+ unsigned int luid;
+ /* The insn of the ref. */
+ rtx insn;
+ /* The merged insn that was formed from the reference's insn and extensions.
+ If all merges failed, it remains NULL. */
+ rtx merged_insn;
+ /* The def extensions of the reference that were not merged with
+ it. */
+ htab_t unmerged_def_se_hash;
+ /* The def extensions of the reference that were merged with
+ it. Implicit extensions of the reference will be stored here too. */
+ htab_t merged_def_se_hash;
+ /* The uses extensions of reference. */
+ htab_t use_se_hash;
+};
+
+/* There is one such structure for every relevant extended register in a
+ specific basic block. This data will help us build the LCM sbitmap_vectors
+ input. */
+struct see_register_properties
+{
+ /* The register number. */
+ unsigned int regno;
+ /* The last luid of the reference that defines this register in this basic
+ block. */
+ int last_def;
+ /* The luid of the reference that has the first extension of this register
+ that appears before any definition in this basic block. */
+ int first_se_before_any_def;
+ /* The luid of the reference that has the first extension of this register
+ that appears after the last definition in this basic block. */
+ int first_se_after_last_def;
+};
+
+/* Occurrence of an expression.
+ There must be at most one available occurrence and at most one anticipatable
+ occurrence per basic block. */
+struct see_occr
+{
+ /* Next occurrence of this expression. */
+ struct see_occr *next;
+ /* The insn that computes the expression. */
+ rtx insn;
+ int block_num;
+};
+
+/* There is one such structure for every relevant extension expression.
+ It holds a copy of this extension instruction as well as a linked lists of
+ pointers to all the antic and avail occurrences of it. */
+struct see_pre_extension_expr
+{
+ /* A copy of the extension instruction. */
+ rtx se_insn;
+ /* Index in the available expression bitmaps. */
+ int bitmap_index;
+ /* List of anticipatable occurrences in basic blocks in the function.
+ An "anticipatable occurrence" is the first occurrence in the basic block,
+ the operands are not modified in the basic block prior to the occurrence
+ and the output is not used between the start of the block and the
+ occurrence. */
+ struct see_occr *antic_occr;
+ /* List of available occurrence in basic blocks in the function.
+ An "available occurrence" is the last occurrence in the basic block and
+ the operands are not modified by following statements in the basic block
+ [including this insn]. */
+ struct see_occr *avail_occr;
+};
+
+/* Helper structure for the note_uses and see_replace_src functions. */
+struct see_replace_data
+{
+ rtx from;
+ rtx to;
+};
+
+/* Helper structure for the note_uses and see_mentioned_reg functions. */
+struct see_mentioned_reg_data
+{
+ rtx reg;
+ bool mentioned;
+};
+
+/* A data flow object that will be created once and used throughout the
+ optimization. */
+static struct df *df = NULL;
+/* An array of web_entries. The i'th definition in the df object is associated
+ with def_entry[i] */
+static struct web_entry *def_entry = NULL;
+/* An array of web_entries. The i'th use in the df object is associated with
+ use_entry[i] */
+static struct web_entry *use_entry = NULL;
+/* Array of splay_trees.
+ see_bb_splay_ar[i] refers to the splay tree of the i'th basic block.
+ The splay tree will hold see_ref_s structures. The key is the luid
+ of the insn. This way we can traverse over the references of each basic
+ block in forward or backward order. */
+static splay_tree *see_bb_splay_ar = NULL;
+/* Array of hashes.
+ see_bb_hash_ar[i] refers to the hash of the i'th basic block.
+ The hash will hold see_register_properties structure. The key is regno. */
+static htab_t *see_bb_hash_ar = NULL;
+/* Hash table that holds a copy of all the extensions. The key is the right
+ hand side of the se_insn field. */
+static htab_t see_pre_extension_hash = NULL;
+
+/* Local LCM properties of expressions. */
+/* Nonzero for expressions that are transparent in the block. */
+static sbitmap *transp = NULL;
+/* Nonzero for expressions that are computed (available) in the block. */
+static sbitmap *comp = NULL;
+/* Nonzero for expressions that are locally anticipatable in the block. */
+static sbitmap *antloc = NULL;
+/* Nonzero for expressions that are locally killed in the block. */
+static sbitmap *ae_kill = NULL;
+/* Nonzero for expressions which should be inserted on a specific edge. */
+static sbitmap *pre_insert_map = NULL;
+/* Nonzero for expressions which should be deleted in a specific block. */
+static sbitmap *pre_delete_map = NULL;
+/* Contains the edge_list returned by pre_edge_lcm. */
+static struct edge_list *edge_list = NULL;
+/* Records the last basic block at the beginning of the optimization. */
+static int last_bb;
+/* Records the number of uses at the beginning of the optimization. */
+static unsigned int uses_num;
+/* Records the number of definitions at the beginning of the optimization. */
+static unsigned int defs_num;
+
+#define ENTRY_EI(ENTRY) ((struct see_entry_extra_info *) (ENTRY)->extra_info)
+
+/* Functions implementation. */
+
+/* Verifies that EXTENSION's pattern is this:
+
+ set (reg/subreg reg1) (sign/zero_extend:WIDEmode (reg/subreg reg2))
+
+ If it doesn't have the expected pattern return NULL.
+ Otherwise, if RETURN_DEST_REG is set, return reg1 else return reg2. */
+
+static rtx
+see_get_extension_reg (rtx extension, bool return_dest_reg)
+{
+ rtx set, rhs, lhs;
+ rtx reg1 = NULL;
+ rtx reg2 = NULL;
+
+ /* Parallel pattern for extension not supported for the moment. */
+ if (GET_CODE (PATTERN (extension)) == PARALLEL)
+ return NULL;
+
+ set = single_set (extension);
+ if (!set)
+ return NULL;
+ lhs = SET_DEST (set);
+ rhs = SET_SRC (set);
+
+ if (REG_P (lhs))
+ reg1 = lhs;
+ else if (REG_P (SUBREG_REG (lhs)))
+ reg1 = SUBREG_REG (lhs);
+ else
+ return NULL;
+
+ if (GET_CODE (rhs) != SIGN_EXTEND && GET_CODE (rhs) != ZERO_EXTEND)
+ return NULL;
+
+ rhs = XEXP (rhs, 0);
+ if (REG_P (rhs))
+ reg2 = rhs;
+ else if (REG_P (SUBREG_REG (rhs)))
+ reg2 = SUBREG_REG (rhs);
+ else
+ return NULL;
+
+ if (return_dest_reg)
+ return reg1;
+ return reg2;
+}
+
+/* Verifies that EXTENSION's pattern is this:
+
+ set (reg/subreg reg1) (sign/zero_extend: (...expr...)
+
+ If it doesn't have the expected pattern return UNKNOWN.
+ Otherwise, set SOURCE_MODE to be the mode of the extended expr and return
+ the rtx code of the extension. */
+
+static enum rtx_code
+see_get_extension_data (rtx extension, enum machine_mode *source_mode)
+{
+ rtx rhs, lhs, set;
+
+ if (!extension || !INSN_P (extension))
+ return UNKNOWN;
+
+ /* Parallel pattern for extension not supported for the moment. */
+ if (GET_CODE (PATTERN (extension)) == PARALLEL)
+ return NOT_RELEVANT;
+
+ set = single_set (extension);
+ if (!set)
+ return NOT_RELEVANT;
+ rhs = SET_SRC (set);
+ lhs = SET_DEST (set);
+
+ /* Don't handle extensions to something other then register or
+ subregister. */
+ if (!REG_P (lhs) && !SUBREG_REG (lhs))
+ return UNKNOWN;
+
+ if (GET_CODE (rhs) != SIGN_EXTEND && GET_CODE (rhs) != ZERO_EXTEND)
+ return UNKNOWN;
+
+ if (!REG_P (XEXP (rhs, 0))
+ && !(GET_CODE (XEXP (rhs, 0)) == SUBREG
+ && REG_P (SUBREG_REG (XEXP (rhs, 0)))))
+ return UNKNOWN;
+
+ *source_mode = GET_MODE (XEXP (rhs, 0));
+
+ if (GET_CODE (rhs) == SIGN_EXTEND)
+ return SIGN_EXTEND;
+ return ZERO_EXTEND;
+}
+
+
+/* Generate instruction with the pattern:
+ set ((reg r) (sign/zero_extend (subreg:mode (reg r))))
+ (the register r on both sides of the set is the same register).
+ And recognize it.
+ If the recognition failed, this is very bad, return NULL (This will abort
+ the entire optimization).
+ Otherwise, return the generated instruction. */
+
+static rtx
+see_gen_normalized_extension (rtx reg, enum rtx_code extension_code,
+ enum machine_mode mode)
+{
+ rtx subreg, insn;
+ rtx extension = NULL;
+
+ if (!reg
+ || !REG_P (reg)
+ || (extension_code != SIGN_EXTEND && extension_code != ZERO_EXTEND))
+ return NULL;
+
+ subreg = gen_lowpart_SUBREG (mode, reg);
+ if (extension_code == SIGN_EXTEND)
+ extension = gen_rtx_SIGN_EXTEND (GET_MODE (reg), subreg);
+ else
+ extension = gen_rtx_ZERO_EXTEND (GET_MODE (reg), subreg);
+
+ start_sequence ();
+ emit_insn (gen_rtx_SET (VOIDmode, reg, extension));
+ insn = get_insns ();
+ end_sequence ();
+
+ if (insn_invalid_p (insn))
+ /* Recognition failed, this is very bad for this optimization.
+ Abort the optimization. */
+ return NULL;
+ return insn;
+}
+
+/* Hashes and splay_trees related functions implementation. */
+
+/* Helper functions for the pre_extension hash.
+ This kind of hash will hold see_pre_extension_expr structures.
+
+ The key is the right hand side of the se_insn field.
+ Note that the se_insn is an expression that looks like:
+
+ set ((reg:WIDEmode r1) (sign_extend:WIDEmode
+ (subreg:NARROWmode (reg:WIDEmode r2)))) */
+
+/* Return TRUE if P1 has the same value in its rhs as P2.
+ Otherwise, return FALSE.
+ P1 and P2 are see_pre_extension_expr structures. */
+
+static int
+eq_descriptor_pre_extension (const void *p1, const void *p2)
+{
+ const struct see_pre_extension_expr *extension1 = p1;
+ const struct see_pre_extension_expr *extension2 = p2;
+ rtx set1 = single_set (extension1->se_insn);
+ rtx set2 = single_set (extension2->se_insn);
+ rtx rhs1, rhs2;
+
+ gcc_assert (set1 && set2);
+ rhs1 = SET_SRC (set1);
+ rhs2 = SET_SRC (set2);
+
+ return rtx_equal_p (rhs1, rhs2);
+}
+
+
+/* P is a see_pre_extension_expr struct, use the RHS of the se_insn field.
+ Note that the RHS is an expression that looks like this:
+ (sign_extend:WIDEmode (subreg:NARROWmode (reg:WIDEmode r))) */
+
+static hashval_t
+hash_descriptor_pre_extension (const void *p)
+{
+ const struct see_pre_extension_expr *extension = p;
+ rtx set = single_set (extension->se_insn);
+ rtx rhs;
+
+ gcc_assert (set);
+ rhs = SET_SRC (set);
+
+ return hash_rtx (rhs, GET_MODE (rhs), 0, NULL, 0);
+}
+
+
+/* Free the allocated memory of the current see_pre_extension_expr struct.
+
+ It frees the two linked list of the occurrences structures. */
+
+static void
+hash_del_pre_extension (void *p)
+{
+ struct see_pre_extension_expr *extension = p;
+ struct see_occr *curr_occr = extension->antic_occr;
+ struct see_occr *next_occr = NULL;
+
+ /* Free the linked list of the anticipatable occurrences. */
+ while (curr_occr)
+ {
+ next_occr = curr_occr->next;
+ free (curr_occr);
+ curr_occr = next_occr;
+ }
+
+ /* Free the linked list of the available occurrences. */
+ curr_occr = extension->avail_occr;
+ while (curr_occr)
+ {
+ next_occr = curr_occr->next;
+ free (curr_occr);
+ curr_occr = next_occr;
+ }
+
+ /* Free the see_pre_extension_expr structure itself. */
+ free (extension);
+}
+
+
+/* Helper functions for the register_properties hash.
+ This kind of hash will hold see_register_properties structures.
+
+ The value of the key is the regno field of the structure. */
+
+/* Return TRUE if P1 has the same value in the regno field as P2.
+ Otherwise, return FALSE.
+ Where P1 and P2 are see_register_properties structures. */
+
+static int
+eq_descriptor_properties (const void *p1, const void *p2)
+{
+ const struct see_register_properties *curr_prop1 = p1;
+ const struct see_register_properties *curr_prop2 = p2;
+
+ return curr_prop1->regno == curr_prop2->regno;
+}
+
+
+/* P is a see_register_properties struct, use the register number in the
+ regno field. */
+
+static hashval_t
+hash_descriptor_properties (const void *p)
+{
+ const struct see_register_properties *curr_prop = p;
+ return curr_prop->regno;
+}
+
+
+/* Free the allocated memory of the current see_register_properties struct. */
+static void
+hash_del_properties (void *p)
+{
+ struct see_register_properties *curr_prop = p;
+ free (curr_prop);
+}
+
+
+/* Helper functions for an extension hash.
+ This kind of hash will hold insns that look like:
+
+ set ((reg:WIDEmode r1) (sign_extend:WIDEmode
+ (subreg:NARROWmode (reg:WIDEmode r2))))
+ or
+ set ((reg:WIDEmode r1) (sign_extend:WIDEmode (reg:NARROWmode r2)))
+
+ The value of the key is (REGNO (reg:WIDEmode r1))
+ It is possible to search this hash in two ways:
+ 1. By a register rtx. The Value that is been compared to the keys is the
+ REGNO of it.
+ 2. By an insn with the above pattern. The Value that is been compared to
+ the keys is the REGNO of the reg on the lhs. */
+
+/* Return TRUE if P1 has the same value as P2. Otherwise, return FALSE.
+ Where P1 is an insn and P2 is an insn or a register. */
+
+static int
+eq_descriptor_extension (const void *p1, const void *p2)
+{
+ const rtx insn = (rtx) p1;
+ const rtx element = (rtx) p2;
+ rtx set1 = single_set (insn);
+ rtx dest_reg1;
+ rtx set2 = NULL;
+ rtx dest_reg2 = NULL;
+
+ gcc_assert (set1 && element && (REG_P (element) || INSN_P (element)));
+
+ dest_reg1 = SET_DEST (set1);
+
+ if (INSN_P (element))
+ {
+ set2 = single_set (element);
+ dest_reg2 = SET_DEST (set2);
+ }
+ else
+ dest_reg2 = element;
+
+ return REGNO (dest_reg1) == REGNO (dest_reg2);
+}
+
+
+/* If P is an insn, use the register number of its lhs
+ otherwise, P is a register, use its number. */
+
+static hashval_t
+hash_descriptor_extension (const void *p)
+{
+ const rtx r = (rtx) p;
+ rtx set, lhs;
+
+ if (r && REG_P (r))
+ return REGNO (r);
+
+ gcc_assert (r && INSN_P (r));
+ set = single_set (r);
+ gcc_assert (set);
+ lhs = SET_DEST (set);
+ return REGNO (lhs);
+}
+
+
+/* Helper function for a see_bb_splay_ar[i] splay tree.
+ It frees all the allocated memory of a struct see_ref_s pointer.
+
+ VALUE is the value of a splay tree node. */
+
+static void
+see_free_ref_s (splay_tree_value value)
+{
+ struct see_ref_s *ref_s = (struct see_ref_s *)value;
+
+ if (ref_s->unmerged_def_se_hash)
+ htab_delete (ref_s->unmerged_def_se_hash);
+ if (ref_s->merged_def_se_hash)
+ htab_delete (ref_s->merged_def_se_hash);
+ if (ref_s->use_se_hash)
+ htab_delete (ref_s->use_se_hash);
+ free (ref_s);
+}
+
+
+/* Rest of the implementation. */
+
+/* Search the extension hash for a suitable entry for EXTENSION.
+ TYPE is the type of EXTENSION (USE_EXTENSION or DEF_EXTENSION).
+
+ If TYPE is DEF_EXTENSION we need to normalize EXTENSION before searching the
+ extension hash.
+
+ If a suitable entry was found, return the slot. Otherwise, store EXTENSION
+ in the hash and return NULL. */
+
+static struct see_pre_extension_expr *
+see_seek_pre_extension_expr (rtx extension, enum extension_type type)
+{
+ struct see_pre_extension_expr **slot_pre_exp, temp_pre_exp;
+ rtx dest_extension_reg = see_get_extension_reg (extension, 1);
+ enum rtx_code extension_code;
+ enum machine_mode source_extension_mode;
+
+ if (type == DEF_EXTENSION)
+ {
+ extension_code = see_get_extension_data (extension,
+ &source_extension_mode);
+ gcc_assert (extension_code != UNKNOWN);
+ extension =
+ see_gen_normalized_extension (dest_extension_reg, extension_code,
+ source_extension_mode);
+ }
+ temp_pre_exp.se_insn = extension;
+ slot_pre_exp =
+ (struct see_pre_extension_expr **) htab_find_slot (see_pre_extension_hash,
+ &temp_pre_exp, INSERT);
+ if (*slot_pre_exp == NULL)
+ /* This is the first time this extension instruction is encountered. Store
+ it in the hash. */
+ {
+ (*slot_pre_exp) = xmalloc (sizeof (struct see_pre_extension_expr));
+ (*slot_pre_exp)->se_insn = extension;
+ (*slot_pre_exp)->bitmap_index =
+ (htab_elements (see_pre_extension_hash) - 1);
+ (*slot_pre_exp)->antic_occr = NULL;
+ (*slot_pre_exp)->avail_occr = NULL;
+ return NULL;
+ }
+ return *slot_pre_exp;
+}
+
+
+/* This function defines how to update the extra_info of the web_entry.
+
+ FIRST is the pointer of the extra_info of the first web_entry.
+ SECOND is the pointer of the extra_info of the second web_entry.
+ The first web_entry will be the predecessor (leader) of the second web_entry
+ after the union.
+
+ Return true if FIRST and SECOND points to the same web entry structure and
+ nothing is done. Otherwise, return false. */
+
+static bool
+see_update_leader_extra_info (struct web_entry *first, struct web_entry *second)
+{
+ struct see_entry_extra_info *first_ei, *second_ei;
+
+ first = unionfind_root (first);
+ second = unionfind_root (second);
+
+ if (unionfind_union (first, second))
+ return true;
+
+ first_ei = (struct see_entry_extra_info *) first->extra_info;
+ second_ei = (struct see_entry_extra_info *) second->extra_info;
+
+ gcc_assert (first_ei && second_ei);
+
+ if (second_ei->relevancy == NOT_RELEVANT)
+ {
+ first_ei->relevancy = NOT_RELEVANT;
+ return false;
+ }
+ switch (first_ei->relevancy)
+ {
+ case NOT_RELEVANT:
+ break;
+ case RELEVANT_USE:
+ switch (second_ei->relevancy)
+ {
+ case RELEVANT_USE:
+ break;
+ case EXTENDED_DEF:
+ first_ei->relevancy = second_ei->relevancy;
+ first_ei->source_mode_signed = second_ei->source_mode_signed;
+ first_ei->source_mode_unsigned = second_ei->source_mode_unsigned;
+ break;
+ case SIGN_EXTENDED_DEF:
+ case ZERO_EXTENDED_DEF:
+ first_ei->relevancy = second_ei->relevancy;
+ first_ei->source_mode = second_ei->source_mode;
+ break;
+ default:
+ gcc_unreachable ();
+ }
+ break;
+ case SIGN_EXTENDED_DEF:
+ switch (second_ei->relevancy)
+ {
+ case SIGN_EXTENDED_DEF:
+ /* The mode of the root should be the wider one in this case. */
+ first_ei->source_mode =
+ (first_ei->source_mode > second_ei->source_mode) ?
+ first_ei->source_mode : second_ei->source_mode;
+ break;
+ case RELEVANT_USE:
+ break;
+ case ZERO_EXTENDED_DEF:
+ /* Don't mix webs with zero extend and sign extend. */
+ first_ei->relevancy = NOT_RELEVANT;
+ break;
+ case EXTENDED_DEF:
+ if (second_ei->source_mode_signed == MAX_MACHINE_MODE)
+ first_ei->relevancy = NOT_RELEVANT;
+ else
+ /* The mode of the root should be the wider one in this case. */
+ first_ei->source_mode =
+ (first_ei->source_mode > second_ei->source_mode_signed) ?
+ first_ei->source_mode : second_ei->source_mode_signed;
+ break;
+ default:
+ gcc_unreachable ();
+ }
+ break;
+ /* This case is similar to the previous one, with little changes. */
+ case ZERO_EXTENDED_DEF:
+ switch (second_ei->relevancy)
+ {
+ case SIGN_EXTENDED_DEF:
+ /* Don't mix webs with zero extend and sign extend. */
+ first_ei->relevancy = NOT_RELEVANT;
+ break;
+ case RELEVANT_USE:
+ break;
+ case ZERO_EXTENDED_DEF:
+ /* The mode of the root should be the wider one in this case. */
+ first_ei->source_mode =
+ (first_ei->source_mode > second_ei->source_mode) ?
+ first_ei->source_mode : second_ei->source_mode;
+ break;
+ case EXTENDED_DEF:
+ if (second_ei->source_mode_unsigned == MAX_MACHINE_MODE)
+ first_ei->relevancy = NOT_RELEVANT;
+ else
+ /* The mode of the root should be the wider one in this case. */
+ first_ei->source_mode =
+ (first_ei->source_mode > second_ei->source_mode_unsigned) ?
+ first_ei->source_mode : second_ei->source_mode_unsigned;
+ break;
+ default:
+ gcc_unreachable ();
+ }
+ break;
+ case EXTENDED_DEF:
+ if (first_ei->source_mode_signed != MAX_MACHINE_MODE
+ && first_ei->source_mode_unsigned != MAX_MACHINE_MODE)
+ {
+ switch (second_ei->relevancy)
+ {
+ case SIGN_EXTENDED_DEF:
+ first_ei->relevancy = SIGN_EXTENDED_DEF;
+ first_ei->source_mode =
+ (first_ei->source_mode_signed > second_ei->source_mode) ?
+ first_ei->source_mode_signed : second_ei->source_mode;
+ break;
+ case RELEVANT_USE:
+ break;
+ case ZERO_EXTENDED_DEF:
+ first_ei->relevancy = ZERO_EXTENDED_DEF;
+ first_ei->source_mode =
+ (first_ei->source_mode_unsigned > second_ei->source_mode) ?
+ first_ei->source_mode_unsigned : second_ei->source_mode;
+ break;
+ case EXTENDED_DEF:
+ if (second_ei->source_mode_unsigned != MAX_MACHINE_MODE)
+ first_ei->source_mode_unsigned =
+ (first_ei->source_mode_unsigned >
+ second_ei->source_mode_unsigned) ?
+ first_ei->source_mode_unsigned :
+ second_ei->source_mode_unsigned;
+ if (second_ei->source_mode_signed != MAX_MACHINE_MODE)
+ first_ei->source_mode_signed =
+ (first_ei->source_mode_signed >
+ second_ei->source_mode_signed) ?
+ first_ei->source_mode_signed : second_ei->source_mode_signed;
+ break;
+ default:
+ gcc_unreachable ();
+ }
+ }
+ else if (first_ei->source_mode_signed == MAX_MACHINE_MODE)
+ {
+ gcc_assert (first_ei->source_mode_unsigned != MAX_MACHINE_MODE);
+ switch (second_ei->relevancy)
+ {
+ case SIGN_EXTENDED_DEF:
+ first_ei->relevancy = NOT_RELEVANT;
+ break;
+ case RELEVANT_USE:
+ break;
+ case ZERO_EXTENDED_DEF:
+ first_ei->relevancy = ZERO_EXTENDED_DEF;
+ first_ei->source_mode =
+ (first_ei->source_mode_unsigned > second_ei->source_mode) ?
+ first_ei->source_mode_unsigned : second_ei->source_mode;
+ break;
+ case EXTENDED_DEF:
+ if (second_ei->source_mode_unsigned == MAX_MACHINE_MODE)
+ first_ei->relevancy = NOT_RELEVANT;
+ else
+ first_ei->source_mode_unsigned =
+ (first_ei->source_mode_unsigned >
+ second_ei->source_mode_unsigned) ?
+ first_ei->source_mode_unsigned :
+ second_ei->source_mode_unsigned;
+ break;
+ default:
+ gcc_unreachable ();
+ }
+ }
+ else
+ {
+ gcc_assert (first_ei->source_mode_unsigned == MAX_MACHINE_MODE);
+ gcc_assert (first_ei->source_mode_signed != MAX_MACHINE_MODE);
+ switch (second_ei->relevancy)
+ {
+ case SIGN_EXTENDED_DEF:
+ first_ei->relevancy = SIGN_EXTENDED_DEF;
+ first_ei->source_mode =
+ (first_ei->source_mode_signed > second_ei->source_mode) ?
+ first_ei->source_mode_signed : second_ei->source_mode;
+ break;
+ case RELEVANT_USE:
+ break;
+ case ZERO_EXTENDED_DEF:
+ first_ei->relevancy = NOT_RELEVANT;
+ break;
+ case EXTENDED_DEF:
+ if (second_ei->source_mode_signed == MAX_MACHINE_MODE)
+ first_ei->relevancy = NOT_RELEVANT;
+ else
+ first_ei->source_mode_signed =
+ (first_ei->source_mode_signed >
+ second_ei->source_mode_signed) ?
+ first_ei->source_mode_signed : second_ei->source_mode_signed;
+ break;
+ default:
+ gcc_unreachable ();
+ }
+ }
+ break;
+ default:
+ /* Unknown patern type. */
+ gcc_unreachable ();
+ }
+
+ return false;
+}
+
+
+/* Free global data structures. */
+
+static void
+see_free_data_structures (void)
+{
+ int i;
+ unsigned int j;
+
+ /* Free the bitmap vectors. */
+ if (transp)
+ {
+ sbitmap_vector_free (transp);
+ transp = NULL;
+ sbitmap_vector_free (comp);
+ comp = NULL;
+ sbitmap_vector_free (antloc);
+ antloc = NULL;
+ sbitmap_vector_free (ae_kill);
+ ae_kill = NULL;
+ }
+ if (pre_insert_map)
+ {
+ sbitmap_vector_free (pre_insert_map);
+ pre_insert_map = NULL;
+ }
+ if (pre_delete_map)
+ {
+ sbitmap_vector_free (pre_delete_map);
+ pre_delete_map = NULL;
+ }
+ if (edge_list)
+ {
+ free_edge_list (edge_list);
+ edge_list = NULL;
+ }
+
+ /* Free the extension hash. */
+ htab_delete (see_pre_extension_hash);
+
+ /* Free the array of hashes. */
+ for (i = 0; i < last_bb; i++)
+ if (see_bb_hash_ar[i])
+ htab_delete (see_bb_hash_ar[i]);
+ free (see_bb_hash_ar);
+
+ /* Free the array of splay trees. */
+ for (i = 0; i < last_bb; i++)
+ if (see_bb_splay_ar[i])
+ splay_tree_delete (see_bb_splay_ar[i]);
+ free (see_bb_splay_ar);
+
+ /* Free the array of web entries and their extra info field. */
+ for (j = 0; j < defs_num; j++)
+ free (def_entry[j].extra_info);
+ free (def_entry);
+ for (j = 0; j < uses_num; j++)
+ free (use_entry[j].extra_info);
+ free (use_entry);
+}
+
+
+/* Initialize global data structures and variables. */
+
+static void
+see_initialize_data_structures (void)
+{
+ /* Build the df object. */
+ df = df_init (DF_HARD_REGS | DF_EQUIV_NOTES | DF_SUBREGS);
+ df_rd_add_problem (df, 0);
+ df_chain_add_problem (df, DF_DU_CHAIN | DF_UD_CHAIN);
+ df_analyze (df);
+
+ if (dump_file)
+ df_dump (df, dump_file);
+
+ /* Record the last basic block at the beginning of the optimization. */
+ last_bb = last_basic_block;
+ /* Record the number of uses at the beginning of the optimization. */
+ uses_num = DF_USES_SIZE (df);
+ /* Record the number of definitions at the beginning of the optimization. */
+ defs_num = DF_DEFS_SIZE (df);
+
+ /* Allocate web entries array for the union-find data structure. */
+ def_entry = xcalloc (defs_num, sizeof (struct web_entry));
+ use_entry = xcalloc (uses_num, sizeof (struct web_entry));
+
+ /* Allocate an array of splay trees.
+ One splay tree for each basic block. */
+ see_bb_splay_ar = xcalloc (last_bb, sizeof (splay_tree));
+
+ /* Allocate an array of hashes.
+ One hash for each basic block. */
+ see_bb_hash_ar = xcalloc (last_bb, sizeof (htab_t));
+
+ /* Allocate the extension hash. It will hold the extensions that we want
+ to PRE. */
+ see_pre_extension_hash = htab_create (10,
+ hash_descriptor_pre_extension,
+ eq_descriptor_pre_extension,
+ hash_del_pre_extension);
+}
+
+
+/* Function called by note_uses to check if a register is used in a
+ subexpressions.
+
+ X is a pointer to the subexpression and DATA is a pointer to a
+ see_mentioned_reg_data structure that contains the register to look for and
+ a place for the result. */
+
+static void
+see_mentioned_reg (rtx *x, void *data)
+{
+ struct see_mentioned_reg_data *d
+ = (struct see_mentioned_reg_data *) data;
+
+ if (reg_mentioned_p (d->reg, *x))
+ d->mentioned = true;
+}
+
+
+/* We don't want to merge a use extension with a reference if the extended
+ register is used only in a simple move instruction. We also don't want to
+ merge a def extension with a reference if the source register of the
+ extension is defined only in a simple move in the reference.
+
+ REF is the reference instruction.
+ EXTENSION is the use extension or def extension instruction.
+ TYPE is the type of the extension (use or def).
+
+ Return true if the reference is complicated enough, so we would like to merge
+ it with the extension. Otherwise, return false. */
+
+static bool
+see_want_to_be_merged_with_extension (rtx ref, rtx extension,
+ enum extension_type type)
+{
+ rtx pat;
+ rtx dest_extension_reg = see_get_extension_reg (extension, 1);
+ rtx source_extension_reg = see_get_extension_reg (extension, 0);
+ enum rtx_code code;
+ struct see_mentioned_reg_data d;
+ int i;
+
+ pat = PATTERN (ref);
+ code = GET_CODE (pat);
+
+ if (code == PARALLEL)
+ {
+ for (i = 0; i < XVECLEN (pat, 0); i++)
+ {
+ rtx sub = XVECEXP (pat, 0, i);
+
+ if (GET_CODE (sub) == SET
+ && (REG_P (SET_DEST (sub))
+ || (GET_CODE (SET_DEST (sub)) == SUBREG
+ && REG_P (SUBREG_REG (SET_DEST (sub)))))
+ && (REG_P (SET_SRC (sub))
+ || (GET_CODE (SET_SRC (sub)) == SUBREG
+ && REG_P (SUBREG_REG (SET_SRC (sub))))))
+ {
+ /* This is a simple move SET. */
+ if (type == DEF_EXTENSION
+ && reg_mentioned_p (source_extension_reg, SET_DEST (sub)))
+ return false;
+ }
+ else
+ {
+ /* This is not a simple move SET.
+ Check if it uses the source of the extension. */
+ if (type == USE_EXTENSION)
+ {
+ d.reg = dest_extension_reg;
+ d.mentioned = false;
+ note_uses (&sub, see_mentioned_reg, &d);
+ if (d.mentioned)
+ return true;
+ }
+ }
+ }
+ if (type == USE_EXTENSION)
+ return false;
+ }
+ else
+ {
+ if (code == SET
+ && (REG_P (SET_DEST (pat))
+ || (GET_CODE (SET_DEST (pat)) == SUBREG
+ && REG_P (SUBREG_REG (SET_DEST (pat)))))
+ && (REG_P (SET_SRC (pat))
+ || (GET_CODE (SET_SRC (pat)) == SUBREG
+ && REG_P (SUBREG_REG (SET_SRC (pat))))))
+ /* This is a simple move SET. */
+ return false;
+ }
+
+ return true;
+}
+
+
+/* Print the register number of the current see_register_properties
+ structure.
+
+ This is a subroutine of see_main called via htab_traverse.
+ SLOT contains the current see_register_properties structure pointer. */
+
+static int
+see_print_register_properties (void **slot, void *b ATTRIBUTE_UNUSED)
+{
+ struct see_register_properties *prop = *slot;
+
+ gcc_assert (prop);
+ fprintf (dump_file, "Property found for register %d\n", prop->regno);
+ return 1;
+}
+
+
+/* Print the extension instruction of the current see_register_properties
+ structure.
+
+ This is a subroutine of see_main called via htab_traverse.
+ SLOT contains the current see_pre_extension_expr structure pointer. */
+
+static int
+see_print_pre_extension_expr (void **slot, void *b ATTRIBUTE_UNUSED)
+{
+ struct see_pre_extension_expr *pre_extension = *slot;
+
+ gcc_assert (pre_extension
+ && pre_extension->se_insn
+ && INSN_P (pre_extension->se_insn));
+
+ fprintf (dump_file, "Index %d for:\n", pre_extension->bitmap_index);
+ print_rtl_single (dump_file, pre_extension->se_insn);
+
+ return 1;
+}
+
+
+/* Phase 4 implementation: Commit changes to the insn stream. */
+
+/* Delete the merged def extension.
+
+ This is a subroutine of see_commit_ref_changes called via htab_traverse.
+
+ SLOT contains the current def extension instruction.
+ B is the see_ref_s structure pointer. */
+
+static int
+see_delete_merged_def_extension (void **slot, void *b ATTRIBUTE_UNUSED)
+{
+ rtx def_se = *slot;
+
+ if (dump_file)
+ {
+ fprintf (dump_file, "Deleting merged def extension:\n");
+ print_rtl_single (dump_file, def_se);
+ }
+
+ if (INSN_DELETED_P (def_se))
+ /* This def extension is an implicit one. No need to delete it since
+ it is not in the insn stream. */
+ return 1;
+
+ delete_insn (def_se);
+ return 1;
+}
+
+
+/* Delete the unmerged def extension.
+
+ This is a subroutine of see_commit_ref_changes called via htab_traverse.
+
+ SLOT contains the current def extension instruction.
+ B is the see_ref_s structure pointer. */
+
+static int
+see_delete_unmerged_def_extension (void **slot, void *b ATTRIBUTE_UNUSED)
+{
+ rtx def_se = *slot;
+
+ if (dump_file)
+ {
+ fprintf (dump_file, "Deleting unmerged def extension:\n");
+ print_rtl_single (dump_file, def_se);
+ }
+
+ delete_insn (def_se);
+ return 1;
+}
+
+
+/* Emit the non-redundant use extension to the instruction stream.
+
+ This is a subroutine of see_commit_ref_changes called via htab_traverse.
+
+ SLOT contains the current use extension instruction.
+ B is the see_ref_s structure pointer. */
+
+static int
+see_emit_use_extension (void **slot, void *b)
+{
+ rtx use_se = *slot;
+ struct see_ref_s *curr_ref_s = (struct see_ref_s *) b;
+
+ if (INSN_DELETED_P (use_se))
+ /* This use extension was previously removed according to the lcm
+ output. */
+ return 1;
+
+ if (dump_file)
+ {
+ fprintf (dump_file, "Inserting use extension:\n");
+ print_rtl_single (dump_file, use_se);
+ }
+
+ add_insn_before (use_se, curr_ref_s->insn);
+
+ return 1;
+}
+
+
+/* For each relevant reference:
+ a. Emit the non-redundant use extensions.
+ b. Delete the def extensions.
+ c. Replace the original reference with the merged one (if exists) and add the
+ move instructions that were generated.
+
+ This is a subroutine of see_commit_changes called via splay_tree_foreach.
+
+ STN is the current node in the see_bb_splay_ar[i] splay tree. It holds a
+ see_ref_s structure. */
+
+static int
+see_commit_ref_changes (splay_tree_node stn,
+ void *data ATTRIBUTE_UNUSED)
+{
+ htab_t use_se_hash = ((struct see_ref_s *) (stn->value))->use_se_hash;
+ htab_t unmerged_def_se_hash =
+ ((struct see_ref_s *) (stn->value))->unmerged_def_se_hash;
+ htab_t merged_def_se_hash =
+ ((struct see_ref_s *) (stn->value))->merged_def_se_hash;
+ rtx ref = ((struct see_ref_s *) (stn->value))->insn;
+ rtx merged_ref = ((struct see_ref_s *) (stn->value))->merged_insn;
+
+ /* Emit the non-redundant use extensions. */
+ if (use_se_hash)
+ htab_traverse_noresize (use_se_hash, see_emit_use_extension,
+ (PTR) (stn->value));
+
+ /* Delete the def extensions. */
+ if (unmerged_def_se_hash)
+ htab_traverse (unmerged_def_se_hash, see_delete_unmerged_def_extension,
+ (PTR) (stn->value));
+
+ if (merged_def_se_hash)
+ htab_traverse (merged_def_se_hash, see_delete_merged_def_extension,
+ (PTR) (stn->value));
+
+ /* Replace the original reference with the merged one (if exists) and add the
+ move instructions that were generated. */
+ if (merged_ref && !INSN_DELETED_P (ref))
+ {
+ if (dump_file)
+ {
+ fprintf (dump_file, "Replacing orig reference:\n");
+ print_rtl_single (dump_file, ref);
+ fprintf (dump_file, "With merged reference:\n");
+ print_rtl_single (dump_file, merged_ref);
+ }
+ emit_insn_after (merged_ref, ref);
+ delete_insn (ref);
+ }
+
+ /* Continue to the next reference. */
+ return 0;
+}
+
+
+/* Insert partially redundant expressions on edges to make the expressions fully
+ redundant.
+
+ INDEX_MAP is a mapping of an index to an expression.
+ Return true if an instruction was inserted on an edge.
+ Otherwise, return false. */
+
+static bool
+see_pre_insert_extensions (struct see_pre_extension_expr **index_map)
+{
+ int num_edges = NUM_EDGES (edge_list);
+ int set_size = pre_insert_map[0]->size;
+ size_t pre_extension_num = htab_elements (see_pre_extension_hash);
+
+ int did_insert = 0;
+ int e;
+ int i;
+ int j;
+
+ for (e = 0; e < num_edges; e++)
+ {
+ int indx;
+ basic_block bb = INDEX_EDGE_PRED_BB (edge_list, e);
+
+ for (i = indx = 0; i < set_size; i++, indx += SBITMAP_ELT_BITS)
+ {
+ SBITMAP_ELT_TYPE insert = pre_insert_map[e]->elms[i];
+
+ for (j = indx; insert && j < (int) pre_extension_num;
+ j++, insert >>= 1)
+ if (insert & 1)
+ {
+ struct see_pre_extension_expr *expr = index_map[j];
+ int idx = expr->bitmap_index;
+ rtx se_insn = NULL;
+ edge eg = INDEX_EDGE (edge_list, e);
+
+ start_sequence ();
+ emit_insn (PATTERN (expr->se_insn));
+ se_insn = get_insns ();
+ end_sequence ();
+
+ if (eg->flags & EDGE_ABNORMAL)
+ {
+ rtx new_insn = NULL;
+
+ new_insn = insert_insn_end_bb_new (se_insn, bb);
+ gcc_assert (new_insn && INSN_P (new_insn));
+
+ if (dump_file)
+ {
+ fprintf (dump_file,
+ "PRE: end of bb %d, insn %d, ",
+ bb->index, INSN_UID (new_insn));
+ fprintf (dump_file,
+ "inserting expression %d\n", idx);
+ }
+ }
+ else
+ {
+ insert_insn_on_edge (se_insn, eg);
+
+ if (dump_file)
+ {
+ fprintf (dump_file, "PRE: edge (%d,%d), ",
+ bb->index,
+ INDEX_EDGE_SUCC_BB (edge_list, e)->index);
+ fprintf (dump_file, "inserting expression %d\n", idx);
+ }
+ }
+ did_insert = true;
+ }
+ }
+ }
+ return did_insert;
+}
+
+
+/* Since all the redundant extensions must be anticipatable, they must be a use
+ extensions. Mark them as deleted. This will prevent them from been emitted
+ in the first place.
+
+ This is a subroutine of see_commit_changes called via htab_traverse.
+
+ SLOT contains the current see_pre_extension_expr structure pointer. */
+
+static int
+see_pre_delete_extension (void **slot, void *b ATTRIBUTE_UNUSED)
+{
+ struct see_pre_extension_expr *expr = *slot;
+ struct see_occr *occr;
+ int indx = expr->bitmap_index;
+
+ for (occr = expr->antic_occr; occr != NULL; occr = occr->next)
+ {
+ if (TEST_BIT (pre_delete_map[occr->block_num], indx))
+ {
+ /* Mark as deleted. */
+ INSN_DELETED_P (occr->insn) = 1;
+ if (dump_file)
+ {
+ fprintf (dump_file,"Redundant extension deleted:\n");
+ print_rtl_single (dump_file, occr->insn);
+ }
+ }
+ }
+ return 1;
+}
+
+
+/* Create the index_map mapping of an index to an expression.
+
+ This is a subroutine of see_commit_changes called via htab_traverse.
+
+ SLOT contains the current see_pre_extension_expr structure pointer.
+ B a pointer to see_pre_extension_expr structure pointer. */
+
+static int
+see_map_extension (void **slot, void *b)
+{
+ struct see_pre_extension_expr *expr = *slot;
+ struct see_pre_extension_expr **index_map =
+ (struct see_pre_extension_expr **) b;
+
+ index_map[expr->bitmap_index] = expr;
+
+ return 1;
+}
+
+
+/* Phase 4 top level function.
+ In this phase we finally change the instruction stream.
+ Here we insert extensions at their best placements and delete the
+ redundant ones according to the output of the LCM. We also replace
+ some of the instructions according to phase 2 merges results. */
+
+static void
+see_commit_changes (void)
+{
+ struct see_pre_extension_expr **index_map;
+ size_t pre_extension_num = htab_elements (see_pre_extension_hash);
+ bool did_insert = false;
+ int i;
+
+ index_map = xcalloc (pre_extension_num,
+ sizeof (struct see_pre_extension_expr *));
+
+ if (dump_file)
+ fprintf (dump_file,
+ "* Phase 4: Commit changes to the insn stream. *\n");
+
+ /* Produce a mapping of all the pre_extensions. */
+ htab_traverse (see_pre_extension_hash, see_map_extension, (PTR) index_map);
+
+ /* Delete redundant extension. This will prevent them from been emitted in
+ the first place. */
+ htab_traverse (see_pre_extension_hash, see_pre_delete_extension, NULL);
+
+ /* At this point, we must free the DF object, since the number of basic blocks
+ may change. */
+ df_finish (df);
+ df = NULL;
+
+ /* Insert extensions on edges, according to the LCM result. */
+ did_insert = see_pre_insert_extensions (index_map);
+
+ if (did_insert)
+ commit_edge_insertions ();
+
+ /* Commit the rest of the changes. */
+ for (i = 0; i < last_bb; i++)
+ {
+ if (see_bb_splay_ar[i])
+ {
+ /* Traverse over all the references in the basic block in forward
+ order. */
+ splay_tree_foreach (see_bb_splay_ar[i],
+ see_commit_ref_changes, NULL);
+ }
+ }
+
+ free (index_map);
+}
+
+
+/* Phase 3 implementation: Eliminate globally redundant extensions. */
+
+/* Analyze the properties of a merged def extension for the LCM and record avail
+ occurrences.
+
+ This is a subroutine of see_analyze_ref_local_prop called
+ via htab_traverse.
+
+ SLOT contains the current def extension instruction.
+ B is the see_ref_s structure pointer. */
+
+static int
+see_analyze_merged_def_local_prop (void **slot, void *b)
+{
+ rtx def_se = *slot;
+ struct see_ref_s *curr_ref_s = (struct see_ref_s *) b;
+ rtx ref = curr_ref_s->insn;
+ struct see_pre_extension_expr *extension_expr;
+ int indx;
+ int bb_num = BLOCK_NUM (ref);
+ htab_t curr_bb_hash;
+ struct see_register_properties *curr_prop, **slot_prop;
+ struct see_register_properties temp_prop;
+ rtx dest_extension_reg = see_get_extension_reg (def_se, 1);
+ struct see_occr *curr_occr = NULL;
+ struct see_occr *tmp_occr = NULL;
+
+ extension_expr = see_seek_pre_extension_expr (def_se, DEF_EXTENSION);
+ /* The extension_expr must be found. */
+ gcc_assert (extension_expr);
+
+ curr_bb_hash = see_bb_hash_ar[bb_num];
+ gcc_assert (curr_bb_hash);
+ temp_prop.regno = REGNO (dest_extension_reg);
+ slot_prop =
+ (struct see_register_properties **) htab_find_slot (curr_bb_hash,
+ &temp_prop, INSERT);
+ curr_prop = *slot_prop;
+ gcc_assert (curr_prop);
+
+ indx = extension_expr->bitmap_index;
+
+ /* Reset the transparency bit. */
+ RESET_BIT (transp[bb_num], indx);
+ /* Reset the killed bit. */
+ RESET_BIT (ae_kill[bb_num], indx);
+
+ if (curr_prop->first_se_after_last_def == DF_INSN_LUID (df, ref))
+ {
+ /* Set the available bit. */
+ SET_BIT (comp[bb_num], indx);
+ /* Record the available occurrence. */
+ curr_occr = xmalloc (sizeof (struct see_occr));
+ curr_occr->next = NULL;
+ curr_occr->insn = def_se;
+ curr_occr->block_num = bb_num;
+ tmp_occr = extension_expr->avail_occr;
+ if (!tmp_occr)
+ extension_expr->avail_occr = curr_occr;
+ else
+ {
+ while (tmp_occr->next)
+ tmp_occr = tmp_occr->next;
+ tmp_occr->next = curr_occr;
+ }
+ }
+
+ return 1;
+}
+
+
+/* Analyze the properties of a unmerged def extension for the LCM.
+
+ This is a subroutine of see_analyze_ref_local_prop called
+ via htab_traverse.
+
+ SLOT contains the current def extension instruction.
+ B is the see_ref_s structure pointer. */
+
+static int
+see_analyze_unmerged_def_local_prop (void **slot, void *b)
+{
+ rtx def_se = *slot;
+ struct see_ref_s *curr_ref_s = (struct see_ref_s *) b;
+ rtx ref = curr_ref_s->insn;
+ struct see_pre_extension_expr *extension_expr;
+ int indx;
+ int bb_num = BLOCK_NUM (ref);
+ htab_t curr_bb_hash;
+ struct see_register_properties *curr_prop, **slot_prop;
+ struct see_register_properties temp_prop;
+ rtx dest_extension_reg = see_get_extension_reg (def_se, 1);
+
+ extension_expr = see_seek_pre_extension_expr (def_se, DEF_EXTENSION);
+ /* The extension_expr must be found. */
+ gcc_assert (extension_expr);
+
+ curr_bb_hash = see_bb_hash_ar[bb_num];
+ gcc_assert (curr_bb_hash);
+ temp_prop.regno = REGNO (dest_extension_reg);
+ slot_prop =
+ (struct see_register_properties **) htab_find_slot (curr_bb_hash,
+ &temp_prop, INSERT);
+ curr_prop = *slot_prop;
+ gcc_assert (curr_prop);
+
+ indx = extension_expr->bitmap_index;
+
+ /* Reset the transparency bit. */
+ RESET_BIT (transp[bb_num], indx);
+ /* Set the killed bit. */
+ SET_BIT (ae_kill[bb_num], indx);
+
+ return 1;
+}
+
+
+/* Analyze the properties of a use extension for the LCM and record anic and
+ avail occurrences.
+
+ This is a subroutine of see_analyze_ref_local_prop called
+ via htab_traverse.
+
+ SLOT contains the current use extension instruction.
+ B is the see_ref_s structure pointer. */
+
+static int
+see_analyze_use_local_prop (void **slot, void *b)
+{
+ struct see_ref_s *curr_ref_s = (struct see_ref_s *) b;
+ rtx use_se = *slot;
+ rtx ref = curr_ref_s->insn;
+ rtx dest_extension_reg = see_get_extension_reg (use_se, 1);
+ struct see_pre_extension_expr *extension_expr;
+ struct see_register_properties *curr_prop, **slot_prop;
+ struct see_register_properties temp_prop;
+ struct see_occr *curr_occr = NULL;
+ struct see_occr *tmp_occr = NULL;
+ htab_t curr_bb_hash;
+ int indx;
+ int bb_num = BLOCK_NUM (ref);
+
+ extension_expr = see_seek_pre_extension_expr (use_se, USE_EXTENSION);
+ /* The extension_expr must be found. */
+ gcc_assert (extension_expr);
+
+ curr_bb_hash = see_bb_hash_ar[bb_num];
+ gcc_assert (curr_bb_hash);
+ temp_prop.regno = REGNO (dest_extension_reg);
+ slot_prop =
+ (struct see_register_properties **) htab_find_slot (curr_bb_hash,
+ &temp_prop, INSERT);
+ curr_prop = *slot_prop;
+ gcc_assert (curr_prop);
+
+ indx = extension_expr->bitmap_index;
+
+ if (curr_prop->first_se_before_any_def == DF_INSN_LUID (df, ref))
+ {
+ /* Set the anticipatable bit. */
+ SET_BIT (antloc[bb_num], indx);
+ /* Record the anticipatable occurrence. */
+ curr_occr = xmalloc (sizeof (struct see_occr));
+ curr_occr->next = NULL;
+ curr_occr->insn = use_se;
+ curr_occr->block_num = bb_num;
+ tmp_occr = extension_expr->antic_occr;
+ if (!tmp_occr)
+ extension_expr->antic_occr = curr_occr;
+ else
+ {
+ while (tmp_occr->next)
+ tmp_occr = tmp_occr->next;
+ tmp_occr->next = curr_occr;
+ }
+ if (curr_prop->last_def < 0)
+ {
+ /* Set the available bit. */
+ SET_BIT (comp[bb_num], indx);
+ /* Record the available occurrence. */
+ curr_occr = xmalloc (sizeof (struct see_occr));
+ curr_occr->next = NULL;
+ curr_occr->insn = use_se;
+ curr_occr->block_num = bb_num;
+ tmp_occr = extension_expr->avail_occr;
+ if (!tmp_occr)
+ extension_expr->avail_occr = curr_occr;
+ else
+ {
+ while (tmp_occr->next)
+ tmp_occr = tmp_occr->next;
+ tmp_occr->next = curr_occr;
+ }
+ }
+ /* Note: there is no need to reset the killed bit since it must be zero at
+ this point. */
+ }
+ else if (curr_prop->first_se_after_last_def == DF_INSN_LUID (df, ref))
+ {
+ /* Set the available bit. */
+ SET_BIT (comp[bb_num], indx);
+ /* Reset the killed bit. */
+ RESET_BIT (ae_kill[bb_num], indx);
+ /* Record the available occurrence. */
+ curr_occr = xmalloc (sizeof (struct see_occr));
+ curr_occr->next = NULL;
+ curr_occr->insn = use_se;
+ curr_occr->block_num = bb_num;
+ tmp_occr = extension_expr->avail_occr;
+ if (!tmp_occr)
+ extension_expr->avail_occr = curr_occr;
+ else
+ {
+ while (tmp_occr->next)
+ tmp_occr = tmp_occr->next;
+ tmp_occr->next = curr_occr;
+ }
+ }
+ return 1;
+}
+
+
+/* Here we traverse over all the merged and unmerged extensions of the reference
+ and analyze their properties for the LCM.
+
+ This is a subroutine of see_execute_LCM called via splay_tree_foreach.
+
+ STN is the current node in the see_bb_splay_ar[i] splay tree. It holds a
+ see_ref_s structure. */
+
+static int
+see_analyze_ref_local_prop (splay_tree_node stn,
+ void *data ATTRIBUTE_UNUSED)
+{
+ htab_t use_se_hash = ((struct see_ref_s *) (stn->value))->use_se_hash;
+ htab_t unmerged_def_se_hash =
+ ((struct see_ref_s *) (stn->value))->unmerged_def_se_hash;
+ htab_t merged_def_se_hash =
+ ((struct see_ref_s *) (stn->value))->merged_def_se_hash;
+
+ /* Analyze use extensions that were not merged with the reference. */
+ if (use_se_hash)
+ htab_traverse_noresize (use_se_hash, see_analyze_use_local_prop,
+ (PTR) (stn->value));
+
+ /* Analyze def extensions that were not merged with the reference. */
+ if (unmerged_def_se_hash)
+ htab_traverse (unmerged_def_se_hash, see_analyze_unmerged_def_local_prop,
+ (PTR) (stn->value));
+
+ /* Analyze def extensions that were merged with the reference. */
+ if (merged_def_se_hash)
+ htab_traverse (merged_def_se_hash, see_analyze_merged_def_local_prop,
+ (PTR) (stn->value));
+
+ /* Continue to the next definition. */
+ return 0;
+}
+
+
+/* Phase 3 top level function.
+ In this phase, we set the input bit vectors of the LCM according to data
+ gathered in phase 2.
+ Then we run the edge based LCM. */
+
+static void
+see_execute_LCM (void)
+{
+ size_t pre_extension_num = htab_elements (see_pre_extension_hash);
+ int i = 0;
+
+ if (dump_file)
+ fprintf (dump_file,
+ "* Phase 3: Eliminate globally redundant extensions. *\n");
+
+ /* Initialize the global sbitmap vectors. */
+ transp = sbitmap_vector_alloc (last_bb, pre_extension_num);
+ comp = sbitmap_vector_alloc (last_bb, pre_extension_num);
+ antloc = sbitmap_vector_alloc (last_bb, pre_extension_num);
+ ae_kill = sbitmap_vector_alloc (last_bb, pre_extension_num);
+ sbitmap_vector_ones (transp, last_bb);
+ sbitmap_vector_zero (comp, last_bb);
+ sbitmap_vector_zero (antloc, last_bb);
+ sbitmap_vector_zero (ae_kill, last_bb);
+
+ /* Traverse over all the splay trees of the basic blocks. */
+ for (i = 0; i < last_bb; i++)
+ {
+ if (see_bb_splay_ar[i])
+ {
+ /* Traverse over all the references in the basic block in forward
+ order. */
+ splay_tree_foreach (see_bb_splay_ar[i],
+ see_analyze_ref_local_prop, NULL);
+ }
+ }
+
+ /* Add fake exit edges before running the lcm. */
+ add_noreturn_fake_exit_edges ();
+
+ /* Run the LCM. */
+ edge_list = pre_edge_lcm (pre_extension_num, transp, comp, antloc,
+ ae_kill, &pre_insert_map, &pre_delete_map);
+
+ /* Remove the fake edges. */
+ remove_fake_exit_edges ();
+}
+
+
+/* Phase 2 implementation: Merge and eliminate locally redundant extensions. */
+
+/* In this function we set the register properties for the register that is
+ defined and extended in the reference.
+ The properties are defined in see_register_properties structure which is
+ allocated per basic block and per register.
+ Later the extension is inserted into the see_pre_extension_hash for the next
+ phase of the optimization.
+
+ This is a subroutine of see_handle_extensions_for_one_ref called
+ via htab_traverse.
+
+ SLOT contains the current def extension instruction.
+ B is the see_ref_s structure pointer. */
+
+static int
+see_set_prop_merged_def (void **slot, void *b)
+{
+ rtx def_se = *slot;
+ struct see_ref_s *curr_ref_s = (struct see_ref_s *) b;
+ rtx insn = curr_ref_s->insn;
+ rtx dest_extension_reg = see_get_extension_reg (def_se, 1);
+ htab_t curr_bb_hash;
+ struct see_register_properties *curr_prop = NULL;
+ struct see_register_properties **slot_prop;
+ struct see_register_properties temp_prop;
+ int ref_luid = DF_INSN_LUID (df, insn);
+
+ curr_bb_hash = see_bb_hash_ar[BLOCK_NUM (curr_ref_s->insn)];
+ if (!curr_bb_hash)
+ {
+ /* The hash doesn't exist yet. Create it. */
+ curr_bb_hash = htab_create (10,
+ hash_descriptor_properties,
+ eq_descriptor_properties,
+ hash_del_properties);
+ see_bb_hash_ar[BLOCK_NUM (curr_ref_s->insn)] = curr_bb_hash;
+ }
+
+ /* Find the right register properties in the right basic block. */
+ temp_prop.regno = REGNO (dest_extension_reg);
+ slot_prop =
+ (struct see_register_properties **) htab_find_slot (curr_bb_hash,
+ &temp_prop, INSERT);
+
+ if (slot_prop && *slot_prop != NULL)
+ {
+ /* Property already exists. */
+ curr_prop = *slot_prop;
+ gcc_assert (curr_prop->regno == REGNO (dest_extension_reg));
+
+ curr_prop->last_def = ref_luid;
+ curr_prop->first_se_after_last_def = ref_luid;
+ }
+ else
+ {
+ /* Property doesn't exist yet. */
+ curr_prop = xmalloc (sizeof (struct see_register_properties));
+ curr_prop->regno = REGNO (dest_extension_reg);
+ curr_prop->last_def = ref_luid;
+ curr_prop->first_se_before_any_def = -1;
+ curr_prop->first_se_after_last_def = ref_luid;
+ *slot_prop = curr_prop;
+ }
+
+ /* Insert the def_se into see_pre_extension_hash if it isn't already
+ there. */
+ see_seek_pre_extension_expr (def_se, DEF_EXTENSION);
+
+ return 1;
+}
+
+
+/* In this function we set the register properties for the register that is
+ defined but not extended in the reference.
+ The properties are defined in see_register_properties structure which is
+ allocated per basic block and per register.
+ Later the extension is inserted into the see_pre_extension_hash for the next
+ phase of the optimization.
+
+ This is a subroutine of see_handle_extensions_for_one_ref called
+ via htab_traverse.
+
+ SLOT contains the current def extension instruction.
+ B is the see_ref_s structure pointer. */
+
+static int
+see_set_prop_unmerged_def (void **slot, void *b)
+{
+ rtx def_se = *slot;
+ struct see_ref_s *curr_ref_s = (struct see_ref_s *) b;
+ rtx insn = curr_ref_s->insn;
+ rtx dest_extension_reg = see_get_extension_reg (def_se, 1);
+ htab_t curr_bb_hash;
+ struct see_register_properties *curr_prop = NULL;
+ struct see_register_properties **slot_prop;
+ struct see_register_properties temp_prop;
+ int ref_luid = DF_INSN_LUID (df, insn);
+
+ curr_bb_hash = see_bb_hash_ar[BLOCK_NUM (curr_ref_s->insn)];
+ if (!curr_bb_hash)
+ {
+ /* The hash doesn't exist yet. Create it. */
+ curr_bb_hash = htab_create (10,
+ hash_descriptor_properties,
+ eq_descriptor_properties,
+ hash_del_properties);
+ see_bb_hash_ar[BLOCK_NUM (curr_ref_s->insn)] = curr_bb_hash;
+ }
+
+ /* Find the right register properties in the right basic block. */
+ temp_prop.regno = REGNO (dest_extension_reg);
+ slot_prop =
+ (struct see_register_properties **) htab_find_slot (curr_bb_hash,
+ &temp_prop, INSERT);
+
+ if (slot_prop && *slot_prop != NULL)
+ {
+ /* Property already exists. */
+ curr_prop = *slot_prop;
+ gcc_assert (curr_prop->regno == REGNO (dest_extension_reg));
+
+ curr_prop->last_def = ref_luid;
+ curr_prop->first_se_after_last_def = -1;
+ }
+ else
+ {
+ /* Property doesn't exist yet. */
+ curr_prop = xmalloc (sizeof (struct see_register_properties));
+ curr_prop->regno = REGNO (dest_extension_reg);
+ curr_prop->last_def = ref_luid;
+ curr_prop->first_se_before_any_def = -1;
+ curr_prop->first_se_after_last_def = -1;
+ *slot_prop = curr_prop;
+ }
+
+ /* Insert the def_se into see_pre_extension_hash if it isn't already
+ there. */
+ see_seek_pre_extension_expr (def_se, DEF_EXTENSION);
+
+ return 1;
+}
+
+
+/* In this function we set the register properties for the register that is used
+ in the reference.
+ The properties are defined in see_register_properties structure which is
+ allocated per basic block and per register.
+ When a redundant use extension is found it is removed from the hash of the
+ reference.
+ If the extension is non redundant it is inserted into the
+ see_pre_extension_hash for the next phase of the optimization.
+
+ This is a subroutine of see_handle_extensions_for_one_ref called
+ via htab_traverse.
+
+ SLOT contains the current use extension instruction.
+ B is the see_ref_s structure pointer. */
+
+static int
+see_set_prop_unmerged_use (void **slot, void *b)
+{
+ rtx use_se = *slot;
+ struct see_ref_s *curr_ref_s = (struct see_ref_s *) b;
+ rtx insn = curr_ref_s->insn;
+ rtx dest_extension_reg = see_get_extension_reg (use_se, 1);
+ htab_t curr_bb_hash;
+ struct see_register_properties *curr_prop = NULL;
+ struct see_register_properties **slot_prop;
+ struct see_register_properties temp_prop;
+ bool locally_redundant = false;
+ int ref_luid = DF_INSN_LUID (df, insn);
+
+ curr_bb_hash = see_bb_hash_ar[BLOCK_NUM (curr_ref_s->insn)];
+ if (!curr_bb_hash)
+ {
+ /* The hash doesn't exist yet. Create it. */
+ curr_bb_hash = htab_create (10,
+ hash_descriptor_properties,
+ eq_descriptor_properties,
+ hash_del_properties);
+ see_bb_hash_ar[BLOCK_NUM (curr_ref_s->insn)] = curr_bb_hash;
+ }
+
+ /* Find the right register properties in the right basic block. */
+ temp_prop.regno = REGNO (dest_extension_reg);
+ slot_prop =
+ (struct see_register_properties **) htab_find_slot (curr_bb_hash,
+ &temp_prop, INSERT);
+
+ if (slot_prop && *slot_prop != NULL)
+ {
+ /* Property already exists. */
+ curr_prop = *slot_prop;
+ gcc_assert (curr_prop->regno == REGNO (dest_extension_reg));
+
+
+ if (curr_prop->last_def < 0 && curr_prop->first_se_before_any_def < 0)
+ curr_prop->first_se_before_any_def = ref_luid;
+ else if (curr_prop->last_def < 0
+ && curr_prop->first_se_before_any_def >= 0)
+ {
+ /* In this case the extension is locally redundant. */
+ htab_clear_slot (curr_ref_s->use_se_hash, (PTR *)slot);
+ locally_redundant = true;
+ }
+ else if (curr_prop->last_def >= 0
+ && curr_prop->first_se_after_last_def < 0)
+ curr_prop->first_se_after_last_def = ref_luid;
+ else if (curr_prop->last_def >= 0
+ && curr_prop->first_se_after_last_def >= 0)
+ {
+ /* In this case the extension is locally redundant. */
+ htab_clear_slot (curr_ref_s->use_se_hash, (PTR *)slot);
+ locally_redundant = true;
+ }
+ else
+ gcc_unreachable ();
+ }
+ else
+ {
+ /* Property doesn't exist yet. Create a new one. */
+ curr_prop = xmalloc (sizeof (struct see_register_properties));
+ curr_prop->regno = REGNO (dest_extension_reg);
+ curr_prop->last_def = -1;
+ curr_prop->first_se_before_any_def = ref_luid;
+ curr_prop->first_se_after_last_def = -1;
+ *slot_prop = curr_prop;
+ }
+
+ /* Insert the use_se into see_pre_extension_hash if it isn't already
+ there. */
+ if (!locally_redundant)
+ see_seek_pre_extension_expr (use_se, USE_EXTENSION);
+ if (locally_redundant && dump_file)
+ {
+ fprintf (dump_file, "Locally redundant extension:\n");
+ print_rtl_single (dump_file, use_se);
+ }
+ return 1;
+}
+
+
+/* Print an extension instruction.
+
+ This is a subroutine of see_handle_extensions_for_one_ref called
+ via htab_traverse.
+ SLOT contains the extension instruction. */
+
+static int
+see_print_one_extension (void **slot, void *b ATTRIBUTE_UNUSED)
+{
+ rtx def_se = *slot;
+
+ gcc_assert (def_se && INSN_P (def_se));
+ print_rtl_single (dump_file, def_se);
+
+ return 1;
+}
+
+/* Function called by note_uses to replace used subexpressions.
+
+ X is a pointer to the subexpression and DATA is a pointer to a
+ see_replace_data structure that contains the data for the replacement. */
+
+static void
+see_replace_src (rtx *x, void *data)
+{
+ struct see_replace_data *d
+ = (struct see_replace_data *) data;
+
+ *x = replace_rtx (*x, d->from, d->to);
+}
+
+
+/* At this point the pattern is expected to be:
+
+ ref: set (dest_reg) (rhs)
+ def_se: set (dest_extension_reg) (sign/zero_extend (source_extension_reg))
+
+ The merge of these two instructions didn't succeed.
+
+ We try to generate the pattern:
+ set (subreg (dest_extension_reg)) (rhs)
+
+ We do this in 4 steps:
+ a. Replace every use of dest_reg with a new pseudo register.
+ b. Replace every instance of dest_reg with the subreg.
+ c. Replace every use of the new pseudo register back to dest_reg.
+ d. Try to recognize and simplify.
+
+ If the manipulation failed, leave the original ref but try to generate and
+ recognize a simple move instruction:
+ set (subreg (dest_extension_reg)) (dest_reg)
+ This move instruction will be emitted right after the ref to the instruction
+ stream and assure the correctness of the code after def_se will be removed.
+
+ CURR_REF_S is the current reference.
+ DEF_SE is the extension that couldn't be merged. */
+
+static void
+see_def_extension_not_merged (struct see_ref_s *curr_ref_s, rtx def_se)
+{
+ struct see_replace_data d;
+ /* If the original insn was already merged with an extension before,
+ take the merged one. */
+ rtx ref = (curr_ref_s->merged_insn) ? curr_ref_s->merged_insn :
+ curr_ref_s->insn;
+ rtx merged_ref_next = (curr_ref_s->merged_insn) ?
+ NEXT_INSN (curr_ref_s->merged_insn): NULL_RTX;
+ rtx ref_copy = copy_rtx (ref);
+ rtx source_extension_reg = see_get_extension_reg (def_se, 0);
+ rtx dest_extension_reg = see_get_extension_reg (def_se, 1);
+ rtx move_insn = NULL;
+ rtx set, rhs;
+ rtx dest_reg, dest_real_reg;
+ rtx new_pseudo_reg, subreg;
+ enum machine_mode source_extension_mode = GET_MODE (source_extension_reg);
+ enum machine_mode dest_mode;
+
+ set = single_set (def_se);
+ gcc_assert (set);
+ rhs = SET_SRC (set);
+ gcc_assert (GET_CODE (rhs) == SIGN_EXTEND
+ || GET_CODE (rhs) == ZERO_EXTEND);
+ dest_reg = XEXP (rhs, 0);
+ gcc_assert (REG_P (dest_reg)
+ || (GET_CODE (dest_reg) == SUBREG
+ && REG_P (SUBREG_REG (dest_reg))));
+ dest_real_reg = REG_P (dest_reg) ? dest_reg : SUBREG_REG (dest_reg);
+ dest_mode = GET_MODE (dest_reg);
+
+ subreg = gen_lowpart_SUBREG (dest_mode, dest_extension_reg);
+ new_pseudo_reg = gen_reg_rtx (source_extension_mode);
+
+ /* Step a: Replace every use of dest_real_reg with a new pseudo register. */
+ d.from = dest_real_reg;
+ d.to = new_pseudo_reg;
+ note_uses (&PATTERN (ref_copy), see_replace_src, &d);
+ /* Step b: Replace every instance of dest_reg with the subreg. */
+ ref_copy = replace_rtx (ref_copy, dest_reg, subreg);
+
+ /* Step c: Replace every use of the new pseudo register back to
+ dest_real_reg. */
+ d.from = new_pseudo_reg;
+ d.to = dest_real_reg;
+ note_uses (&PATTERN (ref_copy), see_replace_src, &d);
+
+ if (rtx_equal_p (PATTERN (ref), PATTERN (ref_copy))
+ || insn_invalid_p (ref_copy))
+ {
+ /* The manipulation failed. */
+
+ /* Create a new copy. */
+ ref_copy = copy_rtx (ref);
+
+ /* Create a simple move instruction that will replace the def_se. */
+ start_sequence ();
+ emit_move_insn (subreg, dest_reg);
+ move_insn = get_insns ();
+ end_sequence ();
+
+ /* Link the manipulated instruction to the newly created move instruction
+ and to the former created move instructions. */
+ PREV_INSN (ref_copy) = NULL_RTX;
+ NEXT_INSN (ref_copy) = move_insn;
+ PREV_INSN (move_insn) = ref_copy;
+ NEXT_INSN (move_insn) = merged_ref_next;
+ if (merged_ref_next != NULL_RTX)
+ PREV_INSN (merged_ref_next) = move_insn;
+ curr_ref_s->merged_insn = ref_copy;
+
+ if (dump_file)
+ {
+ fprintf (dump_file, "Following def merge failure a move ");
+ fprintf (dump_file, "insn was added after the ref.\n");
+ fprintf (dump_file, "Original ref:\n");
+ print_rtl_single (dump_file, ref);
+ fprintf (dump_file, "Move insn that was added:\n");
+ print_rtl_single (dump_file, move_insn);
+ }
+ return;
+ }
+
+ /* The manipulation succeeded. Store the new manipulated reference. */
+
+ /* Try to simplify the new manipulated insn. */
+ validate_simplify_insn (ref_copy);
+
+ /* Create a simple move instruction to assure the correctness of the code. */
+ start_sequence ();
+ emit_move_insn (dest_reg, subreg);
+ move_insn = get_insns ();
+ end_sequence ();
+
+ /* Link the manipulated instruction to the newly created move instruction and
+ to the former created move instructions. */
+ PREV_INSN (ref_copy) = NULL_RTX;
+ NEXT_INSN (ref_copy) = move_insn;
+ PREV_INSN (move_insn) = ref_copy;
+ NEXT_INSN (move_insn) = merged_ref_next;
+ if (merged_ref_next != NULL_RTX)
+ PREV_INSN (merged_ref_next) = move_insn;
+ curr_ref_s->merged_insn = ref_copy;
+
+ if (dump_file)
+ {
+ fprintf (dump_file, "Following merge failure the ref was transformed!\n");
+ fprintf (dump_file, "Original ref:\n");
+ print_rtl_single (dump_file, ref);
+ fprintf (dump_file, "Transformed ref:\n");
+ print_rtl_single (dump_file, ref_copy);
+ fprintf (dump_file, "Move insn that was added:\n");
+ print_rtl_single (dump_file, move_insn);
+ }
+}
+
+
+/* Merge the reference instruction (ref) with the current use extension.
+
+ use_se extends a NARROWmode register to a WIDEmode register.
+ ref uses the WIDEmode register.
+
+ The pattern we try to merge is this:
+ use_se: set (dest_extension_reg) (sign/zero_extend (source_extension_reg))
+ ref: use (dest_extension_reg)
+
+ where dest_extension_reg and source_extension_reg can be subregs.
+
+ The merge is done by generating, simplifying and recognizing the pattern:
+ use (sign/zero_extend (source_extension_reg))
+
+ If ref is too simple (according to see_want_to_be_merged_with_extension ())
+ we don't try to merge it with use_se and we continue as if the merge failed.
+
+ This is a subroutine of see_handle_extensions_for_one_ref called
+ via htab_traverse.
+ SLOT contains the current use extension instruction.
+ B is the see_ref_s structure pointer. */
+
+static int
+see_merge_one_use_extension (void **slot, void *b)
+{
+ struct see_ref_s *curr_ref_s = (struct see_ref_s *) b;
+ rtx use_se = *slot;
+ rtx ref = (curr_ref_s->merged_insn) ? curr_ref_s->merged_insn :
+ curr_ref_s->insn;
+ rtx merged_ref_next = (curr_ref_s->merged_insn) ?
+ NEXT_INSN (curr_ref_s->merged_insn): NULL_RTX;
+ rtx ref_copy = copy_rtx (ref);
+ rtx extension_set = single_set (use_se);
+ rtx extension_rhs = NULL;
+ rtx dest_extension_reg = see_get_extension_reg (use_se, 1);
+ rtx note = NULL;
+ rtx simplified_note = NULL;
+
+ gcc_assert (use_se && curr_ref_s && extension_set);
+
+ extension_rhs = SET_SRC (extension_set);
+
+ /* In REG_EQUIV and REG_EQUAL notes that mention the register we need to
+ replace the uses of the dest_extension_reg with the rhs of the extension
+ instruction. This is necessary since there might not be an extension in
+ the path between the definition and the note when this optimization is
+ over. */
+ note = find_reg_equal_equiv_note (ref_copy);
+ if (note)
+ {
+ simplified_note = simplify_replace_rtx (XEXP (note, 0),
+ dest_extension_reg,
+ extension_rhs);
+ if (rtx_equal_p (XEXP (note, 0), simplified_note))
+ /* Replacement failed. Remove the note. */
+ remove_note (ref_copy, note);
+ else
+ XEXP (note, 0) = simplified_note;
+ }
+
+ if (!see_want_to_be_merged_with_extension (ref, use_se, USE_EXTENSION))
+ {
+ /* The use in the reference is too simple. Don't try to merge. */
+ if (dump_file)
+ {
+ fprintf (dump_file, "Use merge skipped!\n");
+ fprintf (dump_file, "Original instructions:\n");
+ print_rtl_single (dump_file, use_se);
+ print_rtl_single (dump_file, ref);
+ }
+ /* Don't remove the current use_se from the use_se_hash and continue to
+ the next extension. */
+ return 1;
+ }
+
+ validate_replace_src_group (dest_extension_reg, extension_rhs, ref_copy);
+
+ if (!num_changes_pending ())
+ /* In this case this is not a real use (the only use is/was in the notes
+ list). Remove the use extension from the hash. This will prevent it
+ from been emitted in the first place. */
+ {
+ if (dump_file)
+ {
+ fprintf (dump_file, "Use extension not necessary before:\n");
+ print_rtl_single (dump_file, ref);
+ }
+ htab_clear_slot (curr_ref_s->use_se_hash, (PTR *)slot);
+ PREV_INSN (ref_copy) = NULL_RTX;
+ NEXT_INSN (ref_copy) = merged_ref_next;
+ if (merged_ref_next != NULL_RTX)
+ PREV_INSN (merged_ref_next) = ref_copy;
+ curr_ref_s->merged_insn = ref_copy;
+ return 1;
+ }
+
+ if (!apply_change_group ())
+ {
+ /* The merge failed. */
+ if (dump_file)
+ {
+ fprintf (dump_file, "Use merge failed!\n");
+ fprintf (dump_file, "Original instructions:\n");
+ print_rtl_single (dump_file, use_se);
+ print_rtl_single (dump_file, ref);
+ }
+ /* Don't remove the current use_se from the use_se_hash and continue to
+ the next extension. */
+ return 1;
+ }
+
+ /* The merge succeeded! */
+
+ /* Try to simplify the new merged insn. */
+ validate_simplify_insn (ref_copy);
+
+ PREV_INSN (ref_copy) = NULL_RTX;
+ NEXT_INSN (ref_copy) = merged_ref_next;
+ if (merged_ref_next != NULL_RTX)
+ PREV_INSN (merged_ref_next) = ref_copy;
+ curr_ref_s->merged_insn = ref_copy;
+
+ if (dump_file)
+ {
+ fprintf (dump_file, "Use merge succeeded!\n");
+ fprintf (dump_file, "Original instructions:\n");
+ print_rtl_single (dump_file, use_se);
+ print_rtl_single (dump_file, ref);
+ fprintf (dump_file, "Merged instruction:\n");
+ print_rtl_single (dump_file, ref_copy);
+ }
+
+ /* Remove the current use_se from the use_se_hash. This will prevent it from
+ been emitted in the first place. */
+ htab_clear_slot (curr_ref_s->use_se_hash, (PTR *)slot);
+ return 1;
+}
+
+
+/* Merge the reference instruction (ref) with the extension that follows it
+ in the same basic block (def_se).
+ ref sets a NARROWmode register and def_se extends it to WIDEmode register.
+
+ The pattern we try to merge is this:
+ ref: set (dest_reg) (rhs)
+ def_se: set (dest_extension_reg) (sign/zero_extend (source_extension_reg))
+
+ where dest_reg and source_extension_reg can both be subregs (together)
+ and (REGNO (dest_reg) == REGNO (source_extension_reg))
+
+ The merge is done by generating, simplifying and recognizing the pattern:
+ set (dest_extension_reg) (sign/zero_extend (rhs))
+ If ref is a parallel instruction we just replace the relevant set in it.
+
+ If ref is too simple (according to see_want_to_be_merged_with_extension ())
+ we don't try to merge it with def_se and we continue as if the merge failed.
+
+ This is a subroutine of see_handle_extensions_for_one_ref called
+ via htab_traverse.
+
+ SLOT contains the current def extension instruction.
+ B is the see_ref_s structure pointer. */
+
+static int
+see_merge_one_def_extension (void **slot, void *b)
+{
+ struct see_ref_s *curr_ref_s = (struct see_ref_s *) b;
+ rtx def_se = *slot;
+ /* If the original insn was already merged with an extension before,
+ take the merged one. */
+ rtx ref = (curr_ref_s->merged_insn) ? curr_ref_s->merged_insn :
+ curr_ref_s->insn;
+ rtx merged_ref_next = (curr_ref_s->merged_insn) ?
+ NEXT_INSN (curr_ref_s->merged_insn): NULL_RTX;
+ rtx ref_copy = copy_rtx (ref);
+ rtx new_set = NULL;
+ rtx source_extension_reg = see_get_extension_reg (def_se, 0);
+ rtx dest_extension_reg = see_get_extension_reg (def_se, 1);
+ rtx move_insn, *rtx_slot, subreg;
+ rtx temp_extension = NULL;
+ rtx simplified_temp_extension = NULL;
+ rtx *pat;
+ enum rtx_code code;
+ enum rtx_code extension_code;
+ enum machine_mode source_extension_mode;
+ enum machine_mode source_mode;
+ enum machine_mode dest_extension_mode;
+ bool merge_success = false;
+ int i;
+
+ gcc_assert (def_se
+ && INSN_P (def_se)
+ && curr_ref_s
+ && ref
+ && INSN_P (ref));
+
+ if (!see_want_to_be_merged_with_extension (ref, def_se, DEF_EXTENSION))
+ {
+ /* The definition in the reference is too simple. Don't try to merge. */
+ if (dump_file)
+ {
+ fprintf (dump_file, "Def merge skipped!\n");
+ fprintf (dump_file, "Original instructions:\n");
+ print_rtl_single (dump_file, ref);
+ print_rtl_single (dump_file, def_se);
+ }
+
+ see_def_extension_not_merged (curr_ref_s, def_se);
+ /* Continue to the next extension. */
+ return 1;
+ }
+
+ extension_code = see_get_extension_data (def_se, &source_mode);
+
+ /* Try to merge and simplify the extension. */
+ source_extension_mode = GET_MODE (source_extension_reg);
+ dest_extension_mode = GET_MODE (dest_extension_reg);
+
+ pat = &PATTERN (ref_copy);
+ code = GET_CODE (*pat);
+
+ if (code == PARALLEL)
+ {
+ bool need_to_apply_change = false;
+
+ for (i = 0; i < XVECLEN (*pat, 0); i++)
+ {
+ rtx *sub = &XVECEXP (*pat, 0, i);
+
+ if (GET_CODE (*sub) == SET
+ && GET_MODE (SET_SRC (*sub)) != VOIDmode
+ && GET_MODE (SET_DEST (*sub)) == source_mode
+ && ((REG_P (SET_DEST (*sub))
+ && REGNO (SET_DEST (*sub)) == REGNO (source_extension_reg))
+ || (GET_CODE (SET_DEST (*sub)) == SUBREG
+ && REG_P (SUBREG_REG (SET_DEST (*sub)))
+ && (REGNO (SUBREG_REG (SET_DEST (*sub))) ==
+ REGNO (source_extension_reg)))))
+ {
+ rtx orig_src = SET_SRC (*sub);
+
+ if (extension_code == SIGN_EXTEND)
+ temp_extension = gen_rtx_SIGN_EXTEND (dest_extension_mode,
+ orig_src);
+ else
+ temp_extension = gen_rtx_ZERO_EXTEND (dest_extension_mode,
+ orig_src);
+ simplified_temp_extension = simplify_rtx (temp_extension);
+ temp_extension =
+ (simplified_temp_extension) ? simplified_temp_extension :
+ temp_extension;
+ new_set = gen_rtx_SET (VOIDmode, dest_extension_reg,
+ temp_extension);
+ validate_change (ref_copy, sub, new_set, 1);
+ need_to_apply_change = true;
+ }
+ }
+ if (need_to_apply_change)
+ if (apply_change_group ())
+ merge_success = true;
+ }
+ else if (code == SET
+ && GET_MODE (SET_SRC (*pat)) != VOIDmode
+ && GET_MODE (SET_DEST (*pat)) == source_mode
+ && ((REG_P (SET_DEST (*pat))
+ && REGNO (SET_DEST (*pat)) == REGNO (source_extension_reg))
+ || (GET_CODE (SET_DEST (*pat)) == SUBREG
+ && REG_P (SUBREG_REG (SET_DEST (*pat)))
+ && (REGNO (SUBREG_REG (SET_DEST (*pat))) ==
+ REGNO (source_extension_reg)))))
+ {
+ rtx orig_src = SET_SRC (*pat);
+
+ if (extension_code == SIGN_EXTEND)
+ temp_extension = gen_rtx_SIGN_EXTEND (dest_extension_mode, orig_src);
+ else
+ temp_extension = gen_rtx_ZERO_EXTEND (dest_extension_mode, orig_src);
+ simplified_temp_extension = simplify_rtx (temp_extension);
+ temp_extension = (simplified_temp_extension) ? simplified_temp_extension :
+ temp_extension;
+ new_set = gen_rtx_SET (VOIDmode, dest_extension_reg, temp_extension);
+ if (validate_change (ref_copy, pat, new_set, 0))
+ merge_success = true;
+ }
+ if (!merge_success)
+ {
+ /* The merge failed. */
+ if (dump_file)
+ {
+ fprintf (dump_file, "Def merge failed!\n");
+ fprintf (dump_file, "Original instructions:\n");
+ print_rtl_single (dump_file, ref);
+ print_rtl_single (dump_file, def_se);
+ }
+
+ see_def_extension_not_merged (curr_ref_s, def_se);
+ /* Continue to the next extension. */
+ return 1;
+ }
+
+ /* The merge succeeded! */
+
+ /* Create a simple move instruction to assure the correctness of the code. */
+ subreg = gen_lowpart_SUBREG (source_extension_mode, dest_extension_reg);
+ start_sequence ();
+ emit_move_insn (source_extension_reg, subreg);
+ move_insn = get_insns ();
+ end_sequence ();
+
+ /* Link the merged instruction to the newly created move instruction and
+ to the former created move instructions. */
+ PREV_INSN (ref_copy) = NULL_RTX;
+ NEXT_INSN (ref_copy) = move_insn;
+ PREV_INSN (move_insn) = ref_copy;
+ NEXT_INSN (move_insn) = merged_ref_next;
+ if (merged_ref_next != NULL_RTX)
+ PREV_INSN (merged_ref_next) = move_insn;
+ curr_ref_s->merged_insn = ref_copy;
+
+ if (dump_file)
+ {
+ fprintf (dump_file, "Def merge succeeded!\n");
+ fprintf (dump_file, "Original instructions:\n");
+ print_rtl_single (dump_file, ref);
+ print_rtl_single (dump_file, def_se);
+ fprintf (dump_file, "Merged instruction:\n");
+ print_rtl_single (dump_file, ref_copy);
+ fprintf (dump_file, "Move instruction that was added:\n");
+ print_rtl_single (dump_file, move_insn);
+ }
+
+ /* Remove the current def_se from the unmerged_def_se_hash and insert it to
+ the merged_def_se_hash. */
+ htab_clear_slot (curr_ref_s->unmerged_def_se_hash, (PTR *)slot);
+ if (!curr_ref_s->merged_def_se_hash)
+ curr_ref_s->merged_def_se_hash = htab_create (10,
+ hash_descriptor_extension,
+ eq_descriptor_extension,
+ NULL);
+ rtx_slot = (rtx *) htab_find_slot (curr_ref_s->merged_def_se_hash,
+ dest_extension_reg, INSERT);
+ gcc_assert (*rtx_slot == NULL);
+ *rtx_slot = def_se;
+
+ return 1;
+}
+
+
+/* Try to eliminate extensions in this order:
+ a. Try to merge only the def extensions, one by one.
+ b. Try to merge only the use extensions, one by one.
+
+ TODO:
+ Try to merge any couple of use extensions simultaneously.
+ Try to merge any def extension with one or two uses extensions
+ simultaneously.
+
+ After all the merges are done, update the register properties for the basic
+ block and eliminate locally redundant use extensions.
+
+ This is a subroutine of see_merge_and_eliminate_extensions called
+ via splay_tree_foreach.
+ STN is the current node in the see_bb_splay_ar[i] splay tree. It holds a
+ see_ref_s structure. */
+
+static int
+see_handle_extensions_for_one_ref (splay_tree_node stn,
+ void *data ATTRIBUTE_UNUSED)
+{
+ htab_t use_se_hash = ((struct see_ref_s *) (stn->value))->use_se_hash;
+ htab_t unmerged_def_se_hash =
+ ((struct see_ref_s *) (stn->value))->unmerged_def_se_hash;
+ htab_t merged_def_se_hash;
+ rtx ref = ((struct see_ref_s *) (stn->value))->insn;
+
+ if (dump_file)
+ {
+ fprintf (dump_file, "Handling ref:\n");
+ print_rtl_single (dump_file, ref);
+ }
+
+ /* a. Try to eliminate only def extensions, one by one. */
+ if (unmerged_def_se_hash)
+ htab_traverse_noresize (unmerged_def_se_hash, see_merge_one_def_extension,
+ (PTR) (stn->value));
+
+ if (use_se_hash)
+ /* b. Try to eliminate only use extensions, one by one. */
+ htab_traverse_noresize (use_se_hash, see_merge_one_use_extension,
+ (PTR) (stn->value));
+
+ merged_def_se_hash = ((struct see_ref_s *) (stn->value))->merged_def_se_hash;
+
+ if (dump_file)
+ {
+ fprintf (dump_file, "The hashes of the current reference:\n");
+ if (unmerged_def_se_hash)
+ {
+ fprintf (dump_file, "unmerged_def_se_hash:\n");
+ htab_traverse (unmerged_def_se_hash, see_print_one_extension, NULL);
+ }
+ if (merged_def_se_hash)
+ {
+ fprintf (dump_file, "merged_def_se_hash:\n");
+ htab_traverse (merged_def_se_hash, see_print_one_extension, NULL);
+ }
+ if (use_se_hash)
+ {
+ fprintf (dump_file, "use_se_hash:\n");
+ htab_traverse (use_se_hash, see_print_one_extension, NULL);
+ }
+ }
+
+ /* Now that all the merges are done, update the register properties of the
+ basic block and eliminate locally redundant extensions.
+ It is important that we first traverse the use extensions hash and
+ afterwards the def extensions hashes. */
+
+ if (use_se_hash)
+ htab_traverse_noresize (use_se_hash, see_set_prop_unmerged_use,
+ (PTR) (stn->value));
+
+ if (unmerged_def_se_hash)
+ htab_traverse (unmerged_def_se_hash, see_set_prop_unmerged_def,
+ (PTR) (stn->value));
+
+ if (merged_def_se_hash)
+ htab_traverse (merged_def_se_hash, see_set_prop_merged_def,
+ (PTR) (stn->value));
+
+ /* Continue to the next definition. */
+ return 0;
+}
+
+
+/* Phase 2 top level function.
+ In this phase, we try to merge def extensions and use extensions with their
+ references, and eliminate redundant extensions in the same basic block.
+ We also gather information for the next phases. */
+
+static void
+see_merge_and_eliminate_extensions (void)
+{
+ int i = 0;
+
+ if (dump_file)
+ fprintf (dump_file,
+ "* Phase 2: Merge and eliminate locally redundant extensions. *\n");
+
+ /* Traverse over all the splay trees of the basic blocks. */
+ for (i = 0; i < last_bb; i++)
+ {
+ if (see_bb_splay_ar[i])
+ {
+ if (dump_file)
+ fprintf (dump_file, "Handling references for bb %d\n", i);
+ /* Traverse over all the references in the basic block in forward
+ order. */
+ splay_tree_foreach (see_bb_splay_ar[i],
+ see_handle_extensions_for_one_ref, NULL);
+ }
+ }
+}
+
+
+/* Phase 1 implementation: Propagate extensions to uses. */
+
+/* Insert REF_INSN into the splay tree of its basic block.
+ SE_INSN is the extension to store in the proper hash according to TYPE.
+
+ Return true if everything went well.
+ Otherwise, return false (this will cause the optimization to be aborted). */
+
+static bool
+see_store_reference_and_extension (rtx ref_insn, rtx se_insn,
+ enum extension_type type)
+{
+ rtx *rtx_slot;
+ int curr_bb_num;
+ splay_tree_node stn = NULL;
+ htab_t se_hash = NULL;
+ struct see_ref_s *ref_s = NULL;
+
+ /* Check the arguments. */
+ gcc_assert (ref_insn && se_insn);
+ if (!see_bb_splay_ar)
+ return false;
+
+ curr_bb_num = BLOCK_NUM (ref_insn);
+ gcc_assert (curr_bb_num < last_bb && curr_bb_num >= 0);
+
+ /* Insert the reference to the splay tree of its basic block. */
+ if (!see_bb_splay_ar[curr_bb_num])
+ /* The splay tree for this block doesn't exist yet, create it. */
+ see_bb_splay_ar[curr_bb_num] = splay_tree_new (splay_tree_compare_ints,
+ NULL, see_free_ref_s);
+ else
+ /* Splay tree already exists, check if the current reference is already
+ in it. */
+ {
+ stn = splay_tree_lookup (see_bb_splay_ar[curr_bb_num],
+ DF_INSN_LUID (df, ref_insn));
+ if (stn)
+ switch (type)
+ {
+ case EXPLICIT_DEF_EXTENSION:
+ se_hash =
+ ((struct see_ref_s *) (stn->value))->unmerged_def_se_hash;
+ if (!se_hash)
+ {
+ se_hash = htab_create (10,
+ hash_descriptor_extension,
+ eq_descriptor_extension,
+ NULL);
+ ((struct see_ref_s *) (stn->value))->unmerged_def_se_hash =
+ se_hash;
+ }
+ break;
+ case IMPLICIT_DEF_EXTENSION:
+ se_hash = ((struct see_ref_s *) (stn->value))->merged_def_se_hash;
+ if (!se_hash)
+ {
+ se_hash = htab_create (10,
+ hash_descriptor_extension,
+ eq_descriptor_extension,
+ NULL);
+ ((struct see_ref_s *) (stn->value))->merged_def_se_hash =
+ se_hash;
+ }
+ break;
+ case USE_EXTENSION:
+ se_hash = ((struct see_ref_s *) (stn->value))->use_se_hash;
+ if (!se_hash)
+ {
+ se_hash = htab_create (10,
+ hash_descriptor_extension,
+ eq_descriptor_extension,
+ NULL);
+ ((struct see_ref_s *) (stn->value))->use_se_hash = se_hash;
+ }
+ break;
+ default:
+ gcc_unreachable ();
+ }
+ }
+
+ /* Initialize a new see_ref_s structure and insert it to the splay
+ tree. */
+ if (!stn)
+ {
+ ref_s = xmalloc (sizeof (struct see_ref_s));
+ ref_s->luid = DF_INSN_LUID (df, ref_insn);
+ ref_s->insn = ref_insn;
+ ref_s->merged_insn = NULL;
+
+ /* Initialize the hashes. */
+ switch (type)
+ {
+ case EXPLICIT_DEF_EXTENSION:
+ ref_s->unmerged_def_se_hash = htab_create (10,
+ hash_descriptor_extension,
+ eq_descriptor_extension,
+ NULL);
+ se_hash = ref_s->unmerged_def_se_hash;
+ ref_s->merged_def_se_hash = NULL;
+ ref_s->use_se_hash = NULL;
+ break;
+ case IMPLICIT_DEF_EXTENSION:
+ ref_s->merged_def_se_hash = htab_create (10,
+ hash_descriptor_extension,
+ eq_descriptor_extension,
+ NULL);
+ se_hash = ref_s->merged_def_se_hash;
+ ref_s->unmerged_def_se_hash = NULL;
+ ref_s->use_se_hash = NULL;
+ break;
+ case USE_EXTENSION:
+ ref_s->use_se_hash = htab_create (10,
+ hash_descriptor_extension,
+ eq_descriptor_extension,
+ NULL);
+ se_hash = ref_s->use_se_hash;
+ ref_s->unmerged_def_se_hash = NULL;
+ ref_s->merged_def_se_hash = NULL;
+ break;
+ default:
+ gcc_unreachable ();
+ }
+ }
+
+ /* Insert the new extension instruction into the correct se_hash of the
+ current reference. */
+ rtx_slot = (rtx *) htab_find_slot (se_hash, se_insn, INSERT);
+ if (*rtx_slot != NULL)
+ {
+ gcc_assert (type == USE_EXTENSION);
+ gcc_assert (rtx_equal_p (PATTERN (*rtx_slot), PATTERN (se_insn)));
+ }
+ else
+ *rtx_slot = se_insn;
+
+ /* If this is a new reference, insert it into the splay_tree. */
+ if (!stn)
+ splay_tree_insert (see_bb_splay_ar[curr_bb_num],
+ DF_INSN_LUID (df, ref_insn), (splay_tree_value) ref_s);
+ return true;
+}
+
+
+/* Go over all the defs, for each relevant definition (defined below) store its
+ instruction as a reference.
+
+ A definition is relevant if its root has
+ ((entry_type == SIGN_EXTENDED_DEF) || (entry_type == ZERO_EXTENDED_DEF)) and
+ his source_mode is not narrower then the the roots source_mode.
+
+ Return the number of relevant defs or negative number if something bad had
+ happened and the optimization should be aborted. */
+
+static int
+see_handle_relevant_defs (void)
+{
+ rtx insn = NULL;
+ rtx se_insn = NULL;
+ rtx reg = NULL;
+ rtx ref_insn = NULL;
+ struct web_entry *root_entry = NULL;
+ unsigned int i;
+ int num_relevant_defs = 0;
+ enum rtx_code extension_code;
+
+ for (i = 0; i < defs_num; i++)
+ {
+ insn = DF_REF_INSN (DF_DEFS_GET (df, i));
+ reg = DF_REF_REAL_REG (DF_DEFS_GET (df, i));
+
+ if (!insn)
+ continue;
+
+ if (!INSN_P (insn))
+ continue;
+
+ root_entry = unionfind_root (&def_entry[i]);
+
+ if (ENTRY_EI (root_entry)->relevancy != SIGN_EXTENDED_DEF
+ && ENTRY_EI (root_entry)->relevancy != ZERO_EXTENDED_DEF)
+ /* The current web is not relevant. Continue to the next def. */
+ continue;
+
+ if (root_entry->reg)
+ /* It isn't possible to have two different register for the same
+ web. */
+ gcc_assert (rtx_equal_p (root_entry->reg, reg));
+ else
+ root_entry->reg = reg;
+
+ /* The current definition is an EXTENDED_DEF or a definition that its
+ source_mode is narrower then its web's source_mode.
+ This means that we need to generate the implicit extension explicitly
+ and store it in the current reference's merged_def_se_hash. */
+ if (ENTRY_EI (&def_entry[i])->local_relevancy == EXTENDED_DEF
+ || (ENTRY_EI (&def_entry[i])->local_source_mode <
+ ENTRY_EI (root_entry)->source_mode))
+ {
+ num_relevant_defs++;
+
+ if (ENTRY_EI (root_entry)->relevancy == SIGN_EXTENDED_DEF)
+ extension_code = SIGN_EXTEND;
+ else
+ extension_code = ZERO_EXTEND;
+
+ se_insn =
+ see_gen_normalized_extension (reg, extension_code,
+ ENTRY_EI (root_entry)->source_mode);
+
+ /* This is a dummy extension, mark it as deleted. */
+ INSN_DELETED_P (se_insn) = 1;
+
+ if (!see_store_reference_and_extension (insn, se_insn,
+ IMPLICIT_DEF_EXTENSION))
+ /* Something bad happened. Abort the optimization. */
+ return -1;
+ continue;
+ }
+
+ ref_insn = PREV_INSN (insn);
+ gcc_assert (BLOCK_NUM (ref_insn) == BLOCK_NUM (insn));
+
+ num_relevant_defs++;
+
+ if (!see_store_reference_and_extension (ref_insn, insn,
+ EXPLICIT_DEF_EXTENSION))
+ /* Something bad happened. Abort the optimization. */
+ return -1;
+ }
+ return num_relevant_defs;
+}
+
+
+/* Go over all the uses, for each use in relevant web store its instruction as
+ a reference and generate an extension before it.
+
+ Return the number of relevant uses or negative number if something bad had
+ happened and the optimization should be aborted. */
+
+static int
+see_handle_relevant_uses (void)
+{
+ rtx insn = NULL;
+ rtx reg = NULL;
+ struct web_entry *root_entry = NULL;
+ rtx se_insn = NULL;
+ unsigned int i;
+ int num_relevant_uses = 0;
+ enum rtx_code extension_code;
+
+ for (i = 0; i < uses_num; i++)
+ {
+ insn = DF_REF_INSN (DF_USES_GET (df, i));
+ reg = DF_REF_REAL_REG (DF_USES_GET (df, i));
+
+ if (!insn)
+ continue;
+
+ if (!INSN_P (insn))
+ continue;
+
+ root_entry = unionfind_root (&use_entry[i]);
+
+ if (ENTRY_EI (root_entry)->relevancy != SIGN_EXTENDED_DEF
+ && ENTRY_EI (root_entry)->relevancy != ZERO_EXTENDED_DEF)
+ /* The current web is not relevant. Continue to the next use. */
+ continue;
+
+ if (root_entry->reg)
+ /* It isn't possible to have two different register for the same
+ web. */
+ gcc_assert (rtx_equal_p (root_entry->reg, reg));
+ else
+ root_entry->reg = reg;
+
+ /* Generate the use extension. */
+ if (ENTRY_EI (root_entry)->relevancy == SIGN_EXTENDED_DEF)
+ extension_code = SIGN_EXTEND;
+ else
+ extension_code = ZERO_EXTEND;
+
+ se_insn =
+ see_gen_normalized_extension (reg, extension_code,
+ ENTRY_EI (root_entry)->source_mode);
+ if (!se_insn)
+ /* This is very bad, abort the transformation. */
+ return -1;
+
+ num_relevant_uses++;
+
+ if (!see_store_reference_and_extension (insn, se_insn,
+ USE_EXTENSION))
+ /* Something bad happened. Abort the optimization. */
+ return -1;
+ }
+
+ return num_relevant_uses;
+}
+
+
+/* Updates the relevancy of all the uses.
+ The information of the i'th use is stored in use_entry[i].
+ Currently all the uses are relevant for the optimization except for uses that
+ are in LIBCALL or RETVAL instructions. */
+
+static void
+see_update_uses_relevancy (void)
+{
+ rtx insn = NULL;
+ rtx reg = NULL;
+ struct see_entry_extra_info *curr_entry_extra_info;
+ enum entry_type et;
+ unsigned int i;
+
+ if (!df || !use_entry)
+ return;
+
+ for (i = 0; i < uses_num; i++)
+ {
+
+ insn = DF_REF_INSN (DF_USES_GET (df, i));
+ reg = DF_REF_REAL_REG (DF_USES_GET (df, i));
+
+ et = RELEVANT_USE;
+
+ if (insn)
+ {
+ if (!INSN_P (insn))
+ et = NOT_RELEVANT;
+ if (insn && find_reg_note (insn, REG_LIBCALL, NULL_RTX))
+ et = NOT_RELEVANT;
+ if (find_reg_note (insn, REG_RETVAL, NULL_RTX))
+ et = NOT_RELEVANT;
+ }
+ else
+ et = NOT_RELEVANT;
+
+ if (dump_file)
+ {
+ fprintf (dump_file, "u%i insn %i reg %i ",
+ i, (insn ? INSN_UID (insn) : -1), REGNO (reg));
+ if (et == NOT_RELEVANT)
+ fprintf (dump_file, "NOT RELEVANT \n");
+ else
+ fprintf (dump_file, "RELEVANT USE \n");
+ }
+
+ curr_entry_extra_info = xmalloc (sizeof (struct see_entry_extra_info));
+ curr_entry_extra_info->relevancy = et;
+ curr_entry_extra_info->local_relevancy = et;
+ use_entry[i].extra_info = curr_entry_extra_info;
+ use_entry[i].reg = NULL;
+ use_entry[i].pred = NULL;
+ }
+}
+
+
+/* A definition in a candidate for this optimization only if its pattern is
+ recognized as relevant in this function.
+ INSN is the instruction to be recognized.
+
+- If this is the pattern of a common sign extension after definition:
+ PREV_INSN (INSN): def (reg:NARROWmode r)
+ INSN: set ((reg:WIDEmode r')
+ (sign_extend:WIDEmode (reg:NARROWmode r)))
+ return SIGN_EXTENDED_DEF and set SOURCE_MODE to NARROWmode.
+
+- If this is the pattern of a common zero extension after definition:
+ PREV_INSN (INSN): def (reg:NARROWmode r)
+ INSN: set ((reg:WIDEmode r')
+ (zero_extend:WIDEmode (reg:NARROWmode r)))
+ return ZERO_EXTENDED_DEF and set SOURCE_MODE to NARROWmode.
+
+- Otherwise,
+
+ For the pattern:
+ INSN: set ((reg:WIDEmode r) (sign_extend:WIDEmode (...expr...)))
+ return EXTENDED_DEF and set SOURCE_MODE to the mode of expr.
+
+ For the pattern:
+ INSN: set ((reg:WIDEmode r) (zero_extend:WIDEmode (...expr...)))
+ return EXTENDED_DEF and set SOURCE_MODE_UNSIGNED to the mode of expr.
+
+ For the pattern:
+ INSN: set ((reg:WIDEmode r) (CONST_INT (...)))
+ return EXTENDED_DEF and set SOURCE_MODE(_UNSIGNED) to the narrowest mode that
+ is implicitly sign(zero) extended to WIDEmode in the INSN.
+
+- FIXME: Extensions that are not adjacent to their definition and EXTENDED_DEF
+ that is part of a PARALLEL instruction are not handled.
+ These restriction can be relaxed. */
+
+static enum entry_type
+see_analyze_one_def (rtx insn, enum machine_mode *source_mode,
+ enum machine_mode *source_mode_unsigned)
+{
+ enum rtx_code extension_code;
+ rtx rhs = NULL;
+ rtx lhs = NULL;
+ rtx set = NULL;
+ rtx source_register = NULL;
+ rtx prev_insn = NULL;
+ rtx next_insn = NULL;
+ enum machine_mode mode;
+ enum machine_mode next_source_mode;
+ HOST_WIDE_INT val = 0;
+ HOST_WIDE_INT val2 = 0;
+ int i = 0;
+
+ *source_mode = MAX_MACHINE_MODE;
+ *source_mode_unsigned = MAX_MACHINE_MODE;
+
+ if (!insn)
+ return NOT_RELEVANT;
+
+ if (!INSN_P (insn))
+ return NOT_RELEVANT;
+
+ extension_code = see_get_extension_data (insn, source_mode);
+ switch (extension_code)
+ {
+ case SIGN_EXTEND:
+ case ZERO_EXTEND:
+ source_register = see_get_extension_reg (insn, 0);
+ /* FIXME: This restriction can be relaxed. The only thing that is
+ important is that the reference would be inside the same basic block
+ as the extension. */
+ prev_insn = PREV_INSN (insn);
+ if (!prev_insn || !INSN_P (prev_insn))
+ return NOT_RELEVANT;
+
+ if (!reg_set_between_p (source_register, PREV_INSN (prev_insn), insn))
+ return NOT_RELEVANT;
+
+ if (find_reg_note (prev_insn, REG_LIBCALL, NULL_RTX))
+ return NOT_RELEVANT;
+
+ if (find_reg_note (prev_insn, REG_RETVAL, NULL_RTX))
+ return NOT_RELEVANT;
+
+ /* If we can't use copy_rtx on the reference it can't be a reference. */
+ if (GET_CODE (PATTERN (prev_insn)) == PARALLEL
+ && asm_noperands (PATTERN (prev_insn)) >= 0)
+ return NOT_RELEVANT;
+
+ /* Now, check if this extension is a reference itself. If so, it is not
+ relevant. Handling this extension as relevant would make things much
+ more complicated. */
+ next_insn = NEXT_INSN (insn);
+ if (next_insn
+ && INSN_P (next_insn)
+ && (see_get_extension_data (next_insn, &next_source_mode) !=
+ NOT_RELEVANT))
+ {
+ rtx curr_dest_register = see_get_extension_reg (insn, 1);
+ rtx next_source_register = see_get_extension_reg (next_insn, 0);
+
+ if (REGNO (curr_dest_register) == REGNO (next_source_register))
+ return NOT_RELEVANT;
+ }
+
+ if (extension_code == SIGN_EXTEND)
+ return SIGN_EXTENDED_DEF;
+ else
+ return ZERO_EXTENDED_DEF;
+
+ case UNKNOWN:
+ /* This may still be an EXTENDED_DEF. */
+
+ /* FIXME: This restriction can be relaxed. It is possible to handle
+ PARALLEL insns too. */
+ set = single_set (insn);
+ if (!set)
+ return NOT_RELEVANT;
+ rhs = SET_SRC (set);
+ lhs = SET_DEST (set);
+
+ /* Don't handle extensions to something other then register or
+ subregister. */
+ if (!REG_P (lhs) && !SUBREG_REG (lhs))
+ return NOT_RELEVANT;
+
+ switch (GET_CODE (rhs))
+ {
+ case SIGN_EXTEND:
+ *source_mode = GET_MODE (XEXP (rhs, 0));
+ *source_mode_unsigned = MAX_MACHINE_MODE;
+ return EXTENDED_DEF;
+ case ZERO_EXTEND:
+ *source_mode = MAX_MACHINE_MODE;
+ *source_mode_unsigned = GET_MODE (XEXP (rhs, 0));
+ return EXTENDED_DEF;
+ case CONST_INT:
+
+ val = INTVAL (rhs);
+
+ /* Find the narrowest mode, val could fit into. */
+ for (mode = GET_CLASS_NARROWEST_MODE (MODE_INT), i = 0;
+ GET_MODE_BITSIZE (mode) < BITS_PER_WORD;
+ mode = GET_MODE_WIDER_MODE (mode), i++)
+ {
+ val2 = trunc_int_for_mode (val, mode);
+ if (val2 == val && *source_mode == MAX_MACHINE_MODE)
+ *source_mode = mode;
+ if (val == (val & (HOST_WIDE_INT)GET_MODE_MASK (mode))
+ && *source_mode_unsigned == MAX_MACHINE_MODE)
+ *source_mode_unsigned = mode;
+ if (*source_mode != MAX_MACHINE_MODE
+ && *source_mode_unsigned !=MAX_MACHINE_MODE)
+ return EXTENDED_DEF;
+ }
+ if (*source_mode != MAX_MACHINE_MODE
+ || *source_mode_unsigned !=MAX_MACHINE_MODE)
+ return EXTENDED_DEF;
+ return NOT_RELEVANT;
+ default:
+ return NOT_RELEVANT;
+ }
+ default:
+ gcc_unreachable ();
+ }
+}
+
+
+/* Updates the relevancy and source_mode of all the definitions.
+ The information of the i'th definition is stored in def_entry[i]. */
+
+static void
+see_update_defs_relevancy (void)
+{
+ struct see_entry_extra_info *curr_entry_extra_info;
+ unsigned int i;
+ rtx insn = NULL;
+ rtx reg = NULL;
+ enum entry_type et;
+ enum machine_mode source_mode;
+ enum machine_mode source_mode_unsigned;
+
+ if (!df || !def_entry)
+ return;
+
+ for (i = 0; i < defs_num; i++)
+ {
+ insn = DF_REF_INSN (DF_DEFS_GET (df, i));
+ reg = DF_REF_REAL_REG (DF_DEFS_GET (df, i));
+
+ et = see_analyze_one_def (insn, &source_mode, &source_mode_unsigned);
+
+ curr_entry_extra_info = xmalloc (sizeof (struct see_entry_extra_info));
+ curr_entry_extra_info->relevancy = et;
+ curr_entry_extra_info->local_relevancy = et;
+ if (et != EXTENDED_DEF)
+ {
+ curr_entry_extra_info->source_mode = source_mode;
+ curr_entry_extra_info->local_source_mode = source_mode;
+ }
+ else
+ {
+ curr_entry_extra_info->source_mode_signed = source_mode;
+ curr_entry_extra_info->source_mode_unsigned = source_mode_unsigned;
+ }
+ def_entry[i].extra_info = curr_entry_extra_info;
+ def_entry[i].reg = NULL;
+ def_entry[i].pred = NULL;
+
+ if (dump_file)
+ {
+ if (et == NOT_RELEVANT)
+ {
+ fprintf (dump_file, "d%i insn %i reg %i ",
+ i, (insn ? INSN_UID (insn) : -1), REGNO (reg));
+ fprintf (dump_file, "NOT RELEVANT \n");
+ }
+ else
+ {
+ fprintf (dump_file, "d%i insn %i reg %i ",
+ i ,INSN_UID (insn), REGNO (reg));
+ fprintf (dump_file, "RELEVANT - ");
+ switch (et)
+ {
+ case SIGN_EXTENDED_DEF :
+ fprintf (dump_file, "SIGN_EXTENDED_DEF, source_mode = %s\n",
+ GET_MODE_NAME (source_mode));
+ break;
+ case ZERO_EXTENDED_DEF :
+ fprintf (dump_file, "ZERO_EXTENDED_DEF, source_mode = %s\n",
+ GET_MODE_NAME (source_mode));
+ break;
+ case EXTENDED_DEF :
+ fprintf (dump_file, "EXTENDED_DEF, ");
+ if (source_mode != MAX_MACHINE_MODE
+ && source_mode_unsigned != MAX_MACHINE_MODE)
+ {
+ fprintf (dump_file, "positive const, ");
+ fprintf (dump_file, "source_mode_signed = %s, ",
+ GET_MODE_NAME (source_mode));
+ fprintf (dump_file, "source_mode_unsigned = %s\n",
+ GET_MODE_NAME (source_mode_unsigned));
+ }
+ else if (source_mode != MAX_MACHINE_MODE)
+ fprintf (dump_file, "source_mode_signed = %s\n",
+ GET_MODE_NAME (source_mode));
+ else
+ fprintf (dump_file, "source_mode_unsigned = %s\n",
+ GET_MODE_NAME (source_mode_unsigned));
+ break;
+ default :
+ gcc_unreachable ();
+ }
+ }
+ }
+ }
+}
+
+
+/* Phase 1 top level function.
+ In this phase the relevancy of all the definitions and uses are checked,
+ later the webs are produces and the extensions are generated.
+ These extensions are not emitted yet into the insns stream.
+
+ returns true if at list one relevant web was found and there were no
+ problems, otherwise return false. */
+
+static bool
+see_propagate_extensions_to_uses (void)
+{
+ unsigned int i = 0;
+ int num_relevant_uses;
+ int num_relevant_defs;
+
+ if (dump_file)
+ fprintf (dump_file,
+ "* Phase 1: Propagate extensions to uses. *\n");
+
+ /* Update the relevancy of references using the DF object. */
+ see_update_defs_relevancy ();
+ see_update_uses_relevancy ();
+
+ /* Produce the webs and update the extra_info of the root.
+ In general, a web is relevant if all its definitions and uses are relevant
+ and there is at least one definition that was marked as SIGN_EXTENDED_DEF
+ or ZERO_EXTENDED_DEF. */
+ for (i = 0; i < uses_num; i++)
+ union_defs (df, DF_USES_GET (df, i), def_entry, use_entry,
+ see_update_leader_extra_info);
+
+ /* Generate use extensions for references and insert these
+ references to see_bb_splay_ar data structure. */
+ num_relevant_uses = see_handle_relevant_uses ();
+
+ if (num_relevant_uses < 0)
+ return false;
+
+ /* Store the def extensions in their references structures and insert these
+ references to see_bb_splay_ar data structure. */
+ num_relevant_defs = see_handle_relevant_defs ();
+
+ if (num_relevant_defs < 0)
+ return false;
+
+ return num_relevant_uses > 0 || num_relevant_defs > 0;
+}
+
+
+/* Main entry point for the sign extension elimination optimization. */
+
+static void
+see_main (void)
+{
+ bool cont = false;
+ int i = 0;
+
+ /* Initialize global data structures. */
+ see_initialize_data_structures ();
+
+ /* Phase 1: Propagate extensions to uses. */
+ cont = see_propagate_extensions_to_uses ();
+
+ if (cont)
+ {
+ init_recog ();
+
+ /* Phase 2: Merge and eliminate locally redundant extensions. */
+ see_merge_and_eliminate_extensions ();
+
+ /* Phase 3: Eliminate globally redundant extensions. */
+ see_execute_LCM ();
+
+ /* Phase 4: Commit changes to the insn stream. */
+ see_commit_changes ();
+
+ if (dump_file)
+ {
+ /* For debug purpose only. */
+ fprintf (dump_file, "see_pre_extension_hash:\n");
+ htab_traverse (see_pre_extension_hash, see_print_pre_extension_expr,
+ NULL);
+
+ for (i = 0; i < last_bb; i++)
+ {
+ if (see_bb_hash_ar[i])
+ /* Traverse over all the references in the basic block in
+ forward order. */
+ {
+ fprintf (dump_file,
+ "Searching register properties in bb %d\n", i);
+ htab_traverse (see_bb_hash_ar[i],
+ see_print_register_properties, NULL);
+ }
+ }
+ }
+ }
+
+ /* Free global data structures. */
+ see_free_data_structures ();
+}
+
+
+static bool
+gate_handle_see (void)
+{
+ return optimize > 1 && flag_see;
+}
+
+static unsigned int
+rest_of_handle_see (void)
+{
+ int no_new_pseudos_bcp = no_new_pseudos;
+
+ no_new_pseudos = 0;
+ see_main ();
+ no_new_pseudos = no_new_pseudos_bcp;
+
+ delete_trivially_dead_insns (get_insns (), max_reg_num ());
+ update_life_info_in_dirty_blocks (UPDATE_LIFE_GLOBAL_RM_NOTES,
+ (PROP_DEATH_NOTES));
+ cleanup_cfg (CLEANUP_EXPENSIVE);
+ reg_scan (get_insns (), max_reg_num ());
+
+ return 0;
+}
+
+struct tree_opt_pass pass_see =
+{
+ "see", /* name */
+ gate_handle_see, /* gate */
+ rest_of_handle_see, /* execute */
+ NULL, /* sub */
+ NULL, /* next */
+ 0, /* static_pass_number */
+ TV_SEE, /* tv_id */
+ 0, /* properties_required */
+ 0, /* properties_provided */
+ 0, /* properties_destroyed */
+ 0, /* todo_flags_start */
+ TODO_dump_func, /* todo_flags_finish */
+ 'u' /* letter */
+};
+