diff options
Diffstat (limited to 'gcc/config/mn10300/mn10300.c')
-rwxr-xr-x | gcc/config/mn10300/mn10300.c | 1006 |
1 files changed, 1006 insertions, 0 deletions
diff --git a/gcc/config/mn10300/mn10300.c b/gcc/config/mn10300/mn10300.c new file mode 100755 index 0000000..325d8bd --- /dev/null +++ b/gcc/config/mn10300/mn10300.c @@ -0,0 +1,1006 @@ +/* Subroutines for insn-output.c for Matsushita MN10300 series + Copyright (C) 1996, 1997 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 <stdio.h> +#include "rtl.h" +#include "regs.h" +#include "hard-reg-set.h" +#include "real.h" +#include "insn-config.h" +#include "conditions.h" +#include "insn-flags.h" +#include "output.h" +#include "insn-attr.h" +#include "flags.h" +#include "recog.h" +#include "expr.h" +#include "tree.h" +#include "obstack.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]) + +#undef REG_SAVE_BYTES +#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[REGNO (SUBREG_REG (x)) + SUBREG_WORD (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); + print_operand_address (file, GEN_INT (val[0])); + break;; + case SFmode: + REAL_VALUE_FROM_CONST_DOUBLE (rv, x); + REAL_VALUE_TO_TARGET_SINGLE (rv, val[0]); + print_operand_address (file, GEN_INT (val[0])); + break;; + case VOIDmode: + case DImode: + print_operand_address (file, + GEN_INT (CONST_DOUBLE_LOW (x))); + break; + } + break; + } + + case CONST_INT: + print_operand_address (file, x); + 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[REGNO (SUBREG_REG (x)) + SUBREG_WORD (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); + print_operand_address (file, GEN_INT (val[1])); + break;; + case SFmode: + abort (); + case VOIDmode: + case DImode: + print_operand_address (file, + GEN_INT (CONST_DOUBLE_HIGH (x))); + break; + } + break; + } + + case CONST_INT: + if (INTVAL (x) < 0) + print_operand_address (file, GEN_INT (-1)); + else + print_operand_address (file, GEN_INT (0)); + 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[REGNO (SUBREG_REG (x)) + SUBREG_WORD (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); + print_operand_address (file, GEN_INT (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: + if (addr == stack_pointer_rtx) + print_operand_address (file, gen_rtx (PLUS, SImode, + stack_pointer_rtx, + GEN_INT (0))); + else + 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; + } +} + +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); +} + +void +expand_prologue () +{ + unsigned 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, + gen_rtx (PLUS, Pmode, stack_pointer_rtx, + GEN_INT (4))), + gen_rtx (REG, SImode, 0)); + emit_move_insn (gen_rtx (MEM, SImode, + gen_rtx (PLUS, Pmode, stack_pointer_rtx, + GEN_INT (8))), + gen_rtx (REG, SImode, 1)); + } + + /* And now store all the registers onto the stack with a + single two byte instruction. */ + 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) + emit_insn (gen_store_movm ()); + + /* 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 () +{ + unsigned 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 ((regs_ever_live[2] || regs_ever_live[3] + || regs_ever_live[14] || regs_ever_live[15] + || regs_ever_live[16] || regs_ever_live[17] + || regs_ever_live[6] || regs_ever_live[7]) + && size + REG_SAVE_BYTES > 255) + { + emit_insn (gen_addsi3 (stack_pointer_rtx, + stack_pointer_rtx, + GEN_INT (size))); + size = 0; + } + + /* For simplicity, we just movm all the callee saved registers to + the stack with one instruction. + + ?!? Only save registers which are actually used. Reduces + stack requirements and is faster. */ + 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) + emit_jump_insn (gen_return_internal_regs (GEN_INT (size + REG_SAVE_BYTES))); + else + { + if (size) + { + emit_insn (gen_addsi3 (stack_pointer_rtx, + stack_pointer_rtx, + GEN_INT (size))); + emit_jump_insn (gen_return_internal ()); + } + else + { + emit_jump_insn (gen_return ()); + } + } +} + +/* 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_operand[0], cc_status.value1)) + cc_status.value1 = 0; + break; + + case CC_SET_ZN: + /* Insn sets the Z,N flags of CC to recog_operand[0]. + V,C are unusable. */ + CC_STATUS_INIT; + cc_status.flags |= CC_NO_CARRY | CC_OVERFLOW_UNUSABLE; + cc_status.value1 = recog_operand[0]; + break; + + case CC_SET_ZNV: + /* Insn sets the Z,N,V flags of CC to recog_operand[0]. + C is unusable. */ + CC_STATUS_INIT; + cc_status.flags |= CC_NO_CARRY; + cc_status.value1 = recog_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 (); + } +} + +/* Return true if OP is a valid call operand. */ + +int +call_address_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + 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 from 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; +{ + int regno; + + /* 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 + && (mode == QImode || mode == HImode) + && (class == ADDRESS_REGS || class == SP_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 (arglist) + tree arglist; +{ + rtx offset; + 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); + + if (argadj) + offset = plus_constant (current_function_arg_offset_rtx, argadj); + else + offset = current_function_arg_offset_rtx; + + emit_move_insn (gen_rtx (MEM, SImode, current_function_internal_arg_pointer), + gen_rtx (REG, SImode, 0)); + emit_move_insn (gen_rtx (MEM, SImode, + plus_constant + (current_function_internal_arg_pointer, 4)), + 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)); +} + +/* 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; +{ + 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; +{ + 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; +{ + extern rtx *reg_equiv_mem; + rtx reg1, reg2; + + 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; +{ + 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; +{ + 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; + enum machine_mode mode; +{ + /* 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, regx2, 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; +} |