--- gcc/config/arm/arm.c 2018/04/24 18:16:13 1.1.1.2 +++ gcc/config/arm/arm.c 2018/04/24 18:22:26 1.1.1.3 @@ -1,5 +1,5 @@ /* Output routines for GCC for ARM/RISCiX. - Copyright (C) 1991, 1993 Free Software Foundation, Inc. + Copyright (C) 1991, 1993, 1994 Free Software Foundation, Inc. Contributed by Pieter `Tiggr' Schoenmakers (rcpieter@win.tue.nl) and Martin Simmons (@harleqn.co.uk). More major hacks by Richard Earnshaw (rwe11@cl.cam.ac.uk) @@ -21,6 +21,7 @@ along with GNU CC; see the file COPYING. the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ #include +#include #include "assert.h" #include "config.h" #include "rtl.h" @@ -34,6 +35,8 @@ the Free Software Foundation, 675 Mass A #include "insn-attr.h" #include "flags.h" #include "reload.h" +#include "tree.h" +#include "expr.h" /* The maximum number of insns skipped which will be conditionalised if possible. */ @@ -42,9 +45,11 @@ the Free Software Foundation, 675 Mass A /* Some function declarations. */ extern FILE *asm_out_file; extern char *output_multi_immediate (); -extern char *arm_output_asm_insn (); extern void arm_increase_location (); +HOST_WIDE_INT int_log2 PROTO ((HOST_WIDE_INT)); +static int get_prologue_size PROTO ((void)); + /* Define the information needed to generate branch insns. This is stored from the compare operation. */ @@ -52,13 +57,15 @@ rtx arm_compare_op0, arm_compare_op1; int arm_compare_fp; /* What type of cpu are we compiling for? */ - enum processor_type arm_cpu; +/* Waht type of floating point are we compiling for? */ +enum floating_point_type arm_fpu; + /* In case of a PRE_INC, POST_INC, PRE_DEC, POST_DEC memory reference, we must report the mode of the memory reference from PRINT_OPERAND to PRINT_OPERAND_ADDRESS. */ -int output_memory_reference_mode; +enum machine_mode output_memory_reference_mode; /* Nonzero if the prologue must setup `fp'. */ int current_function_anonymous_args; @@ -93,6 +100,15 @@ int arm_ccfsm_state; int arm_current_cc; rtx arm_target_insn; int arm_target_label; + +/* The condition codes of the ARM, and the inverse function. */ +char *arm_condition_codes[] = +{ + "eq", "ne", "cs", "cc", "mi", "pl", "vs", "vc", + "hi", "ls", "ge", "lt", "gt", "le", "al", "nv" +}; + +#define ARM_INVERSE_CONDITION_CODE(X) ((X) ^ 1) /* Return 1 if it is possible to return using a single instruction */ @@ -115,24 +131,6 @@ use_return_insn () return 1; } -/* Return the number of mov instructions needed to get the constant VALUE into - a register. */ - -int -arm_const_nmoves (value) - register int value; -{ - register int i; - - if (value == 0) - return (1); - for (i = 0; value; i++, value &= ~0xff) - while ((value & 3) == 0) - value = (value >> 2) | ((value & 3) << 30); - return (i); -} /* arm_const_nmoves */ - - /* Return TRUE if int I is a valid immediate ARM constant. */ int @@ -141,18 +139,750 @@ const_ok_for_arm (i) { unsigned HOST_WIDE_INT mask = ~0xFF; + /* Fast return for 0 and powers of 2 */ + if ((i & (i - 1)) == 0) + return TRUE; + do { if ((i & mask & (unsigned HOST_WIDE_INT) 0xffffffff) == 0) - return(TRUE); + return TRUE; mask = (mask << 2) | ((mask & (unsigned HOST_WIDE_INT) 0xffffffff) >> (32 - 2)) | ~((unsigned HOST_WIDE_INT) 0xffffffff); } while (mask != ~0xFF); - return (FALSE); -} /* const_ok_for_arm */ + return FALSE; +} + +/* Return true if I is a valid constant for the operation CODE. */ +int +const_ok_for_op (i, code, mode) + HOST_WIDE_INT i; + enum rtx_code code; + enum machine_mode mode; +{ + if (const_ok_for_arm (i)) + return 1; + + switch (code) + { + case PLUS: + return const_ok_for_arm (ARM_SIGN_EXTEND (-i)); + + case MINUS: /* Should only occur with (MINUS I reg) => rsb */ + case XOR: + case IOR: + return 0; + + case AND: + return const_ok_for_arm (ARM_SIGN_EXTEND (~i)); + + default: + abort (); + } +} + +/* Emit a sequence of insns to handle a large constant. + CODE is the code of the operation required, it can be any of SET, PLUS, + IOR, AND, XOR, MINUS; + MODE is the mode in which the operation is being performed; + VAL is the integer to operate on; + SOURCE is the other operand (a register, or a null-pointer for SET); + SUBTARGETS means it is safe to create scratch registers if that will + either produce a simpler sequence, or we will want to cse the values. */ + +int +arm_split_constant (code, mode, val, target, source, subtargets) + enum rtx_code code; + enum machine_mode mode; + HOST_WIDE_INT val; + rtx target; + rtx source; + int subtargets; +{ + int can_add = 0; + int can_invert = 0; + int can_negate = 0; + int can_negate_initial = 0; + int can_shift = 0; + int i; + int num_bits_set = 0; + int set_sign_bit_copies = 0; + int clear_sign_bit_copies = 0; + int clear_zero_bit_copies = 0; + int set_zero_bit_copies = 0; + int insns = 0; + rtx new_src; + unsigned HOST_WIDE_INT temp1, temp2; + unsigned HOST_WIDE_INT remainder = val & 0xffffffff; + + /* find out which operations are safe for a given CODE. Also do a quick + check for degenerate cases; these can occur when DImode operations + are split. */ + switch (code) + { + case SET: + can_invert = 1; + can_shift = 1; + can_negate = 1; + break; + + case PLUS: + can_negate = 1; + can_negate_initial = 1; + break; + + case IOR: + if (remainder == 0xffffffff) + { + emit_insn (gen_rtx (SET, VOIDmode, target, + GEN_INT (ARM_SIGN_EXTEND (val)))); + return 1; + } + if (remainder == 0) + { + if (reload_completed && rtx_equal_p (target, source)) + return 0; + emit_insn (gen_rtx (SET, VOIDmode, target, source)); + return 1; + } + break; + + case AND: + if (remainder == 0) + { + emit_insn (gen_rtx (SET, VOIDmode, target, const0_rtx)); + return 1; + } + if (remainder == 0xffffffff) + { + if (reload_completed && rtx_equal_p (target, source)) + return 0; + emit_insn (gen_rtx (SET, VOIDmode, target, source)); + return 1; + } + can_invert = 1; + break; + + case XOR: + if (remainder == 0) + { + if (reload_completed && rtx_equal_p (target, source)) + return 0; + emit_insn (gen_rtx (SET, VOIDmode, target, source)); + return 1; + } + if (remainder == 0xffffffff) + { + emit_insn (gen_rtx (SET, VOIDmode, target, + gen_rtx (NOT, mode, source))); + return 1; + } + + /* We don't know how to handle this yet below. */ + abort (); + + case MINUS: + /* We treat MINUS as (val - source), since (source - val) is always + passed as (source + (-val)). */ + if (remainder == 0) + { + emit_insn (gen_rtx (SET, VOIDmode, target, + gen_rtx (NEG, mode, source))); + return 1; + } + if (const_ok_for_arm (val)) + { + emit_insn (gen_rtx (SET, VOIDmode, target, + gen_rtx (MINUS, mode, GEN_INT (val), source))); + return 1; + } + can_negate = 1; + + break; + + default: + abort (); + } + + /* If we can do it in one insn get out quickly */ + if (const_ok_for_arm (val) + || (can_negate_initial && const_ok_for_arm (-val)) + || (can_invert && const_ok_for_arm (~val))) + { + emit_insn (gen_rtx (SET, VOIDmode, target, + (source ? gen_rtx (code, mode, source, + GEN_INT (val)) : GEN_INT (val)))); + return 1; + } + + + /* Calculate a few attributes that may be useful for specific + optimizations. */ + + for (i = 31; i >= 0; i--) + { + if ((remainder & (1 << i)) == 0) + clear_sign_bit_copies++; + else + break; + } + + for (i = 31; i >= 0; i--) + { + if ((remainder & (1 << i)) != 0) + set_sign_bit_copies++; + else + break; + } + + for (i = 0; i <= 31; i++) + { + if ((remainder & (1 << i)) == 0) + clear_zero_bit_copies++; + else + break; + } + + for (i = 0; i <= 31; i++) + { + if ((remainder & (1 << i)) != 0) + set_zero_bit_copies++; + else + break; + } + + switch (code) + { + case SET: + /* See if we can do this by sign_extending a constant that is known + to be negative. This is a good, way of doing it, since the shift + may well merge into a subsequent insn. */ + if (set_sign_bit_copies > 1) + { + if (const_ok_for_arm + (temp1 = ARM_SIGN_EXTEND (remainder + << (set_sign_bit_copies - 1)))) + { + new_src = subtargets ? gen_reg_rtx (mode) : target; + emit_insn (gen_rtx (SET, VOIDmode, new_src, GEN_INT (temp1))); + emit_insn (gen_ashrsi3 (target, new_src, + GEN_INT (set_sign_bit_copies - 1))); + return 2; + } + /* For an inverted constant, we will need to set the low bits, + these will be shifted out of harm's way. */ + temp1 |= (1 << (set_sign_bit_copies - 1)) - 1; + if (const_ok_for_arm (~temp1)) + { + new_src = subtargets ? gen_reg_rtx (mode) : target; + emit_insn (gen_rtx (SET, VOIDmode, new_src, GEN_INT (temp1))); + emit_insn (gen_ashrsi3 (target, new_src, + GEN_INT (set_sign_bit_copies - 1))); + return 2; + } + } + + /* See if we can generate this by setting the bottom (or the top) + 16 bits, and then shifting these into the other half of the + word. We only look for the simplest cases, to do more would cost + too much. Be careful, however, not to generate this when the + alternative would take fewer insns. */ + if (val & 0xffff0000) + { + temp1 = remainder & 0xffff0000; + temp2 = remainder & 0x0000ffff; + + /* Overlaps outside this range are best done using other methods. */ + for (i = 9; i < 24; i++) + { + if ((((temp2 | (temp2 << i)) & 0xffffffff) == remainder) + && ! const_ok_for_arm (temp2)) + { + insns + = arm_split_constant (code, mode, temp2, + (new_src + = subtargets ? gen_reg_rtx (mode) + : target), + source, subtargets); + source = new_src; + emit_insn (gen_rtx (SET, VOIDmode, target, + gen_rtx (IOR, mode, + gen_rtx (ASHIFT, mode, source, + GEN_INT (i)), + source))); + return insns + 1; + } + } + + /* Don't duplicate cases already considered. */ + for (i = 17; i < 24; i++) + { + if (((temp1 | (temp1 >> i)) == remainder) + && ! const_ok_for_arm (temp1)) + { + insns + = arm_split_constant (code, mode, temp1, + (new_src + = subtargets ? gen_reg_rtx (mode) + : target), + source, subtargets); + source = new_src; + emit_insn (gen_rtx (SET, VOIDmode, target, + gen_rtx (IOR, mode, + gen_rtx (LSHIFTRT, mode, source, + GEN_INT (i)), + source))); + return insns + 1; + } + } + } + break; + + case IOR: + case XOR: + /* If we have IOR or XOR, and the inverse of the constant can be loaded + in a single instruction, and we can find a temporary to put it in, + then this can be done in two instructions instead of 3-4. */ + if (subtargets + || (reload_completed && ! reg_mentioned_p (target, source))) + { + if (const_ok_for_arm (ARM_SIGN_EXTEND (~ val))) + { + rtx sub = subtargets ? gen_reg_rtx (mode) : target; + + emit_insn (gen_rtx (SET, VOIDmode, sub, + GEN_INT (ARM_SIGN_EXTEND (~ val)))); + emit_insn (gen_rtx (SET, VOIDmode, target, + gen_rtx (code, mode, source, sub))); + return 2; + } + } + + if (code == XOR) + break; + + if (set_sign_bit_copies > 8 + && (val & (-1 << (32 - set_sign_bit_copies))) == val) + { + rtx sub = subtargets ? gen_reg_rtx (mode) : target; + rtx shift = GEN_INT (set_sign_bit_copies); + + emit_insn (gen_rtx (SET, VOIDmode, sub, + gen_rtx (NOT, mode, + gen_rtx (ASHIFT, mode, source, + shift)))); + emit_insn (gen_rtx (SET, VOIDmode, target, + gen_rtx (NOT, mode, + gen_rtx (LSHIFTRT, mode, sub, + shift)))); + return 2; + } + + if (set_zero_bit_copies > 8 + && (remainder & ((1 << set_zero_bit_copies) - 1)) == remainder) + { + rtx sub = subtargets ? gen_reg_rtx (mode) : target; + rtx shift = GEN_INT (set_zero_bit_copies); + + emit_insn (gen_rtx (SET, VOIDmode, sub, + gen_rtx (NOT, mode, + gen_rtx (LSHIFTRT, mode, source, + shift)))); + emit_insn (gen_rtx (SET, VOIDmode, target, + gen_rtx (NOT, mode, + gen_rtx (ASHIFT, mode, sub, + shift)))); + return 2; + } + + if (const_ok_for_arm (temp1 = ARM_SIGN_EXTEND (~ val))) + { + rtx sub = subtargets ? gen_reg_rtx (mode) : target; + emit_insn (gen_rtx (SET, VOIDmode, sub, + gen_rtx (NOT, mode, source))); + source = sub; + if (subtargets) + sub = gen_reg_rtx (mode); + emit_insn (gen_rtx (SET, VOIDmode, sub, + gen_rtx (AND, mode, source, GEN_INT (temp1)))); + emit_insn (gen_rtx (SET, VOIDmode, target, + gen_rtx (NOT, mode, sub))); + return 3; + } + break; + + case AND: + /* See if two shifts will do 2 or more insn's worth of work. */ + if (clear_sign_bit_copies >= 16 && clear_sign_bit_copies < 24) + { + HOST_WIDE_INT shift_mask = ((0xffffffff + << (32 - clear_sign_bit_copies)) + & 0xffffffff); + rtx new_source; + rtx shift = GEN_INT (clear_sign_bit_copies); + + if ((remainder | shift_mask) != 0xffffffff) + { + new_source = subtargets ? gen_reg_rtx (mode) : target; + insns = arm_split_constant (AND, mode, remainder | shift_mask, + new_source, source, subtargets); + source = new_source; + } + + new_source = subtargets ? gen_reg_rtx (mode) : target; + emit_insn (gen_ashlsi3 (new_source, source, shift)); + emit_insn (gen_lshrsi3 (target, new_source, shift)); + return insns + 2; + } + + if (clear_zero_bit_copies >= 16 && clear_zero_bit_copies < 24) + { + HOST_WIDE_INT shift_mask = (1 << clear_zero_bit_copies) - 1; + rtx new_source; + rtx shift = GEN_INT (clear_zero_bit_copies); + + if ((remainder | shift_mask) != 0xffffffff) + { + new_source = subtargets ? gen_reg_rtx (mode) : target; + insns = arm_split_constant (AND, mode, remainder | shift_mask, + new_source, source, subtargets); + source = new_source; + } + + new_source = subtargets ? gen_reg_rtx (mode) : target; + emit_insn (gen_lshrsi3 (new_source, source, shift)); + emit_insn (gen_ashlsi3 (target, new_source, shift)); + return insns + 2; + } + + break; + + default: + break; + } + + for (i = 0; i < 32; i++) + if (remainder & (1 << i)) + num_bits_set++; + + if (code == AND || (can_invert && num_bits_set > 16)) + remainder = (~remainder) & 0xffffffff; + else if (code == PLUS && num_bits_set > 16) + remainder = (-remainder) & 0xffffffff; + else + { + can_invert = 0; + can_negate = 0; + } + + /* Now try and find a way of doing the job in either two or three + instructions. + We start by looking for the largest block of zeros that are aligned on + a 2-bit boundary, we then fill up the temps, wrapping around to the + top of the word when we drop off the bottom. + In the worst case this code should produce no more than four insns. */ + { + int best_start = 0; + int best_consecutive_zeros = 0; + + for (i = 0; i < 32; i += 2) + { + int consecutive_zeros = 0; + + if (! (remainder & (3 << i))) + { + while ((i < 32) && ! (remainder & (3 << i))) + { + consecutive_zeros += 2; + i += 2; + } + if (consecutive_zeros > best_consecutive_zeros) + { + best_consecutive_zeros = consecutive_zeros; + best_start = i - consecutive_zeros; + } + i -= 2; + } + } + + /* Now start emitting the insns, starting with the one with the highest + bit set: we do this so that the smallest number will be emitted last; + this is more likely to be combinable with addressing insns. */ + i = best_start; + do + { + int end; + + if (i <= 0) + i += 32; + if (remainder & (3 << (i - 2))) + { + end = i - 8; + if (end < 0) + end += 32; + temp1 = remainder & ((0x0ff << end) + | ((i < end) ? (0xff >> (32 - end)) : 0)); + remainder &= ~temp1; + + if (code == SET) + { + emit_insn (gen_rtx (SET, VOIDmode, + new_src = (subtargets ? gen_reg_rtx (mode) + : target), + GEN_INT (can_invert ? ~temp1 : temp1))); + can_invert = 0; + code = PLUS; + } + else if (code == MINUS) + { + emit_insn (gen_rtx (SET, VOIDmode, + new_src = (subtargets ? gen_reg_rtx (mode) + : target), + gen_rtx (code, mode, GEN_INT (temp1), + source))); + code = PLUS; + } + else + { + emit_insn (gen_rtx (SET, VOIDmode, + new_src = remainder ? (subtargets + ? gen_reg_rtx (mode) + : target) : target, + gen_rtx (code, mode, source, + GEN_INT (can_invert ? ~temp1 + : (can_negate + ? -temp1 : temp1))))); + } + + insns++; + source = new_src; + i -= 6; + } + i -= 2; + } while (remainder); + } + return insns; +} + +#define REG_OR_SUBREG_REG(X) \ + (GET_CODE (X) == REG \ + || (GET_CODE (X) == SUBREG && GET_CODE (SUBREG_REG (X)) == REG)) + +#define REG_OR_SUBREG_RTX(X) \ + (GET_CODE (X) == REG ? (X) : SUBREG_REG (X)) + +#define ARM_FRAME_RTX(X) \ + ((X) == frame_pointer_rtx || (X) == stack_pointer_rtx \ + || (X) == arg_pointer_rtx) + +int +arm_rtx_costs (x, code, outer_code) + rtx x; + enum rtx_code code, outer_code; +{ + enum machine_mode mode = GET_MODE (x); + enum rtx_code subcode; + int extra_cost; + + switch (code) + { + case MEM: + /* Memory costs quite a lot for the first word, but subsequent words + load at the equivalent of a single insn each. */ + return (10 + 4 * ((GET_MODE_SIZE (mode) - 1) / UNITS_PER_WORD) + + (CONSTANT_POOL_ADDRESS_P (x) ? 4 : 0)); + + case DIV: + case MOD: + return 100; + + case ROTATE: + if (mode == SImode && GET_CODE (XEXP (x, 1)) == REG) + return 4; + /* Fall through */ + case ROTATERT: + if (mode != SImode) + return 8; + /* Fall through */ + case ASHIFT: case LSHIFTRT: case ASHIFTRT: + if (mode == DImode) + return (8 + (GET_CODE (XEXP (x, 1)) == CONST_INT ? 0 : 8) + + ((GET_CODE (XEXP (x, 0)) == REG + || (GET_CODE (XEXP (x, 0)) == SUBREG + && GET_CODE (SUBREG_REG (XEXP (x, 0))) == REG)) + ? 0 : 8)); + return (1 + ((GET_CODE (XEXP (x, 0)) == REG + || (GET_CODE (XEXP (x, 0)) == SUBREG + && GET_CODE (SUBREG_REG (XEXP (x, 0))) == REG)) + ? 0 : 4) + + ((GET_CODE (XEXP (x, 1)) == REG + || (GET_CODE (XEXP (x, 1)) == SUBREG + && GET_CODE (SUBREG_REG (XEXP (x, 1))) == REG) + || (GET_CODE (XEXP (x, 1)) == CONST_INT)) + ? 0 : 4)); + + case MINUS: + if (mode == DImode) + return (4 + (REG_OR_SUBREG_REG (XEXP (x, 1)) ? 0 : 8) + + ((REG_OR_SUBREG_REG (XEXP (x, 0)) + || (GET_CODE (XEXP (x, 0)) == CONST_INT + && const_ok_for_arm (INTVAL (XEXP (x, 0))))) + ? 0 : 8)); + + if (GET_MODE_CLASS (mode) == MODE_FLOAT) + return (2 + ((REG_OR_SUBREG_REG (XEXP (x, 1)) + || (GET_CODE (XEXP (x, 1)) == CONST_DOUBLE + && const_double_rtx_ok_for_fpu (XEXP (x, 1)))) + ? 0 : 8) + + ((REG_OR_SUBREG_REG (XEXP (x, 0)) + || (GET_CODE (XEXP (x, 0)) == CONST_DOUBLE + && const_double_rtx_ok_for_fpu (XEXP (x, 0)))) + ? 0 : 8)); + + if (((GET_CODE (XEXP (x, 0)) == CONST_INT + && const_ok_for_arm (INTVAL (XEXP (x, 0))) + && REG_OR_SUBREG_REG (XEXP (x, 1)))) + || (((subcode = GET_CODE (XEXP (x, 1))) == ASHIFT + || subcode == ASHIFTRT || subcode == LSHIFTRT + || subcode == ROTATE || subcode == ROTATERT + || (subcode == MULT + && GET_CODE (XEXP (XEXP (x, 1), 1)) == CONST_INT + && ((INTVAL (XEXP (XEXP (x, 1), 1)) & + (INTVAL (XEXP (XEXP (x, 1), 1)) - 1)) == 0))) + && REG_OR_SUBREG_REG (XEXP (XEXP (x, 1), 0)) + && (REG_OR_SUBREG_REG (XEXP (XEXP (x, 1), 1)) + || GET_CODE (XEXP (XEXP (x, 1), 1)) == CONST_INT) + && REG_OR_SUBREG_REG (XEXP (x, 0)))) + return 1; + /* Fall through */ + + case PLUS: + if (GET_MODE_CLASS (mode) == MODE_FLOAT) + return (2 + (REG_OR_SUBREG_REG (XEXP (x, 0)) ? 0 : 8) + + ((REG_OR_SUBREG_REG (XEXP (x, 1)) + || (GET_CODE (XEXP (x, 1)) == CONST_DOUBLE + && const_double_rtx_ok_for_fpu (XEXP (x, 1)))) + ? 0 : 8)); + + /* Fall through */ + case AND: case XOR: case IOR: + extra_cost = 0; + + /* Normally the frame registers will be spilt into reg+const during + reload, so it is a bad idea to combine them with other instructions, + since then they might not be moved outside of loops. As a compromise + we allow integration with ops that have a constant as their second + operand. */ + if ((REG_OR_SUBREG_REG (XEXP (x, 0)) + && ARM_FRAME_RTX (REG_OR_SUBREG_RTX (XEXP (x, 0))) + && GET_CODE (XEXP (x, 1)) != CONST_INT) + || (REG_OR_SUBREG_REG (XEXP (x, 0)) + && ARM_FRAME_RTX (REG_OR_SUBREG_RTX (XEXP (x, 0))))) + extra_cost = 4; + + if (mode == DImode) + return (4 + extra_cost + (REG_OR_SUBREG_REG (XEXP (x, 0)) ? 0 : 8) + + ((REG_OR_SUBREG_REG (XEXP (x, 1)) + || (GET_CODE (XEXP (x, 1)) == CONST_INT + && const_ok_for_op (INTVAL (XEXP (x, 1)), code, mode))) + ? 0 : 8)); + + if (REG_OR_SUBREG_REG (XEXP (x, 0))) + return (1 + (GET_CODE (XEXP (x, 1)) == CONST_INT ? 0 : extra_cost) + + ((REG_OR_SUBREG_REG (XEXP (x, 1)) + || (GET_CODE (XEXP (x, 1)) == CONST_INT + && const_ok_for_op (INTVAL (XEXP (x, 1)), code, mode))) + ? 0 : 4)); + + else if (REG_OR_SUBREG_REG (XEXP (x, 1))) + return (1 + extra_cost + + ((((subcode = GET_CODE (XEXP (x, 0))) == ASHIFT + || subcode == LSHIFTRT || subcode == ASHIFTRT + || subcode == ROTATE || subcode == ROTATERT + || (subcode == MULT + && GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT + && ((INTVAL (XEXP (XEXP (x, 0), 1)) & + (INTVAL (XEXP (XEXP (x, 0), 1)) - 1)) == 0)) + && (REG_OR_SUBREG_REG (XEXP (XEXP (x, 0), 0))) + && ((REG_OR_SUBREG_REG (XEXP (XEXP (x, 0), 1))) + || GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT))) + ? 0 : 4)); + + return 8; + + case MULT: + if (GET_MODE_CLASS (mode) == MODE_FLOAT + || mode == DImode) + return 30; + + if (GET_CODE (XEXP (x, 1)) == CONST_INT) + { + HOST_WIDE_INT i = INTVAL (XEXP (x, 1)) & 0xffffffff; + int add_cost = const_ok_for_arm (i) ? 4 : 8; + int j; + + /* This will need adjusting for ARM's with fast multiplies */ + for (j = 0; i && j < 32; j += 2) + { + i &= ~(3 << j); + add_cost += 2; + } + + return add_cost; + } + + return (30 + (REG_OR_SUBREG_REG (XEXP (x, 0)) ? 0 : 4) + + (REG_OR_SUBREG_REG (XEXP (x, 1)) ? 0 : 4)); + + case NEG: + if (GET_MODE_CLASS (mode) == MODE_FLOAT) + return 4 + (REG_OR_SUBREG_REG (XEXP (x, 0)) ? 0 : 6); + /* Fall through */ + case NOT: + if (mode == DImode) + return 4 + (REG_OR_SUBREG_REG (XEXP (x, 0)) ? 0 : 4); + + return 1 + (REG_OR_SUBREG_REG (XEXP (x, 0)) ? 0 : 4); + + case IF_THEN_ELSE: + if (GET_CODE (XEXP (x, 1)) == PC || GET_CODE (XEXP (x, 2)) == PC) + return 14; + return 2; + + case COMPARE: + return 1; + case ABS: + return 4 + (mode == DImode ? 4 : 0); + + case SIGN_EXTEND: + if (GET_MODE (XEXP (x, 0)) == QImode) + return (4 + (mode == DImode ? 4 : 0) + + (GET_CODE (XEXP (x, 0)) == MEM ? 10 : 0)); + /* Fall through */ + case ZERO_EXTEND: + switch (GET_MODE (XEXP (x, 0))) + { + case QImode: + return (1 + (mode == DImode ? 4 : 0) + + (GET_CODE (XEXP (x, 0)) == MEM ? 10 : 0)); + + case HImode: + return (4 + (mode == DImode ? 4 : 0) + + (GET_CODE (XEXP (x, 0)) == MEM ? 10 : 0)); + + case SImode: + return (1 + (GET_CODE (XEXP (x, 0)) == MEM ? 10 : 0)); + } + abort (); + + default: + return 99; + } +} + /* This code has been fixed for cross compilation. */ static int fpa_consts_inited = 0; @@ -181,6 +911,7 @@ init_fpa_table () r = REAL_VALUE_ATOF (strings_fpa[i], DFmode); values_fpa[i] = r; } + fpa_consts_inited = 1; } @@ -199,11 +930,13 @@ const_double_rtx_ok_for_fpu (x) REAL_VALUE_FROM_CONST_DOUBLE (r, x); if (REAL_VALUE_MINUS_ZERO (r)) return 0; + for (i = 0; i < 8; i++) if (REAL_VALUES_EQUAL (r, values_fpa[i])) return 1; + return 0; -} /* const_double_rtx_ok_for_fpu */ +} /* Return TRUE if rtx X is a valid immediate FPU constant. */ @@ -221,11 +954,13 @@ neg_const_double_rtx_ok_for_fpu (x) r = REAL_VALUE_NEGATE (r); if (REAL_VALUE_MINUS_ZERO (r)) return 0; + for (i = 0; i < 8; i++) if (REAL_VALUES_EQUAL (r, values_fpa[i])) return 1; + return 0; -} /* neg_const_double_rtx_ok_for_fpu */ +} /* Predicates for `match_operand' and `match_operator'. */ @@ -241,9 +976,30 @@ s_register_operand (op, mode) return 0; if (GET_CODE (op) == SUBREG) - { - op = SUBREG_REG (op); - } + op = SUBREG_REG (op); + + /* We don't consider registers whose class is NO_REGS + to be a register operand. */ + return (GET_CODE (op) == REG + && (REGNO (op) >= FIRST_PSEUDO_REGISTER + || REGNO_REG_CLASS (REGNO (op)) != NO_REGS)); +} + +/* Only accept reg, subreg(reg), const_int. */ + +int +reg_or_int_operand (op, mode) + register rtx op; + enum machine_mode mode; +{ + if (GET_CODE (op) == CONST_INT) + return 1; + + if (GET_MODE (op) != mode && mode != VOIDmode) + return 0; + + if (GET_CODE (op) == SUBREG) + op = SUBREG_REG (op); /* We don't consider registers whose class is NO_REGS to be a register operand. */ @@ -276,7 +1032,7 @@ arm_rhs_operand (op, mode) { return (s_register_operand (op, mode) || (GET_CODE (op) == CONST_INT && const_ok_for_arm (INTVAL (op)))); -} /* arm_rhs_operand */ +} /* Return TRUE for valid operands for the rhs of an ARM instruction, or a load. */ @@ -289,7 +1045,7 @@ arm_rhsm_operand (op, mode) return (s_register_operand (op, mode) || (GET_CODE (op) == CONST_INT && const_ok_for_arm (INTVAL (op))) || memory_operand (op, mode)); -} /* arm_rhs_operand */ +} /* Return TRUE for valid operands for the rhs of an ARM instruction, or if a constant that is valid when negated. */ @@ -303,7 +1059,7 @@ arm_add_operand (op, mode) || (GET_CODE (op) == CONST_INT && (const_ok_for_arm (INTVAL (op)) || const_ok_for_arm (-INTVAL (op))))); -} /* arm_rhs_operand */ +} int arm_not_operand (op, mode) @@ -314,7 +1070,7 @@ arm_not_operand (op, mode) || (GET_CODE (op) == CONST_INT && (const_ok_for_arm (INTVAL (op)) || const_ok_for_arm (~INTVAL (op))))); -} /* arm_rhs_operand */ +} /* Return TRUE for valid operands for the rhs of an FPU instruction. */ @@ -324,11 +1080,12 @@ fpu_rhs_operand (op, mode) enum machine_mode mode; { if (s_register_operand (op, mode)) - return(TRUE); + return TRUE; else if (GET_CODE (op) == CONST_DOUBLE) return (const_double_rtx_ok_for_fpu (op)); - else return (FALSE); -} /* fpu_rhs_operand */ + + return FALSE; +} int fpu_add_operand (op, mode) @@ -336,11 +1093,12 @@ fpu_add_operand (op, mode) enum machine_mode mode; { if (s_register_operand (op, mode)) - return(TRUE); + return TRUE; else if (GET_CODE (op) == CONST_DOUBLE) - return const_double_rtx_ok_for_fpu (op) - || neg_const_double_rtx_ok_for_fpu (op); - return (FALSE); + return (const_double_rtx_ok_for_fpu (op) + || neg_const_double_rtx_ok_for_fpu (op)); + + return FALSE; } /* Return nonzero if OP is a constant power of two. */ @@ -352,11 +1110,11 @@ power_of_two_operand (op, mode) { if (GET_CODE (op) == CONST_INT) { - int value = INTVAL(op); - return (value != 0 && (value & (value-1)) == 0); + HOST_WIDE_INT value = INTVAL(op); + return value != 0 && (value & (value - 1)) == 0; } - return (FALSE); -} /* power_of_two_operand */ + return FALSE; +} /* Return TRUE for a valid operand of a DImode operation. Either: REG, CONST_DOUBLE or MEM(DImode_address). @@ -369,19 +1127,21 @@ di_operand (op, mode) enum machine_mode mode; { if (s_register_operand (op, mode)) - return (TRUE); + return TRUE; switch (GET_CODE (op)) { case CONST_DOUBLE: case CONST_INT: - return (TRUE); + return TRUE; + case MEM: - return (memory_address_p (DImode, XEXP (op, 0))); + return memory_address_p (DImode, XEXP (op, 0)); + default: - return (FALSE); + return FALSE; } -} /* di_operand */ +} /* Return TRUE for valid index operands. */ @@ -393,7 +1153,7 @@ index_operand (op, mode) return (s_register_operand(op, mode) || (immediate_operand (op, mode) && INTVAL (op) < 4096 && INTVAL (op) > -4096)); -} /* index_operand */ +} /* Return TRUE for valid shifts by a constant. This also accepts any power of two on the (somewhat overly relaxed) assumption that the @@ -407,7 +1167,7 @@ const_shift_operand (op, mode) return (power_of_two_operand (op, mode) || (immediate_operand (op, mode) && (INTVAL (op) < 32 && INTVAL (op) > 0))); -} /* const_shift_operand */ +} /* Return TRUE for arithmetic operators which can be combined with a multiply (shift). */ @@ -426,7 +1186,7 @@ shiftable_operator (x, mode) return (code == PLUS || code == MINUS || code == IOR || code == XOR || code == AND); } -} /* shiftable_operator */ +} /* Return TRUE for shift operators. */ @@ -443,16 +1203,17 @@ shift_operator (x, mode) if (code == MULT) return power_of_two_operand (XEXP (x, 1)); - return (code == ASHIFT || code == LSHIFT - || code == ASHIFTRT || code == LSHIFTRT); + + return (code == ASHIFT || code == ASHIFTRT || code == LSHIFTRT + || code == ROTATERT); } -} /* shift_operator */ +} int equality_operator (x, mode) -rtx x; -enum machine_mode mode; + rtx x; + enum machine_mode mode; { - return (GET_CODE (x) == EQ || GET_CODE (x) == NE); + return GET_CODE (x) == EQ || GET_CODE (x) == NE; } /* Return TRUE for SMIN SMAX UMIN UMAX operators. */ @@ -466,8 +1227,9 @@ minmax_operator (x, mode) if (GET_MODE (x) != mode) return FALSE; + return code == SMIN || code == SMAX || code == UMIN || code == UMAX; -} /* minmax_operator */ +} /* return TRUE if x is EQ or NE */ @@ -476,8 +1238,8 @@ minmax_operator (x, mode) int cc_register (x, mode) -rtx x; -enum machine_mode mode; + rtx x; + enum machine_mode mode; { if (mode == VOIDmode) { @@ -485,30 +1247,59 @@ enum machine_mode mode; if (GET_MODE_CLASS (mode) != MODE_CC) return FALSE; } + if (mode == GET_MODE (x) && GET_CODE (x) == REG && REGNO (x) == 24) return TRUE; + return FALSE; } - + +/* Return TRUE if this is the condition code register, if we aren't given + a mode, accept any mode in class CC_MODE that is reversible */ + +int +reversible_cc_register (x, mode) + rtx x; + enum machine_mode mode; +{ + if (mode == VOIDmode) + { + mode = GET_MODE (x); + if (GET_MODE_CLASS (mode) != MODE_CC + && GET_CODE (x) == REG && REGNO (x) == 24) + abort (); + if (GET_MODE_CLASS (mode) != MODE_CC + || (! flag_fast_math && ! REVERSIBLE_CC_MODE (mode))) + return FALSE; + } + + if (mode == GET_MODE (x) && GET_CODE (x) == REG && REGNO (x) == 24) + return TRUE; + + return FALSE; +} + enum rtx_code minmax_code (x) -rtx x; + rtx x; { enum rtx_code code = GET_CODE (x); if (code == SMAX) return GE; - if (code == SMIN) + else if (code == SMIN) return LE; - if (code == UMIN) + else if (code == UMIN) return LEU; - if (code == UMAX) + else if (code == UMAX) return GEU; + abort (); } /* Return 1 if memory locations are adjacent */ +int adjacent_mem_locations (a, b) rtx a, b; { @@ -544,14 +1335,15 @@ adjacent_mem_locations (a, b) /* Return 1 if OP is a load multiple operation. It is known to be parallel and the first section will be tested. */ +int load_multiple_operation (op, mode) rtx op; enum machine_mode mode; { - int count = XVECLEN (op, 0); + HOST_WIDE_INT count = XVECLEN (op, 0); int dest_regno; rtx src_addr; - int i = 1, base = 0; + HOST_WIDE_INT i = 1, base = 0; rtx elt; if (count <= 1 @@ -575,6 +1367,7 @@ load_multiple_operation (op, mode) || REGNO (XEXP (XVECEXP (op, 0, count - 1), 0)) != REGNO (SET_DEST (elt))) return 0; + count--; } @@ -611,14 +1404,15 @@ load_multiple_operation (op, mode) /* Return 1 if OP is a store multiple operation. It is known to be parallel and the first section will be tested. */ +int store_multiple_operation (op, mode) rtx op; enum machine_mode mode; { - int count = XVECLEN (op, 0); + HOST_WIDE_INT count = XVECLEN (op, 0); int src_regno; rtx dest_addr; - int i = 1, base = 0; + HOST_WIDE_INT i = 1, base = 0; rtx elt; if (count <= 1 @@ -642,6 +1436,7 @@ store_multiple_operation (op, mode) || REGNO (XEXP (XVECEXP (op, 0, count - 1), 0)) != REGNO (SET_DEST (elt))) return 0; + count--; } @@ -674,10 +1469,35 @@ store_multiple_operation (op, mode) return 1; } + +int +multi_register_push (op, mode) + rtx op; + enum machine_mode mode; +{ + if (GET_CODE (op) != PARALLEL + || (GET_CODE (XVECEXP (op, 0, 0)) != SET) + || (GET_CODE (SET_SRC (XVECEXP (op, 0, 0))) != UNSPEC) + || (XINT (SET_SRC (XVECEXP (op, 0, 0)), 1) != 2)) + return 0; + + return 1; +} + + +/* Routines for use with attributes */ + +int +const_pool_offset (symbol) + rtx symbol; +{ + return get_pool_offset (symbol) - get_pool_size () - get_prologue_size (); +} /* Routines for use in generating RTL */ -rtx arm_gen_load_multiple (base_regno, count, from, up, write_back) +rtx +arm_gen_load_multiple (base_regno, count, from, up, write_back) int base_regno; int count; rtx from; @@ -691,27 +1511,30 @@ rtx arm_gen_load_multiple (base_regno, c result = gen_rtx (PARALLEL, VOIDmode, rtvec_alloc (count + (write_back ? 2 : 0))); if (write_back) - { + { XVECEXP (result, 0, 0) - = gen_rtx (SET, GET_MODE (from), from, - plus_constant (from, count * 4 * sign)); + = gen_rtx (SET, GET_MODE (from), from, + plus_constant (from, count * 4 * sign)); i = 1; count++; - } + } + for (j = 0; i < count; i++, j++) - { + { XVECEXP (result, 0, i) - = gen_rtx (SET, VOIDmode, gen_rtx (REG, SImode, base_regno + j), - gen_rtx (MEM, SImode, - plus_constant (from, j * 4 * sign))); - } + = gen_rtx (SET, VOIDmode, gen_rtx (REG, SImode, base_regno + j), + gen_rtx (MEM, SImode, + plus_constant (from, j * 4 * sign))); + } + if (write_back) XVECEXP (result, 0, i) = gen_rtx (CLOBBER, SImode, from); return result; } -rtx arm_gen_store_multiple (base_regno, count, to, up, write_back) +rtx +arm_gen_store_multiple (base_regno, count, to, up, write_back) int base_regno; int count; rtx to; @@ -725,26 +1548,153 @@ rtx arm_gen_store_multiple (base_regno, result = gen_rtx (PARALLEL, VOIDmode, rtvec_alloc (count + (write_back ? 2 : 0))); if (write_back) - { + { XVECEXP (result, 0, 0) - = gen_rtx (SET, GET_MODE (to), to, - plus_constant (to, count * 4 * sign)); + = gen_rtx (SET, GET_MODE (to), to, + plus_constant (to, count * 4 * sign)); i = 1; count++; - } + } + for (j = 0; i < count; i++, j++) - { + { XVECEXP (result, 0, i) - = gen_rtx (SET, VOIDmode, - gen_rtx (MEM, SImode, plus_constant (to, j * 4 * sign)), - gen_rtx (REG, SImode, base_regno + j)); - } + = gen_rtx (SET, VOIDmode, + gen_rtx (MEM, SImode, plus_constant (to, j * 4 * sign)), + gen_rtx (REG, SImode, base_regno + j)); + } + if (write_back) XVECEXP (result, 0, i) = gen_rtx (CLOBBER, SImode, to); return result; } +int +arm_gen_movstrqi (operands) + rtx *operands; +{ + HOST_WIDE_INT in_words_to_go, out_words_to_go, last_bytes; + int i, r; + rtx const_sxteen = gen_rtx (CONST_INT, SImode, 16); + rtx src, dst; + rtx st_src, st_dst, end_src, end_dst, fin_src, fin_dst; + rtx part_bytes_reg = NULL; + extern int optimize; + + if (GET_CODE (operands[2]) != CONST_INT + || GET_CODE (operands[3]) != CONST_INT + || INTVAL (operands[2]) > 64 + || INTVAL (operands[3]) & 3) + return 0; + + st_dst = XEXP (operands[0], 0); + st_src = XEXP (operands[1], 0); + fin_dst = dst = copy_to_mode_reg (SImode, st_dst); + fin_src = src = copy_to_mode_reg (SImode, st_src); + + in_words_to_go = (INTVAL (operands[2]) + 3) / 4; + out_words_to_go = INTVAL (operands[2]) / 4; + last_bytes = INTVAL (operands[2]) & 3; + + if (out_words_to_go != in_words_to_go && ((in_words_to_go - 1) & 3) != 0) + part_bytes_reg = gen_rtx (REG, SImode, (in_words_to_go - 1) & 3); + + for (i = 0; in_words_to_go >= 2; i+=4) + { + emit_insn (arm_gen_load_multiple (0, (in_words_to_go > 4 + ? 4 : in_words_to_go), + src, TRUE, TRUE)); + if (out_words_to_go) + { + if (out_words_to_go != 1) + emit_insn (arm_gen_store_multiple (0, (out_words_to_go > 4 + ? 4 : out_words_to_go), + dst, TRUE, TRUE)); + else + { + emit_move_insn (gen_rtx (MEM, SImode, dst), + gen_rtx (REG, SImode, 0)); + emit_insn (gen_addsi3 (dst, dst, GEN_INT (4))); + } + } + + in_words_to_go -= in_words_to_go < 4 ? in_words_to_go : 4; + out_words_to_go -= out_words_to_go < 4 ? out_words_to_go : 4; + } + + /* OUT_WORDS_TO_GO will be zero here if there are byte stores to do. */ + if (out_words_to_go) + { + rtx sreg; + + emit_move_insn (sreg = gen_reg_rtx (SImode), gen_rtx (MEM, SImode, src)); + emit_move_insn (fin_src = gen_reg_rtx (SImode), plus_constant (src, 4)); + emit_move_insn (gen_rtx (MEM, SImode, dst), sreg); + emit_move_insn (fin_dst = gen_reg_rtx (SImode), plus_constant (dst, 4)); + in_words_to_go--; + + if (in_words_to_go) /* Sanity check */ + abort (); + } + + if (in_words_to_go) + { + if (in_words_to_go < 0) + abort (); + + part_bytes_reg = copy_to_mode_reg (SImode, gen_rtx (MEM, SImode, src)); + emit_insn (gen_addsi3 (src, src, GEN_INT (4))); + } + + if (BYTES_BIG_ENDIAN && last_bytes) + { + rtx tmp = gen_reg_rtx (SImode); + + if (part_bytes_reg == NULL) + abort (); + + /* The bytes we want are in the top end of the word */ + emit_insn (gen_lshrsi3 (tmp, part_bytes_reg, + GEN_INT (8 * (4 - last_bytes)))); + part_bytes_reg = tmp; + + while (last_bytes) + { + emit_move_insn (gen_rtx (MEM, QImode, + plus_constant (dst, last_bytes - 1)), + gen_rtx (SUBREG, QImode, part_bytes_reg, 0)); + if (--last_bytes) + { + tmp = gen_reg_rtx (SImode); + emit_insn (gen_lshrsi3 (tmp, part_bytes_reg, GEN_INT (8))); + part_bytes_reg = tmp; + } + } + + } + else + { + while (last_bytes) + { + if (part_bytes_reg == NULL) + abort (); + + emit_move_insn (gen_rtx (MEM, QImode, dst), + gen_rtx (SUBREG, QImode, part_bytes_reg, 0)); + emit_insn (gen_addsi3 (dst, dst, const1_rtx)); + if (--last_bytes) + { + rtx tmp = gen_reg_rtx (SImode); + emit_insn (gen_lshrsi3 (tmp, part_bytes_reg, GEN_INT (8))); + part_bytes_reg = tmp; + } + } + } + + return 1; +} + /* X and Y are two things to compare using CODE. Emit the compare insn and return the rtx for register 0 in the proper mode. FP means this is a floating point compare: I don't think that it is needed on the arm. */ @@ -763,22 +1713,61 @@ gen_compare_reg (code, x, y, fp) return cc_reg; } +void +arm_reload_in_hi (operands) + rtx *operands; +{ + rtx base = find_replacement (&XEXP (operands[1], 0)); + + emit_insn (gen_zero_extendqisi2 (operands[2], gen_rtx (MEM, QImode, base))); + emit_insn (gen_zero_extendqisi2 (gen_rtx (SUBREG, SImode, operands[0], 0), + gen_rtx (MEM, QImode, + plus_constant (base, 1)))); + if (BYTES_BIG_ENDIAN) + emit_insn (gen_rtx (SET, VOIDmode, gen_rtx (SUBREG, SImode, + operands[0], 0), + gen_rtx (IOR, SImode, + gen_rtx (ASHIFT, SImode, + gen_rtx (SUBREG, SImode, + operands[0], 0), + GEN_INT (8)), + operands[2]))); + else + emit_insn (gen_rtx (SET, VOIDmode, gen_rtx (SUBREG, SImode, + operands[0], 0), + gen_rtx (IOR, SImode, + gen_rtx (ASHIFT, SImode, + operands[2], + GEN_INT (8)), + gen_rtx (SUBREG, SImode, operands[0], 0)))); +} + +void arm_reload_out_hi (operands) -rtx operands[]; + rtx *operands; { rtx base = find_replacement (&XEXP (operands[0], 0)); - emit_insn (gen_rtx (SET, VOIDmode, - gen_rtx (MEM, QImode, base), - gen_rtx (SUBREG, QImode, operands[1], 0))); - emit_insn (gen_rtx (SET, VOIDmode, operands[2], - gen_rtx (LSHIFTRT, SImode, - gen_rtx (SUBREG, SImode, operands[1], 0), - GEN_INT (8)))); - emit_insn (gen_rtx (SET, VOIDmode, - gen_rtx (MEM, QImode, - plus_constant (base, 1)), - gen_rtx (SUBREG, QImode, operands[2], 0))); + if (BYTES_BIG_ENDIAN) + { + emit_insn (gen_movqi (gen_rtx (MEM, QImode, plus_constant (base, 1)), + gen_rtx (SUBREG, QImode, operands[1], 0))); + emit_insn (gen_lshrsi3 (operands[2], + gen_rtx (SUBREG, SImode, operands[1], 0), + GEN_INT (8))); + emit_insn (gen_movqi (gen_rtx (MEM, QImode, base), + gen_rtx (SUBREG, QImode, operands[2], 0))); + } + else + { + emit_insn (gen_movqi (gen_rtx (MEM, QImode, base), + gen_rtx (SUBREG, QImode, operands[1], 0))); + emit_insn (gen_lshrsi3 (operands[2], + gen_rtx (SUBREG, SImode, operands[1], 0), + GEN_INT (8))); + emit_insn (gen_movqi (gen_rtx (MEM, QImode, plus_constant (base, 1)), + gen_rtx (SUBREG, QImode, operands[2], 0))); + } } /* Check to see if a branch is forwards or backwards. Return TRUE if it @@ -786,27 +1775,27 @@ rtx operands[]; int arm_backwards_branch (from, to) -int from, to; + int from, to; { - return (insn_addresses[to] <= insn_addresses[from]); + return insn_addresses[to] <= insn_addresses[from]; } /* Check to see if a branch is within the distance that can be done using an arithmetic expression. */ int short_branch (from, to) -int from, to; + int from, to; { - int delta = insn_addresses[from] + 2 - insn_addresses[to]; + int delta = insn_addresses[from] + 8 - insn_addresses[to]; - return abs (delta) < 245; /* A small margin for safety */ + return abs (delta) < 980; /* A small margin for safety */ } /* Check to see that the insn isn't the target of the conditionalizing code */ int arm_insn_not_targeted (insn) -rtx insn; + rtx insn; { return insn != arm_target_insn; } @@ -814,13 +1803,12 @@ rtx insn; /* Routines to output assembly language. */ -/* fp_immediate_constant - if the rtx is the correct value then return the string of the number. +/* If the rtx is the correct value then return the string of the number. In this way we can ensure that valid double constants are generated even when cross compiling. */ char * fp_immediate_constant (x) -rtx (x); + rtx x; { REAL_VALUE_TYPE r; int i; @@ -832,9 +1820,26 @@ rtx (x); for (i = 0; i < 8; i++) if (REAL_VALUES_EQUAL (r, values_fpa[i])) return strings_fpa[i]; + abort (); } +/* As for fp_immediate_constant, but value is passed directly, not in rtx. */ +static char * +fp_const_from_val (r) + REAL_VALUE_TYPE *r; +{ + int i; + + if (! fpa_consts_inited) + init_fpa_table (); + + for (i = 0; i < 8; i++) + if (REAL_VALUES_EQUAL (*r, values_fpa[i])) + return strings_fpa[i]; + + abort (); +} /* Output the operands of a LDM/STM instruction to STREAM. MASK is the ARM register set mask of which only bits 0-15 are important. @@ -850,39 +1855,42 @@ print_multi_reg (stream, instr, mask, ha int i; int not_first = FALSE; - fprintf (stream, "\t%s, {", instr); + fputc ('\t', stream); + fprintf (stream, instr, ARM_REG_PREFIX); + fputs (", {", stream); for (i = 0; i < 16; i++) if (mask & (1 << i)) { if (not_first) fprintf (stream, ", "); - fprintf (stream, "%s", reg_names[i]); + fprintf (stream, "%s%s", ARM_REG_PREFIX, reg_names[i]); not_first = TRUE; } + fprintf (stream, "}%s\n", hat ? "^" : ""); -} /* print_multi_reg */ +} /* Output a 'call' insn. */ char * output_call (operands) - rtx operands[]; + rtx *operands; { /* Handle calls to lr using ip (which may be clobbered in subr anyway). */ if (REGNO (operands[0]) == 14) { operands[0] = gen_rtx (REG, SImode, 12); - arm_output_asm_insn ("mov\t%0, lr", operands); + output_asm_insn ("mov%?\t%0, %|lr", operands); } - arm_output_asm_insn ("mov\tlr, pc", operands); - arm_output_asm_insn ("mov\tpc, %0", operands); - return (""); -} /* output_call */ + output_asm_insn ("mov%?\t%|lr, %|pc", operands); + output_asm_insn ("mov%?\t%|pc, %0", operands); + return ""; +} static int eliminate_lr2ip (x) -rtx *x; + rtx *x; { int something_changed = 0; rtx x0 = *x; @@ -916,17 +1924,18 @@ rtx *x; char * output_call_mem (operands) - rtx operands[]; + rtx *operands; { operands[0] = copy_rtx (operands[0]); /* Be ultra careful */ /* Handle calls using lr by using ip (which may be clobbered in subr anyway). */ if (eliminate_lr2ip (&operands[0])) - arm_output_asm_insn ("mov\tip, lr", operands); - arm_output_asm_insn ("mov\tlr, pc", operands); - arm_output_asm_insn ("ldr\tpc, %0", operands); - return (""); -} /* output_call */ + output_asm_insn ("mov%?\t%|ip, %|lr", operands); + + output_asm_insn ("mov%?\t%|lr, %|pc", operands); + output_asm_insn ("ldr%?\t%|pc, %0", operands); + return ""; +} /* Output a move from arm registers to an fpu registers. @@ -935,21 +1944,22 @@ output_call_mem (operands) char * output_mov_long_double_fpu_from_arm (operands) - rtx operands[]; + rtx *operands; { int arm_reg0 = REGNO (operands[1]); rtx ops[3]; if (arm_reg0 == 12) abort(); + ops[0] = gen_rtx (REG, SImode, arm_reg0); ops[1] = gen_rtx (REG, SImode, 1 + arm_reg0); ops[2] = gen_rtx (REG, SImode, 2 + arm_reg0); - arm_output_asm_insn ("stmfd\tsp!, {%0, %1, %2}", ops); - arm_output_asm_insn ("ldfe\t%0, [sp], #12", operands); - return (""); -} /* output_mov_long_double_fpu_from_arm */ + output_asm_insn ("stm%?fd\t%|sp!, {%0, %1, %2}", ops); + output_asm_insn ("ldf%?e\t%0, [%|sp], #12", operands); + return ""; +} /* Output a move from an fpu register to arm registers. OPERANDS[0] is the first registers of an arm register pair. @@ -957,28 +1967,29 @@ output_mov_long_double_fpu_from_arm (ope char * output_mov_long_double_arm_from_fpu (operands) - rtx operands[]; + rtx *operands; { int arm_reg0 = REGNO (operands[0]); rtx ops[3]; if (arm_reg0 == 12) abort(); + ops[0] = gen_rtx (REG, SImode, arm_reg0); ops[1] = gen_rtx (REG, SImode, 1 + arm_reg0); ops[2] = gen_rtx (REG, SImode, 2 + arm_reg0); - arm_output_asm_insn ("stfe\t%1, [sp, #-12]!", operands); - arm_output_asm_insn ("ldmfd\tsp!, {%0, %1, %2}", ops); - return(""); -} /* output_mov_long_double_arm_from_fpu */ + output_asm_insn ("stf%?e\t%1, [%|sp, #-12]!", operands); + output_asm_insn ("ldm%?fd\t%|sp!, {%0, %1, %2}", ops); + return ""; +} /* Output a move from arm registers to arm registers of a long double OPERANDS[0] is the destination. OPERANDS[1] is the source. */ char * output_mov_long_double_arm_from_arm (operands) -rtx operands[]; + rtx *operands; { /* We have to be careful here because the two might overlap */ int dest_start = REGNO (operands[0]); @@ -992,7 +2003,7 @@ rtx operands[]; { ops[0] = gen_rtx (REG, SImode, dest_start + i); ops[1] = gen_rtx (REG, SImode, src_start + i); - arm_output_asm_insn ("mov\t%0, %1", ops); + output_asm_insn ("mov%?\t%0, %1", ops); } } else @@ -1001,9 +2012,10 @@ rtx operands[]; { ops[0] = gen_rtx (REG, SImode, dest_start + i); ops[1] = gen_rtx (REG, SImode, src_start + i); - arm_output_asm_insn ("mov\t%0, %1", ops); + output_asm_insn ("mov%?\t%0, %1", ops); } } + return ""; } @@ -1014,7 +2026,7 @@ rtx operands[]; char * output_mov_double_fpu_from_arm (operands) - rtx operands[]; + rtx *operands; { int arm_reg0 = REGNO (operands[1]); rtx ops[2]; @@ -1023,10 +2035,10 @@ output_mov_double_fpu_from_arm (operands abort(); ops[0] = gen_rtx (REG, SImode, arm_reg0); ops[1] = gen_rtx (REG, SImode, 1 + arm_reg0); - arm_output_asm_insn ("stmfd\tsp!, {%0, %1}", ops); - arm_output_asm_insn ("ldfd\t%0, [sp], #8", operands); - return (""); -} /* output_mov_double_fpu_from_arm */ + output_asm_insn ("stm%?fd\t%|sp!, {%0, %1}", ops); + output_asm_insn ("ldf%?d\t%0, [%|sp], #8", operands); + return ""; +} /* Output a move from an fpu register to arm registers. OPERANDS[0] is the first registers of an arm register pair. @@ -1034,19 +2046,20 @@ output_mov_double_fpu_from_arm (operands char * output_mov_double_arm_from_fpu (operands) - rtx operands[]; + rtx *operands; { int arm_reg0 = REGNO (operands[0]); rtx ops[2]; if (arm_reg0 == 12) abort(); + ops[0] = gen_rtx (REG, SImode, arm_reg0); ops[1] = gen_rtx (REG, SImode, 1 + arm_reg0); - arm_output_asm_insn ("stfd\t%1, [sp, #-8]!", operands); - arm_output_asm_insn ("ldmfd\tsp!, {%0, %1}", ops); - return(""); -} /* output_mov_double_arm_from_fpu */ + output_asm_insn ("stf%?d\t%1, [%|sp, #-8]!", operands); + output_asm_insn ("ldm%?fd\t%|sp!, {%0, %1}", ops); + return ""; +} /* Output a move between double words. It must be REG<-REG, REG<-CONST_DOUBLE, REG<-CONST_INT, REG<-MEM @@ -1054,7 +2067,7 @@ output_mov_double_arm_from_fpu (operands char * output_move_double (operands) - rtx operands[]; + rtx *operands; { enum rtx_code code0 = GET_CODE (operands[0]); enum rtx_code code1 = GET_CODE (operands[1]); @@ -1070,18 +2083,19 @@ output_move_double (operands) int reg1 = REGNO (operands[1]); if (reg1 == 12) abort(); + otherops[1] = gen_rtx (REG, SImode, 1 + reg1); /* Ensure the second source is not overwritten */ if (reg0 == 1 + reg1) { - arm_output_asm_insn("mov\t%0, %1", otherops); - arm_output_asm_insn("mov\t%0, %1", operands); + output_asm_insn("mov%?\t%0, %1", otherops); + output_asm_insn("mov%?\t%0, %1", operands); } else { - arm_output_asm_insn("mov\t%0, %1", operands); - arm_output_asm_insn("mov\t%0, %1", otherops); + output_asm_insn("mov%?\t%0, %1", operands); + output_asm_insn("mov%?\t%0, %1", otherops); } } else if (code1 == CONST_DOUBLE) @@ -1100,9 +2114,9 @@ output_move_double (operands) /* Note: output_mov_immediate may clobber operands[1], so we put this out first */ if (INTVAL (operands[1]) < 0) - arm_output_asm_insn ("mvn\t%0, %1", otherops); + output_asm_insn ("mvn%?\t%0, %1", otherops); else - arm_output_asm_insn ("mov\t%0, %1", otherops); + output_asm_insn ("mov%?\t%0, %1", otherops); output_mov_immediate (operands, FALSE, ""); } else if (code1 == MEM) @@ -1112,40 +2126,35 @@ output_move_double (operands) case REG: /* Handle the simple case where address is [r, #0] more efficient. */ - operands[1] = XEXP (operands[1], 0); - arm_output_asm_insn ("ldmia\t%1, %M0", operands); + output_asm_insn ("ldm%?ia\t%m1, %M0", operands); break; case PRE_INC: - operands[1] = XEXP (XEXP (operands[1], 0), 0); - arm_output_asm_insn ("add\t%1, %1, #8", operands); - arm_output_asm_insn ("ldmia\t%1, %M0", operands); + output_asm_insn ("add%?\t%m1, %m1, #8", operands); + output_asm_insn ("ldm%?ia\t%m1, %M0", operands); break; case PRE_DEC: - operands[1] = XEXP (XEXP (operands[1], 0), 0); - arm_output_asm_insn ("sub\t%1, %1, #8", operands); - arm_output_asm_insn ("ldmia\t%1, %M0", operands); + output_asm_insn ("sub%?\t%m1, %m1, #8", operands); + output_asm_insn ("ldm%?ia\t%m1, %M0", operands); break; case POST_INC: - operands[1] = XEXP (XEXP (operands[1], 0), 0); - arm_output_asm_insn ("ldmia\t%1!, %M0", operands); + output_asm_insn ("ldm%?ia\t%m1!, %M0", operands); break; case POST_DEC: - operands[1] = XEXP (XEXP (operands[1], 0), 0); - arm_output_asm_insn ("ldmia\t%1, %M0", operands); - arm_output_asm_insn ("sub\t%1, %1, #8", operands); + output_asm_insn ("ldm%?ia\t%m1, %M0", operands); + output_asm_insn ("sub%?\t%m1, %m1, #8", operands); break; default: otherops[1] = adj_offsettable_operand (operands[1], 4); /* Take care of overlapping base/data reg. */ if (reg_mentioned_p (operands[0], operands[1])) { - arm_output_asm_insn ("ldr\t%0, %1", otherops); - arm_output_asm_insn ("ldr\t%0, %1", operands); + output_asm_insn ("ldr%?\t%0, %1", otherops); + output_asm_insn ("ldr%?\t%0, %1", operands); } else { - arm_output_asm_insn ("ldr\t%0, %1", operands); - arm_output_asm_insn ("ldr\t%0, %1", otherops); + output_asm_insn ("ldr%?\t%0, %1", operands); + output_asm_insn ("ldr%?\t%0, %1", otherops); } } } @@ -1158,39 +2167,34 @@ output_move_double (operands) switch (GET_CODE (XEXP (operands[0], 0))) { case REG: - operands[0] = XEXP (operands[0], 0); - arm_output_asm_insn ("stmia\t%0, %M1", operands); + output_asm_insn ("stm%?ia\t%m0, %M1", operands); break; case PRE_INC: - operands[0] = XEXP (XEXP (operands[0], 0), 0); - arm_output_asm_insn ("add\t%0, %0, #8", operands); - arm_output_asm_insn ("stmia\t%0, %M1", operands); + output_asm_insn ("add%?\t%m0, %m0, #8", operands); + output_asm_insn ("stm%?ia\t%m0, %M1", operands); break; case PRE_DEC: - operands[0] = XEXP (XEXP (operands[0], 0), 0); - arm_output_asm_insn ("sub\t%0, %0, #8", operands); - arm_output_asm_insn ("stmia\t%0, %M1", operands); + output_asm_insn ("sub%?\t%m0, %m0, #8", operands); + output_asm_insn ("stm%?ia\t%m0, %M1", operands); break; case POST_INC: - operands[0] = XEXP (XEXP (operands[0], 0), 0); - arm_output_asm_insn ("stmia\t%0!, %M1", operands); + output_asm_insn ("stm%?ia\t%m0!, %M1", operands); break; case POST_DEC: - operands[0] = XEXP (XEXP (operands[0], 0), 0); - arm_output_asm_insn ("stmia\t%0, %M1", operands); - arm_output_asm_insn ("sub\t%0, %0, #8", operands); + output_asm_insn ("stm%?ia\t%m0, %M1", operands); + output_asm_insn ("sub%?\t%m0, %m0, #8", operands); break; default: otherops[0] = adj_offsettable_operand (operands[0], 4); otherops[1] = gen_rtx (REG, SImode, 1 + REGNO (operands[1])); - arm_output_asm_insn ("str\t%1, %0", operands); - arm_output_asm_insn ("str\t%1, %0", otherops); + output_asm_insn ("str%?\t%1, %0", operands); + output_asm_insn ("str%?\t%1, %0", otherops); } } else abort(); /* Constraints should prevent this */ - return(""); -} /* output_move_double */ + return ""; +} /* Output an arbitrary MOV reg, #n. @@ -1198,23 +2202,25 @@ output_move_double (operands) char * output_mov_immediate (operands) - rtx operands[2]; + rtx *operands; { - int n = INTVAL (operands[1]); + HOST_WIDE_INT n = INTVAL (operands[1]); int n_ones = 0; int i; /* Try to use one MOV */ - if (const_ok_for_arm (n)) - return (arm_output_asm_insn ("mov\t%0, %1", operands)); + { + output_asm_insn ("mov%?\t%0, %1", operands); + return ""; + } /* Try to use one MVN */ - - if (const_ok_for_arm(~n)) + if (const_ok_for_arm (~n)) { - operands[1] = gen_rtx (CONST_INT, VOIDmode, ~n); - return (arm_output_asm_insn ("mvn\t%0, %1", operands)); + operands[1] = GEN_INT (~n); + output_asm_insn ("mvn%?\t%0, %1", operands); + return ""; } /* If all else fails, make it out of ORRs or BICs as appropriate. */ @@ -1224,11 +2230,14 @@ output_mov_immediate (operands) n_ones++; if (n_ones > 16) /* Shorter to use MVN with BIC in this case. */ - output_multi_immediate(operands, "mvn\t%0, %1", "bic\t%0, %0, %1", 1, ~n); + output_multi_immediate(operands, "mvn%?\t%0, %1", "bic%?\t%0, %0, %1", 1, + ~n); else - output_multi_immediate(operands, "mov\t%0, %1", "orr\t%0, %0, %1", 1, n); - return(""); -} /* output_mov_immediate */ + output_multi_immediate(operands, "mov%?\t%0, %1", "orr%?\t%0, %0, %1", 1, + n); + + return ""; +} /* Output an ADD r, s, #n where n may be too big for one instruction. If @@ -1236,22 +2245,24 @@ output_mov_immediate (operands) char * output_add_immediate (operands) - rtx operands[3]; + rtx *operands; { - int n = INTVAL (operands[2]); + HOST_WIDE_INT n = INTVAL (operands[2]); if (n != 0 || REGNO (operands[0]) != REGNO (operands[1])) { if (n < 0) output_multi_immediate (operands, - "sub\t%0, %1, %2", "sub\t%0, %0, %2", 2, -n); + "sub%?\t%0, %1, %2", "sub%?\t%0, %0, %2", 2, + -n); else output_multi_immediate (operands, - "add\t%0, %1, %2", "add\t%0, %0, %2", 2, n); + "add%?\t%0, %1, %2", "add%?\t%0, %0, %2", 2, + n); } - return(""); -} /* output_add_immediate */ + return ""; +} /* Output a multiple immediate operation. OPERANDS is the vector of operands referred to in the output patterns. @@ -1262,14 +2273,19 @@ output_add_immediate (operands) char * output_multi_immediate (operands, instr1, instr2, immed_op, n) - rtx operands[]; + rtx *operands; char *instr1, *instr2; - int immed_op, n; + int immed_op; + HOST_WIDE_INT n; { +#if HOST_BITS_PER_WIDE_INT > 32 + n &= 0xffffffff; +#endif + if (n == 0) { operands[immed_op] = const0_rtx; - arm_output_asm_insn (instr1, operands); /* Quick and easy output */ + output_asm_insn (instr1, operands); /* Quick and easy output */ } else { @@ -1277,21 +2293,19 @@ output_multi_immediate (operands, instr1 char *instr = instr1; /* Note that n is never zero here (which would give no output) */ - for (i = 0; i < 32; i += 2) { if (n & (3 << i)) { - operands[immed_op] = gen_rtx (CONST_INT, VOIDmode, - n & (255 << i)); - arm_output_asm_insn (instr, operands); + operands[immed_op] = GEN_INT (n & (255 << i)); + output_asm_insn (instr, operands); instr = instr2; i += 6; } } } - return (""); -} /* output_multi_immediate */ + return ""; +} /* Return the appropriate ARM instruction for the operation code. @@ -1302,204 +2316,127 @@ output_multi_immediate (operands, instr1 char * arithmetic_instr (op, shift_first_arg) rtx op; + int shift_first_arg; { - switch (GET_CODE(op)) + switch (GET_CODE (op)) { case PLUS: - return ("add"); + return "add"; + case MINUS: - if (shift_first_arg) - return ("rsb"); - else - return ("sub"); + return shift_first_arg ? "rsb" : "sub"; + case IOR: - return ("orr"); + return "orr"; + case XOR: - return ("eor"); + return "eor"; + case AND: - return ("and"); + return "and"; + default: - abort(); + abort (); } - return (""); /* stupid cc */ -} /* arithmetic_instr */ +} /* Ensure valid constant shifts and return the appropriate shift mnemonic for the operation code. The returned result should not be overwritten. OP is the rtx code of the shift. - SHIFT_PTR points to the shift size operand. */ + On exit, *AMOUNTP will be -1 if the shift is by a register, or a constant + shift. */ -char * -shift_instr (op, shift_ptr) - enum rtx_code op; - rtx *shift_ptr; +static char * +shift_op (op, amountp) + rtx op; + HOST_WIDE_INT *amountp; { - int min_shift = 0; - int max_shift = 31; char *mnem; + enum rtx_code code = GET_CODE (op); - switch (op) + if (GET_CODE (XEXP (op, 1)) == REG || GET_CODE (XEXP (op, 1)) == SUBREG) + *amountp = -1; + else if (GET_CODE (XEXP (op, 1)) == CONST_INT) + *amountp = INTVAL (XEXP (op, 1)); + else + abort (); + + switch (code) { case ASHIFT: mnem = "asl"; break; - case LSHIFT: - mnem = "lsl"; - break; + case ASHIFTRT: mnem = "asr"; - max_shift = 32; break; + case LSHIFTRT: mnem = "lsr"; - max_shift = 32; break; + + case ROTATERT: + mnem = "ror"; + break; + case MULT: - *shift_ptr = gen_rtx (CONST_INT, VOIDmode, - int_log2 (INTVAL (*shift_ptr))); - return ("asl"); + /* We never have to worry about the amount being other than a + power of 2, since this case can never be reloaded from a reg. */ + if (*amountp != -1) + *amountp = int_log2 (*amountp); + else + abort (); + return "asl"; + default: - abort(); + abort (); } - if (GET_CODE (*shift_ptr) == CONST_INT) + if (*amountp != -1) { - int shift = INTVAL (*shift_ptr); + /* This is not 100% correct, but follows from the desire to merge + multiplication by a power of 2 with the recognizer for a + shift. >=32 is not a valid shift for "asl", so we must try and + output a shift that produces the correct arithmetical result. + Using lsr #32 is idendical except for the fact that the carry bit + is not set correctly if we set the flags; but we never use the + carry bit from such an operation, so we can ignore that. */ + if (code == ROTATERT) + *amountp &= 31; /* Rotate is just modulo 32 */ + else if (*amountp != (*amountp & 31)) + { + if (code == ASHIFT) + mnem = "lsr"; + *amountp = 32; + } - if (shift < min_shift) - *shift_ptr = gen_rtx (CONST_INT, VOIDmode, 0); - else if (shift > max_shift) - *shift_ptr = gen_rtx (CONST_INT, VOIDmode, max_shift); - } - return (mnem); -} /* shift_instr */ + /* Shifts of 0 are no-ops. */ + if (*amountp == 0) + return NULL; + } + + return mnem; +} /* Obtain the shift from the POWER of two. */ -int +HOST_WIDE_INT int_log2 (power) - unsigned int power; + HOST_WIDE_INT power; { - int shift = 0; + HOST_WIDE_INT shift = 0; while (((1 << shift) & power) == 0) { if (shift > 31) - abort(); + abort (); shift++; } - return (shift); -} /* int_log2 */ - - -/* Output an arithmetic instruction which may set the condition code. - OPERANDS[0] is the destination register. - OPERANDS[1] is the arithmetic operator expression. - OPERANDS[2] is the left hand argument. - OPERANDS[3] is the right hand argument. - CONST_FIRST_ARG is TRUE if the first argument of the operator was constant. - SET_COND is TRUE when the condition code should be set. */ -char * -output_arithmetic (operands, const_first_arg, set_cond) - rtx operands[4]; - int const_first_arg; - int set_cond; -{ - char mnemonic[80]; - char *instr = arithmetic_instr (operands[1], const_first_arg); - - sprintf (mnemonic, "%s%s\t%%0, %%2, %%3", instr, set_cond ? "s" : ""); - return (arm_output_asm_insn (mnemonic, operands)); -} /* output_arithmetic */ - - -/* Output an arithmetic instruction with a shift. - OPERANDS[0] is the destination register. - OPERANDS[1] is the arithmetic operator expression. - OPERANDS[2] is the unshifted register. - OPERANDS[3] is the shift operator expression. - OPERANDS[4] is the shifted register. - OPERANDS[5] is the shift constant or register. - SHIFT_FIRST_ARG is TRUE if the first argument of the operator was shifted. - SET_COND is TRUE when the condition code should be set. */ - -char * -output_arithmetic_with_shift (operands, shift_first_arg, set_cond) - rtx operands[6]; - int shift_first_arg; - int set_cond; -{ - char mnemonic[80]; - char *instr = arithmetic_instr (operands[1], shift_first_arg); - char *condbit = set_cond ? "s" : ""; - char *shift = shift_instr (GET_CODE (operands[3]), &operands[5]); - - sprintf (mnemonic, "%s%s\t%%0, %%2, %%4, %s %%5", instr, condbit, shift); - return (arm_output_asm_insn (mnemonic, operands)); -} /* output_arithmetic_with_shift */ - - -/* Output an arithmetic instruction with a power of two multiplication. - OPERANDS[0] is the destination register. - OPERANDS[1] is the arithmetic operator expression. - OPERANDS[2] is the unmultiplied register. - OPERANDS[3] is the multiplied register. - OPERANDS[4] is the constant multiple (power of two). - SHIFT_FIRST_ARG is TRUE if the first arg of the operator was multiplied. */ - -char * -output_arithmetic_with_immediate_multiply (operands, shift_first_arg) - rtx operands[5]; - int shift_first_arg; -{ - char mnemonic[80]; - char *instr = arithmetic_instr (operands[1], shift_first_arg); - int shift = int_log2 (INTVAL (operands[4])); - - sprintf (mnemonic, "%s\t%%0, %%2, %%3, asl#%d", instr, shift); - return (arm_output_asm_insn (mnemonic, operands)); -} /* output_arithmetic_with_immediate_multiply */ - - -/* Output a move with a shift. - OP is the shift rtx code. - OPERANDS[0] = destination register. - OPERANDS[1] = source register. - OPERANDS[2] = shift constant or register. */ - -char * -output_shifted_move (op, operands) - enum rtx_code op; - rtx operands[2]; -{ - char mnemonic[80]; - - if (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) == 0) - sprintf (mnemonic, "mov\t%%0, %%1"); - else - sprintf (mnemonic, "mov\t%%0, %%1, %s %%2", - shift_instr (op, &operands[2])); - return (arm_output_asm_insn (mnemonic, operands)); -} /* output_shifted_move */ - -char * -output_shift_compare (operands, neg) -rtx *operands; -int neg; -{ - char buf[80]; - - if (neg) - sprintf (buf, "cmn\t%%1, %%3, %s %%4", shift_instr (GET_CODE (operands[2]), - &operands[4])); - else - sprintf (buf, "cmp\t%%1, %%3, %s %%4", shift_instr (GET_CODE (operands[2]), - &operands[4])); - return arm_output_asm_insn (buf, operands); -} /* output_shift_compare */ + return shift; +} /* Output a .ascii pseudo-op, keeping track of lengths. This is because /bin/as is horribly restrictive. */ @@ -1533,6 +2470,7 @@ output_ascii_pseudo_op (stream, p, len) putc('\\', stream); len_so_far++; } + if (c >= ' ' && c < 0177) { putc (c, stream); @@ -1543,11 +2481,13 @@ output_ascii_pseudo_op (stream, p, len) fprintf (stream, "\\%03o", c); len_so_far +=4; } + chars_so_far++; } + fputs ("\"\n", stream); arm_increase_location (chars_so_far); -} /* output_ascii_pseudo_op */ +} /* Try to determine whether a pattern really clobbers the link register. @@ -1560,7 +2500,7 @@ output_ascii_pseudo_op (stream, p, len) static int pattern_really_clobbers_lr (x) -rtx x; + rtx x; { int i; @@ -1571,34 +2511,43 @@ rtx x; { case REG: return REGNO (SET_DEST (x)) == 14; + case SUBREG: if (GET_CODE (XEXP (SET_DEST (x), 0)) == REG) return REGNO (XEXP (SET_DEST (x), 0)) == 14; + if (GET_CODE (XEXP (SET_DEST (x), 0)) == MEM) return 0; abort (); + default: return 0; } + case PARALLEL: for (i = 0; i < XVECLEN (x, 0); i++) if (pattern_really_clobbers_lr (XVECEXP (x, 0, i))) return 1; return 0; + case CLOBBER: switch (GET_CODE (XEXP (x, 0))) { case REG: return REGNO (XEXP (x, 0)) == 14; + case SUBREG: if (GET_CODE (XEXP (XEXP (x, 0), 0)) == REG) return REGNO (XEXP (XEXP (x, 0), 0)) == 14; abort (); + default: return 0; } + case UNSPEC: return 1; + default: return 0; } @@ -1606,7 +2555,7 @@ rtx x; static int function_really_clobbers_lr (first) -rtx first; + rtx first; { rtx insn, next; @@ -1620,15 +2569,18 @@ rtx first; case JUMP_INSN: /* Jump insns only change the PC (and conds) */ case INLINE_HEADER: break; + case INSN: if (pattern_really_clobbers_lr (PATTERN (insn))) return 1; break; + case CALL_INSN: /* Don't yet know how to handle those calls that are not to a SYMBOL_REF */ if (GET_CODE (PATTERN (insn)) != PARALLEL) abort (); + switch (GET_CODE (XVECEXP (PATTERN (insn), 0, 0))) { case CALL: @@ -1636,15 +2588,18 @@ rtx first; != SYMBOL_REF) return 1; break; + case SET: if (GET_CODE (XEXP (XEXP (SET_SRC (XVECEXP (PATTERN (insn), 0, 0)), 0), 0)) != SYMBOL_REF) return 1; break; + default: /* Don't recognize it, be safe */ return 1; } + /* A call can be made (by peepholing) not to clobber lr iff it is followed by a return. There may, however, be a use insn iff we are returning the result of the call. @@ -1654,40 +2609,63 @@ rtx first; must reject this. (Can this be fixed by adding our own insn?) */ if ((next = next_nonnote_insn (insn)) == NULL) return 1; + if (GET_CODE (next) == INSN && GET_CODE (PATTERN (next)) == USE && (GET_CODE (XVECEXP (PATTERN (insn), 0, 0)) == SET) && (REGNO (SET_DEST (XVECEXP (PATTERN (insn), 0, 0))) == REGNO (XEXP (PATTERN (next), 0)))) if ((next = next_nonnote_insn (next)) == NULL) return 1; + if (GET_CODE (next) == JUMP_INSN && GET_CODE (PATTERN (next)) == RETURN) break; return 1; + default: abort (); } } + /* We have reached the end of the chain so lr was _not_ clobbered */ return 0; } char * output_return_instruction (operand, really_return) -rtx operand; -int really_return; + rtx operand; + int really_return; { char instr[100]; int reg, live_regs = 0; + int volatile_func = (optimize > 0 + && TREE_THIS_VOLATILE (current_function_decl)); - if (current_function_calls_alloca && !really_return) + return_used_this_function = 1; + + if (volatile_func) + { + rtx ops[2]; + /* If this function was declared non-returning, and we have found a tail + call, then we have to trust that the called function won't return. */ + if (! really_return) + return ""; + + /* Otherwise, trap an attempted return by aborting. */ + ops[0] = operand; + ops[1] = gen_rtx (SYMBOL_REF, Pmode, "abort"); + output_asm_insn ("bl%d0\t%a1", ops); + return ""; + } + + if (current_function_calls_alloca && ! really_return) abort(); - for (reg = 4; reg < 10; reg++) - if (regs_ever_live[reg]) + for (reg = 0; reg <= 10; reg++) + if (regs_ever_live[reg] && ! call_used_regs[reg]) live_regs++; - if (live_regs || (regs_ever_live[14] && !lr_save_eliminated)) + if (live_regs || (regs_ever_live[14] && ! lr_save_eliminated)) live_regs++; if (frame_pointer_needed) @@ -1695,41 +2673,67 @@ int really_return; if (live_regs) { - if (lr_save_eliminated || !regs_ever_live[14]) + if (lr_save_eliminated || ! regs_ever_live[14]) live_regs++; + if (frame_pointer_needed) - strcpy (instr, "ldm%d0ea\tfp, {"); + strcpy (instr, "ldm%?%d0ea\t%|fp, {"); else - strcpy (instr, "ldm%d0fd\tsp!, {"); - for (reg = 4; reg < 10; reg++) - if (regs_ever_live[reg]) + strcpy (instr, "ldm%?%d0fd\t%|sp!, {"); + + for (reg = 0; reg <= 10; reg++) + if (regs_ever_live[reg] && ! call_used_regs[reg]) { + strcat (instr, "%|"); strcat (instr, reg_names[reg]); if (--live_regs) strcat (instr, ", "); } + if (frame_pointer_needed) { + strcat (instr, "%|"); strcat (instr, reg_names[11]); strcat (instr, ", "); + strcat (instr, "%|"); strcat (instr, reg_names[13]); strcat (instr, ", "); + strcat (instr, "%|"); strcat (instr, really_return ? reg_names[15] : reg_names[14]); } else - strcat (instr, really_return ? reg_names[15] : reg_names[14]); + { + strcat (instr, "%|"); + strcat (instr, really_return ? reg_names[15] : reg_names[14]); + } strcat (instr, (TARGET_6 || !really_return) ? "}" : "}^"); - arm_output_asm_insn (instr, &operand); + output_asm_insn (instr, &operand); } else if (really_return) { - strcpy (instr, TARGET_6 ? "mov%d0\tpc, lr" : "mov%d0s\tpc, lr"); - arm_output_asm_insn (instr, &operand); + strcpy (instr, + TARGET_6 ? "mov%?%d0\t%|pc, lr" : "mov%?%d0s\t%|pc, %|lr"); + output_asm_insn (instr, &operand); } - return_used_this_function = 1; + return ""; } +int +arm_volatile_func () +{ + return (optimize > 0 && TREE_THIS_VOLATILE (current_function_decl)); +} + +/* Return the size of the prologue. It's not too bad if we slightly + over-estimate. */ + +static int +get_prologue_size () +{ + return profile_flag ? 12 : 0; +} + /* The amount of stack adjustment that happens here, in output_return and in output_epilogue must be exactly the same as was calculated during reload, or things will point to the wrong place. The only time we can safely @@ -1740,12 +2744,14 @@ int really_return; onto the stack. */ void -output_prologue (f, frame_size) +output_func_prologue (f, frame_size) FILE *f; int frame_size; { - int reg, live_regs_mask = 0, code_size = 0; + int reg, live_regs_mask = 0; rtx operands[3]; + int volatile_func = (optimize > 0 + && TREE_THIS_VOLATILE (current_function_decl)); /* Nonzero if we must stuff some register arguments onto the stack as if they were passed there. */ @@ -1757,62 +2763,34 @@ output_prologue (f, frame_size) return_used_this_function = 0; lr_save_eliminated = 0; - fprintf (f, "\t@ args = %d, pretend = %d, frame = %d\n", - current_function_args_size, current_function_pretend_args_size, - frame_size); - fprintf (f, "\t@ frame_needed = %d, current_function_anonymous_args = %d\n", - frame_pointer_needed, current_function_anonymous_args); + fprintf (f, "\t%c args = %d, pretend = %d, frame = %d\n", + ARM_COMMENT_CHAR, current_function_args_size, + current_function_pretend_args_size, frame_size); + fprintf (f, "\t%c frame_needed = %d, current_function_anonymous_args = %d\n", + ARM_COMMENT_CHAR, frame_pointer_needed, + current_function_anonymous_args); + + if (volatile_func) + fprintf (f, "\t%c Volatile function.\n", ARM_COMMENT_CHAR); if (current_function_anonymous_args && current_function_pretend_args_size) store_arg_regs = 1; - for (reg = 4; reg < 10; reg++) - if (regs_ever_live[reg]) + for (reg = 0; reg <= 10; reg++) + if (regs_ever_live[reg] && ! call_used_regs[reg]) live_regs_mask |= (1 << reg); if (frame_pointer_needed) - { - live_regs_mask |= 0xD800; - fputs ("\tmov\tip, sp\n", f); - code_size += 4; - } + live_regs_mask |= 0xD800; else if (regs_ever_live[14]) { if (! current_function_args_size - && !function_really_clobbers_lr (get_insns ())) - { - fprintf (f,"\t@ I don't think this function clobbers lr\n"); - lr_save_eliminated = 1; - } + && ! function_really_clobbers_lr (get_insns ())) + lr_save_eliminated = 1; else live_regs_mask |= 0x4000; } - /* If CURRENT_FUNCTION_PRETEND_ARGS_SIZE, adjust the stack pointer to make - room. If also STORE_ARG_REGS store the argument registers involved in - the created slot (this is for stdarg and varargs). */ - if (current_function_pretend_args_size) - { - if (store_arg_regs) - { - int arg_size, mask = 0; - - assert (current_function_pretend_args_size <= 16); - for (reg = 3, arg_size = current_function_pretend_args_size; - arg_size > 0; reg--, arg_size -= 4) - mask |= (1 << reg); - print_multi_reg (f, "stmfd\tsp!", mask, FALSE); - code_size += 4; - } - else - { - operands[0] = operands[1] = stack_pointer_rtx; - operands[2] = gen_rtx (CONST_INT, VOIDmode, - -current_function_pretend_args_size); - output_add_immediate (operands); - } - } - if (live_regs_mask) { /* if a di mode load/store multiple is used, and the base register @@ -1822,41 +2800,17 @@ output_prologue (f, frame_size) live_regs_mask |= 0x4000; lr_save_eliminated = 0; - print_multi_reg (f, "stmfd\tsp!", live_regs_mask, FALSE); - code_size += 4; - } - - for (reg = 23; reg > 19; reg--) - if (regs_ever_live[reg]) - { - fprintf (f, "\tstfe\t%s, [sp, #-12]!\n", reg_names[reg]); - code_size += 4; - } - - if (frame_pointer_needed) - { - /* Make `fp' point to saved value of `pc'. */ - - operands[0] = gen_rtx (REG, SImode, HARD_FRAME_POINTER_REGNUM); - operands[1] = gen_rtx (REG, SImode, 12); - operands[2] = gen_rtx (CONST_INT, VOIDmode, - - (4 + current_function_pretend_args_size)); - output_add_immediate (operands); - } - if (frame_size) - { - operands[0] = operands[1] = stack_pointer_rtx; - operands[2] = gen_rtx (CONST_INT, VOIDmode, -frame_size); - output_add_immediate (operands); } - arm_increase_location (code_size); -} /* output_prologue */ + if (lr_save_eliminated) + fprintf (f,"\t%c I don't think this function clobbers lr\n", + ARM_COMMENT_CHAR); +} void -output_epilogue (f, frame_size) +output_func_epilogue (f, frame_size) FILE *f; int frame_size; { @@ -1864,6 +2818,8 @@ output_epilogue (f, frame_size) /* If we need this then it will always be at lesat this much */ int floats_offset = 24; rtx operands[3]; + int volatile_func = (optimize > 0 + && TREE_THIS_VOLATILE (current_function_decl)); if (use_return_insn() && return_used_this_function) { @@ -1871,30 +2827,38 @@ output_epilogue (f, frame_size) { abort (); } - return; + goto epilogue_done; } - for (reg = 4; reg <= 10; reg++) - if (regs_ever_live[reg]) + /* A volatile function should never return. Call abort. */ + if (volatile_func) + { + rtx op = gen_rtx (SYMBOL_REF, Pmode, "abort"); + output_asm_insn ("bl\t%a0", &op); + code_size = 4; + goto epilogue_done; + } + + for (reg = 0; reg <= 10; reg++) + if (regs_ever_live[reg] && ! call_used_regs[reg]) { live_regs_mask |= (1 << reg); floats_offset += 4; } - if (frame_pointer_needed) { - for (reg = 23; reg >= 20; reg--) - if (regs_ever_live[reg]) + for (reg = 23; reg > 15; reg--) + if (regs_ever_live[reg] && ! call_used_regs[reg]) { - fprintf (f, "\tldfe\t%s, [fp, #-%d]\n", reg_names[reg], - floats_offset); + fprintf (f, "\tldfe\t%s%s, [%sfp, #-%d]\n", ARM_REG_PREFIX, + reg_names[reg], ARM_REG_PREFIX, floats_offset); floats_offset += 12; code_size += 4; } live_regs_mask |= 0xA800; - print_multi_reg (f, "ldmea\tfp", live_regs_mask, + print_multi_reg (f, "ldmea\t%sfp", live_regs_mask, TARGET_6 ? FALSE : TRUE); code_size += 4; } @@ -1908,15 +2872,16 @@ output_epilogue (f, frame_size) output_add_immediate (operands); } - for (reg = 20; reg < 24; reg++) - if (regs_ever_live[reg]) + for (reg = 16; reg < 24; reg++) + if (regs_ever_live[reg] && ! call_used_regs[reg]) { - fprintf (f, "\tldfe\t%s, [sp], #12\n", reg_names[reg]); + fprintf (f, "\tldfe\t%s%s, [%ssp], #12\n", ARM_REG_PREFIX, + reg_names[reg], ARM_REG_PREFIX); code_size += 4; } if (current_function_pretend_args_size == 0 && regs_ever_live[14]) { - print_multi_reg (f, "ldmfd\tsp!", live_regs_mask | 0x8000, + print_multi_reg (f, "ldmfd\t%ssp!", live_regs_mask | 0x8000, TARGET_6 ? FALSE : TRUE); code_size += 4; } @@ -1925,7 +2890,7 @@ output_epilogue (f, frame_size) if (live_regs_mask || regs_ever_live[14]) { live_regs_mask |= 0x4000; - print_multi_reg (f, "ldmfd\tsp!", live_regs_mask, FALSE); + print_multi_reg (f, "ldmfd\t%ssp!", live_regs_mask, FALSE); code_size += 4; } if (current_function_pretend_args_size) @@ -1935,14 +2900,303 @@ output_epilogue (f, frame_size) current_function_pretend_args_size); output_add_immediate (operands); } - fputs (TARGET_6 ? "\tmov\tpc, lr\n" : "\tmovs\tpc, lr\n", f); + fprintf (f, + TARGET_6 ? "\tmov\t%spc, %slr\n" : "\tmovs\t%spc, %slr\n", + ARM_REG_PREFIX, ARM_REG_PREFIX, f); code_size += 4; } } - arm_increase_location (code_size); + + epilogue_done: + + /* insn_addresses isn't allocated when not optimizing */ + + if (optimize > 0) + arm_increase_location (code_size + + insn_addresses[INSN_UID (get_last_insn ())] + + get_prologue_size ()); + current_function_anonymous_args = 0; -} /* output_epilogue */ +} + +static void +emit_multi_reg_push (mask) + int mask; +{ + int num_regs = 0; + int i, j; + rtx par; + + for (i = 0; i < 16; i++) + if (mask & (1 << i)) + num_regs++; + + if (num_regs == 0 || num_regs > 16) + abort (); + + par = gen_rtx (PARALLEL, VOIDmode, rtvec_alloc (num_regs)); + + for (i = 0; i < 16; i++) + { + if (mask & (1 << i)) + { + XVECEXP (par, 0, 0) + = gen_rtx (SET, VOIDmode, gen_rtx (MEM, BLKmode, + gen_rtx (PRE_DEC, BLKmode, + stack_pointer_rtx)), + gen_rtx (UNSPEC, BLKmode, + gen_rtvec (1, gen_rtx (REG, SImode, i)), + 2)); + break; + } + } + + for (j = 1, i++; j < num_regs; i++) + { + if (mask & (1 << i)) + { + XVECEXP (par, 0, j) + = gen_rtx (USE, VOIDmode, gen_rtx (REG, SImode, i)); + j++; + } + } + emit_insn (par); +} + +void +arm_expand_prologue () +{ + int reg; + rtx amount = GEN_INT (- get_frame_size ()); + rtx push_insn; + int num_regs; + int live_regs_mask = 0; + int store_arg_regs = 0; + int volatile_func = (optimize > 0 + && TREE_THIS_VOLATILE (current_function_decl)); + + if (current_function_anonymous_args && current_function_pretend_args_size) + store_arg_regs = 1; + + if (! volatile_func) + for (reg = 0; reg <= 10; reg++) + if (regs_ever_live[reg] && ! call_used_regs[reg]) + live_regs_mask |= 1 << reg; + + if (! volatile_func && regs_ever_live[14]) + live_regs_mask |= 0x4000; + + if (frame_pointer_needed) + { + live_regs_mask |= 0xD800; + emit_insn (gen_movsi (gen_rtx (REG, SImode, 12), + stack_pointer_rtx)); + } + + if (current_function_pretend_args_size) + { + if (store_arg_regs) + emit_multi_reg_push ((0xf0 >> (current_function_pretend_args_size / 4)) + & 0xf); + else + emit_insn (gen_addsi3 (stack_pointer_rtx, stack_pointer_rtx, + GEN_INT (-current_function_pretend_args_size))); + } + + if (live_regs_mask) + { + /* If we have to push any regs, then we must push lr as well, or + we won't get a propper return. */ + live_regs_mask |= 0x4000; + emit_multi_reg_push (live_regs_mask); + } + + /* For now the integer regs are still pushed in output_func_epilogue (). */ + + if (! volatile_func) + for (reg = 23; reg > 15; reg--) + if (regs_ever_live[reg] && ! call_used_regs[reg]) + emit_insn (gen_rtx (SET, VOIDmode, + gen_rtx (MEM, XFmode, + gen_rtx (PRE_DEC, XFmode, + stack_pointer_rtx)), + gen_rtx (REG, XFmode, reg))); + + if (frame_pointer_needed) + emit_insn (gen_addsi3 (hard_frame_pointer_rtx, gen_rtx (REG, SImode, 12), + (GEN_INT + (-(4 + current_function_pretend_args_size))))); + + if (amount != const0_rtx) + { + emit_insn (gen_addsi3 (stack_pointer_rtx, stack_pointer_rtx, amount)); + emit_insn (gen_rtx (CLOBBER, VOIDmode, + gen_rtx (MEM, BLKmode, stack_pointer_rtx))); + } + + /* If we are profiling, make sure no instructions are scheduled before + the call to mcount. */ + if (profile_flag || profile_block_flag) + emit_insn (gen_blockage ()); +} + +/* If CODE is 'd', then the X is a condition operand and the instruction + should only be executed if the condition is true. + if CODE is 'D', then the X is a condition operand and the instruciton + should only be executed if the condition is false: however, if the mode + of the comparison is CCFPEmode, then always execute the instruction -- we + do this because in these circumstances !GE does not necessarily imply LT; + in these cases the instruction pattern will take care to make sure that + an instruction containing %d will follow, thereby undoing the effects of + doing this instrucion unconditionally. + If CODE is 'N' then X is a floating point operand that must be negated + before output. + If CODE is 'B' then output a bitwise inverted value of X (a const int). + If X is a REG and CODE is `M', output a ldm/stm style multi-reg. */ + +void +arm_print_operand (stream, x, code) + FILE *stream; + rtx x; + int code; +{ + switch (code) + { + case '@': + fputc (ARM_COMMENT_CHAR, stream); + return; + + case '|': + fputs (ARM_REG_PREFIX, stream); + return; + + case '?': + if (arm_ccfsm_state == 3 || arm_ccfsm_state == 4) + fputs (arm_condition_codes[arm_current_cc], stream); + return; + + case 'N': + { + REAL_VALUE_TYPE r; + REAL_VALUE_FROM_CONST_DOUBLE (r, x); + r = REAL_VALUE_NEGATE (r); + fprintf (stream, "%s", fp_const_from_val (&r)); + } + return; + + case 'B': + if (GET_CODE (x) == CONST_INT) + fprintf (stream, +#if HOST_BITS_PER_WIDE_INT == HOST_BITS_PER_INT + "%d", +#else + "%ld", +#endif + ARM_SIGN_EXTEND (~ INTVAL (x))); + else + { + putc ('~', stream); + output_addr_const (stream, x); + } + return; + + case 'i': + fprintf (stream, "%s", arithmetic_instr (x, 1)); + return; + + case 'I': + fprintf (stream, "%s", arithmetic_instr (x, 0)); + return; + + case 'S': + { + HOST_WIDE_INT val; + char *shift = shift_op (x, &val); + + if (shift) + { + fprintf (stream, ", %s ", shift_op (x, &val)); + if (val == -1) + arm_print_operand (stream, XEXP (x, 1), 0); + else + fprintf (stream, +#if HOST_BITS_PER_WIDE_INT == HOST_BITS_PER_INT + "#%d", +#else + "#%ld", +#endif + val); + } + } + return; + + case 'R': + if (REGNO (x) > 15) + abort (); + fputs (ARM_REG_PREFIX, stream); + fputs (reg_names[REGNO (x) + 1], stream); + return; + + case 'm': + fputs (ARM_REG_PREFIX, stream); + if (GET_CODE (XEXP (x, 0)) == REG) + fputs (reg_names[REGNO (XEXP (x, 0))], stream); + else + fputs (reg_names[REGNO (XEXP (XEXP (x, 0), 0))], stream); + return; + + case 'M': + fprintf (stream, "{%s%s-%s%s}", ARM_REG_PREFIX, reg_names[REGNO (x)], + ARM_REG_PREFIX, reg_names[REGNO (x) - 1 + + ((GET_MODE_SIZE (GET_MODE (x)) + + GET_MODE_SIZE (SImode) - 1) + / GET_MODE_SIZE (SImode))]); + return; + + case 'd': + if (x) + fputs (arm_condition_codes[get_arm_condition_code (x)], + stream); + return; + + case 'D': + if (x && (flag_fast_math + || GET_CODE (x) == EQ || GET_CODE (x) == NE + || (GET_MODE (XEXP (x, 0)) != CCFPEmode + && (GET_MODE_CLASS (GET_MODE (XEXP (x, 0))) + != MODE_FLOAT)))) + fputs (arm_condition_codes[ARM_INVERSE_CONDITION_CODE + (get_arm_condition_code (x))], + stream); + return; + + default: + if (x == 0) + abort (); + + if (GET_CODE (x) == REG) + { + fputs (ARM_REG_PREFIX, stream); + fputs (reg_names[REGNO (x)], stream); + } + else if (GET_CODE (x) == MEM) + { + output_memory_reference_mode = GET_MODE (x); + output_address (XEXP (x, 0)); + } + else if (GET_CODE (x) == CONST_DOUBLE) + fprintf (stream, "#%s", fp_immediate_constant (x)); + else if (GET_CODE (x) == NEG) + abort (); /* This should never happen now. */ + else + { + fputc ('#', stream); + output_addr_const (stream, x); + } + } +} + /* Increase the `arm_text_location' by AMOUNT if we're in the text segment. */ @@ -1952,26 +3206,7 @@ arm_increase_location (amount) { if (in_text_section ()) arm_text_location += amount; -} /* arm_increase_location */ - - -/* Like output_asm_insn (), but also increases the arm_text_location (if in - the .text segment, of course, even though this will always be true). - Returns the empty string. */ - -char * -arm_output_asm_insn (template, operands) - char *template; - rtx *operands; -{ - extern FILE *asm_out_file; - - output_asm_insn (template, operands); - if (in_text_section ()) - arm_text_location += 4; - fflush (asm_out_file); - return (""); -} /* arm_output_asm_insn */ +} /* Output a label definition. If this label is within the .text segment, it @@ -2007,91 +3242,38 @@ arm_asm_output_label (stream, name) } for (s = real_name; *s; s++) hash += *s; + hash = hash % LABEL_HASH_SIZE; cur = (struct label_offset *) xmalloc (sizeof (struct label_offset)); cur->name = real_name; cur->offset = arm_text_location; cur->cdr = offset_table[hash]; offset_table[hash] = cur; -} /* arm_asm_output_label */ - - -/* Output the instructions needed to perform what Martin's /bin/as called - llc: load an SImode thing from the function's constant pool. +} - XXX This could be enhanced in that we do not really need a pointer in the - constant pool pointing to the real thing. If we can address this pointer, - we can also address what it is pointing at, in fact, anything in the text - segment which has been defined already within this .s file. */ +/* Load a symbol that is known to be in the text segment into a register. + This should never be called when not optimizing. */ char * -arm_output_llc (operands) +output_load_symbol (insn, operands) + rtx insn; rtx *operands; { - char *s, *name = XSTR (XEXP (operands[1], 0), 0); - struct label_offset *he; - int hash = 0, conditional = (arm_ccfsm_state == 3 || arm_ccfsm_state == 4); - - if (*name != '*') - abort (); - - for (s = &name[1]; *s; s++) - hash += *s; - hash = hash % LABEL_HASH_SIZE; - he = offset_table[hash]; - while (he && strcmp (he->name, &name[1])) - he = he->cdr; - - if (!he) - abort (); - - if (arm_text_location + 8 - he->offset < 4095) - { - fprintf (asm_out_file, "\tldr%s\t%s, [pc, #%s - . - 8]\n", - conditional ? arm_condition_codes[arm_current_cc] : "", - reg_names[REGNO (operands[0])], &name[1]); - arm_increase_location (4); - return (""); - } - else - { - int offset = - (arm_text_location + 8 - he->offset); - char *reg_name = reg_names[REGNO (operands[0])]; - - /* ??? This is a hack, assuming the constant pool never is more than - (1 + 255) * 4096 == 1Meg away from the PC. */ - - if (offset > 1000000) - abort (); - - fprintf (asm_out_file, "\tsub%s\t%s, pc, #(8 + . - %s) & ~4095\n", - conditional ? arm_condition_codes[arm_current_cc] : "", - reg_name, &name[1]); - fprintf (asm_out_file, "\tldr%s\t%s, [%s, #- ((4 + . - %s) & 4095)]\n", - conditional ? arm_condition_codes[arm_current_cc] : "", - reg_name, reg_name, &name[1]); - arm_increase_location (8); - } - return (""); -} /* arm_output_llc */ - -/* output_load_symbol () - load a symbol that is known to be in the text segment into a register */ - -char * -output_load_symbol (operands) -rtx *operands; -{ - char *s, *name = XSTR (operands[1], 0); + char *s; + char *name = XSTR (operands[1], 0); struct label_offset *he; int hash = 0; int offset; - - if (*name != '*') + unsigned int mask, never_mask = 0xffffffff; + int shift, inst; + char buffer[100]; + + if (optimize == 0 || *name != '*') abort (); for (s = &name[1]; *s; s++) hash += *s; + hash = hash % LABEL_HASH_SIZE; he = offset_table[hash]; while (he && strcmp (he->name, &name[1])) @@ -2100,45 +3282,48 @@ rtx *operands; if (!he) abort (); - offset = (arm_text_location + 8 - he->offset); + offset = (arm_text_location + insn_addresses[INSN_UID (insn)] + + get_prologue_size () + 8 - he->offset); if (offset < 0) abort (); + /* When generating the instructions, we never mask out the bits that we + think will be always zero, then if a mistake has occured somewhere, the + assembler will spot it and generate an error. */ + /* If the symbol is word aligned then we might be able to reduce the - number of loads */ - if ((offset & 3) == 0) + number of loads. */ + shift = ((offset & 3) == 0) ? 2 : 0; + + /* Clear the bits from NEVER_MASK that will be orred in with the individual + instructions. */ + for (; shift < 32; shift += 8) { - arm_output_asm_insn ("sub\t%0, pc, #(8 + . -%a1) & 1023", operands); - if (offset > 0x3ff) - { - arm_output_asm_insn ("sub\t%0, %0, #(4 + . -%a1) & 261120", - operands); - if (offset > 0x3ffff) - { - arm_output_asm_insn ("sub\t%0, %0, #(. -%a1) & 66846720", - operands); - if (offset > 0x3ffffff) - arm_output_asm_insn ("sub\t%0, %0, #(. - 4 -%a1) & -67108864", - operands); - } - } + mask = 0xff << shift; + if ((offset & mask) || ((unsigned) offset) > mask) + never_mask &= ~mask; } - else + + inst = 8; + mask = 0xff << (shift - 32); + + while (mask && (never_mask & mask) == 0) { - arm_output_asm_insn ("sub\t%0, pc, #(8 + . -%a1) & 255", operands); - if (offset > 0x0ff) - { - arm_output_asm_insn ("sub\t%0, %0, #(4 + . -%a1) & 65280", operands); - if (offset > 0x0ffff) - { - arm_output_asm_insn ("sub\t%0, %0, #(. -%a1) & 16711680", - operands); - if (offset > 0x0ffffff) - arm_output_asm_insn ("sub\t%0, %0, #(. - 4 -%a1) & -16777216", - operands); - } - } + if (inst == 8) + { + strcpy (buffer, "sub%?\t%0, %|pc, #(8 + . -%a1)"); + if ((never_mask | mask) != 0xffffffff) + sprintf (buffer + strlen (buffer), " & 0x%x", mask | never_mask); + } + else + sprintf (buffer, "sub%%?\t%%0, %%0, #(%d + . -%%a1) & 0x%x", + inst, mask | never_mask); + + output_asm_insn (buffer, operands); + mask <<= 8; + inst -= 4; } + return ""; } @@ -2155,14 +3340,14 @@ output_lcomm_directive (stream, name, si char *name; int size, rounded; { - fputs ("\n\t.bss\t@ .lcomm\n", stream); + fprintf (stream, "\n\t.bss\t%c .lcomm\n", ARM_COMMENT_CHAR); assemble_name (stream, name); fprintf (stream, ":\t.space\t%d\n", rounded); if (in_text_section ()) fputs ("\n\t.text\n", stream); else fputs ("\n\t.data\n", stream); -} /* output_lcomm_directive */ +} /* A finite state machine takes care of noticing whether or not instructions can be conditionally executed, and thus decrease execution time and code @@ -2195,15 +3380,6 @@ output_lcomm_directive (stream, name, si time. But then, I want to reduce the code size to somewhere near what /bin/cc produces. */ -/* The condition codes of the ARM, and the inverse function. */ -char *arm_condition_codes[] = -{ - "eq", "ne", "cs", "cc", "mi", "pl", "vs", "vc", - "hi", "ls", "ge", "lt", "gt", "le", "al", "nv" -}; - -#define ARM_INVERSE_CONDITION_CODE(X) ((X) ^ 1) - /* Returns the index of the ARM condition code string in `arm_condition_codes'. COMPARISON should be an rtx like `(eq (...) (...))'. */ @@ -2228,7 +3404,7 @@ get_arm_condition_code (comparison) } /*NOTREACHED*/ return (42); -} /* get_arm_condition_code */ +} void @@ -2338,7 +3514,13 @@ final_prescan_insn (insn, opvec, noperan rtx this_insn = start_insn, label = 0; if (get_attr_conds (insn) == CONDS_JUMP_CLOB) - jump_clobbers = 1; + { + /* The code below is wrong for these, and I haven't time to + fix it now. So we just do the safe thing and return. This + whole function needs re-writing anyway. */ + jump_clobbers = 1; + return; + } /* Register the insn jumped to. */ if (reverse) @@ -2530,6 +3712,6 @@ final_prescan_insn (insn, opvec, noperan call recog direct). */ recog (PATTERN (insn), insn, NULL_PTR); } -} /* final_prescan_insn */ +} /* EOF */