📄 cse.c
字号:
/* Common subexpression elimination for GNU compiler. Copyright (C) 1987, 88, 89, 92-7, 1998 Free Software Foundation, Inc.This file is part of GNU CC.GNU CC is free software; you can redistribute it and/or modifyit under the terms of the GNU General Public License as published bythe Free Software Foundation; either version 2, or (at your option)any later version.GNU CC is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; without even the implied warranty ofMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See theGNU General Public License for more details.You should have received a copy of the GNU General Public Licensealong with GNU CC; see the file COPYING. If not, write tothe Free Software Foundation, 59 Temple Place - Suite 330,Boston, MA 02111-1307, USA. */#include "config.h"/* Must precede rtl.h for FFS. */#include <stdio.h>#include "rtl.h"#include "regs.h"#include "hard-reg-set.h"#include "flags.h"#include "real.h"#include "insn-config.h"#include "recog.h"#include "expr.h"#include <setjmp.h>/* The basic idea of common subexpression elimination is to go through the code, keeping a record of expressions that would have the same value at the current scan point, and replacing expressions encountered with the cheapest equivalent expression. It is too complicated to keep track of the different possibilities when control paths merge; so, at each label, we forget all that is known and start fresh. This can be described as processing each basic block separately. Note, however, that these are not quite the same as the basic blocks found by a later pass and used for data flow analysis and register packing. We do not need to start fresh after a conditional jump instruction if there is no label there. We use two data structures to record the equivalent expressions: a hash table for most expressions, and several vectors together with "quantity numbers" to record equivalent (pseudo) registers. The use of the special data structure for registers is desirable because it is faster. It is possible because registers references contain a fairly small number, the register number, taken from a contiguously allocated series, and two register references are identical if they have the same number. General expressions do not have any such thing, so the only way to retrieve the information recorded on an expression other than a register is to keep it in a hash table.Registers and "quantity numbers": At the start of each basic block, all of the (hardware and pseudo) registers used in the function are given distinct quantity numbers to indicate their contents. During scan, when the code copies one register into another, we copy the quantity number. When a register is loaded in any other way, we allocate a new quantity number to describe the value generated by this operation. `reg_qty' records what quantity a register is currently thought of as containing. All real quantity numbers are greater than or equal to `max_reg'. If register N has not been assigned a quantity, reg_qty[N] will equal N. Quantity numbers below `max_reg' do not exist and none of the `qty_...' variables should be referenced with an index below `max_reg'. We also maintain a bidirectional chain of registers for each quantity number. `qty_first_reg', `qty_last_reg', `reg_next_eqv' and `reg_prev_eqv' hold these chains. The first register in a chain is the one whose lifespan is least local. Among equals, it is the one that was seen first. We replace any equivalent register with that one. If two registers have the same quantity number, it must be true that REG expressions with `qty_mode' must be in the hash table for both registers and must be in the same class. The converse is not true. Since hard registers may be referenced in any mode, two REG expressions might be equivalent in the hash table but not have the same quantity number if the quantity number of one of the registers is not the same mode as those expressions. Constants and quantity numbers When a quantity has a known constant value, that value is stored in the appropriate element of qty_const. This is in addition to putting the constant in the hash table as is usual for non-regs. Whether a reg or a constant is preferred is determined by the configuration macro CONST_COSTS and will often depend on the constant value. In any event, expressions containing constants can be simplified, by fold_rtx. When a quantity has a known nearly constant value (such as an address of a stack slot), that value is stored in the appropriate element of qty_const. Integer constants don't have a machine mode. However, cse determines the intended machine mode from the destination of the instruction that moves the constant. The machine mode is recorded in the hash table along with the actual RTL constant expression so that different modes are kept separate.Other expressions: To record known equivalences among expressions in general we use a hash table called `table'. It has a fixed number of buckets that contain chains of `struct table_elt' elements for expressions. These chains connect the elements whose expressions have the same hash codes. Other chains through the same elements connect the elements which currently have equivalent values. Register references in an expression are canonicalized before hashing the expression. This is done using `reg_qty' and `qty_first_reg'. The hash code of a register reference is computed using the quantity number, not the register number. When the value of an expression changes, it is necessary to remove from the hash table not just that expression but all expressions whose values could be different as a result. 1. If the value changing is in memory, except in special cases ANYTHING referring to memory could be changed. That is because nobody knows where a pointer does not point. The function `invalidate_memory' removes what is necessary. The special cases are when the address is constant or is a constant plus a fixed register such as the frame pointer or a static chain pointer. When such addresses are stored in, we can tell exactly which other such addresses must be invalidated due to overlap. `invalidate' does this. All expressions that refer to non-constant memory addresses are also invalidated. `invalidate_memory' does this. 2. If the value changing is a register, all expressions containing references to that register, and only those, must be removed. Because searching the entire hash table for expressions that contain a register is very slow, we try to figure out when it isn't necessary. Precisely, this is necessary only when expressions have been entered in the hash table using this register, and then the value has changed, and then another expression wants to be added to refer to the register's new value. This sequence of circumstances is rare within any one basic block. The vectors `reg_tick' and `reg_in_table' are used to detect this case. reg_tick[i] is incremented whenever a value is stored in register i. reg_in_table[i] holds -1 if no references to register i have been entered in the table; otherwise, it contains the value reg_tick[i] had when the references were entered. If we want to enter a reference and reg_in_table[i] != reg_tick[i], we must scan and remove old references. Until we want to enter a new entry, the mere fact that the two vectors don't match makes the entries be ignored if anyone tries to match them. Registers themselves are entered in the hash table as well as in the equivalent-register chains. However, the vectors `reg_tick' and `reg_in_table' do not apply to expressions which are simple register references. These expressions are removed from the table immediately when they become invalid, and this can be done even if we do not immediately search for all the expressions that refer to the register. A CLOBBER rtx in an instruction invalidates its operand for further reuse. A CLOBBER or SET rtx whose operand is a MEM:BLK invalidates everything that resides in memory.Related expressions: Constant expressions that differ only by an additive integer are called related. When a constant expression is put in the table, the related expression with no constant term is also entered. These are made to point at each other so that it is possible to find out if there exists any register equivalent to an expression related to a given expression. */ /* One plus largest register number used in this function. */static int max_reg;/* One plus largest instruction UID used in this function at time of cse_main call. */static int max_insn_uid;/* Length of vectors indexed by quantity number. We know in advance we will not need a quantity number this big. */static int max_qty;/* Next quantity number to be allocated. This is 1 + the largest number needed so far. */static int next_qty;/* Indexed by quantity number, gives the first (or last) register in the chain of registers that currently contain this quantity. */static int *qty_first_reg;static int *qty_last_reg;/* Index by quantity number, gives the mode of the quantity. */static enum machine_mode *qty_mode;/* Indexed by quantity number, gives the rtx of the constant value of the quantity, or zero if it does not have a known value. A sum of the frame pointer (or arg pointer) plus a constant can also be entered here. */static rtx *qty_const;/* Indexed by qty number, gives the insn that stored the constant value recorded in `qty_const'. */static rtx *qty_const_insn;/* The next three variables are used to track when a comparison between a quantity and some constant or register has been passed. In that case, we know the results of the comparison in case we see it again. These variables record a comparison that is known to be true. *//* Indexed by qty number, gives the rtx code of a comparison with a known result involving this quantity. If none, it is UNKNOWN. */static enum rtx_code *qty_comparison_code;/* Indexed by qty number, gives the constant being compared against in a comparison of known result. If no such comparison, it is undefined. If the comparison is not with a constant, it is zero. */static rtx *qty_comparison_const;/* Indexed by qty number, gives the quantity being compared against in a comparison of known result. If no such comparison, if it undefined. If the comparison is not with a register, it is -1. */static int *qty_comparison_qty;#ifdef HAVE_cc0/* For machines that have a CC0, we do not record its value in the hash table since its use is guaranteed to be the insn immediately following its definition and any other insn is presumed to invalidate it. Instead, we store below the value last assigned to CC0. If it should happen to be a constant, it is stored in preference to the actual assigned value. In case it is a constant, we store the mode in which the constant should be interpreted. */static rtx prev_insn_cc0;static enum machine_mode prev_insn_cc0_mode;#endif/* Previous actual insn. 0 if at first insn of basic block. */static rtx prev_insn;/* Insn being scanned. */static rtx this_insn;/* Index by register number, gives the quantity number of the register's current contents. */static int *reg_qty;/* Index by register number, gives the number of the next (or previous) register in the chain of registers sharing the same value. Or -1 if this register is at the end of the chain. If reg_qty[N] == N, reg_next_eqv[N] is undefined. */static int *reg_next_eqv;static int *reg_prev_eqv;/* Index by register number, gives the number of times that register has been altered in the current basic block. */static int *reg_tick;/* Index by register number, gives the reg_tick value at which rtx's containing this register are valid in the hash table. If this does not equal the current reg_tick value, such expressions existing in the hash table are invalid. If this is -1, no expressions containing this register have been entered in the table. */static int *reg_in_table;/* A HARD_REG_SET containing all the hard registers for which there is currently a REG expression in the hash table. Note the difference from the above variables, which indicate if the REG is mentioned in some expression in the table. */static HARD_REG_SET hard_regs_in_table;/* A HARD_REG_SET containing all the hard registers that are invalidated by a CALL_INSN. */static HARD_REG_SET regs_invalidated_by_call;/* Two vectors of ints: one containing max_reg -1's; the other max_reg + 500 (an approximation for max_qty) elements where element i contains i. These are used to initialize various other vectors fast. */static int *all_minus_one;static int *consec_ints;/* CUID of insn that starts the basic block currently being cse-processed. */static int cse_basic_block_start;/* CUID of insn that ends the basic block currently being cse-processed. */static int cse_basic_block_end;/* Vector mapping INSN_UIDs to cuids. The cuids are like uids but increase monotonically always. We use them to see whether a reg is used outside a given basic block. */static int *uid_cuid;/* Highest UID in UID_CUID. */static int max_uid;/* Get the cuid of an insn. */#define INSN_CUID(INSN) (uid_cuid[INSN_UID (INSN)])/* Nonzero if cse has altered conditional jump insns in such a way that jump optimization should be redone. */static int cse_jumps_altered;/* Nonzero if we put a LABEL_REF into the hash table. Since we may have put it into an INSN without a REG_LABEL, we have to rerun jump after CSE to put in the note. */static int recorded_label_ref;/* canon_hash stores 1 in do_not_record if it notices a reference to CC0, PC, or some other volatile subexpression. */static int do_not_record;#ifdef LOAD_EXTEND_OP/* Scratch rtl used when looking for load-extended copy of a MEM. */static rtx memory_extend_rtx;#endif/* canon_hash stores 1 in hash_arg_in_memory if it notices a reference to memory within the expression being hashed. */static int hash_arg_in_memory;/* canon_hash stores 1 in hash_arg_in_struct if it notices a reference to memory that's part of a structure. */static int hash_arg_in_struct;/* The hash table contains buckets which are chains of `struct table_elt's, each recording one expression's information. That expression is in the `exp' field. Those elements with the same hash code are chained in both directions through the `next_same_hash' and `prev_same_hash' fields. Each set of expressions with equivalent values are on a two-way chain through the `next_same_value' and `prev_same_value' fields, and all point with the `first_same_value' field at the first element in that chain. The chain is in order of increasing cost. Each element's cost value is in its `cost' field. The `in_memory' field is nonzero for elements that involve any reference to memory. These elements are removed whenever a write is done to an unidentified location in memory. To be safe, we assume that a memory address is unidentified unless the address is either a symbol constant or a constant plus the frame pointer or argument pointer. The `in_struct' field is nonzero for elements that involve any reference to memory inside a structure or array. The `related_value' field is used to connect related expressions (that differ by adding an integer). The related expressions are chained in a circular fashion. `related_value' is zero for expressions for which this chain is not useful. The `cost' field stores the cost of this element's expression. The `is_const' flag is set if the element is a constant (including a fixed address). The `flag' field is used as a temporary during some search routines.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -