| /* Register to Stack convert for GNU compiler. |
| Copyright (C) 1992, 1993, 1994, 1995, 1996 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, 59 Temple Place - Suite 330, |
| Boston, MA 02111-1307, 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. |
| |
| 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 the SET_SRC and SET_DEST are both 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. |
| |
| * asm_operands: |
| |
| There are several rules on the usage of stack-like regs in |
| asm_operands insns. These rules apply only to the operands that are |
| stack-like regs: |
| |
| 1. Given a set of input regs that die in an asm_operands, it is |
| necessary to know which are implicitly popped by the asm, and |
| which must be explicitly popped by gcc. |
| |
| An input reg that is implicitly popped by the asm must be |
| explicitly clobbered, unless it is constrained to match an |
| output operand. |
| |
| 2. For any input reg that is implicitly popped by an asm, it is |
| necessary to know how to adjust the stack to compensate for the pop. |
| If any non-popped input is closer to the top of the reg-stack than |
| the implicitly popped reg, it would not be possible to know what the |
| stack looked like - it's not clear how the rest of the stack "slides |
| up". |
| |
| All implicitly popped input regs must be closer to the top of |
| the reg-stack than any input that is not implicitly popped. |
| |
| 3. It is possible that if an input dies in an insn, reload might |
| use the input reg for an output reload. Consider this example: |
| |
| asm ("foo" : "=t" (a) : "f" (b)); |
| |
| This asm says that input B is not popped by the asm, and that |
| the asm pushes a result onto the reg-stack, ie, the stack is one |
| deeper after the asm than it was before. But, it is possible that |
| reload will think that it can use the same reg for both the input and |
| the output, if input B dies in this insn. |
| |
| If any input operand uses the "f" constraint, all output reg |
| constraints must use the "&" earlyclobber. |
| |
| The asm above would be written as |
| |
| asm ("foo" : "=&t" (a) : "f" (b)); |
| |
| 4. Some operands need to be in particular places on the stack. All |
| output operands fall in this category - there is no other way to |
| know which regs the outputs appear in unless the user indicates |
| this in the constraints. |
| |
| Output operands must specifically indicate which reg an output |
| appears in after an asm. "=f" is not allowed: the operand |
| constraints must select a class with a single reg. |
| |
| 5. Output operands may not be "inserted" between existing stack regs. |
| Since no 387 opcode uses a read/write operand, all output operands |
| are dead before the asm_operands, and are pushed by the asm_operands. |
| It makes no sense to push anywhere but the top of the reg-stack. |
| |
| Output operands must start at the top of the reg-stack: output |
| operands may not "skip" a reg. |
| |
| 6. Some asm statements may need extra stack space for internal |
| calculations. This can be guaranteed by clobbering stack registers |
| unrelated to the inputs and outputs. |
| |
| Here are a couple of reasonable asms to want to write. This asm |
| takes one input, which is internally popped, and produces two outputs. |
| |
| asm ("fsincos" : "=t" (cos), "=u" (sin) : "0" (inp)); |
| |
| This asm takes two inputs, which are popped by the fyl2xp1 opcode, |
| and replaces them with one output. The user must code the "st(1)" |
| clobber for reg-stack.c to know that fyl2xp1 pops both inputs. |
| |
| asm ("fyl2xp1" : "=t" (result) : "0" (x), "u" (y) : "st(1)"); |
| |
| */ |
| |
| #include <stdio.h> |
| #include "config.h" |
| #include "tree.h" |
| #include "rtl.h" |
| #include "insn-config.h" |
| #include "regs.h" |
| #include "hard-reg-set.h" |
| #include "flags.h" |
| #include "insn-flags.h" |
| |
| #ifdef STACK_REGS |
| |
| #define REG_STACK_SIZE (LAST_STACK_REG - FIRST_STACK_REG + 1) |
| |
| /* 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, REG[] is not yet initialized. Stack initialization |
| consists of placing each live reg in array `reg' and setting `top' |
| appropriately. |
| |
| REG_SET indicates which registers are live. */ |
| |
| 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 int *block_number; |
| |
| /* This is the register file for all register after conversion */ |
| static rtx |
| FP_mode_reg[LAST_STACK_REG+1-FIRST_STACK_REG][(int) MAX_MACHINE_MODE]; |
| |
| #define FP_MODE_REG(regno,mode) \ |
| (FP_mode_reg[(regno)-FIRST_STACK_REG][(int)(mode)]) |
| |
| /* 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) \ |
| ? (abort() , -1) : block_number[INSN_UID (INSN)]) |
| |
| extern rtx forced_labels; |
| |
| /* Forward declarations */ |
| |
| static void mark_regs_pat PROTO((rtx, HARD_REG_SET *)); |
| static void straighten_stack PROTO((rtx, stack)); |
| static void record_label_references PROTO((rtx, rtx)); |
| static rtx *get_true_reg PROTO((rtx *)); |
| static int constrain_asm_operands PROTO((int, rtx *, char **, int *, |
| enum reg_class *)); |
| |
| static void record_asm_reg_life PROTO((rtx,stack, rtx *, char **, |
| int, int)); |
| static void record_reg_life_pat PROTO((rtx, HARD_REG_SET *, |
| HARD_REG_SET *, int)); |
| static void get_asm_operand_length PROTO((rtx, int, int *, int *)); |
| static void record_reg_life PROTO((rtx, int, stack)); |
| static void find_blocks PROTO((rtx)); |
| static int uses_reg_or_mem PROTO((rtx)); |
| static rtx stack_result PROTO((tree)); |
| static void stack_reg_life_analysis PROTO((rtx, HARD_REG_SET *)); |
| static void replace_reg PROTO((rtx *, int)); |
| static void remove_regno_note PROTO((rtx, enum reg_note, int)); |
| static int get_hard_regnum PROTO((stack, rtx)); |
| static void delete_insn_for_stacker PROTO((rtx)); |
| static rtx emit_pop_insn PROTO((rtx, stack, rtx, rtx (*) ())); |
| static void emit_swap_insn PROTO((rtx, stack, rtx)); |
| static void move_for_stack_reg PROTO((rtx, stack, rtx)); |
| static void swap_rtx_condition PROTO((rtx)); |
| static void compare_for_stack_reg PROTO((rtx, stack, rtx)); |
| static void subst_stack_regs_pat PROTO((rtx, stack, rtx)); |
| static void subst_asm_stack_regs PROTO((rtx, stack, rtx *, rtx **, |
| char **, int, int)); |
| static void subst_stack_regs PROTO((rtx, stack)); |
| static void change_stack PROTO((rtx, stack, stack, rtx (*) ())); |
| |
| static void goto_block_pat PROTO((rtx, stack, rtx)); |
| static void convert_regs PROTO((void)); |
| static void print_blocks PROTO((FILE *, rtx, rtx)); |
| static void dump_stack_info PROTO((FILE *)); |
| |
| /* Mark all registers needed for this pattern. */ |
| |
| static void |
| mark_regs_pat (pat, set) |
| rtx pat; |
| HARD_REG_SET *set; |
| { |
| enum machine_mode mode; |
| register int regno; |
| register int count; |
| |
| if (GET_CODE (pat) == SUBREG) |
| { |
| mode = GET_MODE (pat); |
| regno = SUBREG_WORD (pat); |
| regno += REGNO (SUBREG_REG (pat)); |
| } |
| else |
| regno = REGNO (pat), mode = GET_MODE (pat); |
| |
| for (count = HARD_REGNO_NREGS (regno, mode); |
| count; count--, regno++) |
| SET_HARD_REG_BIT (*set, regno); |
| } |
| |
| /* Reorganise the stack into ascending numbers, |
| after this insn. */ |
| |
| static void |
| straighten_stack (insn, regstack) |
| rtx insn; |
| stack regstack; |
| { |
| struct stack_def temp_stack; |
| int top; |
| |
| temp_stack.reg_set = regstack->reg_set; |
| |
| for (top = temp_stack.top = regstack->top; top >= 0; top--) |
| temp_stack.reg[top] = FIRST_STACK_REG + temp_stack.top - top; |
| |
| change_stack (insn, regstack, &temp_stack, emit_insn_after); |
| } |
| |
| /* Return non-zero if any stack register is mentioned somewhere within PAT. */ |
| |
| int |
| stack_regs_mentioned_p (pat) |
| 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; |
| enum machine_mode mode; |
| HARD_REG_SET stackentry; |
| |
| CLEAR_HARD_REG_SET (stackentry); |
| |
| { |
| static initialised; |
| if (!initialised) |
| { |
| #if 0 |
| initialised = 1; /* This array can not have been previously |
| initialised, because the rtx's are |
| thrown away between compilations of |
| functions. */ |
| #endif |
| for (i = FIRST_STACK_REG; i <= LAST_STACK_REG; i++) |
| { |
| for (mode = GET_CLASS_NARROWEST_MODE (MODE_FLOAT); mode != VOIDmode; |
| mode = GET_MODE_WIDER_MODE (mode)) |
| FP_MODE_REG (i, mode) = gen_rtx (REG, mode, i); |
| for (mode = GET_CLASS_NARROWEST_MODE (MODE_COMPLEX_FLOAT); mode != VOIDmode; |
| mode = GET_MODE_WIDER_MODE (mode)) |
| FP_MODE_REG (i, mode) = gen_rtx (REG, mode, i); |
| } |
| } |
| } |
| |
| /* Count the basic blocks. Also find maximum insn uid. */ |
| { |
| register RTX_CODE prev_code = BARRIER; |
| register RTX_CODE code; |
| register before_function_beg = 1; |
| |
| 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. Also note that this code is not the |
| same as that used in flow.c. */ |
| |
| 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 |
| && GET_RTX_CLASS (code) == 'i')) |
| blocks++; |
| |
| if (code == NOTE && NOTE_LINE_NUMBER (insn) == NOTE_INSN_FUNCTION_BEG) |
| before_function_beg = 0; |
| |
| /* Remember whether or not this insn mentions an FP regs. |
| Check JUMP_INSNs too, in case someone creates a funny PARALLEL. */ |
| |
| if (GET_RTX_CLASS (code) == 'i' |
| && stack_regs_mentioned_p (PATTERN (insn))) |
| { |
| stack_reg_seen = 1; |
| PUT_MODE (insn, QImode); |
| |
| /* Note any register passing parameters. */ |
| |
| if (before_function_beg && code == INSN |
| && GET_CODE (PATTERN (insn)) == USE) |
| record_reg_life_pat (PATTERN (insn), (HARD_REG_SET *) 0, |
| &stackentry, 1); |
| } |
| else |
| PUT_MODE (insn, VOIDmode); |
| |
| if (code == CODE_LABEL) |
| LABEL_REFS (insn) = insn; /* delete old chain */ |
| |
| 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 ((char *) block_stack_in, blocks * sizeof (struct stack_def)); |
| bzero ((char *) block_out_reg_set, blocks * sizeof (HARD_REG_SET)); |
| |
| block_number = (int *) alloca ((max_uid + 1) * sizeof (int)); |
| |
| find_blocks (first); |
| stack_reg_life_analysis (first, &stackentry); |
| |
| /* 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 (); |
| |
| /* If this is an undefined label, LABEL_REFS (label) contains |
| garbage. */ |
| if (INSN_UID (label) == 0) |
| return; |
| |
| /* Don't make a duplicate in the code_label's chain. */ |
| |
| for (ref = LABEL_REFS (label); |
| ref && 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; |
| { |
| for (;;) |
| switch (GET_CODE (*pat)) |
| { |
| case SUBREG: |
| /* eliminate FP subregister accesses in favour of the |
| actual FP register in use. */ |
| { |
| rtx subreg; |
| if (FP_REG_P (subreg = SUBREG_REG (*pat))) |
| { |
| *pat = FP_MODE_REG (REGNO (subreg) + SUBREG_WORD (*pat), |
| GET_MODE (subreg)); |
| default: |
| return pat; |
| } |
| } |
| case FLOAT: |
| case FIX: |
| case FLOAT_EXTEND: |
| pat = & XEXP (*pat, 0); |
| } |
| } |
| |
| /* Scan the OPERANDS and OPERAND_CONSTRAINTS of an asm_operands. |
| N_OPERANDS is the total number of operands. Return which alternative |
| matched, or -1 is no alternative matches. |
| |
| OPERAND_MATCHES is an array which indicates which operand this |
| operand matches due to the constraints, or -1 if no match is required. |
| If two operands match by coincidence, but are not required to match by |
| the constraints, -1 is returned. |
| |
| OPERAND_CLASS is an array which indicates the smallest class |
| required by the constraints. If the alternative that matches calls |
| for some class `class', and the operand matches a subclass of `class', |
| OPERAND_CLASS is set to `class' as required by the constraints, not to |
| the subclass. If an alternative allows more than one class, |
| OPERAND_CLASS is set to the smallest class that is a union of the |
| allowed classes. */ |
| |
| static int |
| constrain_asm_operands (n_operands, operands, operand_constraints, |
| operand_matches, operand_class) |
| int n_operands; |
| rtx *operands; |
| char **operand_constraints; |
| int *operand_matches; |
| enum reg_class *operand_class; |
| { |
| char **constraints = (char **) alloca (n_operands * sizeof (char *)); |
| char *q; |
| int this_alternative, this_operand; |
| int n_alternatives; |
| int j; |
| |
| for (j = 0; j < n_operands; j++) |
| constraints[j] = operand_constraints[j]; |
| |
| /* Compute the number of alternatives in the operands. reload has |
| already guaranteed that all operands have the same number of |
| alternatives. */ |
| |
| n_alternatives = 1; |
| for (q = constraints[0]; *q; q++) |
| n_alternatives += (*q == ','); |
| |
| this_alternative = 0; |
| while (this_alternative < n_alternatives) |
| { |
| int lose = 0; |
| int i; |
| |
| /* No operands match, no narrow class requirements yet. */ |
| for (i = 0; i < n_operands; i++) |
| { |
| operand_matches[i] = -1; |
| operand_class[i] = NO_REGS; |
| } |
| |
| for (this_operand = 0; this_operand < n_operands; this_operand++) |
| { |
| rtx op = operands[this_operand]; |
| enum machine_mode mode = GET_MODE (op); |
| char *p = constraints[this_operand]; |
| int offset = 0; |
| int win = 0; |
| int c; |
| |
| if (GET_CODE (op) == SUBREG) |
| { |
| if (GET_CODE (SUBREG_REG (op)) == REG |
| && REGNO (SUBREG_REG (op)) < FIRST_PSEUDO_REGISTER) |
| offset = SUBREG_WORD (op); |
| op = SUBREG_REG (op); |
| } |
| |
| /* An empty constraint or empty alternative |
| allows anything which matched the pattern. */ |
| if (*p == 0 || *p == ',') |
| win = 1; |
| |
| while (*p && (c = *p++) != ',') |
| switch (c) |
| { |
| case '=': |
| case '+': |
| case '?': |
| case '&': |
| case '!': |
| case '*': |
| case '%': |
| /* Ignore these. */ |
| break; |
| |
| case '#': |
| /* Ignore rest of this alternative. */ |
| while (*p && *p != ',') p++; |
| break; |
| |
| case '0': |
| case '1': |
| case '2': |
| case '3': |
| case '4': |
| case '5': |
| /* This operand must be the same as a previous one. |
| This kind of constraint is used for instructions such |
| as add when they take only two operands. |
| |
| Note that the lower-numbered operand is passed first. */ |
| |
| if (operands_match_p (operands[c - '0'], |
| operands[this_operand])) |
| { |
| operand_matches[this_operand] = c - '0'; |
| win = 1; |
| } |
| break; |
| |
| case 'p': |
| /* p is used for address_operands. Since this is an asm, |
| just to make sure that the operand is valid for Pmode. */ |
| |
| if (strict_memory_address_p (Pmode, op)) |
| win = 1; |
| break; |
| |
| case 'g': |
| /* Anything goes unless it is a REG and really has a hard reg |
| but the hard reg is not in the class GENERAL_REGS. */ |
| if (GENERAL_REGS == ALL_REGS |
| || GET_CODE (op) != REG |
| || reg_fits_class_p (op, GENERAL_REGS, offset, mode)) |
| { |
| if (GET_CODE (op) == REG) |
| operand_class[this_operand] |
| = reg_class_subunion[(int) operand_class[this_operand]][(int) GENERAL_REGS]; |
| win = 1; |
| } |
| break; |
| |
| case 'r': |
| if (GET_CODE (op) == REG |
| && (GENERAL_REGS == ALL_REGS |
| || reg_fits_class_p (op, GENERAL_REGS, offset, mode))) |
| { |
| operand_class[this_operand] |
| = reg_class_subunion[(int) operand_class[this_operand]][(int) GENERAL_REGS]; |
| win = 1; |
| } |
| break; |
| |
| case 'X': |
| /* This is used for a MATCH_SCRATCH in the cases when we |
| don't actually need anything. So anything goes any time. */ |
| win = 1; |
| break; |
| |
| case 'm': |
| if (GET_CODE (op) == MEM) |
| win = 1; |
| break; |
| |
| case '<': |
| if (GET_CODE (op) == MEM |
| && (GET_CODE (XEXP (op, 0)) == PRE_DEC |
| || GET_CODE (XEXP (op, 0)) == POST_DEC)) |
| win = 1; |
| break; |
| |
| case '>': |
| if (GET_CODE (op) == MEM |
| && (GET_CODE (XEXP (op, 0)) == PRE_INC |
| || GET_CODE (XEXP (op, 0)) == POST_INC)) |
| win = 1; |
| break; |
| |
| case 'E': |
| /* Match any CONST_DOUBLE, but only if |
| we can examine the bits of it reliably. */ |
| if ((HOST_FLOAT_FORMAT != TARGET_FLOAT_FORMAT |
| || HOST_BITS_PER_WIDE_INT != BITS_PER_WORD) |
| && GET_CODE (op) != VOIDmode && ! flag_pretend_float) |
| break; |
| if (GET_CODE (op) == CONST_DOUBLE) |
| win = 1; |
| break; |
| |
| case 'F': |
| if (GET_CODE (op) == CONST_DOUBLE) |
| win = 1; |
| break; |
| |
| case 'G': |
| case 'H': |
| if (GET_CODE (op) == CONST_DOUBLE |
| && CONST_DOUBLE_OK_FOR_LETTER_P (op, c)) |
| win = 1; |
| break; |
| |
| case 's': |
| if (GET_CODE (op) == CONST_INT |
| || (GET_CODE (op) == CONST_DOUBLE |
| && GET_MODE (op) == VOIDmode)) |
| break; |
| /* Fall through */ |
| case 'i': |
| if (CONSTANT_P (op)) |
| win = 1; |
| break; |
| |
| case 'n': |
| if (GET_CODE (op) == CONST_INT |
| || (GET_CODE (op) == CONST_DOUBLE |
| && GET_MODE (op) == VOIDmode)) |
| win = 1; |
| break; |
| |
| case 'I': |
| case 'J': |
| case 'K': |
| case 'L': |
| case 'M': |
| case 'N': |
| case 'O': |
| case 'P': |
| if (GET_CODE (op) == CONST_INT |
| && CONST_OK_FOR_LETTER_P (INTVAL (op), c)) |
| win = 1; |
| break; |
| |
| #ifdef EXTRA_CONSTRAINT |
| case 'Q': |
| case 'R': |
| case 'S': |
| case 'T': |
| case 'U': |
| if (EXTRA_CONSTRAINT (op, c)) |
| win = 1; |
| break; |
| #endif |
| |
| case 'V': |
| if (GET_CODE (op) == MEM && ! offsettable_memref_p (op)) |
| win = 1; |
| break; |
| |
| case 'o': |
| if (offsettable_memref_p (op)) |
| win = 1; |
| break; |
| |
| default: |
| if (GET_CODE (op) == REG |
| && reg_fits_class_p (op, REG_CLASS_FROM_LETTER (c), |
| offset, mode)) |
| { |
| operand_class[this_operand] |
| = reg_class_subunion[(int)operand_class[this_operand]][(int) REG_CLASS_FROM_LETTER (c)]; |
| win = 1; |
| } |
| } |
| |
| constraints[this_operand] = p; |
| /* If this operand did not win somehow, |
| this alternative loses. */ |
| if (! win) |
| lose = 1; |
| } |
| /* This alternative won; the operands are ok. |
| Change whichever operands this alternative says to change. */ |
| if (! lose) |
| break; |
| |
| this_alternative++; |
| } |
| |
| /* For operands constrained to match another operand, copy the other |
| operand's class to this operand's class. */ |
| for (j = 0; j < n_operands; j++) |
| if (operand_matches[j] >= 0) |
| operand_class[j] = operand_class[operand_matches[j]]; |
| |
| return this_alternative == n_alternatives ? -1 : this_alternative; |
| } |
| |
| /* Record the life info of each stack reg in INSN, updating REGSTACK. |
| N_INPUTS is the number of inputs; N_OUTPUTS the outputs. CONSTRAINTS |
| is an array of the constraint strings used in the asm statement. |
| OPERANDS is an array of all operands for the insn, and is assumed to |
| contain all output operands, then all inputs operands. |
| |
| There are many rules that an asm statement for stack-like regs must |
| follow. Those rules are explained at the top of this file: the rule |
| numbers below refer to that explanation. */ |
| |
| static void |
| record_asm_reg_life (insn, regstack, operands, constraints, |
| n_inputs, n_outputs) |
| rtx insn; |
| stack regstack; |
| rtx *operands; |
| char **constraints; |
| int n_inputs, n_outputs; |
| { |
| int i; |
| int n_operands = n_inputs + n_outputs; |
| int first_input = n_outputs; |
| int n_clobbers; |
| int malformed_asm = 0; |
| rtx body = PATTERN (insn); |
| |
| int *operand_matches = (int *) alloca (n_operands * sizeof (int *)); |
| |
| enum reg_class *operand_class |
| = (enum reg_class *) alloca (n_operands * sizeof (enum reg_class *)); |
| |
| int reg_used_as_output[FIRST_PSEUDO_REGISTER]; |
| int implicitly_dies[FIRST_PSEUDO_REGISTER]; |
| |
| rtx *clobber_reg; |
| |
| /* Find out what the constraints require. If no constraint |
| alternative matches, this asm is malformed. */ |
| i = constrain_asm_operands (n_operands, operands, constraints, |
| operand_matches, operand_class); |
| if (i < 0) |
| malformed_asm = 1; |
| |
| /* Strip SUBREGs here to make the following code simpler. */ |
| for (i = 0; i < n_operands; i++) |
| if (GET_CODE (operands[i]) == SUBREG |
| && GET_CODE (SUBREG_REG (operands[i])) == REG) |
| operands[i] = SUBREG_REG (operands[i]); |
| |
| /* Set up CLOBBER_REG. */ |
| |
| n_clobbers = 0; |
| |
| if (GET_CODE (body) == PARALLEL) |
| { |
| clobber_reg = (rtx *) alloca (XVECLEN (body, 0) * sizeof (rtx *)); |
| |
| for (i = 0; i < XVECLEN (body, 0); i++) |
| if (GET_CODE (XVECEXP (body, 0, i)) == CLOBBER) |
| { |
| rtx clobber = XVECEXP (body, 0, i); |
| rtx reg = XEXP (clobber, 0); |
| |
| if (GET_CODE (reg) == SUBREG && GET_CODE (SUBREG_REG (reg)) == REG) |
| reg = SUBREG_REG (reg); |
| |
| if (STACK_REG_P (reg)) |
| { |
| clobber_reg[n_clobbers] = reg; |
| n_clobbers++; |
| } |
| } |
| } |
| |
| /* Enforce rule #4: Output operands must specifically indicate which |
| reg an output appears in after an asm. "=f" is not allowed: the |
| operand constraints must select a class with a single reg. |
| |
| Also enforce rule #5: Output operands must start at the top of |
| the reg-stack: output operands may not "skip" a reg. */ |
| |
| bzero ((char *) reg_used_as_output, sizeof (reg_used_as_output)); |
| for (i = 0; i < n_outputs; i++) |
| if (STACK_REG_P (operands[i])) |
| if (reg_class_size[(int) operand_class[i]] != 1) |
| { |
| error_for_asm |
| (insn, "Output constraint %d must specify a single register", i); |
| malformed_asm = 1; |
| } |
| else |
| reg_used_as_output[REGNO (operands[i])] = 1; |
| |
| |
| /* Search for first non-popped reg. */ |
| for (i = FIRST_STACK_REG; i < LAST_STACK_REG + 1; i++) |
| if (! reg_used_as_output[i]) |
| break; |
| |
| /* If there are any other popped regs, that's an error. */ |
| for (; i < LAST_STACK_REG + 1; i++) |
| if (reg_used_as_output[i]) |
| break; |
| |
| if (i != LAST_STACK_REG + 1) |
| { |
| error_for_asm (insn, "Output regs must be grouped at top of stack"); |
| malformed_asm = 1; |
| } |
| |
| /* Enforce rule #2: All implicitly popped input regs must be closer |
| to the top of the reg-stack than any input that is not implicitly |
| popped. */ |
| |
| bzero ((char *) implicitly_dies, sizeof (implicitly_dies)); |
| for (i = first_input; i < first_input + n_inputs; i++) |
| if (STACK_REG_P (operands[i])) |
| { |
| /* An input reg is implicitly popped if it is tied to an |
| output, or if there is a CLOBBER for it. */ |
| int j; |
| |
| for (j = 0; j < n_clobbers; j++) |
| if (operands_match_p (clobber_reg[j], operands[i])) |
| break; |
| |
| if (j < n_clobbers || operand_matches[i] >= 0) |
| implicitly_dies[REGNO (operands[i])] = 1; |
| } |
| |
| /* Search for first non-popped reg. */ |
| for (i = FIRST_STACK_REG; i < LAST_STACK_REG + 1; i++) |
| if (! implicitly_dies[i]) |
| break; |
| |
| /* If there are any other popped regs, that's an error. */ |
| for (; i < LAST_STACK_REG + 1; i++) |
| if (implicitly_dies[i]) |
| break; |
| |
| if (i != LAST_STACK_REG + 1) |
| { |
| error_for_asm (insn, |
| "Implicitly popped regs must be grouped at top of stack"); |
| malformed_asm = 1; |
| } |
| |
| /* Enfore rule #3: If any input operand uses the "f" constraint, all |
| output constraints must use the "&" earlyclobber. |
| |
| ??? Detect this more deterministically by having constraint_asm_operands |
| record any earlyclobber. */ |
| |
| for (i = first_input; i < first_input + n_inputs; i++) |
| if (operand_matches[i] == -1) |
| { |
| int j; |
| |
| for (j = 0; j < n_outputs; j++) |
| if (operands_match_p (operands[j], operands[i])) |
| { |
| error_for_asm (insn, |
| "Output operand %d must use `&' constraint", j); |
| malformed_asm = 1; |
| } |
| } |
| |
| if (malformed_asm) |
| { |
| /* Avoid further trouble with this insn. */ |
| PATTERN (insn) = gen_rtx (USE, VOIDmode, const0_rtx); |
| PUT_MODE (insn, VOIDmode); |
| return; |
| } |
| |
| /* Process all outputs */ |
| for (i = 0; i < n_outputs; i++) |
| { |
| rtx op = operands[i]; |
| |
| if (! STACK_REG_P (op)) |
| if (stack_regs_mentioned_p (op)) |
| abort (); |
| else |
| continue; |
| |
| /* Each destination is dead before this insn. If the |
| destination is not used after this insn, record this with |
| REG_UNUSED. */ |
| |
| if (! TEST_HARD_REG_BIT (regstack->reg_set, REGNO (op))) |
| REG_NOTES (insn) = gen_rtx (EXPR_LIST, REG_UNUSED, op, |
| REG_NOTES (insn)); |
| |
| CLEAR_HARD_REG_BIT (regstack->reg_set, REGNO (op)); |
| } |
| |
| /* Process all inputs */ |
| for (i = first_input; i < first_input + n_inputs; i++) |
| { |
| if (! STACK_REG_P (operands[i])) |
| if (stack_regs_mentioned_p (operands[i])) |
| abort (); |
| else |
| continue; |
| |
| /* If an input is dead after the insn, record a death note. |
| But don't record a death note if there is already a death note, |
| or if the input is also an output. */ |
| |
| if (! TEST_HARD_REG_BIT (regstack->reg_set, REGNO (operands[i])) |
| && operand_matches[i] == -1 |
| && find_regno_note (insn, REG_DEAD, REGNO (operands[i])) == NULL_RTX) |
| REG_NOTES (insn) = gen_rtx (EXPR_LIST, REG_DEAD, operands[i], |
| REG_NOTES (insn)); |
| |
| SET_HARD_REG_BIT (regstack->reg_set, REGNO (operands[i])); |
| } |
| } |
| |
| /* Scan PAT, which is part of INSN, and record registers appearing in |
| a SET_DEST in DEST, and other registers in SRC. |
| |
| 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 (pat, src, dest, douse) |
| rtx pat; |
| HARD_REG_SET *src, *dest; |
| int douse; |
| { |
| register char *fmt; |
| register int i; |
| |
| if (STACK_REG_P (pat) |
| || (GET_CODE (pat) == SUBREG && STACK_REG_P (SUBREG_REG (pat)))) |
| { |
| if (src) |
| mark_regs_pat (pat, src); |
| |
| if (dest) |
| mark_regs_pat (pat, dest); |
| |
| return; |
| } |
| |
| if (GET_CODE (pat) == SET) |
| { |
| record_reg_life_pat (XEXP (pat, 0), NULL_PTR, dest, 0); |
| record_reg_life_pat (XEXP (pat, 1), src, NULL_PTR, 0); |
| return; |
| } |
| |
| /* We don't need to consider either of these cases. */ |
| if (GET_CODE (pat) == USE && !douse || GET_CODE (pat) == CLOBBER) |
| return; |
| |
| 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--) |
| record_reg_life_pat (XVECEXP (pat, i, j), src, dest, 0); |
| } |
| else if (fmt[i] == 'e') |
| record_reg_life_pat (XEXP (pat, i), src, dest, 0); |
| } |
| } |
| |
| /* Calculate the number of inputs and outputs in BODY, an |
| asm_operands. N_OPERANDS is the total number of operands, and |
| N_INPUTS and N_OUTPUTS are pointers to ints into which the results are |
| placed. */ |
| |
| static void |
| get_asm_operand_lengths (body, n_operands, n_inputs, n_outputs) |
| rtx body; |
| int n_operands; |
| int *n_inputs, *n_outputs; |
| { |
| if (GET_CODE (body) == SET && GET_CODE (SET_SRC (body)) == ASM_OPERANDS) |
| *n_inputs = ASM_OPERANDS_INPUT_LENGTH (SET_SRC (body)); |
| |
| else if (GET_CODE (body) == ASM_OPERANDS) |
| *n_inputs = ASM_OPERANDS_INPUT_LENGTH (body); |
| |
| else if (GET_CODE (body) == PARALLEL |
| && GET_CODE (XVECEXP (body, 0, 0)) == SET) |
| *n_inputs = ASM_OPERANDS_INPUT_LENGTH (SET_SRC (XVECEXP (body, 0, 0))); |
| |
| else if (GET_CODE (body) == PARALLEL |
| && GET_CODE (XVECEXP (body, 0, 0)) == ASM_OPERANDS) |
| *n_inputs = ASM_OPERANDS_INPUT_LENGTH (XVECEXP (body, 0, 0)); |
| else |
| abort (); |
| |
| *n_outputs = n_operands - *n_inputs; |
| } |
| |
| /* 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; |
| int n_operands; |
| |
| 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. */ |
| |
| n_operands = asm_noperands (PATTERN (insn)); |
| if (n_operands >= 0) |
| { |
| /* This insn is an `asm' with operands. Decode the operands, |
| decide how many are inputs, and record the life information. */ |
| |
| rtx operands[MAX_RECOG_OPERANDS]; |
| rtx body = PATTERN (insn); |
| int n_inputs, n_outputs; |
| char **constraints = (char **) alloca (n_operands * sizeof (char *)); |
| |
| decode_asm_operands (body, operands, NULL_PTR, constraints, NULL_PTR); |
| get_asm_operand_lengths (body, n_operands, &n_inputs, &n_outputs); |
| record_asm_reg_life (insn, regstack, operands, constraints, |
| n_inputs, n_outputs); |
| return; |
| } |
| |
| { |
| HARD_REG_SET src, dest; |
| int regno; |
| |
| CLEAR_HARD_REG_SET (src); |
| CLEAR_HARD_REG_SET (dest); |
| |
| if (GET_CODE (insn) == CALL_INSN) |
| for (note = CALL_INSN_FUNCTION_USAGE (insn); |
| note; |
| note = XEXP (note, 1)) |
| if (GET_CODE (XEXP (note, 0)) == USE) |
| record_reg_life_pat (SET_DEST (XEXP (note, 0)), &src, NULL_PTR, 0); |
| |
| record_reg_life_pat (PATTERN (insn), &src, &dest, 0); |
| for (regno = FIRST_STACK_REG; regno <= LAST_STACK_REG; regno++) |
| if (! TEST_HARD_REG_BIT (regstack->reg_set, regno)) |
| { |
| if (TEST_HARD_REG_BIT (src, regno) |
| && ! TEST_HARD_REG_BIT (dest, regno)) |
| REG_NOTES (insn) = gen_rtx (EXPR_LIST, REG_DEAD, |
| FP_MODE_REG (regno, DFmode), |
| REG_NOTES (insn)); |
| else if (TEST_HARD_REG_BIT (dest, regno)) |
| REG_NOTES (insn) = gen_rtx (EXPR_LIST, REG_UNUSED, |
| FP_MODE_REG (regno, DFmode), |
| REG_NOTES (insn)); |
| } |
| |
| if (GET_CODE (insn) == CALL_INSN) |
| { |
| int reg; |
| |
| /* 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(). */ |
| |
| for (reg = FIRST_STACK_REG; reg <= LAST_STACK_REG; reg++) |
| if (! TEST_HARD_REG_BIT (dest, reg) |
| && 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, FP_MODE_REG (reg, DFmode), |
| CONST0_RTX (DFmode)); |
| 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); |
| } |
| |
| AND_COMPL_HARD_REG_SET (regstack->reg_set, dest); |
| IOR_HARD_REG_SET (regstack->reg_set, src); |
| } |
| } |
| |
| /* 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; |
| rtx label_value_list = 0; |
| |
| /* 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, but that these are not the same |
| as those selected in flow.c. */ |
| |
| code = GET_CODE (insn); |
| |
| if (code == CODE_LABEL |
| || (prev_code != INSN |
| && prev_code != CALL_INSN |
| && prev_code != CODE_LABEL |
| && GET_RTX_CLASS (code) == 'i')) |
| { |
| block_begin[++block] = insn; |
| block_end[block] = insn; |
| block_drops_in[block] = prev_code != BARRIER; |
| } |
| else if (GET_RTX_CLASS (code) == 'i') |
| block_end[block] = insn; |
| |
| if (GET_RTX_CLASS (code) == 'i') |
| { |
| rtx note; |
| |
| /* Make a list of all labels referred to other than by jumps. */ |
| for (note = REG_NOTES (insn); note; note = XEXP (note, 1)) |
| if (REG_NOTE_KIND (note) == REG_LABEL) |
| label_value_list = gen_rtx (EXPR_LIST, VOIDmode, XEXP (note, 0), |
| label_value_list); |
| } |
| |
| block_number[INSN_UID (insn)] = block; |
| |
| if (code != NOTE) |
| prev_code = code; |
| } |
| |
| if (block + 1 != blocks) |
| abort (); |
| |
| /* generate all label references to the corresponding jump insn */ |
| for (block = 0; block < blocks; block++) |
| { |
| insn = block_end[block]; |
| |
| if (GET_CODE (insn) == JUMP_INSN) |
| { |
| rtx pat = PATTERN (insn); |
| int computed_jump = 0; |
| rtx x; |
| |
| if (GET_CODE (pat) == PARALLEL) |
| { |
| int len = XVECLEN (pat, 0); |
| int has_use_labelref = 0; |
| int i; |
| |
| for (i = len - 1; i >= 0; i--) |
| if (GET_CODE (XVECEXP (pat, 0, i)) == USE |
| && GET_CODE (XEXP (XVECEXP (pat, 0, i), 0)) == LABEL_REF) |
| has_use_labelref = 1; |
| |
| if (! has_use_labelref) |
| for (i = len - 1; i >= 0; i--) |
| if (GET_CODE (XVECEXP (pat, 0, i)) == SET |
| && SET_DEST (XVECEXP (pat, 0, i)) == pc_rtx |
| && uses_reg_or_mem (SET_SRC (XVECEXP (pat, 0, i)))) |
| computed_jump = 1; |
| } |
| else if (GET_CODE (pat) == SET |
| && SET_DEST (pat) == pc_rtx |
| && uses_reg_or_mem (SET_SRC (pat))) |
| computed_jump = 1; |
| |
| if (computed_jump) |
| { |
| for (x = label_value_list; x; x = XEXP (x, 1)) |
| record_label_references (insn, |
| gen_rtx (LABEL_REF, VOIDmode, |
| XEXP (x, 0))); |
| |
| for (x = forced_labels; x; x = XEXP (x, 1)) |
| record_label_references (insn, |
| gen_rtx (LABEL_REF, VOIDmode, |
| XEXP (x, 0))); |
| } |
| |
| record_label_references (insn, pat); |
| } |
| } |
| } |
| |
| /* Return 1 if X contain a REG or MEM that is not in the constant pool. */ |
| |
| static int |
| uses_reg_or_mem (x) |
| rtx x; |
| { |
| enum rtx_code code = GET_CODE (x); |
| int i, j; |
| char *fmt; |
| |
| if (code == REG |
| || (code == MEM |
| && ! (GET_CODE (XEXP (x, 0)) == SYMBOL_REF |
| && CONSTANT_POOL_ADDRESS_P (XEXP (x, 0))))) |
| return 1; |
| |
| fmt = GET_RTX_FORMAT (code); |
| for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) |
| { |
| if (fmt[i] == 'e' |
| && uses_reg_or_mem (XEXP (x, i))) |
| return 1; |
| |
| if (fmt[i] == 'E') |
| for (j = 0; j < XVECLEN (x, i); j++) |
| if (uses_reg_or_mem (XVECEXP (x, i, j))) |
| return 1; |
| } |
| |
| return 0; |
| } |
| |
| /* If current function returns its result in an fp stack register, |
| return the REG. Otherwise, return 0. */ |
| |
| static rtx |
| stack_result (decl) |
| tree decl; |
| { |
| rtx result = DECL_RTL (DECL_RESULT (decl)); |
| |
| if (result != 0 |
| && ! (GET_CODE (result) == REG |
| && REGNO (result) < FIRST_PSEUDO_REGISTER)) |
| { |
| #ifdef FUNCTION_OUTGOING_VALUE |
| result |
| = FUNCTION_OUTGOING_VALUE (TREE_TYPE (DECL_RESULT (decl)), decl); |
| #else |
| result = FUNCTION_VALUE (TREE_TYPE (DECL_RESULT (decl)), decl); |
| #endif |
| } |
| |
| return result != 0 && STACK_REG_P (result) ? result : 0; |
| } |
| |
| /* 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 dead 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, stackentry) |
| rtx first; |
| HARD_REG_SET *stackentry; |
| { |
| int reg, block; |
| struct stack_def regstack; |
| |
| { |
| rtx retvalue; |
| |
| if (retvalue = stack_result (current_function_decl)) |
| { |
| /* Find all RETURN insns and mark them. */ |
| |
| for (block = blocks - 1; --block >= 0;) |
| if (GET_CODE (block_end[block]) == JUMP_INSN |
| && GET_CODE (PATTERN (block_end[block])) == RETURN) |
| mark_regs_pat (retvalue, block_out_reg_set+block); |
| |
| /* Mark off 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) |
| mark_regs_pat (retvalue, block_out_reg_set+blocks-1); |
| } |
| } |
| |
| /* 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. */ |
| |
| /* Load zero into each live register. The fact that a register |
| appears live at the function start necessarily implies an error |
| in the user program: it means that (unless the offending code is *never* |
| executed) this program is using uninitialised floating point |
| variables. In order to keep broken code like this happy, we initialise |
| those variables with zero. |
| |
| 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) |
| && ! TEST_HARD_REG_BIT (*stackentry, reg)) |
| { |
| rtx init_rtx; |
| |
| init_rtx = gen_rtx (SET, VOIDmode, FP_MODE_REG(reg, DFmode), |
| CONST0_RTX (DFmode)); |
| 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); |
| } |
| } |
| |
| /***************************************************************************** |
| This section deals with stack register substitution, 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 (); |
| |
| switch (GET_MODE_CLASS (GET_MODE (*reg))) |
| { |
| default: abort (); |
| case MODE_FLOAT: |
| case MODE_COMPLEX_FLOAT:; |
| } |
| |
| *reg = FP_MODE_REG (regno, GET_MODE (*reg)); |
| } |
| |
| /* 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; |
| } |
| |
| /* 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, FP_MODE_REG (hard_regno, DFmode), |
| FP_MODE_REG (FIRST_STACK_REG, DFmode)); |
| |
| pop_insn = (*when) (pop_rtx, insn); |
| /* ??? This used to be VOIDmode, but that seems wrong. */ |
| PUT_MODE (pop_insn, QImode); |
| |
| REG_NOTES (pop_insn) = gen_rtx (EXPR_LIST, REG_DEAD, |
| FP_MODE_REG (FIRST_STACK_REG, DFmode), |
| 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_swap_insn (insn, regstack, reg) |
| rtx insn; |
| stack regstack; |
| rtx reg; |
| { |
| int hard_regno; |
| rtx gen_swapdf(); |
| rtx swap_rtx, swap_insn; |
| int tmp, other_reg; /* swap regno temps */ |
| rtx i1; /* the stack-reg insn prior to INSN */ |
| rtx i1set = NULL_RTX; /* the SET rtx within I1 */ |
| |
| hard_regno = get_hard_regnum (regstack, reg); |
| |
| if (hard_regno < FIRST_STACK_REG) |
| abort (); |
| if (hard_regno == FIRST_STACK_REG) |
| return; |
| |
| other_reg = regstack->top - (hard_regno - FIRST_STACK_REG); |
| |
| tmp = regstack->reg[other_reg]; |
| regstack->reg[other_reg] = regstack->reg[regstack->top]; |
| regstack->reg[regstack->top] = tmp; |
| |
| /* Find the previous insn involving stack regs, but don't go past |
| any labels, calls or jumps. */ |
| i1 = prev_nonnote_insn (insn); |
| while (i1 && GET_CODE (i1) == INSN && GET_MODE (i1) != QImode) |
| i1 = prev_nonnote_insn (i1); |
| |
| if (i1) |
| i1set = single_set (i1); |
| |
| if (i1set) |
| { |
| rtx i2; /* the stack-reg insn prior to I1 */ |
| rtx i1src = *get_true_reg (&SET_SRC (i1set)); |
| rtx i1dest = *get_true_reg (&SET_DEST (i1set)); |
| |
| /* If the previous register stack push was from the reg we are to |
| swap with, omit the swap. */ |
| |
| if (GET_CODE (i1dest) == REG && REGNO (i1dest) == FIRST_STACK_REG |
| && GET_CODE (i1src) == REG && REGNO (i1src) == hard_regno - 1 |
| && find_regno_note (i1, REG_DEAD, FIRST_STACK_REG) == NULL_RTX) |
| return; |
| |
| /* If the previous insn wrote to the reg we are to swap with, |
| omit the swap. */ |
| |
| if (GET_CODE (i1dest) == REG && REGNO (i1dest) == hard_regno |
| && GET_CODE (i1src) == REG && REGNO (i1src) == FIRST_STACK_REG |
| && find_regno_note (i1, REG_DEAD, FIRST_STACK_REG) == NULL_RTX) |
| return; |
| } |
| |
| if (GET_RTX_CLASS (GET_CODE (i1)) == 'i' && sets_cc0_p (PATTERN (i1))) |
| { |
| i1 = next_nonnote_insn (i1); |
| if (i1 == insn) |
| abort (); |
| } |
| |
| swap_rtx = gen_swapdf (FP_MODE_REG (hard_regno, DFmode), |
| FP_MODE_REG (FIRST_STACK_REG, DFmode)); |
| swap_insn = emit_insn_after (swap_rtx, i1); |
| /* ??? This used to be VOIDmode, but that seems wrong. */ |
| PUT_MODE (swap_insn, QImode); |
| } |
| |
| /* 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 *psrc = get_true_reg (&SET_SRC (pat)); |
| rtx *pdest = get_true_reg (&SET_DEST (pat)); |
| rtx src, dest; |
| rtx note; |
| |
| src = *psrc; dest = *pdest; |
| |
| 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 (psrc, get_hard_regnum (regstack, src)); |
| |
| regstack->reg[++regstack->top] = REGNO (dest); |
| SET_HARD_REG_BIT (regstack->reg_set, REGNO (dest)); |
| replace_reg (pdest, 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); |
| |
| 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)); |
| } |
| else if (GET_MODE (src) == XFmode && regstack->top < REG_STACK_SIZE - 1) |
| { |
| /* A 387 cannot write an XFmode value to a MEM without |
| clobbering the source reg. The output code can handle |
| this by reading back the value from the MEM. |
| But it is more efficient to use a temp register if one is |
| available. Push the source value here if the register |
| stack is not full, and then write the value to memory via |
| a pop. */ |
| rtx push_rtx, push_insn; |
| rtx top_stack_reg = FP_MODE_REG (FIRST_STACK_REG, XFmode); |
| |
| push_rtx = gen_movxf (top_stack_reg, top_stack_reg); |
| push_insn = emit_insn_before (push_rtx, insn); |
| PUT_MODE (push_insn, QImode); |
| REG_NOTES (insn) = gen_rtx (EXPR_LIST, REG_DEAD, top_stack_reg, |
| REG_NOTES (insn)); |
| } |
| |
| replace_reg (psrc, 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 (pdest, FIRST_STACK_REG); |
| } |
| else |
| abort (); |
| } |
| |
| static void |
| swap_rtx_condition (pat) |
| rtx pat; |
| { |
| register char *fmt; |
| register int i; |
| |
| if (GET_RTX_CLASS (GET_CODE (pat)) == '<') |
| { |
| PUT_CODE (pat, swap_condition (GET_CODE (pat))); |
| return; |
| } |
| |
| 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--) |
| swap_rtx_condition (XVECEXP (pat, i, j)); |
| } |
| else if (fmt[i] == 'e') |
| swap_rtx_condition (XEXP (pat, i)); |
| } |
| } |
| |
| /* 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; |
| rtx cc0_user; |
| |
| src1 = get_true_reg (&XEXP (SET_SRC (pat), 0)); |
| src2 = get_true_reg (&XEXP (SET_SRC (pat), 1)); |
| cc0_user = next_cc0_user (insn); |
| |
| /* If the insn that uses cc0 is a conditional move, then the destination |
| must be the top of stack */ |
| if (GET_CODE (PATTERN (cc0_user)) == SET |
| && SET_DEST (PATTERN (cc0_user)) != pc_rtx |
| && GET_CODE (SET_SRC (PATTERN (cc0_user))) == IF_THEN_ELSE) |
| { |
| rtx *dest, src_note; |
| |
| dest = get_true_reg (&SET_DEST (PATTERN (cc0_user))); |
| if (REGNO (*dest) != regstack->reg[regstack->top]) |
| { |
| emit_swap_insn (insn, regstack, *dest); |
| } |
| } |
| |
| /* ??? If fxch turns out to be cheaper than fstp, give priority to |
| registers that die in this insn - move those to stack top first. */ |
| if (! STACK_REG_P (*src1) |
| || (STACK_REG_P (*src2) |
| && get_hard_regnum (regstack, *src2) == FIRST_STACK_REG)) |
| { |
| rtx temp, next; |
| |
| temp = XEXP (SET_SRC (pat), 0); |
| XEXP (SET_SRC (pat), 0) = XEXP (SET_SRC (pat), 1); |
| XEXP (SET_SRC (pat), 1) = temp; |
| |
| src1 = get_true_reg (&XEXP (SET_SRC (pat), 0)); |
| src2 = get_true_reg (&XEXP (SET_SRC (pat), 1)); |
| |
| next = next_cc0_user (insn); |
| if (next == NULL_RTX) |
| abort (); |
| |
| swap_rtx_condition (PATTERN (next)); |
| INSN_CODE (next) = -1; |
| INSN_CODE (insn) = -1; |
| } |
| |
| /* 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 = NULL_RTX; |
| |
| emit_swap_insn (insn, regstack, *src1); |
| |
| 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. */ |
| |
| link_cc0_insns (insn); |
| |
| 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 = (rtx *) NULL_PTR, *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: |
| { |
| int count; |
| for (count = HARD_REGNO_NREGS (REGNO (*dest), GET_MODE (*dest)); |
| --count >= 0;) |
| { |
| regstack->reg[++regstack->top] = REGNO (*dest) + count; |
| SET_HARD_REG_BIT (regstack->reg_set, REGNO (*dest) + count); |
| } |
| } |
| replace_reg (dest, FIRST_STACK_REG); |
| break; |
| |
| case REG: |
| /* This is a `tstM2' case. */ |
| if (*dest != cc0_rtx) |
| abort (); |
| |
| src1 = src; |
| |
| /* Fall through. */ |
| |
| case FLOAT_TRUNCATE: |
| 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); |
| |
| 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 = NULL_RTX; |
| if (STACK_REG_P (*src2)) |
| src2_note = find_regno_note (insn, REG_DEAD, REGNO (*src2)); |
| else |
| src2_note = NULL_RTX; |
| |
| /* 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); |
| 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. */ |
| |
| 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); |
| } |
| |
| 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; |
| |
| case UNSPEC: |
| switch (XINT (SET_SRC (pat), 1)) |
| { |
| case 1: /* sin */ |
| case 2: /* cos */ |
| /* These insns only operate on the top of the stack. */ |
| |
| src1 = get_true_reg (&XVECEXP (SET_SRC (pat), 0, 0)); |
| |
| emit_swap_insn (insn, regstack, *src1); |
| |
| 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; |
| |
| default: |
| abort (); |
| } |
| break; |
| |
| case IF_THEN_ELSE: |
| /* This insn requires the top of stack to be the destination. */ |
| |
| src1 = get_true_reg (&XEXP (SET_SRC (pat), 1)); |
| src2 = get_true_reg (&XEXP (SET_SRC (pat), 2)); |
| |
| src1_note = find_regno_note (insn, REG_DEAD, REGNO (*src1)); |
| src2_note = find_regno_note (insn, REG_DEAD, REGNO (*src2)); |
| |
| { |
| rtx src_note [3]; |
| int i; |
| |
| src_note[0] = 0; |
| src_note[1] = src1_note; |
| src_note[2] = src2_note; |
| |
| 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)); |
| |
| for (i = 1; i <= 2; i++) |
| if (src_note [i]) |
| { |
| int regno = get_hard_regnum (regstack, XEXP (src_note [i], 0)); |
| |
| /* If the register that dies is not at the top of stack, then |
| move the top of stack to the dead reg */ |
| if (REGNO (XEXP (src_note[i], 0)) |
| != regstack->reg[regstack->top]) |
| { |
| remove_regno_note (insn, REG_DEAD, |
| REGNO (XEXP (src_note [i], 0))); |
| emit_pop_insn (insn, regstack, XEXP (src_note[i], 0), |
| emit_insn_after); |
| } |
| else |
| { |
| CLEAR_HARD_REG_BIT (regstack->reg_set, |
| REGNO (XEXP (src_note[i], 0))); |
| replace_reg (&XEXP (src_note[i], 0), FIRST_STACK_REG); |
| regstack->top--; |
| } |
| |
| } |
| |
| SET_HARD_REG_BIT (regstack->reg_set, REGNO (*dest)); |
| replace_reg (dest, FIRST_STACK_REG); |
| } |
| |
| break; |
| |
| default: |
| abort (); |
| } |
| } |
| |
| /* Substitute hard regnums for any stack regs in INSN, which has |
| N_INPUTS inputs and N_OUTPUTS outputs. REGSTACK is the stack info |
| before the insn, and is updated with changes made here. CONSTRAINTS is |
| an array of the constraint strings used in the asm statement. |
| |
| OPERANDS is an array of the operands, and OPERANDS_LOC is a |
| parallel array of where the operands were found. The output operands |
| all precede the input operands. |
| |
| There are several requirements and assumptions about the use of |
| stack-like regs in asm statements. These rules are enforced by |
| record_asm_stack_regs; see comments there for details. Any |
| asm_operands left in the RTL at this point may be assume to meet the |
| requirements, since record_asm_stack_regs removes any problem asm. */ |
| |
| static void |
| subst_asm_stack_regs (insn, regstack, operands, operands_loc, constraints, |
| n_inputs, n_outputs) |
| rtx insn; |
| stack regstack; |
| rtx *operands, **operands_loc; |
| char **constraints; |
| int n_inputs, n_outputs; |
| { |
| int n_operands = n_inputs + n_outputs; |
| int first_input = n_outputs; |
| rtx body = PATTERN (insn); |
| |
| int *operand_matches = (int *) alloca (n_operands * sizeof (int *)); |
| enum reg_class *operand_class |
| = (enum reg_class *) alloca (n_operands * sizeof (enum reg_class *)); |
| |
| rtx *note_reg; /* Array of note contents */ |
| rtx **note_loc; /* Address of REG field of each note */ |
| enum reg_note *note_kind; /* The type of each note */ |
| |
| rtx *clobber_reg; |
| rtx **clobber_loc; |
| |
| struct stack_def temp_stack; |
| int n_notes; |
| int n_clobbers; |
| rtx note; |
| int i; |
| |
| /* Find out what the constraints required. If no constraint |
| alternative matches, that is a compiler bug: we should have caught |
| such an insn during the life analysis pass (and reload should have |
| caught it regardless). */ |
| |
| i = constrain_asm_operands (n_operands, operands, constraints, |
| operand_matches, operand_class); |
| if (i < 0) |
| abort (); |
| |
| /* Strip SUBREGs here to make the following code simpler. */ |
| for (i = 0; i < n_operands; i++) |
| if (GET_CODE (operands[i]) == SUBREG |
| && GET_CODE (SUBREG_REG (operands[i])) == REG) |
| { |
| operands_loc[i] = & SUBREG_REG (operands[i]); |
| operands[i] = SUBREG_REG (operands[i]); |
| } |
| |
| /* Set up NOTE_REG, NOTE_LOC and NOTE_KIND. */ |
| |
| for (i = 0, note = REG_NOTES (insn); note; note = XEXP (note, 1)) |
| i++; |
| |
| note_reg = (rtx *) alloca (i * sizeof (rtx)); |
| note_loc = (rtx **) alloca (i * sizeof (rtx *)); |
| note_kind = (enum reg_note *) alloca (i * sizeof (enum reg_note)); |
| |
| n_notes = 0; |
| for (note = REG_NOTES (insn); note; note = XEXP (note, 1)) |
| { |
| rtx reg = XEXP (note, 0); |
| rtx *loc = & XEXP (note, 0); |
| |
| if (GET_CODE (reg) == SUBREG && GET_CODE (SUBREG_REG (reg)) == REG) |
| { |
| loc = & SUBREG_REG (reg); |
| reg = SUBREG_REG (reg); |
| } |
| |
| if (STACK_REG_P (reg) |
| && (REG_NOTE_KIND (note) == REG_DEAD |
| || REG_NOTE_KIND (note) == REG_UNUSED)) |
| { |
| note_reg[n_notes] = reg; |
| note_loc[n_notes] = loc; |
| note_kind[n_notes] = REG_NOTE_KIND (note); |
| n_notes++; |
| } |
| } |
| |
| /* Set up CLOBBER_REG and CLOBBER_LOC. */ |
| |
| n_clobbers = 0; |
| |
| if (GET_CODE (body) == PARALLEL) |
| { |
| clobber_reg = (rtx *) alloca (XVECLEN (body, 0) * sizeof (rtx *)); |
| clobber_loc = (rtx **) alloca (XVECLEN (body, 0) * sizeof (rtx **)); |
| |
| for (i = 0; i < XVECLEN (body, 0); i++) |
| if (GET_CODE (XVECEXP (body, 0, i)) == CLOBBER) |
| { |
| rtx clobber = XVECEXP (body, 0, i); |
| rtx reg = XEXP (clobber, 0); |
| rtx *loc = & XEXP (clobber, 0); |
| |
| if (GET_CODE (reg) == SUBREG && GET_CODE (SUBREG_REG (reg)) == REG) |
| { |
| loc = & SUBREG_REG (reg); |
| reg = SUBREG_REG (reg); |
| } |
| |
| if (STACK_REG_P (reg)) |
| { |
| clobber_reg[n_clobbers] = reg; |
| clobber_loc[n_clobbers] = loc; |
| n_clobbers++; |
| } |
| } |
| } |
| |
| bcopy ((char *) regstack, (char *) &temp_stack, sizeof (temp_stack)); |
| |
| /* Put the input regs into the desired place in TEMP_STACK. */ |
| |
| for (i = first_input; i < first_input + n_inputs; i++) |
| if (STACK_REG_P (operands[i]) |
| && reg_class_subset_p (operand_class[i], FLOAT_REGS) |
| && operand_class[i] != FLOAT_REGS) |
| { |
| /* If an operand needs to be in a particular reg in |
| FLOAT_REGS, the constraint was either 't' or 'u'. Since |
| these constraints are for single register classes, and reload |
| guaranteed that operand[i] is already in that class, we can |
| just use REGNO (operands[i]) to know which actual reg this |
| operand needs to be in. */ |
| |
| int regno = get_hard_regnum (&temp_stack, operands[i]); |
| |
| if (regno < 0) |
| abort (); |
| |
| if (regno != REGNO (operands[i])) |
| { |
| /* operands[i] is not in the right place. Find it |
| and swap it with whatever is already in I's place. |
| K is where operands[i] is now. J is where it should |
| be. */ |
| int j, k, temp; |
| |
| k = temp_stack.top - (regno - FIRST_STACK_REG); |
| j = (temp_stack.top |
| - (REGNO (operands[i]) - FIRST_STACK_REG)); |
| |
| temp = temp_stack.reg[k]; |
| temp_stack.reg[k] = temp_stack.reg[j]; |
| temp_stack.reg[j] = temp; |
| } |
| } |
| |
| /* emit insns before INSN to make sure the reg-stack is in the right |
| order. */ |
| |
| change_stack (insn, regstack, &temp_stack, emit_insn_before); |
| |
| /* Make the needed input register substitutions. Do death notes and |
| clobbers too, because these are for inputs, not outputs. */ |
| |
| for (i = first_input; i < first_input + n_inputs; i++) |
| if (STACK_REG_P (operands[i])) |
| { |
| int regnum = get_hard_regnum (regstack, operands[i]); |
| |
| if (regnum < 0) |
| abort (); |
| |
| replace_reg (operands_loc[i], regnum); |
| } |
| |
| for (i = 0; i < n_notes; i++) |
| if (note_kind[i] == REG_DEAD) |
| { |
| int regnum = get_hard_regnum (regstack, note_reg[i]); |
| |
| if (regnum < 0) |
| abort (); |
| |
| replace_reg (note_loc[i], regnum); |
| } |
| |
| for (i = 0; i < n_clobbers; i++) |
| { |
| /* It's OK for a CLOBBER to reference a reg that is not live. |
| Don't try to replace it in that case. */ |
| int regnum = get_hard_regnum (regstack, clobber_reg[i]); |
| |
| if (regnum >= 0) |
| { |
| /* Sigh - clobbers always have QImode. But replace_reg knows |
| that these regs can't be MODE_INT and will abort. Just put |
| the right reg there without calling replace_reg. */ |
| |
| *clobber_loc[i] = FP_MODE_REG (regnum, DFmode); |
| } |
| } |
| |
| /* Now remove from REGSTACK any inputs that the asm implicitly popped. */ |
| |
| for (i = first_input; i < first_input + n_inputs; i++) |
| if (STACK_REG_P (operands[i])) |
| { |
| /* An input reg is implicitly popped if it is tied to an |
| output, or if there is a CLOBBER for it. */ |
| int j; |
| |
| for (j = 0; j < n_clobbers; j++) |
| if (operands_match_p (clobber_reg[j], operands[i])) |
| break; |
| |
| if (j < n_clobbers || operand_matches[i] >= 0) |
| { |
| /* operands[i] might not be at the top of stack. But that's OK, |
| because all we need to do is pop the right number of regs |
| off of the top of the reg-stack. record_asm_stack_regs |
| guaranteed that all implicitly popped regs were grouped |
| at the top of the reg-stack. */ |
| |
| CLEAR_HARD_REG_BIT (regstack->reg_set, |
| regstack->reg[regstack->top]); |
| regstack->top--; |
| } |
| } |
| |
| /* Now add to REGSTACK any outputs that the asm implicitly pushed. |
| Note that there isn't any need to substitute register numbers. |
| ??? Explain why this is true. */ |
| |
| for (i = LAST_STACK_REG; i >= FIRST_STACK_REG; i--) |
| { |
| /* See if there is an output for this hard reg. */ |
| int j; |
| |
| for (j = 0; j < n_outputs; j++) |
| if (STACK_REG_P (operands[j]) && REGNO (operands[j]) == i) |
| { |
| regstack->reg[++regstack->top] = i; |
| SET_HARD_REG_BIT (regstack->reg_set, i); |
| break; |
| } |
| } |
| |
| /* Now emit a pop insn for any REG_UNUSED output, or any REG_DEAD |
| input that the asm didn't implicitly pop. If the asm didn't |
| implicitly pop an input reg, that reg will still be live. |
| |
| Note that we can't use find_regno_note here: the register numbers |
| in the death notes have already been substituted. */ |
| |
| for (i = 0; i < n_outputs; i++) |
| if (STACK_REG_P (operands[i])) |
| { |
| int j; |
| |
| for (j = 0; j < n_notes; j++) |
| if (REGNO (operands[i]) == REGNO (note_reg[j]) |
| && note_kind[j] == REG_UNUSED) |
| { |
| insn = emit_pop_insn (insn, regstack, operands[i], |
| emit_insn_after); |
| break; |
| } |
| } |
| |
| for (i = first_input; i < first_input + n_inputs; i++) |
| if (STACK_REG_P (operands[i])) |
| { |
| int j; |
| |
| for (j = 0; j < n_notes; j++) |
| if (REGNO (operands[i]) == REGNO (note_reg[j]) |
| && note_kind[j] == REG_DEAD |
| && TEST_HARD_REG_BIT (regstack->reg_set, REGNO (operands[i]))) |
| { |
| insn = emit_pop_insn (insn, regstack, operands[i], |
| emit_insn_after); |
| break; |
| } |
| } |
| } |
| |
| /* 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; |
| int n_operands; |
| |
| if (GET_CODE (insn) == CALL_INSN) |
| { |
| int top = regstack->top; |
| |
| /* If there are any floating point parameters to be passed in |
| registers for this call, make sure they are in the right |
| order. */ |
| |
| if (top >= 0) |
| { |
| straighten_stack (PREV_INSN (insn), regstack); |
| |
| /* Now mark the arguments as dead after the call. */ |
| |
| while (regstack->top >= 0) |
| { |
| CLEAR_HARD_REG_BIT (regstack->reg_set, FIRST_STACK_REG + regstack->top); |
| regstack->top--; |
| } |
| } |
| } |
| |
| /* 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) |
| { |
| n_operands = asm_noperands (PATTERN (insn)); |
| if (n_operands >= 0) |
| { |
| /* This insn is an `asm' with operands. Decode the operands, |
| decide how many are inputs, and do register substitution. |
| Any REG_UNUSED notes will be handled by subst_asm_stack_regs. */ |
| |
| rtx operands[MAX_RECOG_OPERANDS]; |
| rtx *operands_loc[MAX_RECOG_OPERANDS]; |
| rtx body = PATTERN (insn); |
| int n_inputs, n_outputs; |
| char **constraints |
| = (char **) alloca (n_operands * sizeof (char *)); |
| |
| decode_asm_operands (body, operands, operands_loc, |
| constraints, NULL_PTR); |
| get_asm_operand_lengths (body, n_operands, &n_inputs, &n_outputs); |
| subst_asm_stack_regs (insn, regstack, operands, operands_loc, |
| constraints, n_inputs, n_outputs); |
| return; |
| } |
| |
| 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 (GET_CODE (insn) == NOTE) |
| 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); |
| insn = 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 before or after INSN. WHEN is emit_insn_before |
| or emit_insn_after. 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, when) |
| rtx insn; |
| stack old; |
| stack new; |
| rtx (*when)(); |
| { |
| int reg; |
| |
| /* We will be inserting new insns "backwards", by calling emit_insn_before. |
| If we are to insert after INSN, find the next insn, and insert before |
| it. */ |
| |
| if (when == emit_insn_after) |
| 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, FP_MODE_REG (old->reg[reg], DFmode), |
| 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, |
| FP_MODE_REG (old->reg[reg], DFmode)); |
| } |
| |
| /* 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, |
| FP_MODE_REG (old->reg[reg], DFmode)); |
| 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; |
| |
| switch (GET_CODE (pat)) |
| { |
| case RETURN: |
| straighten_stack (PREV_INSN (insn), regstack); |
| return; |
| default: |
| { |
| 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; |
| } |
| case LABEL_REF:; |
| } |
| |
| 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. */ |
| if (INSN_UID (label) <= 0) |
| return; |
| |
| 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, emit_insn_after); |
| 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, emit_insn_after); |
| } |
| |
| /* Traverse all basic blocks in a function, converting the register |
| references 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 or if it's a CALL_INSN (register passing of |
| floating point values). */ |
| |
| 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], |
| emit_insn_after); |
| } |
| |
| /* 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. */ |
| |
| { |
| int value_reg_low, value_reg_high; |
| value_reg_low = value_reg_high = -1; |
| { |
| rtx retvalue; |
| if (retvalue = stack_result (current_function_decl)) |
| { |
| value_reg_low = REGNO (retvalue); |
| value_reg_high = value_reg_low + |
| HARD_REGNO_NREGS (value_reg_low, GET_MODE (retvalue)) - 1; |
| } |
| |
| } |
| for (reg = regstack.top; reg >= 0; reg--) |
| if (regstack.reg[reg] < value_reg_low |
| || regstack.reg[reg] > value_reg_high) |
| insn = emit_pop_insn (insn, ®stack, |
| FP_MODE_REG (regstack.reg[reg], DFmode), |
| emit_insn_after); |
| } |
| straighten_stack (insn, ®stack); |
| } |
| |
| /* 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; |
| rt
|