diff options
author | jrv <jrv@138bc75d-0d04-0410-961f-82ee72b054a4> | 1991-12-26 10:05:42 +0000 |
---|---|---|
committer | jrv <jrv@138bc75d-0d04-0410-961f-82ee72b054a4> | 1991-12-26 10:05:42 +0000 |
commit | 535825e69a252f305fbdbb98a0a928ad06d52f02 (patch) | |
tree | 9d830b14dc0b1dab3fb2577a9f05241d7621865b /gcc/reg-stack.c | |
parent | 1acadd8b5f99fe0afb05912c2226d461cd35158e (diff) | |
download | gcc-535825e69a252f305fbdbb98a0a928ad06d52f02.tar.gz |
Initial revision
git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@142 138bc75d-0d04-0410-961f-82ee72b054a4
Diffstat (limited to 'gcc/reg-stack.c')
-rw-r--r-- | gcc/reg-stack.c | 1938 |
1 files changed, 1938 insertions, 0 deletions
diff --git a/gcc/reg-stack.c b/gcc/reg-stack.c new file mode 100644 index 00000000000..f69fd9f5ab0 --- /dev/null +++ b/gcc/reg-stack.c @@ -0,0 +1,1938 @@ +/* Register to Stack convert for GNU compiler. + Copyright (C) 1990 Free Software Foundation, Inc. + +This file is part of GNU CC. + +GNU CC 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. + +GNU CC 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 GNU CC; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* This pass converts stack-like registers from the "flat register + file" model that gcc uses, to a stack convention that the 387 uses. + + * The form of the input: + + On input, the function consists of insn that have had their + registers fully allocated to a set of "virtual" registers. Note that + the word "virtual" is used differently here than elsewhere in gcc: for + each virtual stack reg, there is a hard reg, but the mapping between + them is not known until this pass is run. On output, hard register + numbers have been substituted, and various pop and exchange insns have + been emitted. The hard register numbers and the virtual register + numbers completely overlap - before this pass, all stack register + numbers are virtual, and afterward they are all hard, with the + exception of ASM_OPERANDS, which are discussed below. + + The virtual registers can be manipulated normally by gcc, and their + semantics are the same as for normal registers. After the hard + register numbers are substituted, the semantics of an insn containing + stack-like regs are not the same as for an insn with normal regs: for + instance, it is not safe to delete an insn that appears to be a no-op + move. In general, no insn containing hard regs should be changed + after this pass is done. + + * The form of the output: + + After this pass, hard register numbers represent the distance from + the current top of stack to the desired register. A reference to + FIRST_STACK_REG references the top of stack, FIRST_STACK_REG + 1, + represents the register just below that, and so forth. Also, REG_DEAD + notes indicate whether or not a stack register should be popped. + + A "swap" insn looks like a parallel of two patterns, where each + pattern is a SET: one sets A to B, the other B to A. + + A "push" or "load" insn is a SET whose SET_DEST is FIRST_STACK_REG + and whose SET_DEST is REG or MEM. Any other SET_DEST, such as PLUS, + will replace the existing stack top, not push a new value. + + A store insn is a SET whose SET_DEST is FIRST_STACK_REG, and whose + SET_SRC is REG or MEM. + + The case where both the SET_SRC and SET_DEST FIRST_STACK_REG + appears ambiguous. As a special case, the presence of a REG_DEAD note + for FIRST_STACK_REG differentiates between a load insn and a pop. + + If a REG_DEAD is present, the insn represents a "pop" that discards + the top of the register stack. If there is no REG_DEAD note, then the + insn represents a "dup" or a push of the current top of stack onto the + stack. + + * Methodology: + + Existing REG_DEAD and REG_UNUSED notes for stack registers are + deleted and recreated from scratch. REG_DEAD is never created for a + SET_DEST, only REG_UNUSED. + + Before life analysis, the mode of each insn is set based on whether + or not any stack registers are mentioned within that insn. VOIDmode + means that no regs are mentioned anyway, and QImode means that at + least one pattern within the insn mentions stack registers. This + information is valid until after reg_to_stack returns, and is used + from jump_optimize. + + * Limitations: + + Inline assembly isn't handled yet. */ + +#include <stdio.h> +#include "config.h" +#include "tree.h" +#include "rtl.h" +#include "regs.h" +#include "hard-reg-set.h" +#include "flags.h" + +#ifdef STACK_REGS + +#define REG_STACK_SIZE (LAST_STACK_REG - FIRST_STACK_REG + 1) + +/* True if the current function returns a real value. */ +static int current_function_returns_real; + +/* This is the basic stack record. TOP is an index into REG[] such + that REG[TOP] is the top of stack. If TOP is -1 the stack is empty. + + If TOP is -2 the stack is not yet initialized: reg_set indicates + which registers are live. Stack initialization consists of placing + each live reg in array `reg' and setting `top' appropriately. */ + +typedef struct stack_def +{ + int top; /* index to top stack element */ + HARD_REG_SET reg_set; /* set of live registers */ + char reg[REG_STACK_SIZE]; /* register - stack mapping */ +} *stack; + +/* highest instruction uid */ +static int max_uid = 0; + +/* Number of basic blocks in the current function. */ +static int blocks; + +/* Element N is first insn in basic block N. + This info lasts until we finish compiling the function. */ +static rtx *block_begin; + +/* Element N is last insn in basic block N. + This info lasts until we finish compiling the function. */ +static rtx *block_end; + +/* Element N is nonzero if control can drop into basic block N */ +static char *block_drops_in; + +/* Element N says all about the stack at entry block N */ +static stack block_stack_in; + +/* Element N says all about the stack life at the end of block N */ +static HARD_REG_SET *block_out_reg_set; + +/* This is where the BLOCK_NUM values are really stored. This is set + up by find_blocks and used there and in life_analysis. It can be used + later, but only to look up an insn that is the head or tail of some + block. life_analysis and the stack register conversion process can + add insns within a block. */ +static short *block_number; + +/* This is the register file for all register after conversion */ +static rtx SFmode_reg[FIRST_PSEUDO_REGISTER]; +static rtx DFmode_reg[FIRST_PSEUDO_REGISTER]; + +/* ??? set of register to delete after ASM_OPERAND */ +HARD_REG_SET asm_regs; + +/* Get the basic block number of an insn. See note at block_number + definition are validity of this information. */ + +#define BLOCK_NUM(INSN) \ + (((INSN_UID (INSN) > max_uid) \ + ? (short *)(abort() , 0) \ + : block_number)[INSN_UID (INSN)]) + +extern rtx gen_jump (); +extern rtx gen_movdf (); +extern rtx find_regno_note (); +extern rtx emit_jump_insn_before (); +extern rtx emit_label_after (); + +extern rtx dconst0_rtx; + +/* Forward declarations */ + +static void find_blocks (); +static void stack_reg_life_analysis (); +static void convert_regs (); +static void dump_stack_info (); +static void fatal_for_asm (); + + +/* Return non-zero if any stack register is mentioned somewhere within + PAT. */ + +static int +stack_regs_mentioned_p (pat) + register rtx pat; +{ + register char *fmt; + register int i; + + if (STACK_REG_P (pat)) + return 1; + + fmt = GET_RTX_FORMAT (GET_CODE (pat)); + for (i = GET_RTX_LENGTH (GET_CODE (pat)) - 1; i >= 0; i--) + { + if (fmt[i] == 'E') + { + register int j; + + for (j = XVECLEN (pat, i) - 1; j >= 0; j--) + if (stack_regs_mentioned_p (XVECEXP (pat, i, j))) + return 1; + } + else if (fmt[i] == 'e' && stack_regs_mentioned_p (XEXP (pat, i))) + return 1; + } + + return 0; +} + +/* Convert register usage from "flat" register file usage to a "stack + register file. FIRST is the first insn in the function, FILE is the + dump file, if used. + + First compute the beginning and end of each basic block. Do a + register life analysis on the stack registers, recording the result + for the head and tail of each basic block. The convert each insn one + by one. Run a last jump_optimize() pass, if optimizing, to eliminate + any cross-jumping created when the converter inserts pop insns.*/ + +void +reg_to_stack (first, file) + rtx first; + FILE *file; +{ + register rtx insn; + register int i; + int stack_reg_seen = 0; + + current_function_returns_real + = TREE_CODE (TREE_TYPE (DECL_RESULT (current_function_decl))) == REAL_TYPE; + + for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) + { + SFmode_reg[i] = gen_rtx (REG, SFmode, i); + DFmode_reg[i] = gen_rtx (REG, DFmode, i); + } + + /* Count the basic blocks. Also find maximum insn uid. */ + { + register RTX_CODE prev_code = JUMP_INSN; + register RTX_CODE code; + + max_uid = 0; + blocks = 0; + for (insn = first; insn; insn = NEXT_INSN (insn)) + { + /* Note that this loop must select the same block boundaries + as code in find_blocks. */ + + if (INSN_UID (insn) > max_uid) + max_uid = INSN_UID (insn); + + code = GET_CODE (insn); + + if (code == CODE_LABEL + || (prev_code != INSN + && prev_code != CALL_INSN + && prev_code != CODE_LABEL + && (code == INSN || code == CALL_INSN || code == JUMP_INSN))) + blocks++; + + /* Remember whether or not this insn mentions an FP regs. + Check JUMP_INSNs too, in case someone creates a funny PARALLEL. */ + + if ((GET_CODE (insn) == INSN || GET_CODE (insn) == CALL_INSN + || GET_CODE (insn) == JUMP_INSN) + && stack_regs_mentioned_p (PATTERN (insn))) + { + stack_reg_seen = 1; + PUT_MODE (insn, QImode); + } + else + PUT_MODE (insn, VOIDmode); + + if (code != NOTE) + prev_code = code; + } + } + + /* If no stack register reference exists in this insn, there isn't + anything to convert. */ + + if (! stack_reg_seen) + return; + + /* If there are stack registers, there must be at least one block. */ + + if (! blocks) + abort (); + + /* Allocate some tables that last till end of compiling this function + and some needed only in find_blocks and life_analysis. */ + + block_begin = (rtx *) alloca (blocks * sizeof (rtx)); + block_end = (rtx *) alloca (blocks * sizeof (rtx)); + block_drops_in = (char *) alloca (blocks); + + block_stack_in = (stack) alloca (blocks * sizeof (struct stack_def)); + block_out_reg_set = (HARD_REG_SET *) alloca (blocks * sizeof (HARD_REG_SET)); + bzero (block_stack_in, blocks * sizeof (struct stack_def)); + bzero (block_out_reg_set, blocks * sizeof (HARD_REG_SET)); + + block_number = (short *) alloca ((max_uid + 1) * sizeof (short)); + + find_blocks (first); + stack_reg_life_analysis (first); + + /* Dump the life analysis debug information before jump + optimization, as that will destroy the LABEL_REFS we keep the + information in. */ + + if (file) + dump_stack_info (file); + + convert_regs (); + + if (optimize) + jump_optimize (first, 2, 0, 0); +} + +/* Check PAT, which is in INSN, for LABEL_REFs. Add INSN to the + label's chain of references, and note which insn contains each + reference. */ + +static void +record_label_references (insn, pat) + rtx insn, pat; +{ + register enum rtx_code code = GET_CODE (pat); + register int i; + register char *fmt; + + if (code == LABEL_REF) + { + register rtx label = XEXP (pat, 0); + register rtx ref; + + if (GET_CODE (label) != CODE_LABEL) + abort (); + + /* Don't make a duplicate in the code_label's chain. */ + + for (ref = LABEL_REFS (label); ref != label; ref = LABEL_NEXTREF (ref)) + if (CONTAINING_INSN (ref) == insn) + return; + + CONTAINING_INSN (pat) = insn; + LABEL_NEXTREF (pat) = LABEL_REFS (label); + LABEL_REFS (label) = pat; + + return; + } + + fmt = GET_RTX_FORMAT (code); + for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) + { + if (fmt[i] == 'e') + record_label_references (insn, XEXP (pat, i)); + if (fmt[i] == 'E') + { + register int j; + for (j = 0; j < XVECLEN (pat, i); j++) + record_label_references (insn, XVECEXP (pat, i, j)); + } + } +} + +/* Return a pointer to the REG expression within PAT. If PAT is not a + REG, possible enclosed by a conversion rtx, return the inner part of + PAT that stopped the search. */ + +static rtx * +get_true_reg (pat) + rtx *pat; +{ + while (GET_CODE (*pat) == SUBREG + || GET_CODE (*pat) == FLOAT + || GET_CODE (*pat) == FIX + || GET_CODE (*pat) == FLOAT_EXTEND + || GET_CODE (*pat) == FLOAT_TRUNCATE) + pat = & XEXP (*pat, 0); + + return pat; +} + +/* If REG is a stack register that is marked dead in REGSTACK, then + record that it is now live. If REG is not DEST, add a death note to + INSN if there isn't one already. If DEST is not a reg, it is safe to + assume that it does not mention a reg anywhere within. */ + +static void +record_note_if_dead (insn, regstack, reg, dest) + rtx insn; + stack regstack; + rtx reg, dest; +{ + reg = * get_true_reg (& reg); + + if (STACK_REG_P (reg)) + { + if (! TEST_HARD_REG_BIT (regstack->reg_set, REGNO (reg))) + { + if ((! REG_P (dest) || REGNO (dest) != REGNO (reg)) + && ! find_regno_note (insn, REG_DEAD, REGNO (reg))) + REG_NOTES (insn) = gen_rtx (EXPR_LIST, + REG_DEAD, reg, REG_NOTES (insn)); + + SET_HARD_REG_BIT (regstack->reg_set, REGNO (reg)); + } + } + else + if (stack_regs_mentioned_p (reg)) + abort (); +} + +/* Scan PAT, which is part of INSN, and record the life & death of + stack registers in REGSTACK. If a register was dead, but is an input + operand in this insn, then mark the register live and record a death + note. + + If a register is dead after this insn, but is an output operand in + this insn, record a REG_UNUSED note. + + This function does not know about SET_DESTs that are both input and + output (such as ZERO_EXTRACT) - this cannot happen on a 387. */ + +static void +record_reg_life_pat (insn, regstack, pat) + rtx insn; + stack regstack; + rtx pat; +{ + rtx src, dest; + + if (GET_CODE (pat) == CLOBBER + && GET_CODE (PATTERN (insn)) == PARALLEL + && GET_CODE (SET_SRC (XVECEXP (PATTERN (insn), 0, 0))) == ASM_OPERANDS) + { + if (STACK_REG_P (XEXP (pat, 0))) + abort (); + return; + } + + if (GET_CODE (pat) != SET) + return; + + dest = * get_true_reg (& SET_DEST (pat)); + + /* The destination is dead before this insn. If the destination is + not used after this insn, record this with REG_UNUSED. */ + + if (STACK_REG_P (dest)) + { + /* ??? This check is unnecessary. */ + + if (find_regno_note (insn, REG_UNUSED, REGNO (dest))) + abort (); + + if (! TEST_HARD_REG_BIT (regstack->reg_set, REGNO (dest))) + REG_NOTES (insn) = gen_rtx (EXPR_LIST, REG_UNUSED, dest, + REG_NOTES (insn)); + + CLEAR_HARD_REG_BIT (regstack->reg_set, REGNO (dest)); + } + else + if (dest != cc0_rtx && stack_regs_mentioned_p (dest)) + abort (); + + src = * get_true_reg (& SET_SRC (pat)); + + switch (GET_CODE (src)) + { + /* ??? get_true_reg will make some of these cases redundant. */ + + case PLUS: + case MINUS: + case MULT: + case DIV: + case COMPARE: + record_note_if_dead (insn, regstack, XEXP (src, 0), dest); + record_note_if_dead (insn, regstack, XEXP (src, 1), dest); + break; + + case ABS: + case NEG: + case SQRT: + case FLOAT_EXTEND: + case FLOAT_TRUNCATE: + case FLOAT: + case UNSIGNED_FLOAT: + record_note_if_dead (insn, regstack, XEXP (src, 0), dest); + break; + + case UNSIGNED_FIX: + case FIX: + src = XEXP (src, 0); + if (GET_CODE (src) == FIX) + record_note_if_dead (insn, regstack, XEXP (src, 0), dest); + else + record_note_if_dead (insn, regstack, src, dest); + break; + + case ASM_OPERANDS: + { + register int j; + + /* ??? This needs much improvement */ + + if (stack_regs_mentioned_p (pat)) + abort (); + + for (j = 0; j < XVECLEN (src, 3); j++) + record_note_if_dead (insn, regstack, XVECEXP (src, 3, j), dest); + } + break; + + case REG: + record_note_if_dead (insn, regstack, src, dest); + break; + + default: + /* If a stack register appears in the src RTL, it is a bug, and + code should be added above to handle it. */ + + if (stack_regs_mentioned_p (src)) + abort (); + } +} + +/* Scan INSN, which is in BLOCK, and record the life & death of stack + registers in REGSTACK. This function is called to process insns from + the last insn in a block to the first. The actual scanning is done in + record_reg_life_pat. + + If a register is live after a CALL_INSN, but is not a value return + register for that CALL_INSN, then code is emitted to initialize that + register. The block_end[] data is kept accurate. + + Existing death and unset notes for stack registers are deleted + before processing the insn. */ + +static void +record_reg_life (insn, block, regstack) + rtx insn; + int block; + stack regstack; +{ + rtx note, *note_link; + + if ((GET_CODE (insn) != INSN && GET_CODE (insn) != CALL_INSN) + || INSN_DELETED_P (insn)) + return; + + /* Strip death notes for stack regs from this insn */ + + note_link = ®_NOTES(insn); + for (note = *note_link; note; note = XEXP (note, 1)) + if (STACK_REG_P (XEXP (note, 0)) + && (REG_NOTE_KIND (note) == REG_DEAD + || REG_NOTE_KIND (note) == REG_UNUSED)) + *note_link = XEXP (note, 1); + else + note_link = &XEXP (note, 1); + + /* Process all patterns in the insn. */ + + if (GET_CODE (PATTERN (insn)) == PARALLEL) + { + register int i; + + for (i = 0; i < XVECLEN (PATTERN (insn), 0); i++) + record_reg_life_pat (insn, regstack, XVECEXP (PATTERN (insn), 0, i)); + } + else if (GET_MODE (insn) == QImode) + record_reg_life_pat (insn, regstack, PATTERN (insn)); + + /* There might be a reg that is live after a function call. + Initialize it to zero so that the program does not crash. See comment + towards the end of stack_reg_life_analysis(). */ + + if (GET_CODE (insn) == CALL_INSN) + { + int reg = FIRST_FLOAT_REG; + + /* If a stack reg is mentioned in a CALL_INSN, it must be as the + return value; conversely, if a float is returned, a stack reg + must be mentioned. */ + + if (stack_regs_mentioned_p (PATTERN (insn))) + reg++; + + for (; reg <= LAST_STACK_REG; reg++) + if (TEST_HARD_REG_BIT (regstack->reg_set, reg)) + { + rtx init, pat; + + /* The insn will use virtual register numbers, and so + convert_regs is expected to process these. But BLOCK_NUM + cannot be used on these insns, because they do not appear in + block_number[]. */ + + pat = gen_rtx (SET, VOIDmode, DFmode_reg[reg], dconst0_rtx); + init = emit_insn_after (pat, insn); + PUT_MODE (init, QImode); + + CLEAR_HARD_REG_BIT (regstack->reg_set, reg); + + /* If the CALL_INSN was the end of a block, move the + block_end to point to the new insn. */ + + if (block_end[block] == insn) + block_end[block] = init; + } + + /* Some regs do not survive a CALL */ + + AND_COMPL_HARD_REG_SET (regstack->reg_set, call_used_reg_set); + } +} + +/* Find all basic blocks of the function, which starts with FIRST. + For each JUMP_INSN, build the chain of LABEL_REFS on each CODE_LABEL. */ + +static void +find_blocks (first) + rtx first; +{ + register rtx insn; + register int block; + register RTX_CODE prev_code = BARRIER; + register RTX_CODE code; + + /* Record where all the blocks start and end. + Record which basic blocks control can drop in to. */ + + block = -1; + for (insn = first; insn; insn = NEXT_INSN (insn)) + { + /* Note that this loop must select the same block boundaries + as code in reg_to_stack. */ + + code = GET_CODE (insn); + + if (code == CODE_LABEL + || (prev_code != INSN + && prev_code != CALL_INSN + && prev_code != CODE_LABEL + && (code == INSN || code == CALL_INSN || code == JUMP_INSN))) + { + block_begin[++block] = insn; + block_end[block] = insn; + block_drops_in[block] = prev_code != BARRIER; + } + else if (code == INSN || code == CALL_INSN || code == JUMP_INSN) + block_end[block] = insn; + + BLOCK_NUM (insn) = block; + + if (code == CODE_LABEL) + LABEL_REFS (insn) = insn; /* delete old chain */ + + if (code != NOTE) + prev_code = code; + } + + if (block + 1 != blocks) + abort (); + + /* generate all label references to the correspondending jump insn */ + for (block = 0; block < blocks; block++) + { + insn = block_end[block]; + + if (GET_CODE (insn) == JUMP_INSN) + record_label_references (insn, PATTERN (insn)); + } +} + +/* Determine the which registers are live at the start of each basic + block of the function whose first insn is FIRST. + + First, if the function returns a real_type, mark the function + return type as live at each return point, as the RTL may not give any + hint that the register is live. + + Then, start with the last block and work back to the first block. + Similarly, work backwards within each block, insn by insn, recording + which regs are die and which are used (and therefore live) in the + hard reg set of block_stack_in[]. + + After processing each basic block, if there is a label at the start + of the block, propagate the live registers to all jumps to this block. + + As a special case, if there are regs live in this block, that are + not live in a block containing a jump to this label, and the block + containing the jump has already been processed, we must propagate this + block's entry register life back to the block containing the jump, and + restart life analysis from there. + + In the worst case, this function may traverse the insns + REG_STACK_SIZE times. This is necessary, since a jump towards the end + of the insns may not know that a reg is live at a target that is early + in the insns. So we back up and start over with the new reg live. + + If there are registers that are live at the start of the function, + insns are emitted to initialize these registers. Something similar is + done after CALL_INSNs in record_reg_life. */ + +static void +stack_reg_life_analysis (first) + rtx first; +{ + int reg, block; + struct stack_def regstack; + + if (current_function_returns_real) + { + /* Find all RETURN insns and mark them. */ + + for (block = blocks - 1; block >= 0; block--) + if (GET_CODE (block_end[block]) == JUMP_INSN + && GET_CODE (PATTERN (block_end[block])) == RETURN) + SET_HARD_REG_BIT (block_out_reg_set[block], FIRST_STACK_REG); + + /* Mark of the end of last block if we "fall off" the end of the + function into the epilogue. */ + + if (GET_CODE (block_end[blocks-1]) != JUMP_INSN + || GET_CODE (PATTERN (block_end[blocks-1])) == RETURN) + SET_HARD_REG_BIT (block_out_reg_set[blocks-1], FIRST_STACK_REG); + } + + /* now scan all blocks backward for stack register use */ + + block = blocks - 1; + while (block >= 0) + { + register rtx insn, prev; + + /* current register status at last instruction */ + + COPY_HARD_REG_SET (regstack.reg_set, block_out_reg_set[block]); + + prev = block_end[block]; + do + { + insn = prev; + prev = PREV_INSN (insn); + + /* If the insn is a CALL_INSN, we need to ensure that + everything dies. But otherwise don't process unless there + are some stack regs present. */ + + if (GET_MODE (insn) == QImode || GET_CODE (insn) == CALL_INSN) + record_reg_life (insn, block, ®stack); + + } while (insn != block_begin[block]); + + /* Set the state at the start of the block. Mark that no + register mapping information known yet. */ + + COPY_HARD_REG_SET (block_stack_in[block].reg_set, regstack.reg_set); + block_stack_in[block].top = -2; + + /* If there is a label, propagate our register life to all jumps + to this label. */ + + if (GET_CODE (insn) == CODE_LABEL) + { + register rtx label; + int must_restart = 0; + + for (label = LABEL_REFS (insn); label != insn; + label = LABEL_NEXTREF (label)) + { + int jump_block = BLOCK_NUM (CONTAINING_INSN (label)); + + if (jump_block < block) + IOR_HARD_REG_SET (block_out_reg_set[jump_block], + block_stack_in[block].reg_set); + else + { + /* The block containing the jump has already been + processed. If there are registers that were not known + to be live then, but are live now, we must back up + and restart life analysis from that point with the new + life information. */ + + GO_IF_HARD_REG_SUBSET (block_stack_in[block].reg_set, + block_out_reg_set[jump_block], + win); + + IOR_HARD_REG_SET (block_out_reg_set[jump_block], + block_stack_in[block].reg_set); + + block = jump_block; + must_restart = 1; + + win: + ; + } + } + if (must_restart) + continue; + } + + if (block_drops_in[block]) + IOR_HARD_REG_SET (block_out_reg_set[block-1], + block_stack_in[block].reg_set); + + block -= 1; + } + + { + /* If any reg is live at the start of the first block of a + function, then we must guarantee that the reg holds some value by + generating our own "load" of that register. Otherwise a 387 would + fault trying to access an empty register. */ + + HARD_REG_SET empty_regs; + CLEAR_HARD_REG_SET (empty_regs); + GO_IF_HARD_REG_SUBSET (block_stack_in[0].reg_set, empty_regs, + no_live_regs); + } + + /* Load zero into each live register. The fact that a register + appears live at the function start does not necessarily imply an error + in the user program: it merely means that we could not determine that + there wasn't such an error, just as -Wunused sometimes gives + "incorrect" warnings. In those cases, these initializations will do + no harm. + + Note that we are inserting virtual register references here: + these insns must be processed by convert_regs later. Also, these + insns will not be in block_number, so BLOCK_NUM() will fail for them. */ + + for (reg = LAST_STACK_REG; reg >= FIRST_STACK_REG; reg--) + if (TEST_HARD_REG_BIT (block_stack_in[0].reg_set, reg)) + { + rtx init_rtx; + + init_rtx = gen_rtx (SET, VOIDmode, DFmode_reg[reg], dconst0_rtx); + block_begin[0] = emit_insn_after (init_rtx, first); + PUT_MODE (block_begin[0], QImode); + + CLEAR_HARD_REG_BIT (block_stack_in[0].reg_set, reg); + } + + no_live_regs: + ; +} + +/***************************************************************************** + This section deals with stack register substition, and forms the second + pass over the RTL. + *****************************************************************************/ + +/* Replace REG, which is a pointer to a stack reg RTX, with an RTX for + the desired hard REGNO. */ + +static void +replace_reg (reg, regno) + rtx *reg; + int regno; +{ + if (regno < FIRST_STACK_REG || regno > LAST_STACK_REG + || ! STACK_REG_P (*reg)) + abort (); + + if (GET_MODE (*reg) == DFmode) + *reg = DFmode_reg[regno]; + else if (GET_MODE (*reg) == SFmode) + *reg = SFmode_reg[regno]; + else + abort (); +} + +/* Remove a note of type NOTE, which must be found, for register + number REGNO from INSN. Remove only one such note. */ + +static void +remove_regno_note (insn, note, regno) + rtx insn; + enum reg_note note; + int regno; +{ + register rtx *note_link, this; + + note_link = ®_NOTES(insn); + for (this = *note_link; this; this = XEXP (this, 1)) + if (REG_NOTE_KIND (this) == note + && REG_P (XEXP (this, 0)) && REGNO (XEXP (this, 0)) == regno) + { + *note_link = XEXP (this, 1); + return; + } + else + note_link = &XEXP (this, 1); + + abort (); +} + +/* Find the hard register number of virtual register REG in REGSTACK. + The hard register number is relative to the top of the stack. -1 is + returned if the register is not found. */ + +static int +get_hard_regnum (regstack, reg) + stack regstack; + rtx reg; +{ + int i; + + if (! STACK_REG_P (reg)) + abort (); + + for (i = regstack->top; i >= 0; i--) + if (regstack->reg[i] == REGNO (reg)) + break; + + return i >= 0 ? (FIRST_STACK_REG + regstack->top - i) : -1; +} + +/* Delete INSN from the RTL. Mark the insn, but don't remove it from + the chain of insns. Doing so could confuse block_begin and block_end + if this were the only insn in the block. */ + +static void +delete_insn_for_stacker (insn) + rtx insn; +{ + PUT_CODE (insn, NOTE); + NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED; + NOTE_SOURCE_FILE (insn) = 0; + INSN_DELETED_P (insn) = 1; +} + +/* Emit an insn to pop virtual register REG before or after INSN. + REGSTACK is the stack state after INSN and is updated to reflect this + pop. WHEN is either emit_insn_before or emit_insn_after. A pop insn + is represented as a SET whose destination is the register to be popped + and source is the top of stack. A death note for the top of stack + cases the movdf pattern to pop. */ + +static rtx +emit_pop_insn (insn, regstack, reg, when) + rtx insn; + stack regstack; + rtx reg; + rtx (*when)(); +{ + rtx pop_insn, pop_rtx; + int hard_regno; + + hard_regno = get_hard_regnum (regstack, reg); + + if (hard_regno < FIRST_STACK_REG) + abort (); + + pop_rtx = gen_rtx (SET, VOIDmode, DFmode_reg[hard_regno], + DFmode_reg[FIRST_STACK_REG]); + + pop_insn = (*when) (pop_rtx, insn); + PUT_MODE (pop_insn, VOIDmode); + + REG_NOTES (pop_insn) = gen_rtx (EXPR_LIST, + REG_DEAD, DFmode_reg[FIRST_STACK_REG], + REG_NOTES (pop_insn)); + + regstack->reg[regstack->top - (hard_regno - FIRST_STACK_REG)] + = regstack->reg[regstack->top]; + regstack->top -= 1; + CLEAR_HARD_REG_BIT (regstack->reg_set, REGNO (reg)); + + return pop_insn; +} + +/* Emit an insn before or after INSN to swap virtual register REG with the + top of stack. WHEN should be `emit_insn_before' or `emit_insn_before' + REGSTACK is the stack state before the swap, and is updated to reflect + the swap. A swap insn is represented as a PARALLEL of two patterns: + each pattern moves one reg to the other. + + If REG is already at the top of the stack, no insn is emitted. */ + +static void +emit_hard_swap_insn (insn, regstack, hard_regno, when) + rtx insn; + stack regstack; + int hard_regno; + rtx (*when)(); +{ + rtx gen_swapdf(); + rtx swap_rtx, swap_insn; + int tmp, other; + + if (hard_regno == FIRST_STACK_REG) + return; + + swap_rtx = gen_swapdf (DFmode_reg[hard_regno], DFmode_reg[FIRST_STACK_REG]); + swap_insn = (*when) (swap_rtx, insn); + PUT_MODE (swap_insn, VOIDmode); + + other = regstack->top - (hard_regno - FIRST_STACK_REG); + + tmp = regstack->reg[other]; + regstack->reg[other] = regstack->reg[regstack->top]; + regstack->reg[regstack->top] = tmp; +} + +/* Emit an insn before or after INSN to swap virtual register REG with the + top of stack. See comments before emit_hard_swap_insn. */ + +static void +emit_swap_insn (insn, regstack, reg, when) + rtx insn; + stack regstack; + rtx reg; + rtx (*when)(); +{ + int hard_regno; + + hard_regno = get_hard_regnum (regstack, reg); + + emit_hard_swap_insn (insn, regstack, hard_regno, when); +} + +/* Handle a move to or from a stack register in PAT, which is in INSN. + REGSTACK is the current stack. */ + +static void +move_for_stack_reg (insn, regstack, pat) + rtx insn; + stack regstack; + rtx pat; +{ + rtx *src = get_true_reg (&SET_SRC (pat)); + rtx *dest = get_true_reg (&SET_DEST (pat)); + rtx note; + + if (STACK_REG_P (*src) && STACK_REG_P (*dest)) + { + /* Write from one stack reg to another. If SRC dies here, then + just change the register mapping and delete the insn. */ + + note = find_regno_note (insn, REG_DEAD, REGNO (*src)); + if (note) + { + int i; + + /* If this is a no-op move, there must not be a REG_DEAD note. */ + if (REGNO (*src) == REGNO (*dest)) + abort (); + + for (i = regstack->top; i >= 0; i--) + if (regstack->reg[i] == REGNO (*src)) + break; + + /* The source must be live, and the dest must be dead. */ + if (i < 0 || get_hard_regnum (regstack, *dest) >= FIRST_STACK_REG) + abort (); + + /* It is possible that the dest is unused after this insn. + If so, just pop the src. */ + + if (find_regno_note (insn, REG_UNUSED, REGNO (*dest))) + { + emit_pop_insn (insn, regstack, *src, emit_insn_after); + + delete_insn_for_stacker (insn); + return; + } + + regstack->reg[i] = REGNO (*dest); + + SET_HARD_REG_BIT (regstack->reg_set, REGNO (*dest)); + CLEAR_HARD_REG_BIT (regstack->reg_set, REGNO (*src)); + + delete_insn_for_stacker (insn); + + return; + } + + /* The source reg does not die. */ + + /* If this appears to be a no-op move, delete it, or else it + will confuse the machine description output patterns. But if + it is REG_UNUSED, we must pop the reg now, as per-insn processing + for REG_UNUSED will not work for deleted insns. */ + + if (REGNO (*src) == REGNO (*dest)) + { + if (find_regno_note (insn, REG_UNUSED, REGNO (*dest))) + emit_pop_insn (insn, regstack, *dest, emit_insn_after); + + delete_insn_for_stacker (insn); + return; + } + + /* The destination ought to be dead */ + if (get_hard_regnum (regstack, *dest) >= FIRST_STACK_REG) + abort (); + + replace_reg (src, get_hard_regnum (regstack, *src)); + + regstack->reg[++regstack->top] = REGNO (*dest); + SET_HARD_REG_BIT (regstack->reg_set, REGNO (*dest)); + replace_reg (dest, FIRST_STACK_REG); + } + else if (STACK_REG_P (*src)) + { + /* Save from a stack reg to MEM, or possibly integer reg. Since + only top of stack may be saved, emit an exchange first if + needs be. */ + + emit_swap_insn (insn, regstack, *src, emit_insn_before); + + note = find_regno_note (insn, REG_DEAD, REGNO (*src)); + if (note) + { + replace_reg (&XEXP (note, 0), FIRST_STACK_REG); + regstack->top--; + CLEAR_HARD_REG_BIT (regstack->reg_set, REGNO (*src)); + } + + replace_reg (src, FIRST_STACK_REG); + } + else if (STACK_REG_P (*dest)) + { + /* Load from MEM, or possibly integer REG or constant, into the + stack regs. The actual target is always the top of the + stack. The stack mapping is changed to reflect that DEST is + now at top of stack. */ + + /* The destination ought to be dead */ + if (get_hard_regnum (regstack, *dest) >= FIRST_STACK_REG) + abort (); + + if (regstack->top >= REG_STACK_SIZE) + abort (); + + regstack->reg[++regstack->top] = REGNO (*dest); + SET_HARD_REG_BIT (regstack->reg_set, REGNO (*dest)); + replace_reg (dest, FIRST_STACK_REG); + } + else + abort (); +} + +/* Handle a comparison. Special care needs to be taken to avoid + causing comparisons that a 387 cannot do correctly, such as EQ. + + Also, a pop insn may need to be emitted. The 387 does have an + `fcompp' insn that can pop two regs, but it is sometimes too expensive + to do this - a `fcomp' followed by a `fstpl %st(0)' may be easier to + set up. */ + +static void +compare_for_stack_reg (insn, regstack, pat) + rtx insn; + stack regstack; + rtx pat; +{ + rtx *src1, *src2; + rtx src1_note, src2_note; + + src1 = get_true_reg (&XEXP (SET_SRC (pat), 0)); + src2 = get_true_reg (&XEXP (SET_SRC (pat), 1)); + + /* The first argument must always be a stack reg. */ + /* ??? why? */ + + if (! STACK_REG_P (*src1)) + abort (); + + /* We will fix any death note later. */ + + src1_note = find_regno_note (insn, REG_DEAD, REGNO (*src1)); + + if (STACK_REG_P (*src2)) + src2_note = find_regno_note (insn, REG_DEAD, REGNO (*src2)); + else + src2_note = 0; + + emit_swap_insn (insn, regstack, *src1, emit_insn_before); + + replace_reg (src1, FIRST_STACK_REG); + + if (STACK_REG_P (*src2)) + replace_reg (src2, get_hard_regnum (regstack, *src2)); + + if (src1_note) + { + CLEAR_HARD_REG_BIT (regstack->reg_set, REGNO (XEXP (src1_note, 0))); + replace_reg (&XEXP (src1_note, 0), FIRST_STACK_REG); + regstack->top--; + } + + /* If the second operand dies, handle that. But if the operands are + the same stack register, don't bother, because only one death is + needed, and it was just handled. */ + + if (src2_note + && ! (STACK_REG_P (*src1) + && STACK_REG_P (*src2) + && REGNO (*src1) == REGNO (*src2))) + { + /* As a special case, two regs may die in this insn if src2 is + next to top of stack and the top of stack also dies. Since + we have already popped src1, "next to top of stack" is really + at top (FIRST_STACK_REG) now. */ + + if (get_hard_regnum (regstack, XEXP (src2_note, 0)) == FIRST_STACK_REG + && src1_note) + { + CLEAR_HARD_REG_BIT (regstack->reg_set, REGNO (XEXP (src2_note, 0))); + replace_reg (&XEXP (src2_note, 0), FIRST_STACK_REG + 1); + regstack->top--; + } + else + { + /* The 386 can only represent death of the first operand in + the case handled above. In all other cases, emit a separate + pop and remove the death note from here. */ + + remove_regno_note (insn, REG_DEAD, REGNO (XEXP (src2_note, 0))); + + emit_pop_insn (insn, regstack, XEXP (src2_note, 0), + emit_insn_after); + } + } +} + +/* Substitute new registers in PAT, which is part of INSN. REGSTACK + is the current register layout. */ + +static void +subst_stack_regs_pat (insn, regstack, pat) + rtx insn; + stack regstack; + rtx pat; +{ + rtx *dest, *src; + rtx *src1 = 0, *src2; + rtx src1_note, src2_note; + + if (GET_CODE (pat) != SET) + return; + + dest = get_true_reg (&SET_DEST (pat)); + src = get_true_reg (&SET_SRC (pat)); + + /* See if this is a `movM' pattern, and handle elsewhere if so. */ + + if (*dest != cc0_rtx + && (STACK_REG_P (*src) + || (STACK_REG_P (*dest) + && (GET_CODE (*src) == REG || GET_CODE (*src) == MEM + || GET_CODE (*src) == CONST_DOUBLE)))) + move_for_stack_reg (insn, regstack, pat); + else + switch (GET_CODE (SET_SRC (pat))) + { + case COMPARE: + compare_for_stack_reg (insn, regstack, pat); + break; + + case CALL: + regstack->reg[++regstack->top] = REGNO (*dest); + SET_HARD_REG_BIT (regstack->reg_set, REGNO (*dest)); + replace_reg (dest, FIRST_STACK_REG); + break; + + case REG: + /* This is a `tstM2' case. */ + if (*dest != cc0_rtx) + abort (); + + src1 = src; + + /* Fall through. */ + + case SQRT: + case ABS: + case NEG: + /* These insns only operate on the top of the stack. DEST might + be cc0_rtx if we're processing a tstM pattern. Also, it's + possible that the tstM case results in a REG_DEAD note on the + source. */ + + if (src1 == 0) + src1 = get_true_reg (&XEXP (SET_SRC (pat), 0)); + + emit_swap_insn (insn, regstack, *src1, emit_insn_before); + + src1_note = find_regno_note (insn, REG_DEAD, REGNO (*src1)); + + if (STACK_REG_P (*dest)) + replace_reg (dest, FIRST_STACK_REG); + + if (src1_note) + { + replace_reg (&XEXP (src1_note, 0), FIRST_STACK_REG); + regstack->top--; + CLEAR_HARD_REG_BIT (regstack->reg_set, REGNO (*src1)); + } + + replace_reg (src1, FIRST_STACK_REG); + + break; + + case MINUS: + case DIV: + /* On i386, reversed forms of subM3 and divM3 exist for + MODE_FLOAT, so the same code that works for addM3 and mulM3 + can be used. */ + case MULT: + case PLUS: + /* These insns can accept the top of stack as a destination + from a stack reg or mem, or can use the top of stack as a + source and some other stack register (possibly top of stack) + as a destination. */ + + src1 = get_true_reg (&XEXP (SET_SRC (pat), 0)); + src2 = get_true_reg (&XEXP (SET_SRC (pat), 1)); + + /* We will fix any death note later. */ + + if (STACK_REG_P (*src1)) + src1_note = find_regno_note (insn, REG_DEAD, REGNO (*src1)); + else + src1_note = 0; + if (STACK_REG_P (*src2)) + src2_note = find_regno_note (insn, REG_DEAD, REGNO (*src2)); + else + src2_note = 0; + + /* If either operand is not a stack register, then the dest + must be top of stack. */ + + if (! STACK_REG_P (*src1) || ! STACK_REG_P (*src2)) + emit_swap_insn (insn, regstack, *dest, emit_insn_before); + else + { + /* Both operands are REG. If neither operand is already + at the top of stack, choose to make the one that is the dest + the new top of stack. + + ??? A later optimization here would be to look forward + in the insns and see which source reg will be needed at top + of stack soonest. */ + + int src1_hard_regnum, src2_hard_regnum; + + src1_hard_regnum = get_hard_regnum (regstack, *src1); + src2_hard_regnum = get_hard_regnum (regstack, *src2); + if (src1_hard_regnum == -1 || src2_hard_regnum == -1) + abort (); + + if (src1_hard_regnum != FIRST_STACK_REG + && src2_hard_regnum != FIRST_STACK_REG) + emit_swap_insn (insn, regstack, *dest, emit_insn_before); + } + + if (STACK_REG_P (*src1)) + replace_reg (src1, get_hard_regnum (regstack, *src1)); + if (STACK_REG_P (*src2)) + replace_reg (src2, get_hard_regnum (regstack, *src2)); + + if (src1_note) + { + /* If the register that dies is at the top of stack, then + the destination is somewhere else - merely substitute it. + But if the reg that dies is not at top of stack, then + move the top of stack to the dead reg, as though we had + done the insn and then a store-with-pop. */ + + if (REGNO (XEXP (src1_note, 0)) == regstack->reg[regstack->top]) + { + SET_HARD_REG_BIT (regstack->reg_set, REGNO (*dest)); + replace_reg (dest, get_hard_regnum (regstack, *dest)); + } + else + { + int regno = get_hard_regnum (regstack, XEXP (src1_note, 0)); + + SET_HARD_REG_BIT (regstack->reg_set, REGNO (*dest)); + replace_reg (dest, regno); + + regstack->reg[regstack->top - (regno - FIRST_STACK_REG)] + = regstack->reg[regstack->top]; + } + + CLEAR_HARD_REG_BIT (regstack->reg_set, + REGNO (XEXP (src1_note, 0))); + replace_reg (&XEXP (src1_note, 0), FIRST_STACK_REG); + regstack->top--; + } + else if (src2_note) + { + if (REGNO (XEXP (src2_note, 0)) == regstack->reg[regstack->top]) + { + SET_HARD_REG_BIT (regstack->reg_set, REGNO (*dest)); + replace_reg (dest, get_hard_regnum (regstack, *dest)); + } + else + { + int regno = get_hard_regnum (regstack, XEXP (src2_note, 0)); + + SET_HARD_REG_BIT (regstack->reg_set, REGNO (*dest)); + replace_reg (dest, regno); + + regstack->reg[regstack->top - (regno - FIRST_STACK_REG)] + = regstack->reg[regstack->top]; + } + + CLEAR_HARD_REG_BIT (regstack->reg_set, + REGNO (XEXP (src2_note, 0))); + replace_reg (&XEXP (src2_note, 0), FIRST_STACK_REG); + regstack->top--; + } + else + { + SET_HARD_REG_BIT (regstack->reg_set, REGNO (*dest)); + replace_reg (dest, get_hard_regnum (regstack, *dest)); + } + + break; + + default: + abort (); + } +} + +/* Substitute stack hard reg numbers for stack virtual registers in + INSN. Non-stack register numbers are not changed. REGSTACK is the + current stack content. Insns may be emitted as needed to arrange the + stack for the 387 based on the contents of the insn. */ + +static void +subst_stack_regs (insn, regstack) + rtx insn; + stack regstack; +{ + register rtx *note_link, note; + register int i; + + if ((GET_CODE (insn) != INSN && GET_CODE (insn) != CALL_INSN) + || INSN_DELETED_P (insn)) + return; + + /* The stack should be empty at a call. */ + + if (GET_CODE (insn) == CALL_INSN) + for (i = FIRST_STACK_REG; i <= LAST_STACK_REG; i++) + if (TEST_HARD_REG_BIT (regstack->reg_set, i)) + abort (); + + /* Do the actual substitution if any stack regs are mentioned. + Since we only record whether entire insn mentions stack regs, and + subst_stack_regs_pat only works for patterns that contain stack regs, + we must check each pattern in a parallel here. A call_value_pop could + fail otherwise. */ + + if (GET_MODE (insn) == QImode) + { + if (GET_CODE (PATTERN (insn)) == PARALLEL) + for (i = 0; i < XVECLEN (PATTERN (insn) , 0); i++) + { + if (stack_regs_mentioned_p (XVECEXP (PATTERN (insn), 0, i))) + subst_stack_regs_pat (insn, regstack, + XVECEXP (PATTERN (insn), 0, i)); + } + else + subst_stack_regs_pat (insn, regstack, PATTERN (insn)); + } + + /* subst_stack_regs_pat may have deleted a no-op insn. If so, any + REG_UNUSED will already have been dealt with, so just return. */ + + if (INSN_DELETED_P (insn)) + return; + + /* If there is a REG_UNUSED note on a stack register on this insn, + the indicated reg must be popped. The REG_UNUSED note is removed, + since the form of the newly emitted pop insn references the reg, + making it no longer `unset'. */ + + note_link = ®_NOTES(insn); + for (note = *note_link; note; note = XEXP (note, 1)) + if (REG_NOTE_KIND (note) == REG_UNUSED && STACK_REG_P (XEXP (note, 0))) + { + *note_link = XEXP (note, 1); + emit_pop_insn (insn, regstack, XEXP (note, 0), + emit_insn_after); + } + else + note_link = &XEXP (note, 1); +} + +/* Change the organization of the stack so that it fits a new basic + block. Some registers might have to be popped, but there can never be + a register live in the new block that is not now live. + + Insert any needed insns after INSN. OLD is the original stack + layout, and NEW is the desired form. OLD is updated to reflect the + code emitted, ie, it will be the same as NEW upon return. + + This function will not preserve block_end[]. But that information + is no longer needed once this has executed. */ + +static void +change_stack (insn, old, new) + rtx insn; + stack old; + stack new; +{ + int reg; + + /* We will be inserting new insns after INSN, by first finding the + next insn, and inserting before it. */ + + insn = NEXT_INSN (insn); + + /* Pop any registers that are not needed in the new block. */ + + for (reg = old->top; reg >= 0; reg--) + if (! TEST_HARD_REG_BIT (new->reg_set, old->reg[reg])) + emit_pop_insn (insn, old, DFmode_reg[old->reg[reg]], + emit_insn_before); + + if (new->top == -2) + { + /* If the new block has never been processed, then it can inherit + the old stack order. */ + + new->top = old->top; + bcopy (old->reg, new->reg, sizeof (new->reg)); + } + else + { + /* This block has been entered before, and we must match the + previously selected stack order. */ + + /* By now, the only difference should be the order of the stack, + not their depth or liveliness. */ + + GO_IF_HARD_REG_EQUAL (old->reg_set, new->reg_set, win); + + abort (); + + win: + + if (old->top != new->top) + abort (); + + /* Loop here emitting swaps until the stack is correct. The + worst case number of swaps emitted is N + 2, where N is the + depth of the stack. In some cases, the reg at the top of + stack may be correct, but swapped anyway in order to fix + other regs. But since we never swap any other reg away from + its correct slot, this algorithm will converge. */ + + do + { + /* Swap the reg at top of stack into the position it is + supposed to be in, until the correct top of stack appears. */ + + while (old->reg[old->top] != new->reg[new->top]) + { + for (reg = new->top; reg >= 0; reg--) + if (new->reg[reg] == old->reg[old->top]) + break; + + if (reg == -1) + abort (); + + emit_swap_insn (insn, old, DFmode_reg[old->reg[reg]], + emit_insn_before); + } + + /* See if any regs remain incorrect. If so, bring an + incorrect reg to the top of stack, and let the while loop + above fix it. */ + + for (reg = new->top; reg >= 0; reg--) + if (new->reg[reg] != old->reg[reg]) + { + emit_swap_insn (insn, old, DFmode_reg[old->reg[reg]], + emit_insn_before); + break; + } + } while (reg >= 0); + + /* At this point there must be no differences. */ + + for (reg = old->top; reg >= 0; reg--) + if (old->reg[reg] != new->reg[reg]) + abort (); + } +} + +/* Check PAT, which points to RTL in INSN, for a LABEL_REF. If it is + found, ensure that a jump from INSN to the code_label to which the + label_ref points ends up with the same stack as that at the + code_label. Do this by inserting insns just before the code_label to + pop and rotate the stack until it is in the correct order. REGSTACK + is the order of the register stack in INSN. + + Any code that is emitted here must not be later processed as part + of any block, as it will already contain hard register numbers. */ + +static void +goto_block_pat (insn, regstack, pat) + rtx insn; + stack regstack; + rtx pat; +{ + rtx label; + rtx new_jump, new_label, new_barrier; + rtx *ref; + stack label_stack; + struct stack_def temp_stack; + int reg; + + if (GET_CODE (pat) != LABEL_REF) + { + int i, j; + char *fmt = GET_RTX_FORMAT (GET_CODE (pat)); + + for (i = GET_RTX_LENGTH (GET_CODE (pat)) - 1; i >= 0; i--) + { + if (fmt[i] == 'e') + goto_block_pat (insn, regstack, XEXP (pat, i)); + if (fmt[i] == 'E') + for (j = 0; j < XVECLEN (pat, i); j++) + goto_block_pat (insn, regstack, XVECEXP (pat, i, j)); + } + return; + } + + label = XEXP (pat, 0); + if (GET_CODE (label) != CODE_LABEL) + abort (); + + /* First, see if in fact anything needs to be done to the stack at all. */ + + label_stack = &block_stack_in[BLOCK_NUM (label)]; + + if (label_stack->top == -2) + { + /* If the target block hasn't had a stack order selected, then + we need merely ensure that no pops are needed. */ + + for (reg = regstack->top; reg >= 0; reg--) + if (! TEST_HARD_REG_BIT (label_stack->reg_set, regstack->reg[reg])) + break; + + if (reg == -1) + { + /* change_stack will not emit any code in this case. */ + + change_stack (label, regstack, label_stack); + return; + } + } + else if (label_stack->top == regstack->top) + { + for (reg = label_stack->top; reg >= 0; reg--) + if (label_stack->reg[reg] != regstack->reg[reg]) + break; + + if (reg == -1) + return; + } + + /* At least one insn will need to be inserted before label. Insert + a jump around the code we are about to emit. Emit a label for the new + code, and point the original insn at this new label. We can't use + redirect_jump here, because we're using fld[4] of the code labels as + LABEL_REF chains, no NUSES counters. */ + + new_jump = emit_jump_insn_before (gen_jump (label), label); + record_label_references (new_jump, PATTERN (new_jump)); + JUMP_LABEL (new_jump) = label; + + new_barrier = emit_barrier_after (new_jump); + + new_label = gen_label_rtx (); + emit_label_after (new_label, new_barrier); + LABEL_REFS (new_label) = new_label; + + /* The old label_ref will no longer point to the code_label if now uses, + so strip the label_ref from the code_label's chain of references. */ + + for (ref = &LABEL_REFS (label); *ref != label; ref = &LABEL_NEXTREF (*ref)) + if (*ref == pat) + break; + + if (*ref == label) + abort (); + + *ref = LABEL_NEXTREF (*ref); + + XEXP (pat, 0) = new_label; + record_label_references (insn, PATTERN (insn)); + + if (JUMP_LABEL (insn) == label) + JUMP_LABEL (insn) = new_label; + + /* Now emit the needed code. */ + + temp_stack = *regstack; + + change_stack (new_label, &temp_stack, label_stack); +} + +/* Traverse all basic blocks in a function, converting the register + refereces in each insn from the "flat" register file that gcc uses, to + the stack-like registers the 387 uses. */ + +static void +convert_regs () +{ + register int block, reg; + register rtx insn, next; + struct stack_def regstack; + + for (block = 0; block < blocks; block++) + { + if (block_stack_in[block].top == -2) + { + /* This block has not been previously encountered. Choose a + default mapping for any stack regs live on entry */ + + block_stack_in[block].top = -1; + + for (reg = LAST_STACK_REG; reg >= FIRST_STACK_REG; reg--) + if (TEST_HARD_REG_BIT (block_stack_in[block].reg_set, reg)) + block_stack_in[block].reg[++block_stack_in[block].top] = reg; + } + + /* Process all insns in this block. Keep track of `next' here, + so that we don't process any insns emitted while making + substitutions in INSN. */ + + next = block_begin[block]; + regstack = block_stack_in[block]; + do + { + insn = next; + next = NEXT_INSN (insn); + + /* Don't bother processing unless there is a stack reg + mentioned. + + ??? For now, process CALL_INSNs too to make sure that the + stack regs are dead after a call. Remove this eventually. */ + + if (GET_MODE (insn) == QImode || GET_CODE (insn) == CALL_INSN) + subst_stack_regs (insn, ®stack); + + } while (insn != block_end[block]); + + /* Something failed if the stack life doesn't match. */ + + GO_IF_HARD_REG_EQUAL (regstack.reg_set, block_out_reg_set[block], win); + + abort (); + + win: + + /* Adjust the stack of this block on exit to match the stack of + the target block, or copy stack information into stack of + jump target if the target block's stack order hasn't been set + yet. */ + + if (GET_CODE (insn) == JUMP_INSN) + goto_block_pat (insn, ®stack, PATTERN (insn)); + + /* Likewise handle the case where we fall into the next block. */ + + if ((block < blocks - 1) && block_drops_in[block+1]) + change_stack (insn, ®stack, &block_stack_in[block+1]); + } + + /* If the last basic block is the end of a loop, and that loop has + regs live at its start, then the last basic block will have regs live + at its end that need to be popped before the function returns. */ + + for (reg = regstack.top; reg >= 0; reg--) + if (! current_function_returns_real + || regstack.reg[reg] != FIRST_STACK_REG) + insn = emit_pop_insn (insn, ®stack, DFmode_reg[regstack.reg[reg]], + emit_insn_after); +} + +/* Check expression PAT, which is in INSN, for label references. if + one is found, print the block number of destination to FILE. */ + +static void +print_blocks (file, insn, pat) + FILE *file; + rtx insn, pat; +{ + register RTX_CODE code = GET_CODE (pat); + register int i; + register char *fmt; + + if (code == LABEL_REF) + { + register rtx label = XEXP (pat, 0); + + if (GET_CODE (label) != CODE_LABEL) + abort (); + + fprintf (file, " %d", BLOCK_NUM (label)); + + return; + } + + fmt = GET_RTX_FORMAT (code); + for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) + { + if (fmt[i] == 'e') + print_blocks (file, insn, XEXP (pat, i)); + if (fmt[i] == 'E') + { + register int j; + for (j = 0; j < XVECLEN (pat, i); j++) + print_blocks (file, insn, XVECEXP (pat, i, j)); + } + } +} + +/* Write information about stack registers and stack blocks into FILE. + This is part of making a debugging dump. */ +static void +dump_stack_info (file) + FILE *file; +{ + register int block; + + fprintf (file, "\n%d stack blocks.\n", blocks); + for (block = 0; block < blocks; block++) + { + register rtx head, jump, end; + register int regno; + + fprintf (file, "\nStack block %d: first insn %d, last %d.\n", + block, INSN_UID (block_begin[block]), + INSN_UID (block_end[block])); + + head = block_begin[block]; + + fprintf (file, "Reached from blocks: "); + if (GET_CODE (head) == CODE_LABEL) + for (jump = LABEL_REFS (head); + jump != head; + jump = LABEL_NEXTREF (jump)) + { + register int from_block = BLOCK_NUM (CONTAINING_INSN (jump)); + fprintf (file, " %d", from_block); + } + if (block_drops_in[block]) + fprintf (file, " previous"); + + fprintf (file, "\nlive stack registers on block entry: "); + for (regno = FIRST_STACK_REG; regno <= LAST_STACK_REG ; regno++) + { + if (TEST_HARD_REG_BIT (block_stack_in[block].reg_set, regno)) + fprintf (file, "%d ", regno); + } + + fprintf (file, "\nlive stack registers on block exit: "); + for (regno = FIRST_STACK_REG; regno <= LAST_STACK_REG ; regno++) + { + if (TEST_HARD_REG_BIT (block_out_reg_set[block], regno)) + fprintf (file, "%d ", regno); + } + + end = block_end[block]; + + fprintf (file, "\nJumps to blocks: "); + if (GET_CODE (end) == JUMP_INSN) + print_blocks (file, end, PATTERN (end)); + + if (block + 1 < blocks && block_drops_in[block+1]) + fprintf (file, " next"); + else if (block + 1 == blocks + || (GET_CODE (end) == JUMP_INSN + && GET_CODE (PATTERN (end)) == RETURN)) + fprintf (file, " return"); + + fprintf (file, "\n"); + } +} + +/* Report an error at line LINE of file FILE. + S is a string and an arg for `printf'. */ + +/* Report an fatal error at the line number of the insn INSN (ASM_OPERAND). + S1, S2 is a string and an arg for `printf'. */ + +static void +fatal_for_asm (insn, s1, s2) + rtx insn; + char *s1, *s2; +{ + char *filename; + int line; + rtx body = PATTERN (insn); + rtx asmop = 0; + + /* Find the (or one of the) ASM_OPERANDS in the insn. */ + if (GET_CODE (body) == SET && GET_CODE (SET_SRC (body)) == ASM_OPERANDS) + asmop = SET_SRC (body); + else if (GET_CODE (body) == ASM_OPERANDS) + asmop = body; + else if (GET_CODE (body) == PARALLEL + && GET_CODE (XVECEXP (body, 0, 0)) == SET) + asmop = SET_SRC (XVECEXP (body, 0, 0)); + else if (GET_CODE (body) == PARALLEL + && GET_CODE (XVECEXP (body, 0, 0)) == ASM_OPERANDS) + asmop = XVECEXP (body, 0, 0); + else + abort (); + + filename = ASM_OPERANDS_SOURCE_FILE (asmop); + line = ASM_OPERANDS_SOURCE_LINE (asmop); + + fprintf (stderr, s1); + debug_rtx (insn); + + error_with_file_and_line (filename, line, s2, NULL, NULL); + exit (34); +} +#endif /* STACK_REGS */ |