X-Git-Url: http://git.sourceforge.jp/view?a=blobdiff_plain;f=gcc%2Fconfig%2Fh8300%2Fh8300.c;h=ab2cc1544ed79c2132c3b39c6eb15c99141f05cc;hb=8097f8ec363cc654c2f6ea04db3ddfec27edf576;hp=1f178b8270ba3c6c6baca5361b55c0bb8174801a;hpb=1592a00c9ccb7e8509797dbda7f3c5264e694d83;p=pf3gnuchains%2Fgcc-fork.git diff --git a/gcc/config/h8300/h8300.c b/gcc/config/h8300/h8300.c index 1f178b8270b..ab2cc1544ed 100644 --- a/gcc/config/h8300/h8300.c +++ b/gcc/config/h8300/h8300.c @@ -23,6 +23,8 @@ Boston, MA 02111-1307, USA. */ #include "config.h" #include "system.h" +#include "coretypes.h" +#include "tm.h" #include "rtl.h" #include "tree.h" #include "regs.h" @@ -44,24 +46,30 @@ Boston, MA 02111-1307, USA. */ #include "target-def.h" /* Forward declarations. */ +static const char *byte_reg PARAMS ((rtx, int)); static int h8300_interrupt_function_p PARAMS ((tree)); static int h8300_monitor_function_p PARAMS ((tree)); static int h8300_os_task_function_p PARAMS ((tree)); -static void dosize PARAMS ((FILE *, const char *, unsigned int)); +static void dosize PARAMS ((FILE *, int, unsigned int)); static int round_frame_size PARAMS ((int)); static unsigned int compute_saved_regs PARAMS ((void)); static void push PARAMS ((FILE *, int)); static void pop PARAMS ((FILE *, int)); static const char *cond_string PARAMS ((enum rtx_code)); +static unsigned int h8300_asm_insn_count PARAMS ((const char *)); const struct attribute_spec h8300_attribute_table[]; static tree h8300_handle_fndecl_attribute PARAMS ((tree *, tree, tree, int, bool *)); static tree h8300_handle_eightbit_data_attribute PARAMS ((tree *, tree, tree, int, bool *)); static tree h8300_handle_tiny_data_attribute PARAMS ((tree *, tree, tree, int, bool *)); static void h8300_output_function_prologue PARAMS ((FILE *, HOST_WIDE_INT)); static void h8300_output_function_epilogue PARAMS ((FILE *, HOST_WIDE_INT)); +static void h8300_insert_attributes PARAMS ((tree, tree *)); #ifndef OBJECT_FORMAT_ELF static void h8300_asm_named_section PARAMS ((const char *, unsigned int)); #endif +static void h8300_encode_label PARAMS ((tree)); +static void h8300_encode_section_info PARAMS ((tree, int)); +static const char *h8300_strip_name_encoding PARAMS ((const char *)); /* CPU_TYPE, says what cpu we're compiling for. */ int cpu_type; @@ -109,9 +117,176 @@ const char *h8_push_op, *h8_pop_op, *h8_mov_op; #define TARGET_ASM_FUNCTION_PROLOGUE h8300_output_function_prologue #undef TARGET_ASM_FUNCTION_EPILOGUE #define TARGET_ASM_FUNCTION_EPILOGUE h8300_output_function_epilogue +#undef TARGET_ENCODE_SECTION_INFO +#define TARGET_ENCODE_SECTION_INFO h8300_encode_section_info +#undef TARGET_STRIP_NAME_ENCODING +#define TARGET_STRIP_NAME_ENCODING h8300_strip_name_encoding + +#undef TARGET_INSERT_ATTRIBUTES +#define TARGET_INSERT_ATTRIBUTES h8300_insert_attributes struct gcc_target targetm = TARGET_INITIALIZER; +/* See below where shifts are handled for explanation of this enum. */ + +enum shift_alg +{ + SHIFT_INLINE, + SHIFT_ROT_AND, + SHIFT_SPECIAL, + SHIFT_LOOP +}; + +/* Symbols of the various shifts which can be used as indices. */ + +enum shift_type +{ + SHIFT_ASHIFT, SHIFT_LSHIFTRT, SHIFT_ASHIFTRT +}; + +/* Macros to keep the shift algorithm tables small. */ +#define INL SHIFT_INLINE +#define ROT SHIFT_ROT_AND +#define LOP SHIFT_LOOP +#define SPC SHIFT_SPECIAL + +/* The shift algorithms for each machine, mode, shift type, and shift + count are defined below. The three tables below correspond to + QImode, HImode, and SImode, respectively. Each table is organized + by, in the order of indecies, machine, shift type, and shift count. */ + +static enum shift_alg shift_alg_qi[3][3][8] = { + { + /* TARGET_H8300 */ + /* 0 1 2 3 4 5 6 7 */ + { INL, INL, INL, INL, INL, ROT, ROT, ROT }, /* SHIFT_ASHIFT */ + { INL, INL, INL, INL, INL, ROT, ROT, ROT }, /* SHIFT_LSHIFTRT */ + { INL, INL, INL, INL, INL, LOP, LOP, SPC } /* SHIFT_ASHIFTRT */ + }, + { + /* TARGET_H8300H */ + /* 0 1 2 3 4 5 6 7 */ + { INL, INL, INL, INL, INL, ROT, ROT, ROT }, /* SHIFT_ASHIFT */ + { INL, INL, INL, INL, INL, ROT, ROT, ROT }, /* SHIFT_LSHIFTRT */ + { INL, INL, INL, INL, INL, LOP, LOP, SPC } /* SHIFT_ASHIFTRT */ + }, + { + /* TARGET_H8300S */ + /* 0 1 2 3 4 5 6 7 */ + { INL, INL, INL, INL, INL, INL, ROT, ROT }, /* SHIFT_ASHIFT */ + { INL, INL, INL, INL, INL, INL, ROT, ROT }, /* SHIFT_LSHIFTRT */ + { INL, INL, INL, INL, INL, INL, INL, SPC } /* SHIFT_ASHIFTRT */ + } +}; + +static enum shift_alg shift_alg_hi[3][3][16] = { + { + /* TARGET_H8300 */ + /* 0 1 2 3 4 5 6 7 */ + /* 8 9 10 11 12 13 14 15 */ + { INL, INL, INL, INL, INL, INL, INL, SPC, + SPC, SPC, SPC, SPC, SPC, SPC, SPC, SPC }, /* SHIFT_ASHIFT */ + { INL, INL, INL, INL, INL, LOP, LOP, SPC, + SPC, SPC, SPC, SPC, SPC, SPC, SPC, SPC }, /* SHIFT_LSHIFTRT */ + { INL, INL, INL, INL, INL, LOP, LOP, SPC, + SPC, SPC, SPC, SPC, SPC, SPC, SPC, SPC }, /* SHIFT_ASHIFTRT */ + }, + { + /* TARGET_H8300H */ + /* 0 1 2 3 4 5 6 7 */ + /* 8 9 10 11 12 13 14 15 */ + { INL, INL, INL, INL, INL, INL, INL, SPC, + SPC, SPC, SPC, SPC, SPC, ROT, ROT, ROT }, /* SHIFT_ASHIFT */ + { INL, INL, INL, INL, INL, INL, INL, SPC, + SPC, SPC, SPC, SPC, SPC, ROT, ROT, ROT }, /* SHIFT_LSHIFTRT */ + { INL, INL, INL, INL, INL, INL, INL, SPC, + SPC, SPC, SPC, SPC, SPC, SPC, SPC, SPC }, /* SHIFT_ASHIFTRT */ + }, + { + /* TARGET_H8300S */ + /* 0 1 2 3 4 5 6 7 */ + /* 8 9 10 11 12 13 14 15 */ + { INL, INL, INL, INL, INL, INL, INL, INL, + SPC, SPC, SPC, SPC, SPC, ROT, ROT, ROT }, /* SHIFT_ASHIFT */ + { INL, INL, INL, INL, INL, INL, INL, INL, + SPC, SPC, SPC, SPC, SPC, ROT, ROT, ROT }, /* SHIFT_LSHIFTRT */ + { INL, INL, INL, INL, INL, INL, INL, INL, + SPC, SPC, SPC, SPC, SPC, SPC, SPC, SPC }, /* SHIFT_ASHIFTRT */ + } +}; + +static enum shift_alg shift_alg_si[3][3][32] = { + { + /* TARGET_H8300 */ + /* 0 1 2 3 4 5 6 7 */ + /* 8 9 10 11 12 13 14 15 */ + /* 16 17 18 19 20 21 22 23 */ + /* 24 25 26 27 28 29 30 31 */ + { INL, INL, INL, LOP, LOP, LOP, LOP, LOP, + SPC, LOP, LOP, LOP, LOP, LOP, LOP, LOP, + SPC, SPC, SPC, SPC, SPC, LOP, LOP, LOP, + SPC, SPC, SPC, SPC, LOP, LOP, LOP, SPC }, /* SHIFT_ASHIFT */ + { INL, INL, INL, LOP, LOP, LOP, LOP, LOP, + SPC, SPC, LOP, LOP, LOP, LOP, LOP, SPC, + SPC, SPC, SPC, LOP, LOP, LOP, LOP, LOP, + SPC, SPC, SPC, SPC, SPC, LOP, LOP, SPC }, /* SHIFT_LSHIFTRT */ + { INL, INL, INL, LOP, LOP, LOP, LOP, LOP, + SPC, LOP, LOP, LOP, LOP, LOP, LOP, SPC, + SPC, SPC, LOP, LOP, LOP, LOP, LOP, LOP, + SPC, SPC, SPC, LOP, LOP, LOP, LOP, SPC }, /* SHIFT_ASHIFTRT */ + }, + { + /* TARGET_H8300H */ + /* 0 1 2 3 4 5 6 7 */ + /* 8 9 10 11 12 13 14 15 */ + /* 16 17 18 19 20 21 22 23 */ + /* 24 25 26 27 28 29 30 31 */ + { INL, INL, INL, INL, INL, LOP, LOP, LOP, + SPC, LOP, LOP, LOP, LOP, LOP, LOP, SPC, + SPC, SPC, SPC, SPC, LOP, LOP, LOP, LOP, + SPC, LOP, LOP, LOP, SPC, SPC, SPC, SPC }, /* SHIFT_ASHIFT */ + { INL, INL, INL, INL, INL, LOP, LOP, LOP, + SPC, LOP, LOP, LOP, LOP, LOP, LOP, SPC, + SPC, SPC, SPC, SPC, LOP, LOP, LOP, LOP, + SPC, LOP, LOP, LOP, SPC, SPC, SPC, SPC }, /* SHIFT_LSHIFTRT */ + { INL, INL, INL, INL, INL, LOP, LOP, LOP, + SPC, LOP, LOP, LOP, LOP, LOP, LOP, LOP, + SPC, SPC, SPC, SPC, LOP, LOP, LOP, LOP, + SPC, LOP, LOP, LOP, LOP, LOP, LOP, SPC }, /* SHIFT_ASHIFTRT */ + }, + { + /* TARGET_H8300S */ + /* 0 1 2 3 4 5 6 7 */ + /* 8 9 10 11 12 13 14 15 */ + /* 16 17 18 19 20 21 22 23 */ + /* 24 25 26 27 28 29 30 31 */ + { INL, INL, INL, INL, INL, INL, INL, INL, + INL, INL, INL, LOP, LOP, LOP, LOP, SPC, + SPC, SPC, SPC, SPC, SPC, SPC, LOP, LOP, + SPC, SPC, LOP, LOP, SPC, SPC, SPC, SPC }, /* SHIFT_ASHIFT */ + { INL, INL, INL, INL, INL, INL, INL, INL, + INL, INL, INL, LOP, LOP, LOP, LOP, SPC, + SPC, SPC, SPC, SPC, SPC, SPC, LOP, LOP, + SPC, SPC, LOP, LOP, SPC, SPC, SPC, SPC }, /* SHIFT_LSHIFTRT */ + { INL, INL, INL, INL, INL, INL, INL, INL, + INL, INL, INL, LOP, LOP, LOP, LOP, LOP, + SPC, SPC, SPC, SPC, SPC, SPC, LOP, LOP, + SPC, SPC, LOP, LOP, LOP, LOP, LOP, SPC }, /* SHIFT_ASHIFTRT */ + } +}; + +#undef INL +#undef ROT +#undef LOP +#undef SPC + +enum h8_cpu +{ + H8_300, + H8_300H, + H8_S +}; + /* Initialize various cpu specific globals at start up. */ void @@ -128,7 +303,7 @@ h8300_init_once () } else { - /* For this we treat the H8/300H and H8/S the same. */ + /* For this we treat the H8/300H and H8S the same. */ cpu_type = (int) CPU_H8300H; h8_reg_names = names_extended; } @@ -139,11 +314,51 @@ h8300_init_once () if (!TARGET_H8300S && TARGET_MAC) { error ("-ms2600 is used without -ms"); - target_flags |= 1; + target_flags |= MASK_H8300S; + } + + if (TARGET_H8300 && TARGET_NORMAL_MODE) + { + error ("-mn is used without -mh or -ms"); + target_flags ^= MASK_NORMAL_MODE; + } + + /* Some of the shifts are optimized for speed by default. + See http://gcc.gnu.org/ml/gcc-patches/2002-07/msg01858.html + If optimizing for size, change shift_alg for those shift to + SHIFT_LOOP. */ + if (optimize_size) + { + /* H8/300 */ + shift_alg_hi[H8_300][SHIFT_ASHIFT][5] = SHIFT_LOOP; + shift_alg_hi[H8_300][SHIFT_ASHIFT][6] = SHIFT_LOOP; + shift_alg_hi[H8_300][SHIFT_ASHIFT][13] = SHIFT_LOOP; + shift_alg_hi[H8_300][SHIFT_ASHIFT][14] = SHIFT_LOOP; + + shift_alg_hi[H8_300][SHIFT_LSHIFTRT][13] = SHIFT_LOOP; + shift_alg_hi[H8_300][SHIFT_LSHIFTRT][14] = SHIFT_LOOP; + + shift_alg_hi[H8_300][SHIFT_ASHIFTRT][13] = SHIFT_LOOP; + shift_alg_hi[H8_300][SHIFT_ASHIFTRT][14] = SHIFT_LOOP; + + /* H8/300H */ + shift_alg_hi[H8_300H][SHIFT_ASHIFT][5] = SHIFT_LOOP; + shift_alg_hi[H8_300H][SHIFT_ASHIFT][6] = SHIFT_LOOP; + + shift_alg_hi[H8_300H][SHIFT_LSHIFTRT][5] = SHIFT_LOOP; + shift_alg_hi[H8_300H][SHIFT_LSHIFTRT][6] = SHIFT_LOOP; + + shift_alg_hi[H8_300H][SHIFT_ASHIFTRT][5] = SHIFT_LOOP; + shift_alg_hi[H8_300H][SHIFT_ASHIFTRT][6] = SHIFT_LOOP; + shift_alg_hi[H8_300H][SHIFT_ASHIFTRT][13] = SHIFT_LOOP; + shift_alg_hi[H8_300H][SHIFT_ASHIFTRT][14] = SHIFT_LOOP; + + /* H8S */ + shift_alg_hi[H8_S][SHIFT_ASHIFTRT][14] = SHIFT_LOOP; } } -const char * +static const char * byte_reg (x, b) rtx x; int b; @@ -179,12 +394,12 @@ byte_reg (x, b) SIZE to adjust the stack pointer. */ static void -dosize (file, op, size) +dosize (file, sign, size) FILE *file; - const char *op; + int sign; unsigned int size; { - /* On the H8/300H and H8/S, for sizes <= 8 bytes, it is as good or + /* On the H8/300H and H8S, for sizes <= 8 bytes, it is as good or better to use adds/subs insns rather than add.l/sub.l with an immediate value. @@ -195,8 +410,9 @@ dosize (file, op, size) || ((TARGET_H8300H || TARGET_H8300S) && size <= 8) || (TARGET_H8300 && interrupt_handler) || (TARGET_H8300 && current_function_needs_context - && ! strcmp (op, "sub"))) + && sign < 0)) { + const char *op = (sign > 0) ? "add" : "sub"; unsigned HOST_WIDE_INT amount; /* Try different amounts in descending order. */ @@ -204,16 +420,24 @@ dosize (file, op, size) amount > 0; amount /= 2) { + char insn[100]; + + sprintf (insn, "\t%ss\t#%d,%s\n", op, amount, + TARGET_H8300 ? "r7" : "er7"); for (; size >= amount; size -= amount) - fprintf (file, "\t%ss\t#%d,sp\n", op, amount); + fputs (insn, file); } } else { if (TARGET_H8300) - fprintf (file, "\tmov.w\t#%d,r3\n\t%s.w\tr3,sp\n", size, op); + { + fprintf (file, "\tmov.w\t#%d,r3\n\tadd.w\tr3,r7\n", sign * size); + } else - fprintf (file, "\t%s.l\t#%d,sp\n", op, size); + { + fprintf (file, "\tadd.l\t#%d,er7\n", sign * size); + } } } @@ -223,7 +447,8 @@ static int round_frame_size (size) int size; { - return (size + STACK_BOUNDARY / 8 - 1) & -STACK_BOUNDARY / 8; + return ((size + STACK_BOUNDARY / BITS_PER_UNIT - 1) + & -STACK_BOUNDARY / BITS_PER_UNIT); } /* Compute which registers to push/pop. @@ -236,7 +461,7 @@ compute_saved_regs () int regno; /* Construct a bit vector of registers to be pushed/popped. */ - for (regno = 0; regno <= 6; regno++) + for (regno = 0; regno <= FRAME_POINTER_REGNUM; regno++) { if (WORD_REG_USED (regno)) saved_regs |= 1 << regno; @@ -256,7 +481,10 @@ push (file, rn) FILE *file; int rn; { - fprintf (file, "\t%s\t%s\n", h8_push_op, h8_reg_names[rn]); + if (TARGET_H8300) + fprintf (file, "\t%s\t%s,@-r7\n", h8_mov_op, h8_reg_names[rn]); + else + fprintf (file, "\t%s\t%s,@-er7\n", h8_mov_op, h8_reg_names[rn]); } /* Output assembly language code to pop register RN. */ @@ -266,17 +494,20 @@ pop (file, rn) FILE *file; int rn; { - fprintf (file, "\t%s\t%s\n", h8_pop_op, h8_reg_names[rn]); + if (TARGET_H8300) + fprintf (file, "\t%s\t@r7+,%s\n", h8_mov_op, h8_reg_names[rn]); + else + fprintf (file, "\t%s\t@er7+,%s\n", h8_mov_op, h8_reg_names[rn]); } -/* This is what the stack looks like after the prolog of +/* This is what the stack looks like after the prolog of a function with a frame has been set up: PC FP <- fp - <- sp + <- sp This is what the stack looks like after the prolog of a function which doesn't have a frame: @@ -284,7 +515,7 @@ pop (file, rn) PC - <- sp + <- sp */ /* Output assembly language code for the function prologue. */ @@ -295,7 +526,7 @@ h8300_output_function_prologue (file, size) HOST_WIDE_INT size; { int fsize = round_frame_size (size); - int idx; + int regno; int saved_regs; int n_regs; @@ -361,14 +592,12 @@ h8300_output_function_prologue (file, size) } /* Leave room for locals. */ - dosize (file, "sub", fsize); + dosize (file, -1, fsize); /* Push the rest of the registers in ascending order. */ saved_regs = compute_saved_regs (); - for (idx = 0; idx < FIRST_PSEUDO_REGISTER; idx += n_regs) + for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno += n_regs) { - int regno = idx; - n_regs = 1; if (saved_regs & (1 << regno)) { @@ -388,12 +617,29 @@ h8300_output_function_prologue (file, size) n_regs = 2; } - if (n_regs == 1) - push (file, regno); - else - fprintf (file, "\tstm.l\t%s-%s,@-sp\n", - h8_reg_names[regno], - h8_reg_names[regno + (n_regs - 1)]); + switch (n_regs) + { + case 1: + push (file, regno); + break; + case 2: + fprintf (file, "\tstm.l\t%s-%s,@-er7\n", + h8_reg_names[regno], + h8_reg_names[regno + 1]); + break; + case 3: + fprintf (file, "\tstm.l\t%s-%s,@-er7\n", + h8_reg_names[regno], + h8_reg_names[regno + 2]); + break; + case 4: + fprintf (file, "\tstm.l\t%s-%s,@-er7\n", + h8_reg_names[regno], + h8_reg_names[regno + 3]); + break; + default: + abort (); + } } } } @@ -406,7 +652,7 @@ h8300_output_function_epilogue (file, size) HOST_WIDE_INT size; { int fsize = round_frame_size (size); - int idx; + int regno; rtx insn = get_last_insn (); int saved_regs; int n_regs; @@ -433,10 +679,8 @@ h8300_output_function_epilogue (file, size) /* Pop the saved registers in descending order. */ saved_regs = compute_saved_regs (); - for (idx = 0; idx < FIRST_PSEUDO_REGISTER; idx += n_regs) + for (regno = FIRST_PSEUDO_REGISTER - 1; regno >= 0; regno -= n_regs) { - int regno = (FIRST_PSEUDO_REGISTER - 1) - idx; - n_regs = 1; if (saved_regs & (1 << regno)) { @@ -456,17 +700,34 @@ h8300_output_function_epilogue (file, size) n_regs = 2; } - if (n_regs == 1) - pop (file, regno); - else - fprintf (file, "\tldm.l\t@sp+,%s-%s\n", - h8_reg_names[regno - (n_regs - 1)], - h8_reg_names[regno]); + switch (n_regs) + { + case 1: + pop (file, regno); + break; + case 2: + fprintf (file, "\tldm.l\t@er7+,%s-%s\n", + h8_reg_names[regno - 1], + h8_reg_names[regno]); + break; + case 3: + fprintf (file, "\tldm.l\t@er7+,%s-%s\n", + h8_reg_names[regno - 2], + h8_reg_names[regno]); + break; + case 4: + fprintf (file, "\tldm.l\t@er7+,%s-%s\n", + h8_reg_names[regno - 3], + h8_reg_names[regno]); + break; + default: + abort (); + } } } /* Deallocate locals. */ - dosize (file, "add", fsize); + dosize (file, 1, fsize); /* Pop frame pointer if we had one. */ if (frame_pointer_needed) @@ -492,7 +753,10 @@ asm_file_start (file) { fprintf (file, ";\tGCC For the Hitachi H8/300\n"); fprintf (file, ";\tBy Hitachi America Ltd and Cygnus Support\n"); - if (optimize) + + if (optimize_size) + fprintf (file, "; -Os\n"); + else if (optimize) fprintf (file, "; -O%d\n", optimize); if (TARGET_H8300H) fprintf (file, "\n\t.h8300h\n"); @@ -512,28 +776,6 @@ asm_file_end (file) fprintf (file, "\t.end\n"); } -/* Return true if VALUE is a valid constant for constraint 'P'. - IE: VALUE is a power of two <= 2**15. */ - -int -small_power_of_two (value) - HOST_WIDE_INT value; -{ - int power = exact_log2 (value); - return power >= 0 && power <= 15; -} - -/* Return true if VALUE is a valid constant for constraint 'O', which - means that the constant would be ok to use as a bit for a bclr - instruction. */ - -int -ok_for_bclr (value) - HOST_WIDE_INT value; -{ - return small_power_of_two ((~value) & 0xff); -} - /* Return true if OP is a valid source operand for an integer move instruction. */ @@ -560,26 +802,54 @@ general_operand_dst (op, mode) return general_operand (op, mode); } -/* Return true if OP is a const valid for a bit clear instruction. */ +/* Return true if OP is a constant that contains only one 1 in its + binary representation. */ int -o_operand (operand, mode) +single_one_operand (operand, mode) rtx operand; enum machine_mode mode ATTRIBUTE_UNUSED; { - return (GET_CODE (operand) == CONST_INT - && CONST_OK_FOR_O (INTVAL (operand))); + if (GET_CODE (operand) == CONST_INT) + { + /* We really need to do this masking because 0x80 in QImode is + represented as -128 for example. */ + unsigned HOST_WIDE_INT mask = + (GET_MODE_BITSIZE (mode) < HOST_BITS_PER_WIDE_INT) + ? ((unsigned HOST_WIDE_INT) 1 << GET_MODE_BITSIZE (mode)) - 1 + : ~(unsigned HOST_WIDE_INT) 0; + unsigned HOST_WIDE_INT value = INTVAL (operand); + + if (exact_log2 (value & mask) >= 0) + return 1; + } + + return 0; } -/* Return true if OP is a const valid for a bit set or bit xor instruction. */ +/* Return true if OP is a constant that contains only one 0 in its + binary representation. */ int -p_operand (operand, mode) +single_zero_operand (operand, mode) rtx operand; enum machine_mode mode ATTRIBUTE_UNUSED; { - return (GET_CODE (operand) == CONST_INT - && CONST_OK_FOR_P (INTVAL (operand))); + if (GET_CODE (operand) == CONST_INT) + { + /* We really need to do this masking because 0x80 in QImode is + represented as -128 for example. */ + unsigned HOST_WIDE_INT mask = + (GET_MODE_BITSIZE (mode) < HOST_BITS_PER_WIDE_INT) + ? ((unsigned HOST_WIDE_INT) 1 << GET_MODE_BITSIZE (mode)) - 1 + : ~(unsigned HOST_WIDE_INT) 0; + unsigned HOST_WIDE_INT value = INTVAL (operand); + + if (exact_log2 (~value & mask) >= 0) + return 1; + } + + return 0; } /* Return true if OP is a valid call operand. */ @@ -632,12 +902,11 @@ two_insn_adds_subs_operand (op, mode) } else { - /* A constant addition/subtraction takes 2 states in - QImode. It takes 6 states in HImode, requiring the - constant to be loaded to a register first, and a lot more - in SImode. Thus the only case we can win is when either - HImode or SImode is used. */ - if (mode != QImode + /* We do not profit directly by splitting addition or + subtraction of 3 and 4. However, since these are + implemented as a sequence of adds or subs, they do not + clobber (cc0) unlike a sequence of add.b and add.x. */ + if (mode == HImode && (value == 2 + 1 || value == 2 + 2)) return 1; @@ -729,7 +998,7 @@ jump_address_operand (op, mode) return 0; } -/* Recognize valid operands for bitfield instructions. */ +/* Recognize valid operands for bit-field instructions. */ extern int rtx_equal_function_value_matters; @@ -794,26 +1063,6 @@ h8300_pr_saveall (pfile) pragma_saveall = 1; } -static const char *const hand_list[] = -{ - "__main", - "__cmpsi2", - "__divhi3", - "__modhi3", - "__udivhi3", - "__umodhi3", - "__divsi3", - "__modsi3", - "__udivsi3", - "__umodsi3", - "__mulhi3", - "__mulsi3", - "__reg_memcpy", - "__reg_memset", - "__ucmpsi2", - 0, -}; - /* If the next function argument with MODE and TYPE is to be passed in a register, return a reg RTX for the hard register in which to pass the argument. CUM represents the state after the last argument. @@ -826,6 +1075,25 @@ function_arg (cum, mode, type, named) tree type; int named; { + static const char *const hand_list[] = { + "__main", + "__cmpsi2", + "__divhi3", + "__modhi3", + "__udivhi3", + "__umodhi3", + "__divsi3", + "__modsi3", + "__udivsi3", + "__umodsi3", + "__mulhi3", + "__mulsi3", + "__reg_memcpy", + "__reg_memset", + "__ucmpsi2", + 0, + }; + rtx result = NULL_RTX; const char *fname; int regpass = 0; @@ -873,9 +1141,10 @@ function_arg (cum, mode, type, named) /* Return the cost of the rtx R with code CODE. */ int -const_costs (r, c) +const_costs (r, c, outer_code) rtx r; enum rtx_code c; + enum rtx_code outer_code; { switch (c) { @@ -883,15 +1152,16 @@ const_costs (r, c) switch (INTVAL (r)) { case 0: + return 0; case 1: case 2: case -1: case -2: - return 0; + return 0 + (outer_code == SET); case 4: case -4: if (TARGET_H8300H || TARGET_H8300S) - return 0; + return 0 + (outer_code == SET); else return 1; default: @@ -910,6 +1180,44 @@ const_costs (r, c) return 4; } } + +int +h8300_and_costs (x) + rtx x; +{ + rtx operands[4]; + + if (GET_MODE (x) == QImode) + return 1; + + if (GET_MODE (x) != HImode + && GET_MODE (x) != SImode) + return 100; + + operands[0] = NULL; + operands[1] = NULL; + operands[2] = XEXP (x, 1); + operands[3] = x; + return compute_logical_op_length (GET_MODE (x), operands); +} + +int +h8300_shift_costs (x) + rtx x; +{ + rtx operands[4]; + + if (GET_MODE (x) != QImode + && GET_MODE (x) != HImode + && GET_MODE (x) != SImode) + return 100; + + operands[0] = NULL; + operands[1] = NULL; + operands[2] = XEXP (x, 1); + operands[3] = x; + return compute_a_shift_length (NULL, operands); +} /* Documentation for the machine specific operand escapes: @@ -1032,16 +1340,16 @@ print_operand (file, x, code) goto def; break; case 'V': - bitint = exact_log2 (INTVAL (x)); + bitint = exact_log2 (INTVAL (x) & 0xff); if (bitint == -1) abort (); - fprintf (file, "#%d", bitint & 7); + fprintf (file, "#%d", bitint); break; case 'W': bitint = exact_log2 ((~INTVAL (x)) & 0xff); if (bitint == -1) abort (); - fprintf (file, "#%d", bitint & 7); + fprintf (file, "#%d", bitint); break; case 'R': case 'X': @@ -1138,10 +1446,10 @@ print_operand (file, x, code) } break; case 'j': - asm_fprintf (file, cond_string (GET_CODE (x))); + fputs (cond_string (GET_CODE (x)), file); break; case 'k': - asm_fprintf (file, cond_string (reverse_condition (GET_CODE (x)))); + fputs (cond_string (reverse_condition (GET_CODE (x))), file); break; case 's': if (GET_CODE (x) == CONST_INT) @@ -1214,22 +1522,39 @@ print_operand (file, x, code) break; case MEM: - fprintf (file, "@"); - output_address (XEXP (x, 0)); - - /* If this is an 'R' operand (reference into the 8-bit - area), then specify a symbolic address as "foo:8", - otherwise if operand is still in eight bit section, use - "foo:16". */ - if (GET_CODE (XEXP (x, 0)) == SYMBOL_REF - && SYMBOL_REF_FLAG (XEXP (x, 0))) - fprintf (file, (code == 'R' ? ":8" : ":16")); - else if (GET_CODE (XEXP (x, 0)) == SYMBOL_REF - && TINY_DATA_NAME_P (XSTR (XEXP (x, 0), 0))) - fprintf (file, ":16"); - else if ((code == 'R') - && EIGHTBIT_CONSTANT_ADDRESS_P (XEXP (x, 0))) - fprintf (file, ":8"); + { + rtx addr = XEXP (x, 0); + + fprintf (file, "@"); + output_address (addr); + + /* We fall back from smaller addressing to larger + addressing in various ways depending on CODE. */ + switch (code) + { + case 'R': + /* Used for mov.b and bit operations. */ + if (h8300_eightbit_constant_address_p (addr)) + { + fprintf (file, ":8"); + break; + } + + /* Fall through. We should not get here if we are + processing bit operations on H8/300 or H8/300H + because 'U' constraint does not allow bit + operations on the tiny area on these machines. */ + + case 'T': + case 'S': + /* Used for mov.w and mov.l. */ + if (h8300_tiny_constant_address_p (addr)) + fprintf (file, ":16"); + break; + default: + break; + } + } break; case CONST_INT: @@ -1373,7 +1698,7 @@ do_movsi (operands) the other its replacement, at the start of a routine. */ int -initial_offset (from, to) +h8300_initial_elimination_offset (from, to) int from, to; { int offset = 0; @@ -1393,12 +1718,15 @@ initial_offset (from, to) /* See the comments for get_frame_size. We need to round it up to STACK_BOUNDARY. */ - offset += ((get_frame_size () + STACK_BOUNDARY / BITS_PER_UNIT - 1) - & ~(STACK_BOUNDARY / BITS_PER_UNIT - 1)); + offset += round_frame_size (get_frame_size ()); if (from == ARG_POINTER_REGNUM && to == STACK_POINTER_REGNUM) offset += UNITS_PER_WORD; /* Skip saved PC */ } + + if ((TARGET_H8300H || TARGET_H8300S) && TARGET_NORMAL_MODE) + offset -= 2; + return offset; } @@ -1440,6 +1768,9 @@ notice_update_cc (body, insn) if (cc_status.value1 != 0 && reg_overlap_mentioned_p (recog_data.operand[0], cc_status.value1)) cc_status.value1 = 0; + if (cc_status.value2 != 0 + && reg_overlap_mentioned_p (recog_data.operand[0], cc_status.value2)) + cc_status.value2 = 0; break; case CC_SET_ZN: @@ -1458,6 +1789,8 @@ notice_update_cc (body, insn) CC_STATUS_INIT; cc_status.flags |= CC_NO_CARRY; cc_status.value1 = recog_data.operand[0]; + if (GET_CODE (body) == SET && REG_P (SET_SRC (body))) + cc_status.value2 = SET_SRC (body); break; case CC_COMPARE: @@ -1488,11 +1821,12 @@ bit_operator (x, mode) } const char * -output_logical_op (mode, code, operands) +output_logical_op (mode, operands) enum machine_mode mode; - int code; rtx *operands; { + /* Figure out the logical op that we need to perform. */ + enum rtx_code code = GET_CODE (operands[3]); /* Pretend that every byte is affected if both operands are registers. */ unsigned HOST_WIDE_INT intval = (unsigned HOST_WIDE_INT) ((GET_CODE (operands[2]) == CONST_INT) @@ -1569,9 +1903,8 @@ output_logical_op (mode, code, operands) 1) the special insn (in case of AND or XOR), 2) the word-wise insn, and 3) The byte-wise insn. */ - if ((TARGET_H8300H || TARGET_H8300S) - && ((det & 0x0000ffff) == 0x0000ffff) - && code != IOR) + if ((det & 0x0000ffff) == 0x0000ffff + && (TARGET_H8300 ? (code == AND) : (code != IOR))) output_asm_insn ((code == AND) ? "sub.w\t%f0,%f0" : "not.w\t%f0", operands); @@ -1596,9 +1929,8 @@ output_logical_op (mode, code, operands) } } - if ((TARGET_H8300H || TARGET_H8300S) - && ((det & 0xffff0000) == 0xffff0000) - && code != IOR) + if ((det & 0xffff0000) == 0xffff0000 + && (TARGET_H8300 ? (code == AND) : (code != IOR))) output_asm_insn ((code == AND) ? "sub.w\t%e0,%e0" : "not.w\t%e0", operands); @@ -1630,15 +1962,176 @@ output_logical_op (mode, code, operands) } return ""; } - -/* Shifts. - - We devote a fair bit of code to getting efficient shifts since we - can only shift one bit at a time on the H8/300 and H8/300H and only - one or two bits at a time on the H8/S. - All shift code falls into one of the following ways of - implementation: +unsigned int +compute_logical_op_length (mode, operands) + enum machine_mode mode; + rtx *operands; +{ + /* Figure out the logical op that we need to perform. */ + enum rtx_code code = GET_CODE (operands[3]); + /* Pretend that every byte is affected if both operands are registers. */ + unsigned HOST_WIDE_INT intval = + (unsigned HOST_WIDE_INT) ((GET_CODE (operands[2]) == CONST_INT) + ? INTVAL (operands[2]) : 0x55555555); + /* The determinant of the algorithm. If we perform an AND, 0 + affects a bit. Otherwise, 1 affects a bit. */ + unsigned HOST_WIDE_INT det = (code != AND) ? intval : ~intval; + /* Insn length. */ + unsigned int length = 0; + + switch (mode) + { + case HImode: + /* First, see if we can finish with one insn. */ + if ((TARGET_H8300H || TARGET_H8300S) + && ((det & 0x00ff) != 0) + && ((det & 0xff00) != 0)) + { + if (REG_P (operands[2])) + length += 2; + else + length += 4; + } + else + { + /* Take care of the lower byte. */ + if ((det & 0x00ff) != 0) + length += 2; + + /* Take care of the upper byte. */ + if ((det & 0xff00) != 0) + length += 2; + } + break; + case SImode: + /* First, see if we can finish with one insn. + + If code is either AND or XOR, we exclude two special cases, + 0xffffff00 and 0xffff00ff, because insns like sub.w or not.w + can do a better job. */ + if ((TARGET_H8300H || TARGET_H8300S) + && ((det & 0x0000ffff) != 0) + && ((det & 0xffff0000) != 0) + && (code == IOR || det != 0xffffff00) + && (code == IOR || det != 0xffff00ff)) + { + if (REG_P (operands[2])) + length += 4; + else + length += 6; + } + else + { + /* Take care of the lower and upper words individually. For + each word, we try different methods in the order of + + 1) the special insn (in case of AND or XOR), + 2) the word-wise insn, and + 3) The byte-wise insn. */ + if ((det & 0x0000ffff) == 0x0000ffff + && (TARGET_H8300 ? (code == AND) : (code != IOR))) + { + length += 2; + } + else if ((TARGET_H8300H || TARGET_H8300S) + && ((det & 0x000000ff) != 0) + && ((det & 0x0000ff00) != 0)) + { + length += 4; + } + else + { + if ((det & 0x000000ff) != 0) + length += 2; + + if ((det & 0x0000ff00) != 0) + length += 2; + } + + if ((det & 0xffff0000) == 0xffff0000 + && (TARGET_H8300 ? (code == AND) : (code != IOR))) + { + length += 2; + } + else if (TARGET_H8300H || TARGET_H8300S) + { + if ((det & 0xffff0000) != 0) + length += 4; + } + else + { + if ((det & 0x00ff0000) != 0) + length += 2; + + if ((det & 0xff000000) != 0) + length += 2; + } + } + break; + default: + abort (); + } + return length; +} + +int +compute_logical_op_cc (mode, operands) + enum machine_mode mode; + rtx *operands; +{ + /* Figure out the logical op that we need to perform. */ + enum rtx_code code = GET_CODE (operands[3]); + /* Pretend that every byte is affected if both operands are registers. */ + unsigned HOST_WIDE_INT intval = + (unsigned HOST_WIDE_INT) ((GET_CODE (operands[2]) == CONST_INT) + ? INTVAL (operands[2]) : 0x55555555); + /* The determinant of the algorithm. If we perform an AND, 0 + affects a bit. Otherwise, 1 affects a bit. */ + unsigned HOST_WIDE_INT det = (code != AND) ? intval : ~intval; + /* Condition code. */ + enum attr_cc cc = CC_CLOBBER; + + switch (mode) + { + case HImode: + /* First, see if we can finish with one insn. */ + if ((TARGET_H8300H || TARGET_H8300S) + && ((det & 0x00ff) != 0) + && ((det & 0xff00) != 0)) + { + cc = CC_SET_ZNV; + } + break; + case SImode: + /* First, see if we can finish with one insn. + + If code is either AND or XOR, we exclude two special cases, + 0xffffff00 and 0xffff00ff, because insns like sub.w or not.w + can do a better job. */ + if ((TARGET_H8300H || TARGET_H8300S) + && ((det & 0x0000ffff) != 0) + && ((det & 0xffff0000) != 0) + && (code == IOR || det != 0xffffff00) + && (code == IOR || det != 0xffff00ff)) + { + cc = CC_SET_ZNV; + } + break; + default: + abort (); + } + return cc; +} + +/* Shifts. + + We devote a fair bit of code to getting efficient shifts since we + can only shift one bit at a time on the H8/300 and H8/300H and only + one or two bits at a time on the H8S. + + All shift code falls into one of the following ways of + implementation: o SHIFT_INLINE: Emit straight line code for the shift; this is used when a straight line shift is about the same size or smaller than @@ -1654,98 +2147,18 @@ output_logical_op (mode, code, operands) 16. This case also includes other oddballs that are not worth explaning here. - o SHIFT_LOOP: Emit a loop using one (or two on H8/S) bit shifts. - - Here are some thoughts on what the absolutely positively best code - is. "Best" here means some rational trade-off between code size - and speed, where speed is more preferred but not at the expense of - generating 20 insns. - - Below, a trailing '*' after the shift count indicates the "best" - mode isn't implemented. We only describe SHIFT_SPECIAL cases to - simplify the table. For other cases, refer to shift_alg_[qhs]i. - - H8/300 QImode shifts - 7 - ASHIFTRT: shll, subx (propagate carry bit to all bits) - - H8/300 HImode shifts - 7 - shift 2nd half other way into carry. - copy 1st half into 2nd half - rotate 2nd half other way with carry - rotate 1st half other way (no carry) - mask off bits in 1st half (ASHIFT | LSHIFTRT). - sign extend 1st half (ASHIFTRT) - 8 - move byte, zero (ASHIFT | LSHIFTRT) or sign extend other (ASHIFTRT) - 9-12 - do shift by 8, inline remaining shifts - 15 - ASHIFTRT: shll, subx, set other byte - - H8/300 SImode shifts - 7* - shift other way once, move bytes into place, - move carry into place (possibly with sign extension) - 8 - move bytes into place, zero or sign extend other - 15* - shift other way once, move word into place, move carry into place - 16 - move word, zero or sign extend other - 24* - move bytes into place, zero or sign extend other - 31 - ASHIFTRT: shll top byte, subx, copy to other bytes - - H8/300H QImode shifts (same as H8/300 QImode shifts) - 7 - ASHIFTRT: shll, subx (propagate carry bit to all bits) - - H8/300H HImode shifts - 7 - shift 2nd half other way into carry. - copy 1st half into 2nd half - rotate entire word other way using carry - mask off remaining bits (ASHIFT | LSHIFTRT) - sign extend remaining bits (ASHIFTRT) - 8 - move byte, zero (ASHIFT | LSHIFTRT) or sign extend other (ASHIFTRT) - 9-12 - do shift by 8, inline remaining shifts - 15 - ASHIFTRT: shll, subx, set other byte - - H8/300H SImode shifts - (These are complicated by the fact that we don't have byte level access to - the top word.) - A word is: bytes 3,2,1,0 (msb -> lsb), word 1,0 (msw -> lsw) - 15* - shift other way once, move word into place, move carry into place - (with sign extension for ASHIFTRT) - 16 - move word into place, zero or sign extend other - 17-20 - do 16bit shift, then inline remaining shifts - 24* - ASHIFT: move byte 0(msb) to byte 1, zero byte 0, - move word 0 to word 1, zero word 0 - LSHIFTRT: move word 1 to word 0, move byte 1 to byte 0, - zero word 1, zero byte 1 - ASHIFTRT: move word 1 to word 0, move byte 1 to byte 0, - sign extend byte 0, sign extend word 0 - 25-27* - either loop, or - do 24 bit shift, inline rest - 31 - shll, subx byte 0, sign extend byte 0, sign extend word 0 - - H8/S QImode shifts - 7 - ASHIFTRT: shll, subx (propagate carry bit to all bits) - - H8/S HImode shifts - 8 - move byte, zero (ASHIFT | LSHIFTRT) or sign extend other (ASHIFTRT) - 9-12 - do shift by 8, inline remaining shifts - 15 - ASHIFTRT: shll, subx, set other byte - - H8/S SImode shifts - (These are complicated by the fact that we don't have byte level access to - the top word.) - A word is: bytes 3,2,1,0 (msb -> lsb), word 1,0 (msw -> lsw) - 15* - shift other way once, move word into place, move carry into place - (with sign extension for ASHIFTRT) - 16 - move word into place, zero or sign extend other - 17-20 - do 16bit shift, then inline remaining shifts - 24* - ASHIFT: move byte 0(msb) to byte 1, zero byte 0, - move word 0 to word 1, zero word 0 - LSHIFTRT: move word 1 to word 0, move byte 1 to byte 0, - zero word 1, zero byte 1 - ASHIFTRT: move word 1 to word 0, move byte 1 to byte 0, - sign extend byte 0, sign extend word 0 - 25-27* - either loop, or - do 24 bit shift, inline rest - 31 - shll, subx byte 0, sign extend byte 0, sign extend word 0 - - Panic!!! */ + o SHIFT_LOOP: Emit a loop using one (or two on H8S) bit shifts. + + For each shift count, we try to use code that has no trade-off + between code size and speed whenever possible. + + If the trade-off is unavoidable, we try to be reasonable. + Specifically, the fastest version is one instruction longer than + the shortest version, we take the fastest version. We also provide + the use a way to switch back to the shortest version with -Os. + + For the details of the shift algorithms for various shift counts, + refer to shift_alg_[qhs]i. */ int nshift_operator (x, mode) @@ -1791,23 +2204,6 @@ expand_a_shift (mode, code, operands) return 1; } -/* See above for explanation of this enum. */ - -enum shift_alg -{ - SHIFT_INLINE, - SHIFT_ROT_AND, - SHIFT_SPECIAL, - SHIFT_LOOP -}; - -/* Symbols of the various shifts which can be used as indices. */ - -enum shift_type -{ - SHIFT_ASHIFT, SHIFT_LSHIFTRT, SHIFT_ASHIFTRT -}; - /* Symbols of the various modes which can be used as indices. */ enum shift_mode @@ -1970,142 +2366,6 @@ static const char *const rotate_two[3][3] = } }; -/* Macros to keep the shift algorithm tables small. */ -#define INL SHIFT_INLINE -#define ROT SHIFT_ROT_AND -#define LOP SHIFT_LOOP -#define SPC SHIFT_SPECIAL - -/* The shift algorithms for each machine, mode, shift type, and shift - count are defined below. The three tables below correspond to - QImode, HImode, and SImode, respectively. Each table is organized - by, in the order of indecies, machine, shift type, and shift count. */ - -static const enum shift_alg shift_alg_qi[3][3][8] = { - { - /* TARGET_H8300 */ - /* 0 1 2 3 4 5 6 7 */ - { INL, INL, INL, INL, INL, ROT, ROT, ROT }, /* SHIFT_ASHIFT */ - { INL, INL, INL, INL, INL, ROT, ROT, ROT }, /* SHIFT_LSHIFTRT */ - { INL, INL, INL, INL, INL, LOP, LOP, SPC } /* SHIFT_ASHIFTRT */ - }, - { - /* TARGET_H8300H */ - /* 0 1 2 3 4 5 6 7 */ - { INL, INL, INL, INL, INL, ROT, ROT, ROT }, /* SHIFT_ASHIFT */ - { INL, INL, INL, INL, INL, ROT, ROT, ROT }, /* SHIFT_LSHIFTRT */ - { INL, INL, INL, INL, INL, LOP, LOP, SPC } /* SHIFT_ASHIFTRT */ - }, - { - /* TARGET_H8300S */ - /* 0 1 2 3 4 5 6 7 */ - { INL, INL, INL, INL, INL, INL, ROT, ROT }, /* SHIFT_ASHIFT */ - { INL, INL, INL, INL, INL, INL, ROT, ROT }, /* SHIFT_LSHIFTRT */ - { INL, INL, INL, INL, INL, INL, INL, SPC } /* SHIFT_ASHIFTRT */ - } -}; - -static const enum shift_alg shift_alg_hi[3][3][16] = { - { - /* TARGET_H8300 */ - /* 0 1 2 3 4 5 6 7 */ - /* 8 9 10 11 12 13 14 15 */ - { INL, INL, INL, INL, INL, LOP, LOP, SPC, - SPC, SPC, SPC, SPC, SPC, LOP, LOP, ROT }, /* SHIFT_ASHIFT */ - { INL, INL, INL, INL, INL, LOP, LOP, SPC, - SPC, SPC, SPC, SPC, SPC, LOP, LOP, ROT }, /* SHIFT_LSHIFTRT */ - { INL, INL, INL, INL, INL, LOP, LOP, SPC, - SPC, SPC, SPC, SPC, SPC, LOP, LOP, SPC }, /* SHIFT_ASHIFTRT */ - }, - { - /* TARGET_H8300H */ - /* 0 1 2 3 4 5 6 7 */ - /* 8 9 10 11 12 13 14 15 */ - { INL, INL, INL, INL, INL, LOP, LOP, SPC, - SPC, SPC, SPC, SPC, SPC, ROT, ROT, ROT }, /* SHIFT_ASHIFT */ - { INL, INL, INL, INL, INL, LOP, LOP, SPC, - SPC, SPC, SPC, SPC, SPC, ROT, ROT, ROT }, /* SHIFT_LSHIFTRT */ - { INL, INL, INL, INL, INL, LOP, LOP, SPC, - SPC, SPC, SPC, SPC, SPC, LOP, LOP, SPC }, /* SHIFT_ASHIFTRT */ - }, - { - /* TARGET_H8300S */ - /* 0 1 2 3 4 5 6 7 */ - /* 8 9 10 11 12 13 14 15 */ - { INL, INL, INL, INL, INL, INL, INL, INL, - SPC, SPC, SPC, SPC, SPC, ROT, ROT, ROT }, /* SHIFT_ASHIFT */ - { INL, INL, INL, INL, INL, INL, INL, INL, - SPC, SPC, SPC, SPC, SPC, ROT, ROT, ROT }, /* SHIFT_LSHIFTRT */ - { INL, INL, INL, INL, INL, INL, INL, INL, - SPC, SPC, SPC, SPC, SPC, LOP, LOP, SPC }, /* SHIFT_ASHIFTRT */ - } -}; - -static const enum shift_alg shift_alg_si[3][3][32] = { - { - /* TARGET_H8300 */ - /* 0 1 2 3 4 5 6 7 */ - /* 8 9 10 11 12 13 14 15 */ - /* 16 17 18 19 20 21 22 23 */ - /* 24 25 26 27 28 29 30 31 */ - { INL, INL, INL, LOP, LOP, LOP, LOP, LOP, - SPC, LOP, LOP, LOP, LOP, LOP, LOP, LOP, - SPC, LOP, LOP, LOP, LOP, LOP, LOP, LOP, - LOP, LOP, LOP, LOP, LOP, LOP, LOP, SPC }, /* SHIFT_ASHIFT */ - { INL, INL, INL, LOP, LOP, LOP, LOP, LOP, - SPC, LOP, LOP, LOP, LOP, LOP, LOP, LOP, - SPC, LOP, LOP, LOP, LOP, LOP, LOP, LOP, - LOP, LOP, LOP, LOP, LOP, LOP, LOP, SPC }, /* SHIFT_LSHIFTRT */ - { INL, INL, INL, LOP, LOP, LOP, LOP, LOP, - SPC, LOP, LOP, LOP, LOP, LOP, LOP, LOP, - SPC, LOP, LOP, LOP, LOP, LOP, LOP, LOP, - LOP, LOP, LOP, LOP, LOP, LOP, LOP, SPC }, /* SHIFT_ASHIFTRT */ - }, - { - /* TARGET_H8300H */ - /* 0 1 2 3 4 5 6 7 */ - /* 8 9 10 11 12 13 14 15 */ - /* 16 17 18 19 20 21 22 23 */ - /* 24 25 26 27 28 29 30 31 */ - { INL, INL, INL, INL, INL, LOP, LOP, LOP, - SPC, LOP, LOP, LOP, LOP, LOP, LOP, SPC, - SPC, SPC, SPC, SPC, LOP, LOP, LOP, LOP, - SPC, LOP, LOP, LOP, ROT, ROT, ROT, SPC }, /* SHIFT_ASHIFT */ - { INL, INL, INL, INL, INL, LOP, LOP, LOP, - SPC, LOP, LOP, LOP, LOP, LOP, LOP, SPC, - SPC, SPC, SPC, SPC, LOP, LOP, LOP, LOP, - SPC, LOP, LOP, LOP, ROT, ROT, ROT, SPC }, /* SHIFT_LSHIFTRT */ - { INL, INL, INL, INL, INL, LOP, LOP, LOP, - SPC, LOP, LOP, LOP, LOP, LOP, LOP, LOP, - SPC, SPC, SPC, SPC, LOP, LOP, LOP, LOP, - SPC, LOP, LOP, LOP, LOP, LOP, LOP, SPC }, /* SHIFT_ASHIFTRT */ - }, - { - /* TARGET_H8300S */ - /* 0 1 2 3 4 5 6 7 */ - /* 8 9 10 11 12 13 14 15 */ - /* 16 17 18 19 20 21 22 23 */ - /* 24 25 26 27 28 29 30 31 */ - { INL, INL, INL, INL, INL, INL, INL, INL, - INL, INL, INL, LOP, LOP, LOP, LOP, SPC, - SPC, SPC, SPC, SPC, SPC, SPC, LOP, LOP, - SPC, SPC, LOP, LOP, ROT, ROT, ROT, SPC }, /* SHIFT_ASHIFT */ - { INL, INL, INL, INL, INL, INL, INL, INL, - INL, INL, INL, LOP, LOP, LOP, LOP, SPC, - SPC, SPC, SPC, SPC, SPC, SPC, LOP, LOP, - SPC, SPC, LOP, LOP, ROT, ROT, ROT, SPC }, /* SHIFT_LSHIFTRT */ - { INL, INL, INL, INL, INL, INL, INL, INL, - INL, INL, INL, LOP, LOP, LOP, LOP, LOP, - SPC, SPC, SPC, SPC, SPC, SPC, LOP, LOP, - SPC, SPC, LOP, LOP, LOP, LOP, LOP, SPC }, /* SHIFT_ASHIFTRT */ - } -}; - -#undef INL -#undef ROT -#undef LOP -#undef SPC - struct shift_info { /* Shift algorithm. */ enum shift_alg alg; @@ -2118,11 +2378,11 @@ struct shift_info { const char *special; /* Insn for a one-bit shift. Valid when ALG is either SHIFT_INLINE - or SHIFT_SPECIAL, and REMAINDER is non-zero. */ + or SHIFT_SPECIAL, and REMAINDER is nonzero. */ const char *shift1; /* Insn for a two-bit shift. Valid when ALG is either SHIFT_INLINE - or SHIFT_SPECIAL, and REMAINDER is non-zero. */ + or SHIFT_SPECIAL, and REMAINDER is nonzero. */ const char *shift2; /* Valid CC flags. */ @@ -2135,16 +2395,15 @@ static void get_shift_alg PARAMS ((enum shift_type, /* Given SHIFT_TYPE, SHIFT_MODE, and shift count COUNT, determine the best algorithm for doing the shift. The assembler code is stored - in the pointers in INFO. We don't achieve maximum efficiency in - all cases, but the hooks are here to do so. - - For now we just use lots of switch statements. Since we don't even come - close to supporting all the cases, this is simplest. If this function ever - gets too big, perhaps resort to a more table based lookup. Of course, - at this point you may just wish to do it all in rtl. + in the pointers in INFO. We achieve the maximum efficiency in most + cases when !TARGET_H8300. In case of TARGET_H8300, shifts in + SImode in particular have a lot of room to optimize. - WARNING: The constraints on insns shiftbyn_QI/HI/SI assume shifts of - 1,2,3,4 will be inlined (1,2 for SI). */ + We first determine the strategy of the shift algorithm by a table + lookup. If that tells us to use a hand crafted assembly code, we + go into the big switch statement to find what that is. Otherwise, + we resort to a generic way, such as inlining. In either case, the + result is returned through INFO. */ static void get_shift_alg (shift_type, shift_mode, count, info) @@ -2153,37 +2412,32 @@ get_shift_alg (shift_type, shift_mode, count, info) unsigned int count; struct shift_info *info; { - int cpu; + enum h8_cpu cpu; /* Find the target CPU. */ if (TARGET_H8300) - cpu = 0; + cpu = H8_300; else if (TARGET_H8300H) - cpu = 1; + cpu = H8_300H; else - cpu = 2; + cpu = H8_S; /* Find the shift algorithm. */ + info->alg = SHIFT_LOOP; switch (shift_mode) { case QIshift: - if (GET_MODE_BITSIZE (QImode) <= count) - info->alg = SHIFT_LOOP; - else + if (count < GET_MODE_BITSIZE (QImode)) info->alg = shift_alg_qi[cpu][shift_type][count]; break; case HIshift: - if (GET_MODE_BITSIZE (HImode) <= count) - info->alg = SHIFT_LOOP; - else + if (count < GET_MODE_BITSIZE (HImode)) info->alg = shift_alg_hi[cpu][shift_type][count]; break; case SIshift: - if (GET_MODE_BITSIZE (SImode) <= count) - info->alg = SHIFT_LOOP; - else + if (count < GET_MODE_BITSIZE (SImode)) info->alg = shift_alg_si[cpu][shift_type][count]; break; @@ -2255,7 +2509,7 @@ get_shift_alg (shift_type, shift_mode, count, info) goto end; } } - else if (8 <= count && count <= 12) + else if (8 <= count && count <= 13) { info->remainder = count - 8; @@ -2281,16 +2535,50 @@ get_shift_alg (shift_type, shift_mode, count, info) goto end; } } - else if (count == 15 && shift_type == SHIFT_ASHIFTRT) + else if (count == 14) { - info->special = "shll\t%t0\n\tsubx\t%t0,%t0\n\tmov.b\t%t0,%s0"; - goto end; + switch (shift_type) + { + case SHIFT_ASHIFT: + if (TARGET_H8300) + info->special = "mov.b\t%s0,%t0\n\trotr.b\t%t0\n\trotr.b\t%t0\n\tand.b\t#0xC0,%t0\n\tsub.b\t%s0,%s0"; + goto end; + case SHIFT_LSHIFTRT: + if (TARGET_H8300) + info->special = "mov.b\t%t0,%s0\n\trotl.b\t%s0\n\trotl.b\t%s0\n\tand.b\t#3,%s0\n\tsub.b\t%t0,%t0"; + goto end; + case SHIFT_ASHIFTRT: + if (TARGET_H8300) + info->special = "mov.b\t%t0,%s0\n\tshll.b\t%s0\n\tsubx.b\t%t0,%t0\n\tshll.b\t%s0\n\tmov.b\t%t0,%s0\n\tbst.b\t#0,%s0"; + else if (TARGET_H8300H) + info->special = "shll.b\t%t0\n\tsubx.b\t%s0,%s0\n\tshll.b\t%t0\n\trotxl.b\t%s0\n\texts.w\t%T0"; + else /* TARGET_H8300S */ + info->special = "mov.b\t%t0,%s0\n\texts.w\t%T0\n\tshar.w\t#2,%T0\n\tshar.w\t#2,%T0\n\tshar.w\t#2,%T0"; + goto end; + } + } + else if (count == 15) + { + switch (shift_type) + { + case SHIFT_ASHIFT: + info->special = "bld\t#0,%s0\n\txor\t%s0,%s0\n\txor\t%t0,%t0\n\tbst\t#7,%t0"; + goto end; + case SHIFT_LSHIFTRT: + info->special = "bld\t#7,%t0\n\txor\t%s0,%s0\n\txor\t%t0,%t0\n\tbst\t#0,%s0"; + goto end; + case SHIFT_ASHIFTRT: + info->special = "shll\t%t0\n\tsubx\t%t0,%t0\n\tmov.b\t%t0,%s0"; + goto end; + } } abort (); case SIshift: - if (count == 8 && TARGET_H8300) + if (TARGET_H8300 && 8 <= count && count <= 9) { + info->remainder = count - 8; + switch (shift_type) { case SHIFT_ASHIFT: @@ -2298,6 +2586,7 @@ get_shift_alg (shift_type, shift_mode, count, info) goto end; case SHIFT_LSHIFTRT: info->special = "mov.b\t%x0,%w0\n\tmov.b\t%y0,%x0\n\tmov.b\t%z0,%y0\n\tsub.b\t%z0,%z0"; + info->shift1 = "shlr\t%y0\n\trotxr\t%x0\n\trotxr\t%w0"; goto end; case SHIFT_ASHIFTRT: info->special = "mov.b\t%x0,%w0\n\tmov.b\t%y0,%x0\n\tmov.b\t%z0,%y0\n\tshll\t%z0\n\tsubx\t%z0,%z0"; @@ -2319,6 +2608,20 @@ get_shift_alg (shift_type, shift_mode, count, info) goto end; } } + else if (count == 15 && TARGET_H8300) + { + switch (shift_type) + { + case SHIFT_ASHIFT: + abort (); + case SHIFT_LSHIFTRT: + info->special = "bld\t#7,%z0\n\tmov.w\t%e0,%f0\n\txor\t%y0,%y0\n\txor\t%z0,%z0\n\trotxl\t%w0,%w0\n\trotxl\t%x0,%x0\n\trotxl\t%y0,%y0"; + goto end; + case SHIFT_ASHIFTRT: + info->special = "bld\t#7,%z0\n\tmov.w\t%e0,%f0\n\trotxl\t%w0,%w0\n\trotxl\t%x0,%x0\n\tsubx\t%y0,%y0\n\tsubx\t%z0,%z0"; + goto end; + } + } else if (count == 15 && !TARGET_H8300) { switch (shift_type) @@ -2333,7 +2636,7 @@ get_shift_alg (shift_type, shift_mode, count, info) abort (); } } - else if ((TARGET_H8300 && count == 16) + else if ((TARGET_H8300 && 16 <= count && count <= 20) || (TARGET_H8300H && 16 <= count && count <= 19) || (TARGET_H8300S && 16 <= count && count <= 21)) { @@ -2343,21 +2646,60 @@ get_shift_alg (shift_type, shift_mode, count, info) { case SHIFT_ASHIFT: info->special = "mov.w\t%f0,%e0\n\tsub.w\t%f0,%f0"; - info->shift1 = "shll.l\t%S0"; - info->shift2 = "shll.l\t#2,%S0"; + if (TARGET_H8300) + { + info->shift1 = "add.w\t%e0,%e0"; + } + else + { + info->shift1 = "shll.l\t%S0"; + info->shift2 = "shll.l\t#2,%S0"; + } goto end; case SHIFT_LSHIFTRT: info->special = "mov.w\t%e0,%f0\n\tsub.w\t%e0,%e0"; - info->shift1 = "shlr.l\t%S0"; - info->shift2 = "shlr.l\t#2,%S0"; + if (TARGET_H8300) + { + info->shift1 = "shlr\t%x0\n\trotxr\t%w0"; + } + else + { + info->shift1 = "shlr.l\t%S0"; + info->shift2 = "shlr.l\t#2,%S0"; + } goto end; case SHIFT_ASHIFTRT: if (TARGET_H8300) - info->special = "mov.w\t%e0,%f0\n\tshll\t%z0\n\tsubx\t%z0,%z0\n\tmov.b\t%z0,%y0"; + { + info->special = "mov.w\t%e0,%f0\n\tshll\t%z0\n\tsubx\t%z0,%z0\n\tmov.b\t%z0,%y0"; + info->shift1 = "shar\t%x0\n\trotxr\t%w0"; + } else - info->special = "mov.w\t%e0,%f0\n\texts.l\t%S0"; - info->shift1 = "shar.l\t%S0"; - info->shift2 = "shar.l\t#2,%S0"; + { + info->special = "mov.w\t%e0,%f0\n\texts.l\t%S0"; + info->shift1 = "shar.l\t%S0"; + info->shift2 = "shar.l\t#2,%S0"; + } + goto end; + } + } + else if (TARGET_H8300 && 24 <= count && count <= 28) + { + info->remainder = count - 24; + + switch (shift_type) + { + case SHIFT_ASHIFT: + info->special = "mov.b\t%w0,%z0\n\tsub.b\t%y0,%y0\n\tsub.w\t%f0,%f0"; + info->shift1 = "shll.b\t%z0"; + goto end; + case SHIFT_LSHIFTRT: + info->special = "mov.b\t%z0,%w0\n\tsub.b\t%x0,%x0\n\tsub.w\t%e0,%e0"; + info->shift1 = "shlr.b\t%w0"; + goto end; + case SHIFT_ASHIFTRT: + info->special = "mov.b\t%z0,%w0\n\tbld\t#7,%w0\n\tsubx\t%x0,%x0\n\tsubx\t%x0,%x0\n\tsubx\t%x0,%x0"; + info->shift1 = "shar.b\t%w0"; goto end; } } @@ -2385,6 +2727,78 @@ get_shift_alg (shift_type, shift_mode, count, info) goto end; } } + else if (!TARGET_H8300 && count == 28) + { + switch (shift_type) + { + case SHIFT_ASHIFT: + if (TARGET_H8300H) + info->special = "sub.w\t%e0,%e0\n\trotr.l\t%S0\n\trotr.l\t%S0\n\trotr.l\t%S0\n\trotr.l\t%S0\n\tsub.w\t%f0,%f0"; + else + info->special = "sub.w\t%e0,%e0\n\trotr.l\t#2,%S0\n\trotr.l\t#2,%S0\n\tsub.w\t%f0,%f0"; + info->shift1 = ""; + info->shift2 = ""; + goto end; + case SHIFT_LSHIFTRT: + if (TARGET_H8300H) + info->special = "sub.w\t%f0,%f0\n\trotl.l\t%S0\n\trotl.l\t%S0\n\trotl.l\t%S0\n\trotl.l\t%S0\n\tsub.w\t%e0,%e0"; + else + info->special = "sub.w\t%f0,%f0\n\trotl.l\t#2,%S0\n\trotl.l\t#2,%S0\n\tsub.w\t%e0,%e0"; + info->shift1 = ""; + info->shift2 = ""; + goto end; + case SHIFT_ASHIFTRT: + abort (); + } + } + else if (!TARGET_H8300 && count == 29) + { + switch (shift_type) + { + case SHIFT_ASHIFT: + if (TARGET_H8300H) + info->special = "sub.w\t%e0,%e0\n\trotr.l\t%S0\n\trotr.l\t%S0\n\trotr.l\t%S0\n\tsub.w\t%f0,%f0"; + else + info->special = "sub.w\t%e0,%e0\n\trotr.l\t#2,%S0\n\trotr.l\t%S0\n\tsub.w\t%f0,%f0"; + info->shift1 = ""; + info->shift2 = ""; + goto end; + case SHIFT_LSHIFTRT: + if (TARGET_H8300H) + info->special = "sub.w\t%f0,%f0\n\trotl.l\t%S0\n\trotl.l\t%S0\n\trotl.l\t%S0\n\tsub.w\t%e0,%e0"; + else + info->special = "sub.w\t%f0,%f0\n\trotl.l\t#2,%S0\n\trotl.l\t%S0\n\tsub.w\t%e0,%e0"; + info->shift1 = ""; + info->shift2 = ""; + goto end; + case SHIFT_ASHIFTRT: + abort (); + } + } + else if (!TARGET_H8300 && count == 30) + { + switch (shift_type) + { + case SHIFT_ASHIFT: + if (TARGET_H8300H) + info->special = "sub.w\t%e0,%e0\n\trotr.l\t%S0\n\trotr.l\t%S0\n\tsub.w\t%f0,%f0"; + else + info->special = "sub.w\t%e0,%e0\n\trotr.l\t#2,%S0\n\tsub.w\t%f0,%f0"; + info->shift1 = ""; + info->shift2 = ""; + goto end; + case SHIFT_LSHIFTRT: + if (TARGET_H8300H) + info->special = "sub.w\t%f0,%f0\n\trotl.l\t%S0\n\trotl.l\t%S0\n\tsub.w\t%e0,%e0"; + else + info->special = "sub.w\t%f0,%f0\n\trotl.l\t#2,%S0\n\tsub.w\t%e0,%e0"; + info->shift1 = ""; + info->shift2 = ""; + goto end; + case SHIFT_ASHIFTRT: + abort (); + } + } else if (count == 31) { if (TARGET_H8300) @@ -2424,9 +2838,61 @@ get_shift_alg (shift_type, shift_mode, count, info) abort (); } - end: - if (!TARGET_H8300S) - info->shift2 = NULL; + end: + if (!TARGET_H8300S) + info->shift2 = NULL; +} + +/* Given COUNT and MODE of a shift, return 1 if a scratch reg may be + needed for some shift with COUNT and MODE. Return 0 otherwise. */ + +int +h8300_shift_needs_scratch_p (count, mode) + int count; + enum machine_mode mode; +{ + enum h8_cpu cpu; + int a, lr, ar; + + if (GET_MODE_BITSIZE (mode) <= count) + return 1; + + /* Find out the target CPU. */ + if (TARGET_H8300) + cpu = H8_300; + else if (TARGET_H8300H) + cpu = H8_300H; + else + cpu = H8_S; + + /* Find the shift algorithm. */ + switch (mode) + { + case QImode: + a = shift_alg_qi[cpu][SHIFT_ASHIFT][count]; + lr = shift_alg_qi[cpu][SHIFT_LSHIFTRT][count]; + ar = shift_alg_qi[cpu][SHIFT_ASHIFTRT][count]; + break; + + case HImode: + a = shift_alg_hi[cpu][SHIFT_ASHIFT][count]; + lr = shift_alg_hi[cpu][SHIFT_LSHIFTRT][count]; + ar = shift_alg_hi[cpu][SHIFT_ASHIFTRT][count]; + break; + + case SImode: + a = shift_alg_si[cpu][SHIFT_ASHIFT][count]; + lr = shift_alg_si[cpu][SHIFT_LSHIFTRT][count]; + ar = shift_alg_si[cpu][SHIFT_ASHIFTRT][count]; + break; + + default: + abort (); + } + + /* On H8/300H and H8S, count == 8 uses the scratch register. */ + return (a == SHIFT_LOOP || lr == SHIFT_LOOP || ar == SHIFT_LOOP + || (!TARGET_H8300 && mode == SImode && count == 8)); } /* Emit the assembler code for doing shifts. */ @@ -2560,30 +3026,28 @@ output_a_shift (operands) output_asm_insn (info.shift1, operands); /* Now mask off the high bits. */ - if (TARGET_H8300) + switch (mode) { - switch (mode) + case QImode: + sprintf (insn_buf, "and\t#%d,%%X0", mask); + cc_status.value1 = operands[0]; + cc_status.flags |= CC_NO_CARRY; + break; + case HImode: + if (TARGET_H8300) { - case QImode: - sprintf (insn_buf, "and\t#%d,%%X0", mask); - cc_status.value1 = operands[0]; - cc_status.flags |= CC_NO_CARRY; - break; - case HImode: sprintf (insn_buf, "and\t#%d,%%s0\n\tand\t#%d,%%t0", mask & 255, mask >> 8); - break; - default: - abort (); } - } - else - { - sprintf (insn_buf, "and.%c\t#%d,%%%c0", - "bwl"[shift_mode], mask, - mode == QImode ? 'X' : mode == HImode ? 'T' : 'S'); - cc_status.value1 = operands[0]; - cc_status.flags |= CC_NO_CARRY; + else + { + sprintf (insn_buf, "and.w\t#%d,%%T0", mask); + cc_status.value1 = operands[0]; + cc_status.flags |= CC_NO_CARRY; + } + break; + default: + abort (); } output_asm_insn (insn_buf, operands); return ""; @@ -2619,6 +3083,169 @@ output_a_shift (operands) } } } + +static unsigned int +h8300_asm_insn_count (template) + const char *template; +{ + unsigned int count = 1; + + for (; *template; template++) + if (*template == '\n') + count++; + + return count; +} + +unsigned int +compute_a_shift_length (insn, operands) + rtx insn ATTRIBUTE_UNUSED; + rtx *operands; +{ + rtx shift = operands[3]; + enum machine_mode mode = GET_MODE (shift); + enum rtx_code code = GET_CODE (shift); + enum shift_type shift_type; + enum shift_mode shift_mode; + struct shift_info info; + unsigned int wlength = 0; + + switch (mode) + { + case QImode: + shift_mode = QIshift; + break; + case HImode: + shift_mode = HIshift; + break; + case SImode: + shift_mode = SIshift; + break; + default: + abort (); + } + + switch (code) + { + case ASHIFTRT: + shift_type = SHIFT_ASHIFTRT; + break; + case LSHIFTRT: + shift_type = SHIFT_LSHIFTRT; + break; + case ASHIFT: + shift_type = SHIFT_ASHIFT; + break; + default: + abort (); + } + + if (GET_CODE (operands[2]) != CONST_INT) + { + /* Get the assembler code to do one shift. */ + get_shift_alg (shift_type, shift_mode, 1, &info); + + return (4 + h8300_asm_insn_count (info.shift1)) * 2; + } + else + { + int n = INTVAL (operands[2]); + + /* If the count is negative, make it 0. */ + if (n < 0) + n = 0; + /* If the count is too big, truncate it. + ANSI says shifts of GET_MODE_BITSIZE are undefined - we choose to + do the intuitive thing. */ + else if ((unsigned int) n > GET_MODE_BITSIZE (mode)) + n = GET_MODE_BITSIZE (mode); + + get_shift_alg (shift_type, shift_mode, n, &info); + + switch (info.alg) + { + case SHIFT_SPECIAL: + wlength += h8300_asm_insn_count (info.special); + + /* Every assembly instruction used in SHIFT_SPECIAL case + takes 2 bytes except xor.l, which takes 4 bytes, so if we + see xor.l, we just pretend that xor.l counts as two insns + so that the insn length will be computed correctly. */ + if (strstr (info.special, "xor.l") != NULL) + wlength++; + + /* Fall through. */ + + case SHIFT_INLINE: + n = info.remainder; + + if (info.shift2 != NULL) + { + wlength += h8300_asm_insn_count (info.shift2) * (n / 2); + n = n % 2; + } + + wlength += h8300_asm_insn_count (info.shift1) * n; + + return 2 * wlength; + + case SHIFT_ROT_AND: + { + int m = GET_MODE_BITSIZE (mode) - n; + + /* Not all possibilities of rotate are supported. They shouldn't + be generated, but let's watch for 'em. */ + if (info.shift1 == 0) + abort (); + + if (info.shift2 != NULL) + { + wlength += h8300_asm_insn_count (info.shift2) * (m / 2); + m = m % 2; + } + + wlength += h8300_asm_insn_count (info.shift1) * m; + + /* Now mask off the high bits. */ + switch (mode) + { + case QImode: + wlength += 1; + break; + case HImode: + wlength += 2; + break; + case SImode: + if (TARGET_H8300) + abort (); + wlength += 3; + break; + default: + abort (); + } + return 2 * wlength; + } + + case SHIFT_LOOP: + /* A loop to shift by a "large" constant value. + If we have shift-by-2 insns, use them. */ + if (info.shift2 != NULL) + { + wlength += 3 + h8300_asm_insn_count (info.shift2); + if (n % 2) + wlength += h8300_asm_insn_count (info.shift1); + } + else + { + wlength += 3 + h8300_asm_insn_count (info.shift1); + } + return 2 * wlength; + + default: + abort (); + } + } +} /* A rotation by a non-constant will cause a loop to be generated, in which a rotation by one bit is used. A rotation by a constant, @@ -2663,7 +3290,7 @@ expand_a_rotate (code, operands) tmp = gen_rtx_PLUS (QImode, counter, GEN_INT (-1)); emit_insn (gen_rtx_SET (VOIDmode, counter, tmp)); - /* If the loop counter is non-zero, we go back to the beginning + /* If the loop counter is nonzero, we go back to the beginning of the loop. */ emit_cmp_and_jump_insns (counter, GEN_INT (0), NE, NULL_RTX, QImode, 1, start_label); @@ -2736,7 +3363,7 @@ emit_a_rotate (code, operands) /* Determine the faster direction. After this phase, amount will be at most a half of GET_MODE_BITSIZE (mode). */ - if ((unsigned int) amount > GET_MODE_BITSIZE (mode) / 2U) + if ((unsigned int) amount > GET_MODE_BITSIZE (mode) / (unsigned) 2) { /* Flip the direction. */ amount = GET_MODE_BITSIZE (mode) - amount; @@ -2761,7 +3388,7 @@ emit_a_rotate (code, operands) break; case SImode: - /* This code works on the H8/300H and H8/S. */ + /* This code works on the H8/300H and H8S. */ insn_buf = "xor.w\t%e0,%f0\n\txor.w\t%f0,%e0\n\txor.w\t%e0,%f0"; output_asm_insn (insn_buf, operands); break; @@ -2804,32 +3431,30 @@ fix_bit_operand (operands, what, type) only 'U' memory afterwards, so if this is a MEM operand, we must force it to be valid for 'U' by reloading the address. */ - if (GET_CODE (operands[2]) == CONST_INT) + if ((what == 0 && single_zero_operand (operands[2], QImode)) + || (what == 1 && single_one_operand (operands[2], QImode))) { - if (CONST_OK_FOR_LETTER_P (INTVAL (operands[2]), what)) + /* OK to have a memory dest. */ + if (GET_CODE (operands[0]) == MEM + && !EXTRA_CONSTRAINT (operands[0], 'U')) { - /* Ok to have a memory dest. */ - if (GET_CODE (operands[0]) == MEM - && !EXTRA_CONSTRAINT (operands[0], 'U')) - { - rtx mem = gen_rtx_MEM (GET_MODE (operands[0]), - copy_to_mode_reg (Pmode, - XEXP (operands[0], 0))); - MEM_COPY_ATTRIBUTES (mem, operands[0]); - operands[0] = mem; - } + rtx mem = gen_rtx_MEM (GET_MODE (operands[0]), + copy_to_mode_reg (Pmode, + XEXP (operands[0], 0))); + MEM_COPY_ATTRIBUTES (mem, operands[0]); + operands[0] = mem; + } - if (GET_CODE (operands[1]) == MEM - && !EXTRA_CONSTRAINT (operands[1], 'U')) - { - rtx mem = gen_rtx_MEM (GET_MODE (operands[1]), - copy_to_mode_reg (Pmode, - XEXP (operands[1], 0))); - MEM_COPY_ATTRIBUTES (mem, operands[0]); - operands[1] = mem; - } - return 0; + if (GET_CODE (operands[1]) == MEM + && !EXTRA_CONSTRAINT (operands[1], 'U')) + { + rtx mem = gen_rtx_MEM (GET_MODE (operands[1]), + copy_to_mode_reg (Pmode, + XEXP (operands[1], 0))); + MEM_COPY_ATTRIBUTES (mem, operands[0]); + operands[1] = mem; } + return 0; } /* Dest and src op must be register. */ @@ -2940,6 +3565,22 @@ h8300_tiny_data_p (decl) return a != NULL_TREE; } +/* Generate an 'interrupt_handler' attribute for decls. */ + +static void +h8300_insert_attributes (node, attributes) + tree node; + tree *attributes; +{ + if (!interrupt_handler + || TREE_CODE (node) != FUNCTION_DECL) + return; + + /* Add an 'interrupt_handler' attribute. */ + *attributes = tree_cons (get_identifier ("interrupt_handler"), + NULL, *attributes); +} + /* Supported attributes: interrupt_handler: output a prologue and epilogue suitable for an @@ -3037,7 +3678,7 @@ h8300_handle_tiny_data_attribute (node, name, args, flags, no_add_attrs) return NULL_TREE; } -void +static void h8300_encode_label (decl) tree decl; { @@ -3052,23 +3693,67 @@ h8300_encode_label (decl) ggc_alloc_string (newstr, len + 1); } +/* If we are referencing a function that is supposed to be called + through the function vector, the SYMBOL_REF_FLAG in the rtl + so the call patterns can generate the correct code. */ + +static void +h8300_encode_section_info (decl, first) + tree decl; + int first; +{ + if (TREE_CODE (decl) == FUNCTION_DECL + && h8300_funcvec_function_p (decl)) + SYMBOL_REF_FLAG (XEXP (DECL_RTL (decl), 0)) = 1; + else if (TREE_CODE (decl) == VAR_DECL + && (TREE_STATIC (decl) || DECL_EXTERNAL (decl))) + { + if (h8300_eightbit_data_p (decl)) + SYMBOL_REF_FLAG (XEXP (DECL_RTL (decl), 0)) = 1; + else if (first && h8300_tiny_data_p (decl)) + h8300_encode_label (decl); + } +} + +/* Undo the effects of the above. */ + +static const char * +h8300_strip_name_encoding (str) + const char *str; +{ + return str + (*str == '*' || *str == '@' || *str == '&'); +} + const char * output_simode_bld (bild, operands) int bild; rtx operands[]; { - /* Clear the destination register. */ - if (TARGET_H8300H || TARGET_H8300S) - output_asm_insn ("sub.l\t%S0,%S0", operands); - else - output_asm_insn ("sub.w\t%e0,%e0\n\tsub.w\t%f0,%f0", operands); + if (TARGET_H8300) + { + /* Clear the destination register. */ + output_asm_insn ("sub.w\t%e0,%e0\n\tsub.w\t%f0,%f0", operands); + + /* Now output the bit load or bit inverse load, and store it in + the destination. */ + if (bild) + output_asm_insn ("bild\t%Z2,%Y1", operands); + else + output_asm_insn ("bld\t%Z2,%Y1", operands); - /* Now output the bit load or bit inverse load, and store it in - the destination. */ - if (bild) - output_asm_insn ("bild\t%Z2,%Y1\n\tbst\t#0,%w0", operands); + output_asm_insn ("bst\t#0,%w0", operands); + } else - output_asm_insn ("bld\t%Z2,%Y1\n\tbst\t#0,%w0", operands); + { + /* Output the bit load or bit inverse load. */ + if (bild) + output_asm_insn ("bild\t%Z2,%Y1", operands); + else + output_asm_insn ("bld\t%Z2,%Y1", operands); + + /* Clear the destination register and perform the bit store. */ + output_asm_insn ("xor.l\t%S0,%S0\n\tbst\t#0,%w0", operands); + } /* All done. */ return ""; @@ -3110,34 +3795,51 @@ h8300_adjust_insn_length (insn, length) else addr = XEXP (SET_DEST (pat), 0); - /* On the H8/300, only one adjustment is necessary; if the - address mode is register indirect, then this insn is two - bytes shorter than indicated in the machine description. */ - if (TARGET_H8300 && GET_CODE (addr) == REG) - return -2; + if (TARGET_H8300) + { + /* On the H8/300, we subtract the difference between the + actual length and the longest one, which is @(d:16,ERs). */ - /* On the H8/300H and H8/S, register indirect is 6 bytes shorter than - indicated in the machine description. */ - if ((TARGET_H8300H || TARGET_H8300S) - && GET_CODE (addr) == REG) - return -6; + /* @Rs is 2 bytes shorter than the longest. */ + if (GET_CODE (addr) == REG) + return -2; - /* On the H8/300H and H8/S, reg + d, for small displacements is - 4 bytes shorter than indicated in the machine description. */ - if ((TARGET_H8300H || TARGET_H8300S) - && GET_CODE (addr) == PLUS - && GET_CODE (XEXP (addr, 0)) == REG - && GET_CODE (XEXP (addr, 1)) == CONST_INT - && INTVAL (XEXP (addr, 1)) > -32768 - && INTVAL (XEXP (addr, 1)) < 32767) - return -4; - - /* On the H8/300H and H8/S, abs:16 is two bytes shorter than the - more general abs:24. */ - if ((TARGET_H8300H || TARGET_H8300S) - && GET_CODE (addr) == SYMBOL_REF - && TINY_DATA_NAME_P (XSTR (addr, 0))) - return -2; + /* @aa:8 is 2 bytes shorter than the longest. */ + if (GET_MODE (SET_SRC (pat)) == QImode + && h8300_eightbit_constant_address_p (addr)) + return -2; + } + else + { + /* On the H8/300H and H8S, we subtract the difference + between the actual length and the longest one, which is + @(d:24,ERs). */ + + /* @ERs is 6 bytes shorter than the longest. */ + if (GET_CODE (addr) == REG) + return -6; + + /* @(d:16,ERs) is 6 bytes shorter than the longest. */ + if (GET_CODE (addr) == PLUS + && GET_CODE (XEXP (addr, 0)) == REG + && GET_CODE (XEXP (addr, 1)) == CONST_INT + && INTVAL (XEXP (addr, 1)) > -32768 + && INTVAL (XEXP (addr, 1)) < 32767) + return -4; + + /* @aa:8 is 6 bytes shorter than the longest. */ + if (GET_MODE (SET_SRC (pat)) == QImode + && h8300_eightbit_constant_address_p (addr)) + return -6; + + /* @aa:16 is 4 bytes shorter than the longest. */ + if (h8300_tiny_constant_address_p (addr)) + return -4; + + /* @aa:24 is 2 bytes shorter than the longest. */ + if (GET_CODE (addr) == CONST_INT) + return -2; + } } /* Loading some constants needs adjustment. */ @@ -3157,57 +3859,24 @@ h8300_adjust_insn_length (insn, length) { if (val == (val & 0xff) || val == (val & 0xff00)) - return -6; + return 4 - 6; - if (val == -4 || val == -2 || val == -1) - return -6; + switch (val & 0xffffffff) + { + case 0xffffffff: + case 0xfffffffe: + case 0xfffffffc: + case 0x0000ffff: + case 0x0000fffe: + case 0xffff0000: + case 0xfffe0000: + case 0x00010000: + case 0x00020000: + return 4 - 6; + } } } - /* Shifts need various adjustments. */ - if (GET_CODE (pat) == PARALLEL - && GET_CODE (XVECEXP (pat, 0, 0)) == SET - && (GET_CODE (SET_SRC (XVECEXP (pat, 0, 0))) == ASHIFTRT - || GET_CODE (SET_SRC (XVECEXP (pat, 0, 0))) == LSHIFTRT - || GET_CODE (SET_SRC (XVECEXP (pat, 0, 0))) == ASHIFT)) - { - rtx src = SET_SRC (XVECEXP (pat, 0, 0)); - enum machine_mode mode = GET_MODE (src); - int shift; - - if (GET_CODE (XEXP (src, 1)) != CONST_INT) - return 0; - - shift = INTVAL (XEXP (src, 1)); - /* According to ANSI, negative shift is undefined. It is - considered to be zero in this case (see function - output_a_shift above). */ - if (shift < 0) - shift = 0; - - /* QImode shifts by small constants take one insn - per shift. So the adjustment is 20 (md length) - - # shifts * 2. */ - if (mode == QImode && shift <= 4) - return -(20 - shift * 2); - - /* Similarly for HImode and SImode shifts by small constants on - the H8/300H and H8/S. */ - if ((TARGET_H8300H || TARGET_H8300S) - && (mode == HImode || mode == SImode) && shift <= 4) - return -(20 - shift * 2); - - /* HImode shifts by small constants for the H8/300. */ - if (mode == HImode && shift <= 4) - return -(20 - (shift * (GET_CODE (src) == ASHIFT ? 2 : 4))); - - /* SImode shifts by small constants for the H8/300. */ - if (mode == SImode && shift <= 2) - return -(20 - (shift * (GET_CODE (src) == ASHIFT ? 6 : 8))); - - /* XXX ??? Could check for more shift/rotate cases here. */ - } - /* Rotations need various adjustments. */ if (GET_CODE (pat) == SET && (GET_CODE (SET_SRC (pat)) == ROTATE @@ -3231,7 +3900,7 @@ h8300_adjust_insn_length (insn, length) /* Determine the faster direction. After this phase, amount will be at most a half of GET_MODE_BITSIZE (mode). */ - if ((unsigned int) amount > GET_MODE_BITSIZE (mode) / 2U) + if ((unsigned int) amount > GET_MODE_BITSIZE (mode) / (unsigned) 2) /* Flip the direction. */ amount = GET_MODE_BITSIZE (mode) - amount; @@ -3248,7 +3917,7 @@ h8300_adjust_insn_length (insn, length) states += 6; } - /* We use 2-bit rotatations on the H8/S. */ + /* We use 2-bit rotatations on the H8S. */ if (TARGET_H8300S) amount = amount / 2 + amount % 2; @@ -3272,3 +3941,70 @@ h8300_asm_named_section (name, flags) fprintf (asm_out_file, "\t.section %s\n", name); } #endif /* ! OBJECT_FORMAT_ELF */ + +/* Nonzero if X is a constant address suitable as an 8-bit absolute, + which is a special case of the 'R' operand. */ + +int +h8300_eightbit_constant_address_p (x) + rtx x; +{ + /* The ranges of the 8-bit area. */ + const unsigned HOST_WIDE_INT n1 = trunc_int_for_mode (0xff00, HImode); + const unsigned HOST_WIDE_INT n2 = trunc_int_for_mode (0xffff, HImode); + const unsigned HOST_WIDE_INT h1 = trunc_int_for_mode (0x00ffff00, SImode); + const unsigned HOST_WIDE_INT h2 = trunc_int_for_mode (0x00ffffff, SImode); + const unsigned HOST_WIDE_INT s1 = trunc_int_for_mode (0xffffff00, SImode); + const unsigned HOST_WIDE_INT s2 = trunc_int_for_mode (0xffffffff, SImode); + + unsigned HOST_WIDE_INT addr; + + /* We accept symbols declared with eightbit_data. */ + if (GET_CODE (x) == SYMBOL_REF && SYMBOL_REF_FLAG (x)) + return 1; + + if (GET_CODE (x) != CONST_INT) + return 0; + + addr = INTVAL (x); + + return (0 + || ((TARGET_H8300 || TARGET_NORMAL_MODE) && IN_RANGE (addr, n1, n2)) + || (TARGET_H8300H && IN_RANGE (addr, h1, h2)) + || (TARGET_H8300S && IN_RANGE (addr, s1, s2))); +} + +/* Nonzero if X is a constant address suitable as an 16-bit absolute + on H8/300H and H8S. */ + +int +h8300_tiny_constant_address_p (x) + rtx x; +{ + /* The ranges of the 16-bit area. */ + const unsigned HOST_WIDE_INT h1 = trunc_int_for_mode (0x00000000, SImode); + const unsigned HOST_WIDE_INT h2 = trunc_int_for_mode (0x00007fff, SImode); + const unsigned HOST_WIDE_INT h3 = trunc_int_for_mode (0x00ff8000, SImode); + const unsigned HOST_WIDE_INT h4 = trunc_int_for_mode (0x00ffffff, SImode); + const unsigned HOST_WIDE_INT s1 = trunc_int_for_mode (0x00000000, SImode); + const unsigned HOST_WIDE_INT s2 = trunc_int_for_mode (0x00007fff, SImode); + const unsigned HOST_WIDE_INT s3 = trunc_int_for_mode (0xffff8000, SImode); + const unsigned HOST_WIDE_INT s4 = trunc_int_for_mode (0xffffffff, SImode); + + unsigned HOST_WIDE_INT addr; + + /* We accept symbols declared with tiny_data. */ + if (GET_CODE (x) == SYMBOL_REF && TINY_DATA_NAME_P (XSTR (x, 0))) + return 1; + + if (GET_CODE (x) != CONST_INT) + return 0; + + addr = INTVAL (x); + + return (0 + || ((TARGET_H8300H && !TARGET_NORMAL_MODE) + && (IN_RANGE (addr, h1, h2) || IN_RANGE (addr, h3, h4))) + || ((TARGET_H8300S && !TARGET_NORMAL_MODE) + && (IN_RANGE (addr, s1, s2) || IN_RANGE (addr, s3, s4)))); +}