📄 h8300.c
字号:
/* 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;}intcompute_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 a loop. o SHIFT_ROT_AND: Rotate the value the opposite direction, then mask off the bits we don't need. This is used when only a few of the bits in the original value will survive in the shifted value. o SHIFT_SPECIAL: Often it's possible to move a byte or a word to simulate a shift by 8, 16, or 24 bits. Once moved, a few inline shifts can be added if the shift count is slightly more than 8 or 16. This case also includes other oddballs that are not worth explaning here. 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. */intnshift_operator (x, mode) rtx x; enum machine_mode mode ATTRIBUTE_UNUSED;{ switch (GET_CODE (x)) { case ASHIFTRT: case LSHIFTRT: case ASHIFT: return 1; default: return 0; }}/* Called from the .md file to emit code to do shifts. Return a boolean indicating success. (Currently this is always TRUE). */intexpand_a_shift (mode, code, operands) enum machine_mode mode; int code; rtx operands[];{ emit_move_insn (operands[0], operands[1]); /* Need a loop to get all the bits we want - we generate the code at emit time, but need to allocate a scratch reg now. */ emit_insn (gen_rtx_PARALLEL (VOIDmode, gen_rtvec (2, gen_rtx_SET (VOIDmode, operands[0], gen_rtx (code, mode, operands[0], operands[2])), gen_rtx_CLOBBER (VOIDmode, gen_rtx_SCRATCH (QImode))))); return 1;}/* Symbols of the various modes which can be used as indices. */enum shift_mode{ QIshift, HIshift, SIshift};/* For single bit shift insns, record assembler and what bits of the condition code are valid afterwards (represented as various CC_FOO bits, 0 means CC isn't left in a usable state). */struct shift_insn{ const char *const assembler; const int cc_valid;};/* Assembler instruction shift table. These tables are used to look up the basic shifts. They are indexed by cpu, shift_type, and mode. */static const struct shift_insn shift_one[2][3][3] ={/* H8/300 */ {/* SHIFT_ASHIFT */ { { "shll\t%X0", CC_NO_CARRY }, { "add.w\t%T0,%T0", CC_OVERFLOW_UNUSABLE | CC_NO_CARRY }, { "add.w\t%f0,%f0\n\taddx\t%y0,%y0\n\taddx\t%z0,%z0", 0 } },/* SHIFT_LSHIFTRT */ { { "shlr\t%X0", CC_NO_CARRY }, { "shlr\t%t0\n\trotxr\t%s0", 0 }, { "shlr\t%z0\n\trotxr\t%y0\n\trotxr\t%x0\n\trotxr\t%w0", 0 } },/* SHIFT_ASHIFTRT */ { { "shar\t%X0", CC_OVERFLOW_UNUSABLE | CC_NO_CARRY }, { "shar\t%t0\n\trotxr\t%s0", 0 }, { "shar\t%z0\n\trotxr\t%y0\n\trotxr\t%x0\n\trotxr\t%w0", 0 } } },/* H8/300H */ {/* SHIFT_ASHIFT */ { { "shll.b\t%X0", CC_NO_CARRY }, { "shll.w\t%T0", CC_NO_CARRY }, { "shll.l\t%S0", CC_NO_CARRY } },/* SHIFT_LSHIFTRT */ { { "shlr.b\t%X0", CC_NO_CARRY }, { "shlr.w\t%T0", CC_NO_CARRY }, { "shlr.l\t%S0", CC_NO_CARRY } },/* SHIFT_ASHIFTRT */ { { "shar.b\t%X0", CC_OVERFLOW_UNUSABLE | CC_NO_CARRY }, { "shar.w\t%T0", CC_OVERFLOW_UNUSABLE | CC_NO_CARRY }, { "shar.l\t%S0", CC_OVERFLOW_UNUSABLE | CC_NO_CARRY } } }};static const struct shift_insn shift_two[3][3] ={/* SHIFT_ASHIFT */ { { "shll.b\t#2,%X0", CC_NO_CARRY }, { "shll.w\t#2,%T0", CC_NO_CARRY }, { "shll.l\t#2,%S0", CC_NO_CARRY } },/* SHIFT_LSHIFTRT */ { { "shlr.b\t#2,%X0", CC_NO_CARRY }, { "shlr.w\t#2,%T0", CC_NO_CARRY }, { "shlr.l\t#2,%S0", CC_NO_CARRY } },/* SHIFT_ASHIFTRT */ { { "shar.b\t#2,%X0", CC_OVERFLOW_UNUSABLE | CC_NO_CARRY }, { "shar.w\t#2,%T0", CC_OVERFLOW_UNUSABLE | CC_NO_CARRY }, { "shar.l\t#2,%S0", CC_OVERFLOW_UNUSABLE | CC_NO_CARRY } }};/* Rotates are organized by which shift they'll be used in implementing. There's no need to record whether the cc is valid afterwards because it is the AND insn that will decide this. */static const char *const rotate_one[2][3][3] ={/* H8/300 */ {/* SHIFT_ASHIFT */ { "rotr\t%X0", "shlr\t%t0\n\trotxr\t%s0\n\tbst\t#7,%t0", 0 },/* SHIFT_LSHIFTRT */ { "rotl\t%X0", "shll\t%s0\n\trotxl\t%t0\n\tbst\t#0,%s0", 0 },/* SHIFT_ASHIFTRT */ { "rotl\t%X0", "shll\t%s0\n\trotxl\t%t0\n\tbst\t#0,%s0", 0 } },/* H8/300H */ {/* SHIFT_ASHIFT */ { "rotr.b\t%X0", "rotr.w\t%T0", "rotr.l\t%S0" },/* SHIFT_LSHIFTRT */ { "rotl.b\t%X0", "rotl.w\t%T0", "rotl.l\t%S0" },/* SHIFT_ASHIFTRT */ { "rotl.b\t%X0", "rotl.w\t%T0", "rotl.l\t%S0" } }};static const char *const rotate_two[3][3] ={/* SHIFT_ASHIFT */ { "rotr.b\t#2,%X0", "rotr.w\t#2,%T0", "rotr.l\t#2,%S0" },/* SHIFT_LSHIFTRT */ { "rotl.b\t#2,%X0", "rotl.w\t#2,%T0", "rotl.l\t#2,%S0" },/* SHIFT_ASHIFTRT */ { "rotl.b\t#2,%X0", "rotl.w\t#2,%T0", "rotl.l\t#2,%S0" }};struct shift_info { /* Shift algorithm. */ enum shift_alg alg; /* The number of bits to be shifted by shift1 and shift2. Valid when ALG is SHIFT_SPECIAL. */ unsigned int remainder; /* Special insn for a shift. Valid when ALG is SHIFT_SPECIAL. */ const char *special; /* Insn for a one-bit shift. Valid when ALG is either SHIFT_INLINE 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 nonzero. */ const char *shift2; /* Valid CC flags. */ int cc_valid_p;};static void get_shift_alg PARAMS ((enum shift_type, enum shift_mode, unsigned int, struct shift_info *));/* 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 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. 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 voidget_shift_alg (shift_type, shift_mode, count, info) enum shift_type shift_type; enum shift_mode shift_mode; unsigned int count; struct shift_info *info;{ enum h8_cpu cpu; /* Find the target CPU. */ if (TARGET_H8300) cpu = H8_300; else if (TARGET_H8300H) cpu = H8_300H; else cpu = H8_S; /* Find the shift algorithm. */ info->alg = SHIFT_LOOP; switch (shift_mode) { case QIshift: if (count < GET_MODE_BITSIZE (QImode)) info->alg = shift_alg_qi[cpu][shift_type][count]; break; case HIshift: if (count < GET_MODE_BITSIZE (HImode)) info->alg = shift_alg_hi[cpu][shift_type][count]; break; case SIshift: if (count < GET_MODE_BITSIZE (SImode)) info->alg = shift_alg_si[cpu][shift_type][count]; break; default: abort (); } /* Fill in INFO. Return unless we have SHIFT_SPECIAL. */ switch (info->alg) { case SHIFT_INLINE: info->remainder = count; /* Fall through. */ case SHIFT_LOOP: /* It is up to the caller to know that looping clobbers cc. */ info->shift1 = shift_one[cpu_type][shift_type][shift_mode].assembler; info->shift2 = shift_two[shift_type][shift_mode].assembler; info->cc_valid_p = shift_one[cpu_type][shift_type][shift_mode].cc_valid; goto end; case SHIFT_ROT_AND: info->shift1 = rotate_one[cpu_type][shift_type][shift_mode]; info->shift2 = rotate_two[shift_type][shift_mode]; info->cc_valid_p = 0; goto end; case SHIFT_SPECIAL: /* REMAINDER is 0 for most cases, so initialize it to 0. */ info->remainder = 0; info->shift1 = shift_one[cpu_type][shift_type][shift_mode].assembler; info->s
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -