From 5397b1559a09f05f41f99e7f20f670ffc7b2d38e Mon Sep 17 00:00:00 2001 From: Geoff Keating Date: Mon, 26 Jun 2000 21:36:58 +0000 Subject: [PATCH] ssa.c (struct rename_set_data): Change the name of field 'set_dest' to 'old_reg'. * ssa.c (struct rename_set_data): Change the name of field 'set_dest' to 'old_reg'. Add comments. (struct rename_context): Change the name of 'set_data' to 'new_renames'. Add new field 'done_renames'. (create_delayed_rename): New function. (apply_delayed_renames): New function. (rename_insn_1): Use the new functions. Handle CLOBBERS. Handle SUBREGs and similar by emitting a move. (new_registers_for_updates): Delete, functionality moved to apply_delayed_renames. (rename_block): Handle moves emitted by rename_insn_1 by putting them into a SEQUENCE with the original insn. Add sanity checks and comments. (rename_equivalent_regs_in_insn): Don't handle SUBREGs specially. (rename_equivalent_regs): Expand SEQUENCEs out to individual insns. From-SVN: r34720 --- gcc/ChangeLog | 18 +++ gcc/ssa.c | 320 +++++++++++++++++++++++++++++--------------------- 2 files changed, 203 insertions(+), 135 deletions(-) diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 27cb53fa5a2..3ad4f50028e 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,21 @@ +2000-06-26 Geoff Keating + + * ssa.c (struct rename_set_data): Change the name of field + 'set_dest' to 'old_reg'. Add comments. + (struct rename_context): Change the name of 'set_data' to + 'new_renames'. Add new field 'done_renames'. + (create_delayed_rename): New function. + (apply_delayed_renames): New function. + (rename_insn_1): Use the new functions. Handle CLOBBERS. Handle + SUBREGs and similar by emitting a move. + (new_registers_for_updates): Delete, functionality moved to + apply_delayed_renames. + (rename_block): Handle moves emitted by rename_insn_1 by putting + them into a SEQUENCE with the original insn. Add sanity checks + and comments. + (rename_equivalent_regs_in_insn): Don't handle SUBREGs specially. + (rename_equivalent_regs): Expand SEQUENCEs out to individual insns. + 2000-06-26 Andrew Macleod Jason Merrill diff --git a/gcc/ssa.c b/gcc/ssa.c index dd4b4736164..46fa38f8253 100644 --- a/gcc/ssa.c +++ b/gcc/ssa.c @@ -93,6 +93,8 @@ static unsigned int ssa_max_reg_num; /* Local function prototypes. */ +struct rename_context; + static inline rtx * phi_alternative PARAMS ((rtx, int)); @@ -114,6 +116,10 @@ static void insert_phi_node PARAMS ((int regno, int b)); static void insert_phi_nodes PARAMS ((sbitmap *idfs, sbitmap *evals, int nregs)); +static void create_delayed_rename + PARAMS ((struct rename_context *, rtx *)); +static void apply_delayed_renames + PARAMS ((struct rename_context *)); static int rename_insn_1 PARAMS ((rtx *ptr, void *data)); static void rename_block @@ -521,10 +527,17 @@ insert_phi_nodes (idfs, evals, nregs) struct rename_set_data { struct rename_set_data *next; + /* This is the SET_DEST of the (first) SET that sets the REG. */ rtx *reg_loc; - rtx set_dest; + /* This is what used to be at *REG_LOC. */ + rtx old_reg; + /* This is the REG that will replace OLD_REG. It's set only + when the rename data is moved onto the DONE_RENAMES queue. */ rtx new_reg; + /* This is what to restore ssa_rename_to[REGNO (old_reg)] to. + It is usually the previous contents of ssa_rename_to[REGNO (old_reg)]. */ rtx prev_reg; + /* This is the insn that contains all the SETs of the REG. */ rtx set_insn; }; @@ -532,13 +545,31 @@ struct rename_set_data renaming registers. */ struct rename_context { - struct rename_set_data *set_data; + struct rename_set_data *new_renames; + struct rename_set_data *done_renames; rtx current_insn; }; -static void new_registers_for_updates - PARAMS ((struct rename_set_data *set_data, - struct rename_set_data *old_set_data, rtx insn)); +/* Queue the rename of *REG_LOC. */ +static void +create_delayed_rename (c, reg_loc) + struct rename_context *c; + rtx *reg_loc; +{ + struct rename_set_data *r; + r = (struct rename_set_data *) xmalloc (sizeof(*r)); + + if (GET_CODE (*reg_loc) != REG + || REGNO (*reg_loc) < FIRST_PSEUDO_REGISTER) + abort(); + + r->reg_loc = reg_loc; + r->old_reg = *reg_loc; + r->prev_reg = ssa_rename_to [REGNO (r->old_reg) - FIRST_PSEUDO_REGISTER]; + r->set_insn = c->current_insn; + r->next = c->new_renames; + c->new_renames = r; +} /* This is part of a rather ugly hack to allow the pre-ssa regno to be reused. If, during processing, a register has not yet been touched, @@ -549,6 +580,60 @@ static void new_registers_for_updates already been reused. */ #define RENAME_NO_RTX pc_rtx +/* Move all the entries from NEW_RENAMES onto DONE_RENAMES by + applying all the renames on NEW_RENAMES. */ + +static void +apply_delayed_renames (c) + struct rename_context *c; +{ + struct rename_set_data *r; + struct rename_set_data *last_r = NULL; + + for (r = c->new_renames; r != NULL; r = r->next) + { + int regno = REGNO (r->old_reg); + int new_regno; + + /* Failure here means that someone has a PARALLEL that sets + a register twice (bad!). */ + if (ssa_rename_to [regno - FIRST_PSEUDO_REGISTER] != r->prev_reg) + abort(); + /* Failure here means we have changed REG_LOC before applying + the rename. */ + /* For the first set we come across, reuse the original regno. */ + if (r->prev_reg == NULL_RTX) + { + r->new_reg = r->old_reg; + /* We want to restore RENAME_NO_RTX rather than NULL_RTX. */ + r->prev_reg = RENAME_NO_RTX; + } + else + r->new_reg = gen_reg_rtx (GET_MODE (r->old_reg)); + new_regno = REGNO (r->new_reg); + ssa_rename_to[regno - FIRST_PSEUDO_REGISTER] = r->new_reg; + + if (new_regno >= (int) ssa_definition->num_elements) + { + int new_limit = new_regno * 5 / 4; + ssa_definition = VARRAY_GROW (ssa_definition, new_limit); + ssa_uses = VARRAY_GROW (ssa_uses, new_limit); + ssa_rename_from = VARRAY_GROW (ssa_rename_from, new_limit); + } + + VARRAY_RTX (ssa_definition, new_regno) = r->set_insn; + VARRAY_RTX (ssa_rename_from, new_regno) = r->old_reg; + + last_r = r; + } + if (last_r != NULL) + { + last_r->next = c->done_renames; + c->done_renames = c->new_renames; + c->new_renames = NULL; + } +} + /* Part one of the first step of rename_block, called through for_each_rtx. Mark pseudos that are set for later update. Transform uses of pseudos. */ @@ -559,47 +644,76 @@ rename_insn_1 (ptr, data) { rtx x = *ptr; struct rename_context *context = data; - struct rename_set_data **set_datap = &(context->set_data); if (x == NULL_RTX) return 0; switch (GET_CODE (x)) { + case CLOBBER: case SET: { rtx *destp = &SET_DEST (x); rtx dest = SET_DEST (x); - /* Subregs at word 0 are interesting. Subregs at word != 0 are - presumed to be part of a contiguous multi-word set sequence. */ - while (GET_CODE (dest) == SUBREG - && SUBREG_WORD (dest) == 0) + /* Some SETs also use the REG specified in their LHS. + These can be detected by the presence of + STRICT_LOW_PART, SUBREG, SIGN_EXTRACT, and ZERO_EXTRACT + in the LHS. Handle these by changing + (set (subreg (reg foo)) ...) + into + (sequence [(set (reg foo_1) (reg foo)) + (set (subreg (reg foo_1)) ...)]) + + FIXME: Much of the time this is too much. For many libcalls, + paradoxical SUBREGs, etc., the input register is dead. We should + recognise this in rename_block or here and not make a false + dependency. */ + + if (GET_CODE (dest) == STRICT_LOW_PART + || GET_CODE (dest) == SUBREG + || GET_CODE (dest) == SIGN_EXTRACT + || GET_CODE (dest) == ZERO_EXTRACT) { - destp = &SUBREG_REG (dest); - dest = SUBREG_REG (dest); + rtx i, reg; + reg = dest; + + while (GET_CODE (reg) == STRICT_LOW_PART + || GET_CODE (reg) == SUBREG + || GET_CODE (reg) == SIGN_EXTRACT + || GET_CODE (reg) == ZERO_EXTRACT) + reg = XEXP (reg, 0); + + if (GET_CODE (reg) == REG + && REGNO (reg) >= FIRST_PSEUDO_REGISTER) + { + /* Generate (set reg reg), and do renaming on it so + that it becomes (set reg_1 reg_0), and we will + replace reg with reg_1 in the SUBREG. */ + + struct rename_set_data *saved_new_renames; + saved_new_renames = context->new_renames; + context->new_renames = NULL; + i = emit_insn (gen_rtx_SET (VOIDmode, reg, reg)); + for_each_rtx (&i, rename_insn_1, data); + apply_delayed_renames (context); + context->new_renames = saved_new_renames; + } } - - if (GET_CODE (dest) == REG - && REGNO (dest) >= FIRST_PSEUDO_REGISTER) + else if (GET_CODE (dest) == REG + && REGNO (dest) >= FIRST_PSEUDO_REGISTER) { /* We found a genuine set of an interesting register. Tag it so that we can create a new name for it after we finish processing this insn. */ - struct rename_set_data *r; - r = (struct rename_set_data *) xmalloc (sizeof(*r)); - - r->reg_loc = destp; - r->set_dest = SET_DEST (x); - r->set_insn = context->current_insn; - r->next = *set_datap; - *set_datap = r; + create_delayed_rename (context, destp); /* Since we do not wish to (directly) traverse the SET_DEST, recurse through for_each_rtx for the SET_SRC and return. */ - for_each_rtx (&SET_SRC (x), rename_insn_1, data); + if (GET_CODE (x) == SET) + for_each_rtx (&SET_SRC (x), rename_insn_1, data); return -1; } @@ -634,55 +748,6 @@ rename_insn_1 (ptr, data) } } -/* Second part of the first step of rename_block. The portion of the list - beginning at SET_DATA through OLD_SET_DATA contain the sets present in - INSN. Update data structures accordingly. */ - -static void -new_registers_for_updates (set_data, old_set_data, insn) - struct rename_set_data *set_data, *old_set_data; - rtx insn; -{ - while (set_data != old_set_data) - { - int regno, new_regno; - rtx old_reg, new_reg, prev_reg; - - old_reg = *set_data->reg_loc; - regno = REGNO (*set_data->reg_loc); - - /* For the first set we come across, reuse the original regno. */ - if (ssa_rename_to[regno - FIRST_PSEUDO_REGISTER] == NULL_RTX) - { - new_reg = old_reg; - prev_reg = RENAME_NO_RTX; - } - else - { - prev_reg = ssa_rename_to[regno - FIRST_PSEUDO_REGISTER]; - new_reg = gen_reg_rtx (GET_MODE (old_reg)); - } - - set_data->new_reg = new_reg; - set_data->prev_reg = prev_reg; - new_regno = REGNO (new_reg); - ssa_rename_to[regno - FIRST_PSEUDO_REGISTER] = new_reg; - - if (new_regno >= (int) ssa_definition->num_elements) - { - int new_limit = new_regno * 5 / 4; - ssa_definition = VARRAY_GROW (ssa_definition, new_limit); - ssa_uses = VARRAY_GROW (ssa_uses, new_limit); - ssa_rename_from = VARRAY_GROW (ssa_rename_from, new_limit); - } - - VARRAY_RTX (ssa_definition, new_regno) = insn; - VARRAY_RTX (ssa_rename_from, new_regno) = old_reg; - - set_data = set_data->next; - } -} - static void rename_block (bb, idom) int bb; @@ -705,14 +770,29 @@ rename_block (bb, idom) if (GET_RTX_CLASS (GET_CODE (insn)) == 'i') { struct rename_context context; - context.set_data = set_data; + context.done_renames = set_data; + context.new_renames = NULL; context.current_insn = insn; + start_sequence (); for_each_rtx (&PATTERN (insn), rename_insn_1, &context); for_each_rtx (®_NOTES (insn), rename_insn_1, &context); + + /* Sometimes, we end up with a sequence of insns that + SSA needs to treat as a single insn. Wrap these in a + SEQUENCE. (Any notes now get attached to the SEQUENCE, + not to the old version inner insn.) */ + if (get_insns () != NULL_RTX) + { + int i; + + emit (PATTERN (insn)); + PATTERN (insn) = gen_sequence (); + } + end_sequence (); - new_registers_for_updates (context.set_data, set_data, insn); - set_data = context.set_data; + apply_delayed_renames (&context); + set_data = context.done_renames; } next = NEXT_INSN (insn); @@ -780,29 +860,18 @@ rename_block (bb, idom) if (idom[c] == bb) rename_block (c, idom); - /* Step Four: Update the sets to refer to their new register. */ + /* Step Four: Update the sets to refer to their new register, + and restore ssa_rename_to to its previous state. */ while (set_data) { struct rename_set_data *next; rtx old_reg = *set_data->reg_loc; - /* If the set is of a subreg only, copy the entire reg first so - that unmodified bits are preserved. Of course, we don't - strictly have SSA any more, but that's the best we can do - without a lot of hard work. */ - - if (GET_CODE (set_data->set_dest) == SUBREG) - { - if (old_reg != set_data->new_reg) - { - rtx copy = gen_rtx_SET (GET_MODE (old_reg), - set_data->new_reg, old_reg); - emit_insn_before (copy, set_data->set_insn); - } - } - + if (*set_data->reg_loc != set_data->old_reg) + abort(); *set_data->reg_loc = set_data->new_reg; + ssa_rename_to[REGNO (old_reg)-FIRST_PSEUDO_REGISTER] = set_data->prev_reg; @@ -1478,14 +1547,6 @@ coalesce_regs_in_copies (bb, p, conflicts) src = SET_SRC (pattern); dest = SET_DEST (pattern); - /* If src or dest are subregs, find the underlying reg. */ - while (GET_CODE (src) == SUBREG - && SUBREG_WORD (src) != 0) - src = SUBREG_REG (src); - while (GET_CODE (dest) == SUBREG - && SUBREG_WORD (dest) != 0) - dest = SUBREG_REG (dest); - /* We're only looking for copies. */ if (GET_CODE (src) != REG || GET_CODE (dest) != REG) continue; @@ -1709,40 +1770,6 @@ rename_equivalent_regs_in_insn (ptr, data) switch (GET_CODE (x)) { - case SET: - { - rtx *destp = &SET_DEST (x); - rtx dest = SET_DEST (x); - - /* Subregs at word 0 are interesting. Subregs at word != 0 are - presumed to be part of a contiguous multi-word set sequence. */ - while (GET_CODE (dest) == SUBREG - && SUBREG_WORD (dest) == 0) - { - destp = &SUBREG_REG (dest); - dest = SUBREG_REG (dest); - } - - if (GET_CODE (dest) == REG - && REGNO (dest) >= FIRST_PSEUDO_REGISTER) - { - /* Got a pseudo; replace it. */ - int regno = REGNO (dest); - int new_regno = partition_find (reg_partition, regno); - if (regno != new_regno) - *destp = regno_reg_rtx[new_regno]; - - for_each_rtx (&SET_SRC (x), - rename_equivalent_regs_in_insn, - data); - return -1; - } - - /* Otherwise, this was not an interesting destination. Continue - on, marking uses as normal. */ - return 0; - } - case REG: if (REGNO (x) >= FIRST_PSEUDO_REGISTER) { @@ -1769,7 +1796,8 @@ rename_equivalent_regs_in_insn (ptr, data) } } -/* Rename regs that are equivalent in REG_PARTITION. */ +/* Rename regs that are equivalent in REG_PARTITION. + Also collapse any SEQUENCE insns. */ static void rename_equivalent_regs (reg_partition) @@ -1795,6 +1823,28 @@ rename_equivalent_regs (reg_partition) for_each_rtx (®_NOTES (insn), rename_equivalent_regs_in_insn, reg_partition); + + if (GET_CODE (PATTERN (insn)) == SEQUENCE) + { + rtx s = PATTERN (insn); + int slen = XVECLEN (s, 0); + int i; + + if (slen <= 1) + abort(); + + PATTERN (insn) = PATTERN (XVECEXP (s, 0, slen-1)); + for (i = 0; i < slen - 1; i++) + { + rtx new_insn; + rtx old_insn = XVECEXP (s, 0, i); + + new_insn = emit_block_insn_before (PATTERN (old_insn), + insn, b); + REG_NOTES (new_insn) = REG_NOTES (old_insn); + } + + } } next = NEXT_INSN (insn); -- 2.30.2