diff options
author | Adam Nemet <anemet@caviumnetworks.com> | 2009-05-28 07:42:52 +0000 |
---|---|---|
committer | Adam Nemet <nemet@gcc.gnu.org> | 2009-05-28 07:42:52 +0000 |
commit | 2c5bfdf70b616ace5c3f5322299ccc9729e08384 (patch) | |
tree | b6849b0da1547659fc7e169123e25b958cc36e69 /gcc/cse.c | |
parent | 51fb7760dd6fcad1570ef1462459136cefb9c8f0 (diff) | |
download | gcc-2c5bfdf70b616ace5c3f5322299ccc9729e08384.tar.gz |
re PR middle-end/33699 (missing optimization on const addr area store)
PR middle-end/33699
* target.h (struct gcc_target): Fix indentation. Add
const_anchor.
* target-def.h (TARGET_CONST_ANCHOR): New macro.
(TARGET_INITIALIZER): Use it.
* cse.c (CHEAPER): Move it up to the other macros.
(insert): Rename this ...
(insert_with_costs): ... to this. Add cost parameters. Update
function comment.
(insert): New function. Call insert_with_costs.
(compute_const_anchors, insert_const_anchor, insert_const_anchors,
find_reg_offset_for_const, try_const_anchors): New functions.
(cse_insn): Call try_const_anchors. Adjust cost of src_related
when using a const-anchor. Call insert_const_anchors.
* config/mips/mips.c (mips_set_mips16_mode): Set
targetm.const_anchor.
* doc/tm.texi (Misc): Document TARGET_CONST_ANCHOR.
testsuite/
* gcc.target/mips/const-anchor-1.c: New test.
* gcc.target/mips/const-anchor-2.c: New test.
From-SVN: r147944
Diffstat (limited to 'gcc/cse.c')
-rw-r--r-- | gcc/cse.c | 240 |
1 files changed, 229 insertions, 11 deletions
diff --git a/gcc/cse.c b/gcc/cse.c index a697ed428e0..8e37b64ecbb 100644 --- a/gcc/cse.c +++ b/gcc/cse.c @@ -502,6 +502,11 @@ struct table_elt #define REGNO_QTY_VALID_P(N) (REG_QTY (N) >= 0) +/* Compare table_elt X and Y and return true iff X is cheaper than Y. */ + +#define CHEAPER(X, Y) \ + (preferable ((X)->cost, (X)->regcost, (Y)->cost, (Y)->regcost) < 0) + static struct table_elt *table[HASH_SIZE]; /* Chain of `struct table_elt's made so far for this function @@ -563,6 +568,8 @@ static void remove_pseudo_from_table (rtx, unsigned); static struct table_elt *lookup (rtx, unsigned, enum machine_mode); static struct table_elt *lookup_for_remove (rtx, unsigned, enum machine_mode); static rtx lookup_as_function (rtx, enum rtx_code); +static struct table_elt *insert_with_costs (rtx, struct table_elt *, unsigned, + enum machine_mode, int, int); static struct table_elt *insert (rtx, struct table_elt *, unsigned, enum machine_mode); static void merge_equiv_classes (struct table_elt *, struct table_elt *); @@ -1213,6 +1220,174 @@ insert_regs (rtx x, struct table_elt *classp, int modified) return mention_regs (x); } + +/* Compute upper and lower anchors for CST. Also compute the offset of CST + from these anchors/bases such that *_BASE + *_OFFS = CST. Return false iff + CST is equal to an anchor. */ + +static bool +compute_const_anchors (rtx cst, + HOST_WIDE_INT *lower_base, HOST_WIDE_INT *lower_offs, + HOST_WIDE_INT *upper_base, HOST_WIDE_INT *upper_offs) +{ + HOST_WIDE_INT n = INTVAL (cst); + + *lower_base = n & ~(targetm.const_anchor - 1); + if (*lower_base == n) + return false; + + *upper_base = + (n + (targetm.const_anchor - 1)) & ~(targetm.const_anchor - 1); + *upper_offs = n - *upper_base; + *lower_offs = n - *lower_base; + return true; +} + +/* Insert the equivalence between ANCHOR and (REG + OFF) in mode MODE. */ + +static void +insert_const_anchor (HOST_WIDE_INT anchor, rtx reg, HOST_WIDE_INT offs, + enum machine_mode mode) +{ + struct table_elt *elt; + unsigned hash; + rtx anchor_exp; + rtx exp; + + anchor_exp = GEN_INT (anchor); + hash = HASH (anchor_exp, mode); + elt = lookup (anchor_exp, hash, mode); + if (!elt) + elt = insert (anchor_exp, NULL, hash, mode); + + exp = plus_constant (reg, offs); + /* REG has just been inserted and the hash codes recomputed. */ + mention_regs (exp); + hash = HASH (exp, mode); + + /* Use the cost of the register rather than the whole expression. When + looking up constant anchors we will further offset the corresponding + expression therefore it does not make sense to prefer REGs over + reg-immediate additions. Prefer instead the oldest expression. Also + don't prefer pseudos over hard regs so that we derive constants in + argument registers from other argument registers rather than from the + original pseudo that was used to synthesize the constant. */ + insert_with_costs (exp, elt, hash, mode, COST (reg), 1); +} + +/* The constant CST is equivalent to the register REG. Create + equivalences between the two anchors of CST and the corresponding + register-offset expressions using REG. */ + +static void +insert_const_anchors (rtx reg, rtx cst, enum machine_mode mode) +{ + HOST_WIDE_INT lower_base, lower_offs, upper_base, upper_offs; + + if (!compute_const_anchors (cst, &lower_base, &lower_offs, + &upper_base, &upper_offs)) + return; + + /* Ignore anchors of value 0. Constants accessible from zero are + simple. */ + if (lower_base != 0) + insert_const_anchor (lower_base, reg, -lower_offs, mode); + + if (upper_base != 0) + insert_const_anchor (upper_base, reg, -upper_offs, mode); +} + +/* We need to express ANCHOR_ELT->exp + OFFS. Walk the equivalence list of + ANCHOR_ELT and see if offsetting any of the entries by OFFS would create a + valid expression. Return the cheapest and oldest of such expressions. In + *OLD, return how old the resulting expression is compared to the other + equivalent expressions. */ + +static rtx +find_reg_offset_for_const (struct table_elt *anchor_elt, HOST_WIDE_INT offs, + unsigned *old) +{ + struct table_elt *elt; + unsigned idx; + struct table_elt *match_elt; + rtx match; + + /* Find the cheapest and *oldest* expression to maximize the chance of + reusing the same pseudo. */ + + match_elt = NULL; + match = NULL_RTX; + for (elt = anchor_elt->first_same_value, idx = 0; + elt; + elt = elt->next_same_value, idx++) + { + if (match_elt && CHEAPER (match_elt, elt)) + return match; + + if (REG_P (elt->exp) + || (GET_CODE (elt->exp) == PLUS + && REG_P (XEXP (elt->exp, 0)) + && GET_CODE (XEXP (elt->exp, 1)) == CONST_INT)) + { + rtx x; + + /* Ignore expressions that are no longer valid. */ + if (!REG_P (elt->exp) && !exp_equiv_p (elt->exp, elt->exp, 1, false)) + continue; + + x = plus_constant (elt->exp, offs); + if (REG_P (x) + || (GET_CODE (x) == PLUS + && IN_RANGE (INTVAL (XEXP (x, 1)), + -targetm.const_anchor, + targetm.const_anchor - 1))) + { + match = x; + match_elt = elt; + *old = idx; + } + } + } + + return match; +} + +/* Try to express the constant SRC_CONST using a register+offset expression + derived from a constant anchor. Return it if successful or NULL_RTX, + otherwise. */ + +static rtx +try_const_anchors (rtx src_const, enum machine_mode mode) +{ + struct table_elt *lower_elt, *upper_elt; + HOST_WIDE_INT lower_base, lower_offs, upper_base, upper_offs; + rtx lower_anchor_rtx, upper_anchor_rtx; + rtx lower_exp = NULL_RTX, upper_exp = NULL_RTX; + unsigned lower_old, upper_old; + + if (!compute_const_anchors (src_const, &lower_base, &lower_offs, + &upper_base, &upper_offs)) + return NULL_RTX; + + lower_anchor_rtx = GEN_INT (lower_base); + upper_anchor_rtx = GEN_INT (upper_base); + lower_elt = lookup (lower_anchor_rtx, HASH (lower_anchor_rtx, mode), mode); + upper_elt = lookup (upper_anchor_rtx, HASH (upper_anchor_rtx, mode), mode); + + if (lower_elt) + lower_exp = find_reg_offset_for_const (lower_elt, lower_offs, &lower_old); + if (upper_elt) + upper_exp = find_reg_offset_for_const (upper_elt, upper_offs, &upper_old); + + if (!lower_exp) + return upper_exp; + if (!upper_exp) + return lower_exp; + + /* Return the older expression. */ + return (upper_old > lower_old ? upper_exp : lower_exp); +} + /* Look in or update the hash table. */ /* Remove table element ELT from use in the table. @@ -1380,11 +1555,11 @@ lookup_as_function (rtx x, enum rtx_code code) return 0; } -/* Insert X in the hash table, assuming HASH is its hash code - and CLASSP is an element of the class it should go in - (or 0 if a new class should be made). - It is inserted at the proper position to keep the class in - the order cheapest first. +/* Insert X in the hash table, assuming HASH is its hash code and + CLASSP is an element of the class it should go in (or 0 if a new + class should be made). COST is the code of X and reg_cost is the + cost of registers in X. It is inserted at the proper position to + keep the class in the order cheapest first. MODE is the machine-mode of X, or if X is an integer constant with VOIDmode then MODE is the mode with which X will be used. @@ -1404,11 +1579,9 @@ lookup_as_function (rtx x, enum rtx_code code) If necessary, update table showing constant values of quantities. */ -#define CHEAPER(X, Y) \ - (preferable ((X)->cost, (X)->regcost, (Y)->cost, (Y)->regcost) < 0) - static struct table_elt * -insert (rtx x, struct table_elt *classp, unsigned int hash, enum machine_mode mode) +insert_with_costs (rtx x, struct table_elt *classp, unsigned int hash, + enum machine_mode mode, int cost, int reg_cost) { struct table_elt *elt; @@ -1430,8 +1603,8 @@ insert (rtx x, struct table_elt *classp, unsigned int hash, enum machine_mode mo elt->exp = x; elt->canon_exp = NULL_RTX; - elt->cost = COST (x); - elt->regcost = approx_reg_cost (x); + elt->cost = cost; + elt->regcost = reg_cost; elt->next_same_value = 0; elt->prev_same_value = 0; elt->next_same_hash = table[hash]; @@ -1566,6 +1739,17 @@ insert (rtx x, struct table_elt *classp, unsigned int hash, enum machine_mode mo return elt; } + +/* Wrap insert_with_costs by passing the default costs. */ + +static struct table_elt * +insert (rtx x, struct table_elt *classp, unsigned int hash, + enum machine_mode mode) +{ + return + insert_with_costs (x, classp, hash, mode, COST (x), approx_reg_cost (x)); +} + /* Given two equivalence classes, CLASS1 and CLASS2, put all the entries from CLASS2 into CLASS1. This is done when we have reached an insn which makes @@ -4253,6 +4437,7 @@ cse_insn (rtx insn) rtx src_eqv_here; rtx src_const = 0; rtx src_related = 0; + bool src_related_is_const_anchor = false; struct table_elt *src_const_elt = 0; int src_cost = MAX_COST; int src_eqv_cost = MAX_COST; @@ -4602,6 +4787,19 @@ cse_insn (rtx insn) } #endif /* LOAD_EXTEND_OP */ + /* Try to express the constant using a register+offset expression + derived from a constant anchor. */ + + if (targetm.const_anchor + && !src_related + && src_const + && GET_CODE (src_const) == CONST_INT) + { + src_related = try_const_anchors (src_const, mode); + src_related_is_const_anchor = src_related != NULL_RTX; + } + + if (src == src_folded) src_folded = 0; @@ -4706,6 +4904,18 @@ cse_insn (rtx insn) { src_related_cost = COST (src_related); src_related_regcost = approx_reg_cost (src_related); + + /* If a const-anchor is used to synthesize a constant that + normally requires multiple instructions then slightly prefer + it over the original sequence. These instructions are likely + to become redundant now. We can't compare against the cost + of src_eqv_here because, on MIPS for example, multi-insn + constants have zero cost; they are assumed to be hoisted from + loops. */ + if (src_related_is_const_anchor + && src_related_cost == src_cost + && src_eqv_here) + src_related_cost--; } } @@ -5440,6 +5650,14 @@ cse_insn (rtx insn) elt = insert (dest, sets[i].src_elt, sets[i].dest_hash, GET_MODE (dest)); + /* If this is a constant, insert the constant anchors with the + equivalent register-offset expressions using register DEST. */ + if (targetm.const_anchor + && REG_P (dest) + && SCALAR_INT_MODE_P (GET_MODE (dest)) + && GET_CODE (sets[i].src_elt->exp) == CONST_INT) + insert_const_anchors (dest, sets[i].src_elt->exp, GET_MODE (dest)); + elt->in_memory = (MEM_P (sets[i].inner_dest) && !MEM_READONLY_P (sets[i].inner_dest)); |