📄 m88k.c
字号:
#define MOVSTR_DI 96 /* __movstrDI96x96 .. __movstrDI96x16 */#define MOVSTR_ODD_HI 16 /* __movstrHI15x15 .. __movstrHI15x5 */#define MOVSTR_ODD_SI 48 /* __movstrSI47x47 .. __movstrSI47x11, __movstrSI46x46 .. __movstrSI46x10, __movstrSI45x45 .. __movstrSI45x9 */#define MOVSTR_ODD_DI 48 /* __movstrDI47x47 .. __movstrDI47x23, __movstrDI46x46 .. __movstrDI46x22, __movstrDI45x45 .. __movstrDI45x21, __movstrDI44x44 .. __movstrDI44x20, __movstrDI43x43 .. __movstrDI43x19, __movstrDI42x42 .. __movstrDI42x18, __movstrDI41x41 .. __movstrDI41x17 *//* Limits for using the non-looping movstr functions. For the m88100 processor, we assume the source and destination are word aligned. The QImode and HImode limits are the break even points where memcpy does just as well and beyond which memcpy does better. For the m88110, we tend to assume double word alignment, but also analyze the word aligned cases. The analysis is complicated because memcpy may use the cache control instructions for better performance. */#define MOVSTR_QI_LIMIT_88100 13#define MOVSTR_HI_LIMIT_88100 38#define MOVSTR_SI_LIMIT_88100 MOVSTR_SI#define MOVSTR_DI_LIMIT_88100 MOVSTR_SI #define MOVSTR_QI_LIMIT_88000 16#define MOVSTR_HI_LIMIT_88000 38#define MOVSTR_SI_LIMIT_88000 72#define MOVSTR_DI_LIMIT_88000 72#define MOVSTR_QI_LIMIT_88110 16#define MOVSTR_HI_LIMIT_88110 38#define MOVSTR_SI_LIMIT_88110 72#define MOVSTR_DI_LIMIT_88110 72static const enum machine_mode mode_from_align[] = {VOIDmode, QImode, HImode, VOIDmode, SImode, VOIDmode, VOIDmode, VOIDmode, DImode};static const int max_from_align[] = {0, MOVSTR_QI, MOVSTR_HI, 0, MOVSTR_SI, 0, 0, 0, MOVSTR_DI};static const int all_from_align[] = {0, MOVSTR_QI, MOVSTR_ODD_HI, 0, MOVSTR_ODD_SI, 0, 0, 0, MOVSTR_ODD_DI};static const int best_from_align[3][9] = { {0, MOVSTR_QI_LIMIT_88100, MOVSTR_HI_LIMIT_88100, 0, MOVSTR_SI_LIMIT_88100, 0, 0, 0, MOVSTR_DI_LIMIT_88100}, {0, MOVSTR_QI_LIMIT_88110, MOVSTR_HI_LIMIT_88110, 0, MOVSTR_SI_LIMIT_88110, 0, 0, 0, MOVSTR_DI_LIMIT_88110}, {0, MOVSTR_QI_LIMIT_88000, MOVSTR_HI_LIMIT_88000, 0, MOVSTR_SI_LIMIT_88000, 0, 0, 0, MOVSTR_DI_LIMIT_88000}};static void block_move_loop PARAMS ((rtx, rtx, rtx, rtx, int, int));static void block_move_no_loop PARAMS ((rtx, rtx, rtx, rtx, int, int));static void block_move_sequence PARAMS ((rtx, rtx, rtx, rtx, int, int, int));static void output_short_branch_defs PARAMS ((FILE *));static int output_option PARAMS ((FILE *, const char *, const char *, const char *, const char *, int, int));/* Emit code to perform a block move. Choose the best method. OPERANDS[0] is the destination. OPERANDS[1] is the source. OPERANDS[2] is the size. OPERANDS[3] is the alignment safe to use. */voidexpand_block_move (dest_mem, src_mem, operands) rtx dest_mem; rtx src_mem; rtx *operands;{ int align = INTVAL (operands[3]); int constp = (GET_CODE (operands[2]) == CONST_INT); int bytes = (constp ? INTVAL (operands[2]) : 0); int target = (int) m88k_cpu; if (! (PROCESSOR_M88100 == 0 && PROCESSOR_M88110 == 1 && PROCESSOR_M88000 == 2)) abort (); if (constp && bytes <= 0) return; /* Determine machine mode to do move with. */ if (align > 4 && !TARGET_88110) align = 4; else if (align <= 0 || align == 3) abort (); /* block move invalid alignment. */ if (constp && bytes <= 3 * align) block_move_sequence (operands[0], dest_mem, operands[1], src_mem, bytes, align, 0); else if (constp && bytes <= best_from_align[target][align]) block_move_no_loop (operands[0], dest_mem, operands[1], src_mem, bytes, align); else if (constp && align == 4 && TARGET_88100) block_move_loop (operands[0], dest_mem, operands[1], src_mem, bytes, align); else {#ifdef TARGET_MEM_FUNCTIONS emit_library_call (gen_rtx_SYMBOL_REF (Pmode, "memcpy"), 0, VOIDmode, 3, operands[0], Pmode, operands[1], Pmode, convert_to_mode (TYPE_MODE (sizetype), operands[2], TREE_UNSIGNED (sizetype)), TYPE_MODE (sizetype));#else emit_library_call (gen_rtx_SYMBOL_REF (Pmode, "bcopy"), 0, VOIDmode, 3, operands[1], Pmode, operands[0], Pmode, convert_to_mode (TYPE_MODE (integer_type_node), operands[2], TREE_UNSIGNED (integer_type_node)), TYPE_MODE (integer_type_node));#endif }}/* Emit code to perform a block move by calling a looping movstr library function. SIZE and ALIGN are known constants. DEST and SRC are registers. */static voidblock_move_loop (dest, dest_mem, src, src_mem, size, align) rtx dest, dest_mem; rtx src, src_mem; int size; int align;{ enum machine_mode mode; int count; int units; int remainder; rtx offset_rtx; rtx value_rtx; char entry[30]; tree entry_name; /* Determine machine mode to do move with. */ if (align != 4) abort (); /* Determine the structure of the loop. */ count = size / MOVSTR_LOOP; units = (size - count * MOVSTR_LOOP) / align; if (units < 2) { count--; units += MOVSTR_LOOP / align; } if (count <= 0) { block_move_no_loop (dest, dest_mem, src, src_mem, size, align); return; } remainder = size - count * MOVSTR_LOOP - units * align; mode = mode_from_align[align]; sprintf (entry, "__movstr%s%dn%d", GET_MODE_NAME (mode), MOVSTR_LOOP, units * align); entry_name = get_identifier (entry); offset_rtx = GEN_INT (MOVSTR_LOOP + (1 - units) * align); value_rtx = gen_rtx_MEM (MEM_IN_STRUCT_P (src_mem) ? mode : BLKmode, gen_rtx_PLUS (Pmode, gen_rtx_REG (Pmode, 3), offset_rtx)); MEM_COPY_ATTRIBUTES (value_rtx, src_mem); emit_insn (gen_call_movstrsi_loop (gen_rtx_SYMBOL_REF (Pmode, IDENTIFIER_POINTER (entry_name)), dest, src, offset_rtx, value_rtx, gen_rtx_REG (mode, ((units & 1) ? 4 : 5)), GEN_INT (count))); if (remainder) block_move_sequence (gen_rtx_REG (Pmode, 2), dest_mem, gen_rtx_REG (Pmode, 3), src_mem, remainder, align, MOVSTR_LOOP + align);}/* Emit code to perform a block move by calling a non-looping library function. SIZE and ALIGN are known constants. DEST and SRC are registers. OFFSET is the known starting point for the output pattern. */static voidblock_move_no_loop (dest, dest_mem, src, src_mem, size, align) rtx dest, dest_mem; rtx src, src_mem; int size; int align;{ enum machine_mode mode = mode_from_align[align]; int units = size / align; int remainder = size - units * align; int most; int value_reg; rtx offset_rtx; rtx value_rtx; char entry[30]; tree entry_name; if (remainder && size <= all_from_align[align]) { most = all_from_align[align] - (align - remainder); remainder = 0; } else { most = max_from_align[align]; } sprintf (entry, "__movstr%s%dx%d", GET_MODE_NAME (mode), most, size - remainder); entry_name = get_identifier (entry); offset_rtx = GEN_INT (most - (size - remainder)); value_rtx = gen_rtx_MEM (MEM_IN_STRUCT_P (src_mem) ? mode : BLKmode, gen_rtx_PLUS (Pmode, gen_rtx_REG (Pmode, 3), offset_rtx)); MEM_COPY_ATTRIBUTES (value_rtx, src_mem); value_reg = ((((most - (size - remainder)) / align) & 1) == 0 ? (align == 8 ? 6 : 5) : 4); emit_insn (gen_call_block_move (gen_rtx_SYMBOL_REF (Pmode, IDENTIFIER_POINTER (entry_name)), dest, src, offset_rtx, value_rtx, gen_rtx_REG (mode, value_reg))); if (remainder) block_move_sequence (gen_rtx_REG (Pmode, 2), dest_mem, gen_rtx_REG (Pmode, 3), src_mem, remainder, align, most);}/* Emit code to perform a block move with an offset sequence of ld/st instructions (..., ld 0, st 1, ld 1, st 0, ...). SIZE and ALIGN are known constants. DEST and SRC are registers. OFFSET is the known starting point for the output pattern. */static voidblock_move_sequence (dest, dest_mem, src, src_mem, size, align, offset) rtx dest, dest_mem; rtx src, src_mem; int size; int align; int offset;{ rtx temp[2]; enum machine_mode mode[2]; int amount[2]; int active[2]; int phase = 0; int next; int offset_ld = offset; int offset_st = offset; active[0] = active[1] = FALSE; /* Establish parameters for the first load and for the second load if it is known to be the same mode as the first. */ amount[0] = amount[1] = align; mode[0] = mode_from_align[align]; temp[0] = gen_reg_rtx (mode[0]); if (size >= 2 * align) { mode[1] = mode[0]; temp[1] = gen_reg_rtx (mode[1]); } do { rtx srcp, dstp; next = phase; phase = !phase; if (size > 0) { /* Change modes as the sequence tails off. */ if (size < amount[next]) { amount[next] = (size >= 4 ? 4 : (size >= 2 ? 2 : 1)); mode[next] = mode_from_align[amount[next]]; temp[next] = gen_reg_rtx (mode[next]); } size -= amount[next]; srcp = gen_rtx_MEM (MEM_IN_STRUCT_P (src_mem) ? mode[next] : BLKmode, plus_constant (src, offset_ld)); MEM_COPY_ATTRIBUTES (srcp, src_mem); emit_insn (gen_rtx_SET (VOIDmode, temp[next], srcp)); offset_ld += amount[next]; active[next] = TRUE; } if (active[phase]) { active[phase] = FALSE; dstp = gen_rtx_MEM (MEM_IN_STRUCT_P (dest_mem) ? mode[phase] : BLKmode, plus_constant (dest, offset_st)); MEM_COPY_ATTRIBUTES (dstp, dest_mem); emit_insn (gen_rtx_SET (VOIDmode, dstp, temp[phase])); offset_st += amount[phase]; } } while (active[next]);}/* Emit the code to do an AND operation. */const char *output_and (operands) rtx operands[];{ unsigned int value; if (REG_P (operands[2])) return "and %0,%1,%2"; value = INTVAL (operands[2]); if (SMALL_INTVAL (value)) return "mask %0,%1,%2"; else if ((value & 0xffff0000) == 0xffff0000) return "and %0,%1,%x2"; else if ((value & 0xffff) == 0xffff) return "and.u %0,%1,%X2"; else if ((value & 0xffff) == 0) return "mask.u %0,%1,%X2"; else if (integer_ok_for_set (~value)) return "clr %0,%1,%S2"; else return "and.u %0,%1,%X2\n\tand %0,%0,%x2";}/* Emit the code to do an inclusive OR operation. */const char *output_ior (operands) rtx operands[];{ unsigned int value; if (REG_P (operands[2])) return "or %0,%1,%2"; value = INTVAL (operands[2]); if (SMALL_INTVAL (value)) return "or %0,%1,%2"; else if ((value & 0xffff) == 0) return "or.u %0,%1,%X2"; else if (integer_ok_for_set (value)) return "set %0,%1,%s2"; else return "or.u %0,%1,%X2\n\tor %0,%0,%x2";}/* Emit the instructions for doing an XOR. */const char *output_xor (operands) rtx operands[];{ unsigned int value; if (REG_P (operands[2])) return "xor %0,%1,%2"; value = INTVAL (operands[2]); if (SMALL_INTVAL (value)) return "xor %0,%1,%2"; else if ((value & 0xffff) == 0) return "xor.u %0,%1,%X2"; else return "xor.u %0,%1,%X2\n\txor %0,%0,%x2";}/* Output a call. Normally this is just bsr or jsr, but this also deals with accomplishing a branch after the call by incrementing r1. This requires that various assembler bugs be accommodated. The 4.30 DG/UX assembler requires that forward references not occur when computing the difference of two labels. The [version?] Motorola assembler computes a word difference. No doubt there's more to come! It would seem the same idea could be used to tail call, but in this case, the epilogue will be non-null. */static rtx sb_name = 0;static rtx sb_high = 0;static rtx sb_low = 0;const char *output_call (operands, addr) rtx operands[]; rtx addr;{ operands[0] = addr; if (final_sequence) { rtx jump; rtx seq_insn; /* This can be generalized, but there is currently no need. */ if (XVECLEN (final_sequence, 0) != 2) abort (); /* The address of interior insns is not computed, so use the sequence. */ seq_insn = NEXT_INSN (PREV_INSN (XVECEXP (final_sequence, 0, 0))); jump = XVECEXP (final_sequence, 0, 1); if (GET_CODE (jump) == JUMP_INSN) { rtx low, high; const char *last; rtx dest = XEXP (SET_SRC (PATTERN (jump)), 0); int delta = 4 * (INSN_ADDRESSES (INSN_UID (dest)) - INSN_ADDRESSES (INSN_UID (seq_insn)) - 2);#if (MONITOR_GCC & 0x2) /* How often do long branches happen? */ if ((unsigned) (delta + 0x8000) >= 0x10000) warning ("internal gcc monitor: short-branch(%x)", delta);#endif /* Delete the jump. */ PUT_CODE (jump, NOTE); NOTE_LINE_NUMBER (jump) = NOTE_INSN_DELETED; NOTE_SOURCE_FILE (jump) = 0; /* We only do this optimization if -O2, modifying the value of r1 in the delay slot confuses debuggers and profilers on some systems. If we loose, we must use the non-delay form. This is unlikely to ever happen. If it becomes a problem, claim that a call has two delay slots and only the second can be filled with a jump. The 88110 can lose when a jsr.n r1 is issued and a page fault occurs accessing the delay slot. So don't use jsr.n form when jumping thru r1. */#ifdef AS_BUG_IMMEDIATE_LABEL /* The assembler restricts immediate values. */ if (optimize < 2 || ! ADD_INTVAL (delta * 2)#else if (optimize < 2 || ! ADD_INTVAL (delta)#endif
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -