freebsd-nq/contrib/gcc/see.c

3783 lines
115 KiB
C
Raw Normal View History

2007-05-19 01:19:51 +00:00
/* 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 */
};