| /* Subroutines for insn-output.c for Matsushita MN10300 series |
| Copyright (C) 1996, 1997, 1998, 1999, 2000 Free Software Foundation, Inc. |
| Contributed by Jeff Law (law@cygnus.com). |
| |
| 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. */ |
| |
| #include "config.h" |
| #include "system.h" |
| #include "rtl.h" |
| #include "tree.h" |
| #include "regs.h" |
| #include "hard-reg-set.h" |
| #include "real.h" |
| #include "insn-config.h" |
| #include "conditions.h" |
| #include "output.h" |
| #include "insn-attr.h" |
| #include "flags.h" |
| #include "recog.h" |
| #include "expr.h" |
| #include "function.h" |
| #include "obstack.h" |
| #include "toplev.h" |
| #include "tm_p.h" |
| |
| /* The size of the callee register save area. Right now we save everything |
| on entry since it costs us nothing in code size. It does cost us from a |
| speed standpoint, so we want to optimize this sooner or later. */ |
| #define REG_SAVE_BYTES (4 * regs_ever_live[2] \ |
| + 4 * regs_ever_live[3] \ |
| + 4 * regs_ever_live[6] \ |
| + 4 * regs_ever_live[7] \ |
| + 16 * (regs_ever_live[14] || regs_ever_live[15] \ |
| || regs_ever_live[16] || regs_ever_live[17])) |
| |
| void |
| asm_file_start (file) |
| FILE *file; |
| { |
| fprintf (file, "#\tGCC For the Matsushita MN10300\n"); |
| if (optimize) |
| fprintf (file, "# -O%d\n", optimize); |
| else |
| fprintf (file, "\n\n"); |
| |
| if (TARGET_AM33) |
| fprintf (file, "\t.am33\n"); |
| output_file_directive (file, main_input_filename); |
| } |
| |
| |
| /* Print operand X using operand code CODE to assembly language output file |
| FILE. */ |
| |
| void |
| print_operand (file, x, code) |
| FILE *file; |
| rtx x; |
| int code; |
| { |
| switch (code) |
| { |
| case 'b': |
| case 'B': |
| /* These are normal and reversed branches. */ |
| switch (code == 'b' ? GET_CODE (x) : reverse_condition (GET_CODE (x))) |
| { |
| case NE: |
| fprintf (file, "ne"); |
| break; |
| case EQ: |
| fprintf (file, "eq"); |
| break; |
| case GE: |
| fprintf (file, "ge"); |
| break; |
| case GT: |
| fprintf (file, "gt"); |
| break; |
| case LE: |
| fprintf (file, "le"); |
| break; |
| case LT: |
| fprintf (file, "lt"); |
| break; |
| case GEU: |
| fprintf (file, "cc"); |
| break; |
| case GTU: |
| fprintf (file, "hi"); |
| break; |
| case LEU: |
| fprintf (file, "ls"); |
| break; |
| case LTU: |
| fprintf (file, "cs"); |
| break; |
| default: |
| abort (); |
| } |
| break; |
| case 'C': |
| /* This is used for the operand to a call instruction; |
| if it's a REG, enclose it in parens, else output |
| the operand normally. */ |
| if (GET_CODE (x) == REG) |
| { |
| fputc ('(', file); |
| print_operand (file, x, 0); |
| fputc (')', file); |
| } |
| else |
| print_operand (file, x, 0); |
| break; |
| |
| /* These are the least significant word in a 64bit value. */ |
| case 'L': |
| switch (GET_CODE (x)) |
| { |
| case MEM: |
| fputc ('(', file); |
| output_address (XEXP (x, 0)); |
| fputc (')', file); |
| break; |
| |
| case REG: |
| fprintf (file, "%s", reg_names[REGNO (x)]); |
| break; |
| |
| case SUBREG: |
| fprintf (file, "%s", reg_names[subreg_regno (x)]); |
| break; |
| |
| case CONST_DOUBLE: |
| { |
| long val[2]; |
| REAL_VALUE_TYPE rv; |
| |
| switch (GET_MODE (x)) |
| { |
| case DFmode: |
| REAL_VALUE_FROM_CONST_DOUBLE (rv, x); |
| REAL_VALUE_TO_TARGET_DOUBLE (rv, val); |
| fprintf (file, "0x%lx", val[0]); |
| break;; |
| case SFmode: |
| REAL_VALUE_FROM_CONST_DOUBLE (rv, x); |
| REAL_VALUE_TO_TARGET_SINGLE (rv, val[0]); |
| fprintf (file, "0x%lx", val[0]); |
| break;; |
| case VOIDmode: |
| case DImode: |
| print_operand_address (file, |
| GEN_INT (CONST_DOUBLE_LOW (x))); |
| break; |
| default: |
| break; |
| } |
| break; |
| } |
| |
| case CONST_INT: |
| { |
| rtx low, high; |
| split_double (x, &low, &high); |
| fprintf (file, "%ld", (long)INTVAL (low)); |
| break; |
| } |
| |
| default: |
| abort (); |
| } |
| break; |
| |
| /* Similarly, but for the most significant word. */ |
| case 'H': |
| switch (GET_CODE (x)) |
| { |
| case MEM: |
| fputc ('(', file); |
| x = adj_offsettable_operand (x, 4); |
| output_address (XEXP (x, 0)); |
| fputc (')', file); |
| break; |
| |
| case REG: |
| fprintf (file, "%s", reg_names[REGNO (x) + 1]); |
| break; |
| |
| case SUBREG: |
| fprintf (file, "%s", reg_names[subreg_regno (x) + 1]); |
| break; |
| |
| case CONST_DOUBLE: |
| { |
| long val[2]; |
| REAL_VALUE_TYPE rv; |
| |
| switch (GET_MODE (x)) |
| { |
| case DFmode: |
| REAL_VALUE_FROM_CONST_DOUBLE (rv, x); |
| REAL_VALUE_TO_TARGET_DOUBLE (rv, val); |
| fprintf (file, "0x%lx", val[1]); |
| break;; |
| case SFmode: |
| abort (); |
| case VOIDmode: |
| case DImode: |
| print_operand_address (file, |
| GEN_INT (CONST_DOUBLE_HIGH (x))); |
| break; |
| default: |
| break; |
| } |
| break; |
| } |
| |
| case CONST_INT: |
| { |
| rtx low, high; |
| split_double (x, &low, &high); |
| fprintf (file, "%ld", (long)INTVAL (high)); |
| break; |
| } |
| |
| default: |
| abort (); |
| } |
| break; |
| |
| case 'A': |
| fputc ('(', file); |
| if (GET_CODE (XEXP (x, 0)) == REG) |
| output_address (gen_rtx_PLUS (SImode, XEXP (x, 0), GEN_INT (0))); |
| else |
| output_address (XEXP (x, 0)); |
| fputc (')', file); |
| break; |
| |
| case 'N': |
| output_address (GEN_INT ((~INTVAL (x)) & 0xff)); |
| break; |
| |
| /* For shift counts. The hardware ignores the upper bits of |
| any immediate, but the assembler will flag an out of range |
| shift count as an error. So we mask off the high bits |
| of the immediate here. */ |
| case 'S': |
| if (GET_CODE (x) == CONST_INT) |
| { |
| fprintf (file, "%d", INTVAL (x) & 0x1f); |
| break; |
| } |
| /* FALL THROUGH */ |
| |
| default: |
| switch (GET_CODE (x)) |
| { |
| case MEM: |
| fputc ('(', file); |
| output_address (XEXP (x, 0)); |
| fputc (')', file); |
| break; |
| |
| case PLUS: |
| output_address (x); |
| break; |
| |
| case REG: |
| fprintf (file, "%s", reg_names[REGNO (x)]); |
| break; |
| |
| case SUBREG: |
| fprintf (file, "%s", reg_names[subreg_regno (x)]); |
| break; |
| |
| /* This will only be single precision.... */ |
| case CONST_DOUBLE: |
| { |
| unsigned long val; |
| REAL_VALUE_TYPE rv; |
| |
| REAL_VALUE_FROM_CONST_DOUBLE (rv, x); |
| REAL_VALUE_TO_TARGET_SINGLE (rv, val); |
| fprintf (file, "0x%lx", val); |
| break; |
| } |
| |
| case CONST_INT: |
| case SYMBOL_REF: |
| case CONST: |
| case LABEL_REF: |
| case CODE_LABEL: |
| print_operand_address (file, x); |
| break; |
| default: |
| abort (); |
| } |
| break; |
| } |
| } |
| |
| /* Output assembly language output for the address ADDR to FILE. */ |
| |
| void |
| print_operand_address (file, addr) |
| FILE *file; |
| rtx addr; |
| { |
| switch (GET_CODE (addr)) |
| { |
| case POST_INC: |
| print_operand_address (file, XEXP (addr, 0)); |
| fputc ('+', file); |
| break; |
| case REG: |
| print_operand (file, addr, 0); |
| break; |
| case PLUS: |
| { |
| rtx base, index; |
| if (REG_P (XEXP (addr, 0)) |
| && REG_OK_FOR_BASE_P (XEXP (addr, 0))) |
| base = XEXP (addr, 0), index = XEXP (addr, 1); |
| else if (REG_P (XEXP (addr, 1)) |
| && REG_OK_FOR_BASE_P (XEXP (addr, 1))) |
| base = XEXP (addr, 1), index = XEXP (addr, 0); |
| else |
| abort (); |
| print_operand (file, index, 0); |
| fputc (',', file); |
| print_operand (file, base, 0);; |
| break; |
| } |
| case SYMBOL_REF: |
| output_addr_const (file, addr); |
| break; |
| default: |
| output_addr_const (file, addr); |
| break; |
| } |
| } |
| |
| /* Print a set of registers in the format required by "movm" and "ret". |
| Register K is saved if bit K of MASK is set. The data and address |
| registers can be stored individually, but the extended registers cannot. |
| We assume that the mask alread takes that into account. For instance, |
| bits 14 to 17 must have the same value. */ |
| |
| void |
| mn10300_print_reg_list (file, mask) |
| FILE *file; |
| int mask; |
| { |
| int need_comma; |
| int i; |
| |
| need_comma = 0; |
| fputc ('[', file); |
| |
| for (i = 0; i < FIRST_EXTENDED_REGNUM; i++) |
| if ((mask & (1 << i)) != 0) |
| { |
| if (need_comma) |
| fputc (',', file); |
| fputs (reg_names [i], file); |
| need_comma = 1; |
| } |
| |
| if ((mask & 0x3c000) != 0) |
| { |
| if ((mask & 0x3c000) != 0x3c000) |
| abort(); |
| if (need_comma) |
| fputc (',', file); |
| fputs ("exreg1", file); |
| need_comma = 1; |
| } |
| |
| fputc (']', file); |
| } |
| |
| int |
| can_use_return_insn () |
| { |
| /* size includes the fixed stack space needed for function calls. */ |
| int size = get_frame_size () + current_function_outgoing_args_size; |
| |
| /* And space for the return pointer. */ |
| size += current_function_outgoing_args_size ? 4 : 0; |
| |
| return (reload_completed |
| && size == 0 |
| && !regs_ever_live[2] |
| && !regs_ever_live[3] |
| && !regs_ever_live[6] |
| && !regs_ever_live[7] |
| && !regs_ever_live[14] |
| && !regs_ever_live[15] |
| && !regs_ever_live[16] |
| && !regs_ever_live[17] |
| && !frame_pointer_needed); |
| } |
| |
| /* Returns the set of live, callee-saved registers as a bitmask. The |
| callee-saved extended registers cannot be stored individually, so |
| all of them will be included in the mask if any one of them is used. */ |
| |
| int |
| mn10300_get_live_callee_saved_regs () |
| { |
| int mask; |
| int i; |
| |
| mask = 0; |
| for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) |
| if (regs_ever_live[i] && ! call_used_regs[i]) |
| mask |= (1 << i); |
| if ((mask & 0x3c000) != 0) |
| mask |= 0x3c000; |
| |
| return mask; |
| } |
| |
| /* Generate an instruction that pushes several registers onto the stack. |
| Register K will be saved if bit K in MASK is set. The function does |
| nothing if MASK is zero. |
| |
| To be compatible with the "movm" instruction, the lowest-numbered |
| register must be stored in the lowest slot. If MASK is the set |
| { R1,...,RN }, where R1...RN are ordered least first, the generated |
| instruction will have the form: |
| |
| (parallel |
| (set (reg:SI 9) (plus:SI (reg:SI 9) (const_int -N*4))) |
| (set (mem:SI (plus:SI (reg:SI 9) |
| (const_int -1*4))) |
| (reg:SI RN)) |
| ... |
| (set (mem:SI (plus:SI (reg:SI 9) |
| (const_int -N*4))) |
| (reg:SI R1))) */ |
| |
| void |
| mn10300_gen_multiple_store (mask) |
| int mask; |
| { |
| if (mask != 0) |
| { |
| int i; |
| int count; |
| rtx par; |
| int pari; |
| |
| /* Count how many registers need to be saved. */ |
| count = 0; |
| for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) |
| if ((mask & (1 << i)) != 0) |
| count += 1; |
| |
| /* We need one PARALLEL element to update the stack pointer and |
| an additional element for each register that is stored. */ |
| par = gen_rtx_PARALLEL (VOIDmode, rtvec_alloc (count + 1)); |
| |
| /* Create the instruction that updates the stack pointer. */ |
| XVECEXP (par, 0, 0) |
| = gen_rtx_SET (SImode, |
| stack_pointer_rtx, |
| gen_rtx_PLUS (SImode, |
| stack_pointer_rtx, |
| GEN_INT (-count * 4))); |
| |
| /* Create each store. */ |
| pari = 1; |
| for (i = FIRST_PSEUDO_REGISTER - 1; i >= 0; i--) |
| if ((mask & (1 << i)) != 0) |
| { |
| rtx address = gen_rtx_PLUS (SImode, |
| stack_pointer_rtx, |
| GEN_INT (-pari * 4)); |
| XVECEXP(par, 0, pari) |
| = gen_rtx_SET (VOIDmode, |
| gen_rtx_MEM (SImode, address), |
| gen_rtx_REG (SImode, i)); |
| pari += 1; |
| } |
| |
| par = emit_insn (par); |
| RTX_FRAME_RELATED_P (par) = 1; |
| } |
| } |
| |
| void |
| expand_prologue () |
| { |
| HOST_WIDE_INT size; |
| |
| /* SIZE includes the fixed stack space needed for function calls. */ |
| size = get_frame_size () + current_function_outgoing_args_size; |
| size += (current_function_outgoing_args_size ? 4 : 0); |
| |
| /* If this is an old-style varargs function, then its arguments |
| need to be flushed back to the stack. */ |
| if (current_function_varargs) |
| { |
| emit_move_insn (gen_rtx_MEM (SImode, |
| plus_constant (stack_pointer_rtx, 4)), |
| gen_rtx_REG (SImode, 0)); |
| emit_move_insn (gen_rtx_MEM (SImode, |
| plus_constant (stack_pointer_rtx, 8)), |
| gen_rtx_REG (SImode, 1)); |
| } |
| |
| /* If we use any of the callee-saved registers, save them now. */ |
| mn10300_gen_multiple_store (mn10300_get_live_callee_saved_regs ()); |
| |
| /* Now put the frame pointer into the frame pointer register. */ |
| if (frame_pointer_needed) |
| emit_move_insn (frame_pointer_rtx, stack_pointer_rtx); |
| |
| /* Allocate stack for this frame. */ |
| if (size) |
| emit_insn (gen_addsi3 (stack_pointer_rtx, |
| stack_pointer_rtx, |
| GEN_INT (-size))); |
| } |
| |
| void |
| expand_epilogue () |
| { |
| HOST_WIDE_INT size; |
| |
| /* SIZE includes the fixed stack space needed for function calls. */ |
| size = get_frame_size () + current_function_outgoing_args_size; |
| size += (current_function_outgoing_args_size ? 4 : 0); |
| |
| /* Maybe cut back the stack, except for the register save area. |
| |
| If the frame pointer exists, then use the frame pointer to |
| cut back the stack. |
| |
| If the stack size + register save area is more than 255 bytes, |
| then the stack must be cut back here since the size + register |
| save size is too big for a ret/retf instruction. |
| |
| Else leave it alone, it will be cut back as part of the |
| ret/retf instruction, or there wasn't any stack to begin with. |
| |
| Under no circumstanes should the register save area be |
| deallocated here, that would leave a window where an interrupt |
| could occur and trash the register save area. */ |
| if (frame_pointer_needed) |
| { |
| emit_move_insn (stack_pointer_rtx, frame_pointer_rtx); |
| size = 0; |
| } |
| else if (size + REG_SAVE_BYTES > 255) |
| { |
| emit_insn (gen_addsi3 (stack_pointer_rtx, |
| stack_pointer_rtx, |
| GEN_INT (size))); |
| size = 0; |
| } |
| |
| /* Adjust the stack and restore callee-saved registers, if any. */ |
| if (size || regs_ever_live[2] || regs_ever_live[3] |
| || regs_ever_live[6] || regs_ever_live[7] |
| || regs_ever_live[14] || regs_ever_live[15] |
| || regs_ever_live[16] || regs_ever_live[17] |
| || frame_pointer_needed) |
| emit_jump_insn (gen_return_internal_regs |
| (GEN_INT (size + REG_SAVE_BYTES))); |
| else |
| emit_jump_insn (gen_return_internal ()); |
| } |
| |
| /* Update the condition code from the insn. */ |
| |
| void |
| notice_update_cc (body, insn) |
| rtx body; |
| rtx insn; |
| { |
| switch (get_attr_cc (insn)) |
| { |
| case CC_NONE: |
| /* Insn does not affect CC at all. */ |
| break; |
| |
| case CC_NONE_0HIT: |
| /* Insn does not change CC, but the 0'th operand has been changed. */ |
| if (cc_status.value1 != 0 |
| && reg_overlap_mentioned_p (recog_data.operand[0], cc_status.value1)) |
| cc_status.value1 = 0; |
| break; |
| |
| case CC_SET_ZN: |
| /* Insn sets the Z,N flags of CC to recog_data.operand[0]. |
| V,C are unusable. */ |
| CC_STATUS_INIT; |
| cc_status.flags |= CC_NO_CARRY | CC_OVERFLOW_UNUSABLE; |
| cc_status.value1 = recog_data.operand[0]; |
| break; |
| |
| case CC_SET_ZNV: |
| /* Insn sets the Z,N,V flags of CC to recog_data.operand[0]. |
| C is unusable. */ |
| CC_STATUS_INIT; |
| cc_status.flags |= CC_NO_CARRY; |
| cc_status.value1 = recog_data.operand[0]; |
| break; |
| |
| case CC_COMPARE: |
| /* The insn is a compare instruction. */ |
| CC_STATUS_INIT; |
| cc_status.value1 = SET_SRC (body); |
| break; |
| |
| case CC_INVERT: |
| /* The insn is a compare instruction. */ |
| CC_STATUS_INIT; |
| cc_status.value1 = SET_SRC (body); |
| cc_status.flags |= CC_INVERTED; |
| break; |
| |
| case CC_CLOBBER: |
| /* Insn doesn't leave CC in a usable state. */ |
| CC_STATUS_INIT; |
| break; |
| |
| default: |
| abort (); |
| } |
| } |
| |
| /* Recognise the PARALLEL rtx generated by mn10300_gen_multiple_store(). |
| This function is for MATCH_PARALLEL and so assumes OP is known to be |
| parallel. If OP is a multiple store, return a mask indicating which |
| registers it saves. Return 0 otherwise. */ |
| |
| int |
| store_multiple_operation (op, mode) |
| rtx op; |
| enum machine_mode mode ATTRIBUTE_UNUSED; |
| { |
| int count; |
| int mask; |
| int i; |
| unsigned int last; |
| rtx elt; |
| |
| count = XVECLEN (op, 0); |
| if (count < 2) |
| return 0; |
| |
| /* Check that first instruction has the form (set (sp) (plus A B)) */ |
| elt = XVECEXP (op, 0, 0); |
| if (GET_CODE (elt) != SET |
| || GET_CODE (SET_DEST (elt)) != REG |
| || REGNO (SET_DEST (elt)) != STACK_POINTER_REGNUM |
| || GET_CODE (SET_SRC (elt)) != PLUS) |
| return 0; |
| |
| /* Check that A is the stack pointer and B is the expected stack size. |
| For OP to match, each subsequent instruction should push a word onto |
| the stack. We therefore expect the first instruction to create |
| COUNT-1 stack slots. */ |
| elt = SET_SRC (elt); |
| if (GET_CODE (XEXP (elt, 0)) != REG |
| || REGNO (XEXP (elt, 0)) != STACK_POINTER_REGNUM |
| || GET_CODE (XEXP (elt, 1)) != CONST_INT |
| || INTVAL (XEXP (elt, 1)) != -(count - 1) * 4) |
| return 0; |
| |
| /* Now go through the rest of the vector elements. They must be |
| ordered so that the first instruction stores the highest-numbered |
| register to the highest stack slot and that subsequent instructions |
| store a lower-numbered register to the slot below. |
| |
| LAST keeps track of the smallest-numbered register stored so far. |
| MASK is the set of stored registers. */ |
| last = FIRST_PSEUDO_REGISTER; |
| mask = 0; |
| for (i = 1; i < count; i++) |
| { |
| /* Check that element i is a (set (mem M) R) and that R is valid. */ |
| elt = XVECEXP (op, 0, i); |
| if (GET_CODE (elt) != SET |
| || GET_CODE (SET_DEST (elt)) != MEM |
| || GET_CODE (SET_SRC (elt)) != REG |
| || REGNO (SET_SRC (elt)) >= last) |
| return 0; |
| |
| /* R was OK, so provisionally add it to MASK. We return 0 in any |
| case if the rest of the instruction has a flaw. */ |
| last = REGNO (SET_SRC (elt)); |
| mask |= (1 << last); |
| |
| /* Check that M has the form (plus (sp) (const_int -I*4)) */ |
| elt = XEXP (SET_DEST (elt), 0); |
| if (GET_CODE (elt) != PLUS |
| || GET_CODE (XEXP (elt, 0)) != REG |
| || REGNO (XEXP (elt, 0)) != STACK_POINTER_REGNUM |
| || GET_CODE (XEXP (elt, 1)) != CONST_INT |
| || INTVAL (XEXP (elt, 1)) != -i * 4) |
| return 0; |
| } |
| |
| /* All or none of the callee-saved extended registers must be in the set. */ |
| if ((mask & 0x3c000) != 0 |
| && (mask & 0x3c000) != 0x3c000) |
| return 0; |
| |
| return mask; |
| } |
| |
| /* Return true if OP is a valid call operand. */ |
| |
| int |
| call_address_operand (op, mode) |
| rtx op; |
| enum machine_mode mode ATTRIBUTE_UNUSED; |
| { |
| return (GET_CODE (op) == SYMBOL_REF || GET_CODE (op) == REG); |
| } |
| |
| /* What (if any) secondary registers are needed to move IN with mode |
| MODE into a register in register class CLASS. |
| |
| We might be able to simplify this. */ |
| enum reg_class |
| secondary_reload_class (class, mode, in) |
| enum reg_class class; |
| enum machine_mode mode; |
| rtx in; |
| { |
| /* Memory loads less than a full word wide can't have an |
| address or stack pointer destination. They must use |
| a data register as an intermediate register. */ |
| if ((GET_CODE (in) == MEM |
| || (GET_CODE (in) == REG |
| && REGNO (in) >= FIRST_PSEUDO_REGISTER) |
| || (GET_CODE (in) == SUBREG |
| && GET_CODE (SUBREG_REG (in)) == REG |
| && REGNO (SUBREG_REG (in)) >= FIRST_PSEUDO_REGISTER)) |
| && (mode == QImode || mode == HImode) |
| && (class == ADDRESS_REGS || class == SP_REGS |
| || class == SP_OR_ADDRESS_REGS)) |
| { |
| if (TARGET_AM33) |
| return DATA_OR_EXTENDED_REGS; |
| return DATA_REGS; |
| } |
| |
| /* We can't directly load sp + const_int into a data register; |
| we must use an address register as an intermediate. */ |
| if (class != SP_REGS |
| && class != ADDRESS_REGS |
| && class != SP_OR_ADDRESS_REGS |
| && class != SP_OR_EXTENDED_REGS |
| && class != ADDRESS_OR_EXTENDED_REGS |
| && class != SP_OR_ADDRESS_OR_EXTENDED_REGS |
| && (in == stack_pointer_rtx |
| || (GET_CODE (in) == PLUS |
| && (XEXP (in, 0) == stack_pointer_rtx |
| || XEXP (in, 1) == stack_pointer_rtx)))) |
| return ADDRESS_REGS; |
| |
| if (GET_CODE (in) == PLUS |
| && (XEXP (in, 0) == stack_pointer_rtx |
| || XEXP (in, 1) == stack_pointer_rtx)) |
| { |
| if (TARGET_AM33) |
| return DATA_OR_EXTENDED_REGS; |
| return DATA_REGS; |
| } |
| |
| /* Otherwise assume no secondary reloads are needed. */ |
| return NO_REGS; |
| } |
| |
| int |
| initial_offset (from, to) |
| int from, to; |
| { |
| /* The difference between the argument pointer and the frame pointer |
| is the size of the callee register save area. */ |
| if (from == ARG_POINTER_REGNUM && to == FRAME_POINTER_REGNUM) |
| { |
| if (regs_ever_live[2] || regs_ever_live[3] |
| || regs_ever_live[6] || regs_ever_live[7] |
| || regs_ever_live[14] || regs_ever_live[15] |
| || regs_ever_live[16] || regs_ever_live[17] |
| || frame_pointer_needed) |
| return REG_SAVE_BYTES; |
| else |
| return 0; |
| } |
| |
| /* The difference between the argument pointer and the stack pointer is |
| the sum of the size of this function's frame, the callee register save |
| area, and the fixed stack space needed for function calls (if any). */ |
| if (from == ARG_POINTER_REGNUM && to == STACK_POINTER_REGNUM) |
| { |
| if (regs_ever_live[2] || regs_ever_live[3] |
| || regs_ever_live[6] || regs_ever_live[7] |
| || regs_ever_live[14] || regs_ever_live[15] |
| || regs_ever_live[16] || regs_ever_live[17] |
| || frame_pointer_needed) |
| return (get_frame_size () + REG_SAVE_BYTES |
| + (current_function_outgoing_args_size |
| ? current_function_outgoing_args_size + 4 : 0)); |
| else |
| return (get_frame_size () |
| + (current_function_outgoing_args_size |
| ? current_function_outgoing_args_size + 4 : 0)); |
| } |
| |
| /* The difference between the frame pointer and stack pointer is the sum |
| of the size of this function's frame and the fixed stack space needed |
| for function calls (if any). */ |
| if (from == FRAME_POINTER_REGNUM && to == STACK_POINTER_REGNUM) |
| return (get_frame_size () |
| + (current_function_outgoing_args_size |
| ? current_function_outgoing_args_size + 4 : 0)); |
| |
| abort (); |
| } |
| |
| /* Flush the argument registers to the stack for a stdarg function; |
| return the new argument pointer. */ |
| rtx |
| mn10300_builtin_saveregs () |
| { |
| rtx offset, mem; |
| tree fntype = TREE_TYPE (current_function_decl); |
| int argadj = ((!(TYPE_ARG_TYPES (fntype) != 0 |
| && (TREE_VALUE (tree_last (TYPE_ARG_TYPES (fntype))) |
| != void_type_node))) |
| ? UNITS_PER_WORD : 0); |
| int set = get_varargs_alias_set (); |
| |
| if (argadj) |
| offset = plus_constant (current_function_arg_offset_rtx, argadj); |
| else |
| offset = current_function_arg_offset_rtx; |
| |
| mem = gen_rtx_MEM (SImode, current_function_internal_arg_pointer); |
| MEM_ALIAS_SET (mem) = set; |
| emit_move_insn (mem, gen_rtx_REG (SImode, 0)); |
| |
| mem = gen_rtx_MEM (SImode, |
| plus_constant (current_function_internal_arg_pointer, 4)); |
| MEM_ALIAS_SET (mem) = set; |
| emit_move_insn (mem, gen_rtx_REG (SImode, 1)); |
| |
| return copy_to_reg (expand_binop (Pmode, add_optab, |
| current_function_internal_arg_pointer, |
| offset, 0, 0, OPTAB_LIB_WIDEN)); |
| } |
| |
| void |
| mn10300_va_start (stdarg_p, valist, nextarg) |
| int stdarg_p; |
| tree valist; |
| rtx nextarg; |
| { |
| if (stdarg_p) |
| nextarg = expand_builtin_saveregs (); |
| |
| std_expand_builtin_va_start (stdarg_p, valist, nextarg); |
| } |
| |
| rtx |
| mn10300_va_arg (valist, type) |
| tree valist, type; |
| { |
| HOST_WIDE_INT align, rsize; |
| tree t, ptr, pptr; |
| |
| /* Compute the rounded size of the type. */ |
| align = PARM_BOUNDARY / BITS_PER_UNIT; |
| rsize = (((int_size_in_bytes (type) + align - 1) / align) * align); |
| |
| t = build (POSTINCREMENT_EXPR, TREE_TYPE (valist), valist, |
| build_int_2 ((rsize > 8 ? 4 : rsize), 0)); |
| TREE_SIDE_EFFECTS (t) = 1; |
| |
| ptr = build_pointer_type (type); |
| |
| /* "Large" types are passed by reference. */ |
| if (rsize > 8) |
| { |
| pptr = build_pointer_type (ptr); |
| t = build1 (NOP_EXPR, pptr, t); |
| TREE_SIDE_EFFECTS (t) = 1; |
| |
| t = build1 (INDIRECT_REF, ptr, t); |
| TREE_SIDE_EFFECTS (t) = 1; |
| } |
| else |
| { |
| t = build1 (NOP_EXPR, ptr, t); |
| TREE_SIDE_EFFECTS (t) = 1; |
| } |
| |
| /* Calculate! */ |
| return expand_expr (t, NULL_RTX, Pmode, EXPAND_NORMAL); |
| } |
| |
| /* Return an RTX to represent where a value with mode MODE will be returned |
| from a function. If the result is 0, the argument is pushed. */ |
| |
| rtx |
| function_arg (cum, mode, type, named) |
| CUMULATIVE_ARGS *cum; |
| enum machine_mode mode; |
| tree type; |
| int named ATTRIBUTE_UNUSED; |
| { |
| rtx result = 0; |
| int size, align; |
| |
| /* We only support using 2 data registers as argument registers. */ |
| int nregs = 2; |
| |
| /* Figure out the size of the object to be passed. */ |
| if (mode == BLKmode) |
| size = int_size_in_bytes (type); |
| else |
| size = GET_MODE_SIZE (mode); |
| |
| /* Figure out the alignment of the object to be passed. */ |
| align = size; |
| |
| cum->nbytes = (cum->nbytes + 3) & ~3; |
| |
| /* Don't pass this arg via a register if all the argument registers |
| are used up. */ |
| if (cum->nbytes > nregs * UNITS_PER_WORD) |
| return 0; |
| |
| /* Don't pass this arg via a register if it would be split between |
| registers and memory. */ |
| if (type == NULL_TREE |
| && cum->nbytes + size > nregs * UNITS_PER_WORD) |
| return 0; |
| |
| switch (cum->nbytes / UNITS_PER_WORD) |
| { |
| case 0: |
| result = gen_rtx_REG (mode, 0); |
| break; |
| case 1: |
| result = gen_rtx_REG (mode, 1); |
| break; |
| default: |
| result = 0; |
| } |
| |
| return result; |
| } |
| |
| /* Return the number of registers to use for an argument passed partially |
| in registers and partially in memory. */ |
| |
| int |
| function_arg_partial_nregs (cum, mode, type, named) |
| CUMULATIVE_ARGS *cum; |
| enum machine_mode mode; |
| tree type; |
| int named ATTRIBUTE_UNUSED; |
| { |
| int size, align; |
| |
| /* We only support using 2 data registers as argument registers. */ |
| int nregs = 2; |
| |
| /* Figure out the size of the object to be passed. */ |
| if (mode == BLKmode) |
| size = int_size_in_bytes (type); |
| else |
| size = GET_MODE_SIZE (mode); |
| |
| /* Figure out the alignment of the object to be passed. */ |
| align = size; |
| |
| cum->nbytes = (cum->nbytes + 3) & ~3; |
| |
| /* Don't pass this arg via a register if all the argument registers |
| are used up. */ |
| if (cum->nbytes > nregs * UNITS_PER_WORD) |
| return 0; |
| |
| if (cum->nbytes + size <= nregs * UNITS_PER_WORD) |
| return 0; |
| |
| /* Don't pass this arg via a register if it would be split between |
| registers and memory. */ |
| if (type == NULL_TREE |
| && cum->nbytes + size > nregs * UNITS_PER_WORD) |
| return 0; |
| |
| return (nregs * UNITS_PER_WORD - cum->nbytes) / UNITS_PER_WORD; |
| } |
| |
| /* Output a tst insn. */ |
| char * |
| output_tst (operand, insn) |
| rtx operand, insn; |
| { |
| rtx temp; |
| int past_call = 0; |
| |
| /* We can save a byte if we can find a register which has the value |
| zero in it. */ |
| temp = PREV_INSN (insn); |
| while (optimize && temp) |
| { |
| rtx set; |
| |
| /* We allow the search to go through call insns. We record |
| the fact that we've past a CALL_INSN and reject matches which |
| use call clobbered registers. */ |
| if (GET_CODE (temp) == CODE_LABEL |
| || GET_CODE (temp) == JUMP_INSN |
| || GET_CODE (temp) == BARRIER) |
| break; |
| |
| if (GET_CODE (temp) == CALL_INSN) |
| past_call = 1; |
| |
| if (GET_CODE (temp) == NOTE) |
| { |
| temp = PREV_INSN (temp); |
| continue; |
| } |
| |
| /* It must be an insn, see if it is a simple set. */ |
| set = single_set (temp); |
| if (!set) |
| { |
| temp = PREV_INSN (temp); |
| continue; |
| } |
| |
| /* Are we setting a data register to zero (this does not win for |
| address registers)? |
| |
| If it's a call clobbered register, have we past a call? |
| |
| Make sure the register we find isn't the same as ourself; |
| the mn10300 can't encode that. |
| |
| ??? reg_set_between_p return nonzero anytime we pass a CALL_INSN |
| so the code to detect calls here isn't doing anything useful. */ |
| if (REG_P (SET_DEST (set)) |
| && SET_SRC (set) == CONST0_RTX (GET_MODE (SET_DEST (set))) |
| && !reg_set_between_p (SET_DEST (set), temp, insn) |
| && (REGNO_REG_CLASS (REGNO (SET_DEST (set))) |
| == REGNO_REG_CLASS (REGNO (operand))) |
| && REGNO_REG_CLASS (REGNO (SET_DEST (set))) != EXTENDED_REGS |
| && REGNO (SET_DEST (set)) != REGNO (operand) |
| && (!past_call |
| || !call_used_regs[REGNO (SET_DEST (set))])) |
| { |
| rtx xoperands[2]; |
| xoperands[0] = operand; |
| xoperands[1] = SET_DEST (set); |
| |
| output_asm_insn ("cmp %1,%0", xoperands); |
| return ""; |
| } |
| |
| if (REGNO_REG_CLASS (REGNO (operand)) == EXTENDED_REGS |
| && REG_P (SET_DEST (set)) |
| && SET_SRC (set) == CONST0_RTX (GET_MODE (SET_DEST (set))) |
| && !reg_set_between_p (SET_DEST (set), temp, insn) |
| && (REGNO_REG_CLASS (REGNO (SET_DEST (set))) |
| != REGNO_REG_CLASS (REGNO (operand))) |
| && REGNO_REG_CLASS (REGNO (SET_DEST (set))) == EXTENDED_REGS |
| && REGNO (SET_DEST (set)) != REGNO (operand) |
| && (!past_call |
| || !call_used_regs[REGNO (SET_DEST (set))])) |
| { |
| rtx xoperands[2]; |
| xoperands[0] = operand; |
| xoperands[1] = SET_DEST (set); |
| |
| output_asm_insn ("cmp %1,%0", xoperands); |
| return ""; |
| } |
| temp = PREV_INSN (temp); |
| } |
| return "cmp 0,%0"; |
| } |
| |
| int |
| impossible_plus_operand (op, mode) |
| rtx op; |
| enum machine_mode mode ATTRIBUTE_UNUSED; |
| { |
| if (GET_CODE (op) != PLUS) |
| return 0; |
| |
| if (XEXP (op, 0) == stack_pointer_rtx |
| || XEXP (op, 1) == stack_pointer_rtx) |
| return 1; |
| |
| return 0; |
| } |
| |
| /* Return 1 if X is a CONST_INT that is only 8 bits wide. This is used |
| for the btst insn which may examine memory or a register (the memory |
| variant only allows an unsigned 8 bit integer). */ |
| int |
| const_8bit_operand (op, mode) |
| register rtx op; |
| enum machine_mode mode ATTRIBUTE_UNUSED; |
| { |
| return (GET_CODE (op) == CONST_INT |
| && INTVAL (op) >= 0 |
| && INTVAL (op) < 256); |
| } |
| |
| /* Similarly, but when using a zero_extract pattern for a btst where |
| the source operand might end up in memory. */ |
| int |
| mask_ok_for_mem_btst (len, bit) |
| int len; |
| int bit; |
| { |
| int mask = 0; |
| |
| while (len > 0) |
| { |
| mask |= (1 << bit); |
| bit++; |
| len--; |
| } |
| |
| /* MASK must bit into an 8bit value. */ |
| return (((mask & 0xff) == mask) |
| || ((mask & 0xff00) == mask) |
| || ((mask & 0xff0000) == mask) |
| || ((mask & 0xff000000) == mask)); |
| } |
| |
| /* Return 1 if X contains a symbolic expression. We know these |
| expressions will have one of a few well defined forms, so |
| we need only check those forms. */ |
| int |
| symbolic_operand (op, mode) |
| register rtx op; |
| enum machine_mode mode ATTRIBUTE_UNUSED; |
| { |
| switch (GET_CODE (op)) |
| { |
| case SYMBOL_REF: |
| case LABEL_REF: |
| return 1; |
| case CONST: |
| op = XEXP (op, 0); |
| return ((GET_CODE (XEXP (op, 0)) == SYMBOL_REF |
| || GET_CODE (XEXP (op, 0)) == LABEL_REF) |
| && GET_CODE (XEXP (op, 1)) == CONST_INT); |
| default: |
| return 0; |
| } |
| } |
| |
| /* Try machine dependent ways of modifying an illegitimate address |
| to be legitimate. If we find one, return the new valid address. |
| This macro is used in only one place: `memory_address' in explow.c. |
| |
| OLDX is the address as it was before break_out_memory_refs was called. |
| In some cases it is useful to look at this to decide what needs to be done. |
| |
| MODE and WIN are passed so that this macro can use |
| GO_IF_LEGITIMATE_ADDRESS. |
| |
| Normally it is always safe for this macro to do nothing. It exists to |
| recognize opportunities to optimize the output. |
| |
| But on a few ports with segmented architectures and indexed addressing |
| (mn10300, hppa) it is used to rewrite certain problematical addresses. */ |
| rtx |
| legitimize_address (x, oldx, mode) |
| rtx x; |
| rtx oldx ATTRIBUTE_UNUSED; |
| enum machine_mode mode ATTRIBUTE_UNUSED; |
| { |
| /* Uh-oh. We might have an address for x[n-100000]. This needs |
| special handling to avoid creating an indexed memory address |
| with x-100000 as the base. */ |
| if (GET_CODE (x) == PLUS |
| && symbolic_operand (XEXP (x, 1), VOIDmode)) |
| { |
| /* Ugly. We modify things here so that the address offset specified |
| by the index expression is computed first, then added to x to form |
| the entire address. */ |
| |
| rtx regx1, regy1, regy2, y; |
| |
| /* Strip off any CONST. */ |
| y = XEXP (x, 1); |
| if (GET_CODE (y) == CONST) |
| y = XEXP (y, 0); |
| |
| if (GET_CODE (y) == PLUS || GET_CODE (y) == MINUS) |
| { |
| regx1 = force_reg (Pmode, force_operand (XEXP (x, 0), 0)); |
| regy1 = force_reg (Pmode, force_operand (XEXP (y, 0), 0)); |
| regy2 = force_reg (Pmode, force_operand (XEXP (y, 1), 0)); |
| regx1 = force_reg (Pmode, |
| gen_rtx (GET_CODE (y), Pmode, regx1, regy2)); |
| return force_reg (Pmode, gen_rtx_PLUS (Pmode, regx1, regy1)); |
| } |
| } |
| return x; |
| } |
| |
| int |
| mn10300_address_cost (x, unsig) |
| rtx x; |
| int *unsig; |
| { |
| int _s = 0; |
| if (unsig == 0) |
| unsig = &_s; |
| |
| switch (GET_CODE (x)) |
| { |
| case REG: |
| switch (REGNO_REG_CLASS (REGNO (x))) |
| { |
| case SP_REGS: |
| *unsig = 1; |
| return 0; |
| |
| case ADDRESS_REGS: |
| return 1; |
| |
| case DATA_REGS: |
| case EXTENDED_REGS: |
| return 3; |
| |
| case NO_REGS: |
| return 5; |
| |
| default: |
| abort (); |
| } |
| |
| case PLUS: |
| case MINUS: |
| case ASHIFT: |
| case AND: |
| case IOR: |
| return (mn10300_address_cost (XEXP (x, 0), unsig) |
| + mn10300_address_cost (XEXP (x, 1), unsig)); |
| |
| case EXPR_LIST: |
| case SUBREG: |
| case MEM: |
| return ADDRESS_COST (XEXP (x, 0)); |
| |
| case ZERO_EXTEND: |
| *unsig = 1; |
| return mn10300_address_cost (XEXP (x, 0), unsig); |
| |
| case CONST_INT: |
| if (INTVAL (x) == 0) |
| return 0; |
| if (INTVAL (x) + (*unsig ? 0 : 0x80) < 0x100) |
| return 1; |
| if (INTVAL (x) + (*unsig ? 0 : 0x8000) < 0x10000) |
| return 3; |
| if (INTVAL (x) + (*unsig ? 0 : 0x800000) < 0x1000000) |
| return 5; |
| return 7; |
| |
| case CONST: |
| case SYMBOL_REF: |
| case LABEL_REF: |
| return 8; |
| |
| case ADDRESSOF: |
| switch (GET_CODE (XEXP (x, 0))) |
| { |
| case MEM: |
| return ADDRESS_COST (XEXP (x, 0)); |
| |
| case REG: |
| return 1; |
| |
| default: |
| abort (); |
| } |
| |
| default: |
| abort (); |
| |
| } |
| } |