📄 mips.c
字号:
#if 0 /* Don't generate unaligned moves here, rather defer those to the general movestrsi_internal pattern. If this gets commented back in, then should add the dword equivalent. */ else if (bytes >= UNITS_PER_MIPS_WORD) { mode = SImode; size = UNITS_PER_MIPS_WORD; load_func = gen_movsi_ulw; store_func = gen_movsi_usw; }#endif else if (bytes >= UNITS_PER_MIPS_SHORT && align >= UNITS_PER_MIPS_SHORT) { mode = HImode; size = UNITS_PER_MIPS_SHORT; load_func = gen_movhi; store_func = gen_movhi; } else { mode = QImode; size = 1; load_func = gen_movqi; store_func = gen_movqi; } offset = *p_offset; *p_offset = offset + size; *p_bytes = bytes - size; if (offset == 0) { src_addr = src_reg; dest_addr = dest_reg; } else { src_addr = gen_rtx (PLUS, Pmode, src_reg, GEN_INT (offset)); dest_addr = gen_rtx (PLUS, Pmode, dest_reg, GEN_INT (offset)); } reg = gen_reg_rtx (mode); insn = emit_insn ((*load_func) (reg, gen_rtx (MEM, mode, src_addr))); orig_src_addr = XEXP (orig_src, 0); if (CONSTANT_P (orig_src_addr)) REG_NOTES (insn) = gen_rtx (EXPR_LIST, REG_EQUIV, plus_constant (orig_src_addr, offset), REG_NOTES (insn)); return (*store_func) (gen_rtx (MEM, mode, dest_addr), reg);}#endif/* Write a series of loads/stores to move some bytes. Generate load/stores as follows: load 1 load 2 load 3 store 1 load 4 store 2 load 5 store 3 ... This way, no NOP's are needed, except at the end, and only two temp registers are needed. Two delay slots are used in deference to the R4000. */#if 0static voidblock_move_sequence (dest_reg, src_reg, bytes, align, orig_src) rtx dest_reg; /* register holding destination address */ rtx src_reg; /* register holding source address */ int bytes; /* # bytes to move */ int align; /* max alignment to assume */ rtx orig_src; /* original source for making a reg note */{ int offset = 0; rtx prev2_store = (rtx)0; rtx prev_store = (rtx)0; rtx cur_store = (rtx)0; while (bytes > 0) { /* Is there a store to do? */ if (prev2_store) emit_insn (prev2_store); prev2_store = prev_store; prev_store = cur_store; cur_store = block_move_load_store (dest_reg, src_reg, &bytes, &offset, align, orig_src); } /* Finish up last three stores. */ if (prev2_store) emit_insn (prev2_store); if (prev_store) emit_insn (prev_store); if (cur_store) emit_insn (cur_store);}#endif/* Write a loop to move a constant number of bytes. Generate load/stores as follows: do { temp1 = src[0]; temp2 = src[1]; ... temp<last> = src[MAX_MOVE_REGS-1]; dest[0] = temp1; dest[1] = temp2; ... dest[MAX_MOVE_REGS-1] = temp<last>; src += MAX_MOVE_REGS; dest += MAX_MOVE_REGS; } while (src != final); This way, no NOP's are needed, and only MAX_MOVE_REGS+3 temp registers are needed. Aligned moves move MAX_MOVE_REGS*4 bytes every (2*MAX_MOVE_REGS)+3 cycles, unaligned moves move MAX_MOVE_REGS*4 bytes every (4*MAX_MOVE_REGS)+3 cycles, assuming no cache misses. */#define MAX_MOVE_REGS 4#define MAX_MOVE_BYTES (MAX_MOVE_REGS * UNITS_PER_WORD)/* ??? Should add code to use DWORD load/stores. */static voidblock_move_loop (dest_reg, src_reg, bytes, align, orig_src) rtx dest_reg; /* register holding destination address */ rtx src_reg; /* register holding source address */ int bytes; /* # bytes to move */ int align; /* alignment */ rtx orig_src; /* original source for making a reg note */{ rtx dest_mem = gen_rtx (MEM, BLKmode, dest_reg); rtx src_mem = gen_rtx (MEM, BLKmode, src_reg); rtx align_rtx = GEN_INT (align); rtx label; rtx final_src; rtx bytes_rtx; int leftover; if (bytes < 2*MAX_MOVE_BYTES) abort (); leftover = bytes % MAX_MOVE_BYTES; bytes -= leftover; label = gen_label_rtx (); final_src = gen_reg_rtx (Pmode); bytes_rtx = GEN_INT (bytes); if (bytes > 0x7fff) { if (TARGET_LONG64) { emit_insn (gen_movdi (final_src, bytes_rtx)); emit_insn (gen_adddi3 (final_src, final_src, src_reg)); } else { emit_insn (gen_movsi (final_src, bytes_rtx)); emit_insn (gen_addsi3 (final_src, final_src, src_reg)); } } else { if (TARGET_LONG64) emit_insn (gen_adddi3 (final_src, src_reg, bytes_rtx)); else emit_insn (gen_addsi3 (final_src, src_reg, bytes_rtx)); } emit_label (label); bytes_rtx = GEN_INT (MAX_MOVE_BYTES); emit_insn (gen_movstrsi_internal (dest_mem, src_mem, bytes_rtx, align_rtx)); if (TARGET_LONG64) { emit_insn (gen_adddi3 (src_reg, src_reg, bytes_rtx)); emit_insn (gen_adddi3 (dest_reg, dest_reg, bytes_rtx)); emit_insn (gen_cmpdi (src_reg, final_src)); } else { emit_insn (gen_addsi3 (src_reg, src_reg, bytes_rtx)); emit_insn (gen_addsi3 (dest_reg, dest_reg, bytes_rtx)); emit_insn (gen_cmpsi (src_reg, final_src)); } emit_jump_insn (gen_bne (label)); if (leftover) emit_insn (gen_movstrsi_internal (dest_mem, src_mem, GEN_INT (leftover), align_rtx));}/* Use a library function to move some bytes. */static voidblock_move_call (dest_reg, src_reg, bytes_rtx) rtx dest_reg; rtx src_reg; rtx bytes_rtx;{ /* We want to pass the size as Pmode, which will normally be SImode but will be DImode if we are using 64 bit longs and pointers. */ if (GET_MODE (bytes_rtx) != VOIDmode && GET_MODE (bytes_rtx) != Pmode) bytes_rtx = convert_to_mode (Pmode, bytes_rtx, TRUE);#ifdef TARGET_MEM_FUNCTIONS emit_library_call (gen_rtx (SYMBOL_REF, Pmode, "memcpy"), 0, VOIDmode, 3, dest_reg, Pmode, src_reg, Pmode, bytes_rtx, Pmode);#else emit_library_call (gen_rtx (SYMBOL_REF, Pmode, "bcopy"), 0, VOIDmode, 3, src_reg, Pmode, dest_reg, Pmode, bytes_rtx, Pmode);#endif}/* Expand string/block move operations. operands[0] is the pointer to the destination. operands[1] is the pointer to the source. operands[2] is the number of bytes to move. operands[3] is the alignment. */voidexpand_block_move (operands) rtx operands[];{ rtx bytes_rtx = operands[2]; rtx align_rtx = operands[3]; int constp = (GET_CODE (bytes_rtx) == CONST_INT); int bytes = (constp ? INTVAL (bytes_rtx) : 0); int align = INTVAL (align_rtx); rtx orig_src = operands[1]; rtx src_reg; rtx dest_reg; if (constp && bytes <= 0) return; if (align > UNITS_PER_WORD) align = UNITS_PER_WORD; /* Move the address into scratch registers. */ dest_reg = copy_addr_to_reg (XEXP (operands[0], 0)); src_reg = copy_addr_to_reg (XEXP (orig_src, 0)); if (TARGET_MEMCPY) block_move_call (dest_reg, src_reg, bytes_rtx);#if 0 else if (constp && bytes <= 3*align) block_move_sequence (dest_reg, src_reg, bytes, align, orig_src);#endif else if (constp && bytes <= 2*MAX_MOVE_BYTES) emit_insn (gen_movstrsi_internal (gen_rtx (MEM, BLKmode, dest_reg), gen_rtx (MEM, BLKmode, src_reg), bytes_rtx, align_rtx)); else if (constp && align >= UNITS_PER_WORD && optimize) block_move_loop (dest_reg, src_reg, bytes, align, orig_src); else if (constp && optimize) { /* If the alignment is not word aligned, generate a test at runtime, to see whether things wound up aligned, and we can use the faster lw/sw instead ulw/usw. */ rtx temp = gen_reg_rtx (Pmode); rtx aligned_label = gen_label_rtx (); rtx join_label = gen_label_rtx (); int leftover = bytes % MAX_MOVE_BYTES; bytes -= leftover; if (TARGET_LONG64) { emit_insn (gen_iordi3 (temp, src_reg, dest_reg)); emit_insn (gen_anddi3 (temp, temp, GEN_INT (UNITS_PER_WORD-1))); emit_insn (gen_cmpdi (temp, const0_rtx)); } else { emit_insn (gen_iorsi3 (temp, src_reg, dest_reg)); emit_insn (gen_andsi3 (temp, temp, GEN_INT (UNITS_PER_WORD-1))); emit_insn (gen_cmpsi (temp, const0_rtx)); } emit_jump_insn (gen_beq (aligned_label)); /* Unaligned loop. */ block_move_loop (dest_reg, src_reg, bytes, 1, orig_src); emit_jump_insn (gen_jump (join_label)); emit_barrier (); /* Aligned loop. */ emit_label (aligned_label); block_move_loop (dest_reg, src_reg, bytes, UNITS_PER_WORD, orig_src); emit_label (join_label); /* Bytes at the end of the loop. */ if (leftover) {#if 0 if (leftover <= 3*align) block_move_sequence (dest_reg, src_reg, leftover, align, orig_src); else#endif emit_insn (gen_movstrsi_internal (gen_rtx (MEM, BLKmode, dest_reg), gen_rtx (MEM, BLKmode, src_reg), GEN_INT (leftover), GEN_INT (align))); } } else block_move_call (dest_reg, src_reg, bytes_rtx);}/* Emit load/stores for a small constant block_move. operands[0] is the memory address of the destination. operands[1] is the memory address of the source. operands[2] is the number of bytes to move. operands[3] is the alignment. operands[4] is a temp register. operands[5] is a temp register. ... operands[3+num_regs] is the last temp register. The block move type can be one of the following: BLOCK_MOVE_NORMAL Do all of the block move. BLOCK_MOVE_NOT_LAST Do all but the last store. BLOCK_MOVE_LAST Do just the last store. */char *output_block_move (insn, operands, num_regs, move_type) rtx insn; rtx operands[]; int num_regs; enum block_move_type move_type;{ rtx dest_reg = XEXP (operands[0], 0); rtx src_reg = XEXP (operands[1], 0); int bytes = INTVAL (operands[2]); int align = INTVAL (operands[3]); int num = 0; int offset = 0; int use_lwl_lwr = FALSE; int last_operand = num_regs+4; int safe_regs = 4; int i; rtx xoperands[10]; struct { char *load; /* load insn without nop */ char *load_nop; /* load insn with trailing nop */ char *store; /* store insn */ char *final; /* if last_store used: NULL or swr */ char *last_store; /* last store instruction */ int offset; /* current offset */ enum machine_mode mode; /* mode to use on (MEM) */ } load_store[4]; /* Detect a bug in GCC, where it can give us a register the same as one of the addressing registers and reduce the number of registers available. */ for (i = 4; i < last_operand && safe_regs < (sizeof(xoperands) / sizeof(xoperands[0])); i++) { if (!reg_mentioned_p (operands[i], operands[0]) && !reg_mentioned_p (operands[i], operands[1])) xoperands[safe_regs++] = operands[i]; } if (safe_regs < last_operand) { xoperands[0] = operands[0]; xoperands[1] = operands[1]; xoperands[2] = operands[2]; xoperands[3] = operands[3]; return output_block_move (insn, xoperands, safe_regs-4, move_type); } /* If we are given global or static addresses, and we would be emitting a few instructions, try to save time by using a temporary register for the pointer. */ if (num_regs > 2 && (bytes > 2*align || move_type != BLOCK_MOVE_NORMAL)) { if (CONSTANT_P (src_reg)) { if (TARGET_STATS) mips_count_memory_refs (operands[1], 1); src_reg = operands[ 3 + num_regs-- ]; if (move_type != BLOCK_MOVE_LAST) { xoperands[1] = operands[1]; xoperands[0] = src_reg; if (Pmode == DImode) output_asm_insn ("dla\t%0,%1", xoperands); else output_asm_insn ("la\t%0,%1", xoperands); } } if (CONSTANT_P (dest_reg)) { if (TARGET_STATS) mips_count_memory_refs (operands[0], 1); dest_reg = operands[ 3 + num_regs-- ]; if (move_type != BLOCK_MOVE_LAST) { xoperands[1] = operands[0]; xoperands[0] = dest_reg; if (Pmode == DImode) output_asm_insn ("dla\t%0,%1", xoperands); else output_asm_insn ("la\t%0,%1", xoperands); } } } if (num_regs > (sizeof (load_store) / sizeof (load_store[0]))) num_regs = (sizeof (load_store) / sizeof (load_store[0])); else if (num_regs < 1) abort_with_insn (insn, "Cannot do block move, not enough scratch registers"); while (bytes > 0) { load_store[num].offset = offset; if (TARGET_64BIT && bytes >= 8 && align >= 8) { load_store[num].load = "ld\t%0,%1"; load_store[num].load_nop = "ld\t%0,%1%#"; load_store[num].store = "sd\t%0,%1"; load_store[num].last_store = "sd\t%0,%1"; load_store[num].final = (char *)0; load_store[num].mode = DImode; offset += 8; bytes -= 8; } /* ??? Fails because of a MIPS assembler bug? */ else if (TARGET_64BIT && bytes >= 8) { if (BYTES_BIG_ENDIAN) { load_store[num].load = "ldl\t%0,%1\n\tldr\t%0,%2"; load_store[num].load_nop = "ldl\t%0,%1\n\tldr\t%0,%2%#"; load_store[num].store = "sdl\t%0,%1\n\tsdr\t%0,%2"; load_store[num].last_store = "sdr\t%0,%2"; load_store[num].final = "sdl\t%0,%1"; } else { load_store[num].load = "ldl\t%0,%2\n\tldr\t%0,%1"; load_store[num].load_nop = "ldl\t%0,%2\n\tldr\t%0,%1%#"; load_store[num].store = "sdl\t%0,%2\n\tsdr\t%0,%1"; load_store[num].last_store = "sdr\t%0,%1"; load_store[num].final = "sdl\t%0,%2"; } load_store[num].mode = DImode; offset += 8; bytes -= 8; use_lwl_lwr = TRUE; } else if (bytes >= 4 && align >= 4) { load_store[num].load = "lw\t%0,%1"; load_store[num].load_nop = "lw\t%0,%1%#"; load_store[num].store = "sw\t%0,%1"; load_store[num].last_store = "sw\t%0,%1"; load_store[num].final = (char *)0; load_store[num].mode = SImode; offset += 4; bytes -= 4; } else if (bytes >= 4) { if (BYTES_BIG_ENDIAN) { load_store[num].load =
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -