📄 m88k.c
字号:
{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 ();static void block_move_no_loop ();static void block_move_sequence ();/* 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)); RTX_UNCHANGING_P (value_rtx) = RTX_UNCHANGING_P (src_mem); 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)); RTX_UNCHANGING_P (value_rtx) = RTX_UNCHANGING_P (src_mem); 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, gen_rtx (PLUS, Pmode, src, GEN_INT (offset_ld))); RTX_UNCHANGING_P (srcp) = RTX_UNCHANGING_P (src_mem); 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, gen_rtx (PLUS, Pmode, dest, GEN_INT (offset_st))); RTX_UNCHANGING_P (dstp) = RTX_UNCHANGING_P (dest_mem); 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. */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. */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. */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;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; 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 || (REG_P (addr) && REGNO (addr) == 1)) { operands[1] = dest; return (REG_P (addr) ? "jsr %0\n\tbr %l1" : (flag_pic ? "bsr %0#plt\n\tbr %l1" : "bsr %0\n\tbr %l1")); } /* Output the short branch form. */ output_asm_insn ((REG_P (addr) ? "jsr.n %0" : (flag_pic ? "bsr.n %0#plt" : "bsr.n %0")), operands);#ifdef USE_GAS last = (delta < 0 ? "subu %#r1,%#r1,.-%l0+4" : "addu %#r1,%#r1,%l0-.-4"); operands[0] = dest;#else operands[0] = gen_label_rtx (); operands[1] = gen_label_rtx (); if (delta < 0) { low = dest; high = operands[1]; last = "subu %#r1,%#r1,%l0\n%l1:"; } else { low = operands[1]; high = dest; last = "addu %#r1,%#r1,%l0\n%l1:"; } /* Record the values to be computed later as "def name,high-low". */ sb_name = gen_rtx (EXPR_LIST, VOIDmode, operands[0], sb_name); sb_high = gen_rtx (EXPR_LIST, VOIDmode, high, sb_high); sb_low = gen_rtx (EXPR_LIST, VOIDmode, low, sb_low);#endif /* Don't USE_GAS */ return last; } } return (REG_P (addr) ? "jsr%. %0" : (flag_pic ? "bsr%. %0#plt" : "bsr%. %0"));}static voidoutput_short_branch_defs (stream)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -