📄 i386.c
字号:
return addr; abort ();}/* Output an insn to add the constant N to the register X. */static voidasm_add (n, x) int n; rtx x;{ rtx xops[2]; xops[0] = x; if (n == -1) output_asm_insn (AS1 (dec%L0,%0), xops); else if (n == 1) output_asm_insn (AS1 (inc%L0,%0), xops); else if (n < 0 || n == 128) { xops[1] = GEN_INT (-n); output_asm_insn (AS2 (sub%L0,%1,%0), xops); } else if (n > 0) { xops[1] = GEN_INT (n); output_asm_insn (AS2 (add%L0,%1,%0), xops); }}/* Output assembler code to perform a doubleword move insn with operands OPERANDS. */char *output_move_double (operands) rtx *operands;{ enum {REGOP, OFFSOP, MEMOP, PUSHOP, POPOP, CNSTOP, RNDOP } optype0, optype1; rtx latehalf[2]; rtx middlehalf[2]; rtx xops[2]; rtx addreg0 = 0, addreg1 = 0; int dest_overlapped_low = 0; int size = GET_MODE_SIZE (GET_MODE (operands[0])); middlehalf[0] = 0; middlehalf[1] = 0; /* First classify both operands. */ if (REG_P (operands[0])) optype0 = REGOP; else if (offsettable_memref_p (operands[0])) optype0 = OFFSOP; else if (GET_CODE (XEXP (operands[0], 0)) == POST_INC) optype0 = POPOP; else if (GET_CODE (XEXP (operands[0], 0)) == PRE_DEC) optype0 = PUSHOP; else if (GET_CODE (operands[0]) == MEM) optype0 = MEMOP; else optype0 = RNDOP; if (REG_P (operands[1])) optype1 = REGOP; else if (CONSTANT_P (operands[1])) optype1 = CNSTOP; else if (offsettable_memref_p (operands[1])) optype1 = OFFSOP; else if (GET_CODE (XEXP (operands[1], 0)) == POST_INC) optype1 = POPOP; else if (GET_CODE (XEXP (operands[1], 0)) == PRE_DEC) optype1 = PUSHOP; else if (GET_CODE (operands[1]) == MEM) optype1 = MEMOP; else optype1 = RNDOP; /* Check for the cases that the operand constraints are not supposed to allow to happen. Abort if we get one, because generating code for these cases is painful. */ if (optype0 == RNDOP || optype1 == RNDOP) abort (); /* If one operand is decrementing and one is incrementing decrement the former register explicitly and change that operand into ordinary indexing. */ if (optype0 == PUSHOP && optype1 == POPOP) { /* ??? Can this ever happen on i386? */ operands[0] = XEXP (XEXP (operands[0], 0), 0); asm_add (-size, operands[0]); if (GET_MODE (operands[1]) == XFmode) operands[0] = gen_rtx (MEM, XFmode, operands[0]); else if (GET_MODE (operands[0]) == DFmode) operands[0] = gen_rtx (MEM, DFmode, operands[0]); else operands[0] = gen_rtx (MEM, DImode, operands[0]); optype0 = OFFSOP; } if (optype0 == POPOP && optype1 == PUSHOP) { /* ??? Can this ever happen on i386? */ operands[1] = XEXP (XEXP (operands[1], 0), 0); asm_add (-size, operands[1]); if (GET_MODE (operands[1]) == XFmode) operands[1] = gen_rtx (MEM, XFmode, operands[1]); else if (GET_MODE (operands[1]) == DFmode) operands[1] = gen_rtx (MEM, DFmode, operands[1]); else operands[1] = gen_rtx (MEM, DImode, operands[1]); optype1 = OFFSOP; } /* If an operand is an unoffsettable memory ref, find a register we can increment temporarily to make it refer to the second word. */ if (optype0 == MEMOP) addreg0 = find_addr_reg (XEXP (operands[0], 0)); if (optype1 == MEMOP) addreg1 = find_addr_reg (XEXP (operands[1], 0)); /* Ok, we can do one word at a time. Normally we do the low-numbered word first, but if either operand is autodecrementing then we do the high-numbered word first. In either case, set up in LATEHALF the operands to use for the high-numbered word and in some cases alter the operands in OPERANDS to be suitable for the low-numbered word. */ if (size == 12) { if (optype0 == REGOP) { middlehalf[0] = gen_rtx (REG, SImode, REGNO (operands[0]) + 1); latehalf[0] = gen_rtx (REG, SImode, REGNO (operands[0]) + 2); } else if (optype0 == OFFSOP) { middlehalf[0] = adj_offsettable_operand (operands[0], 4); latehalf[0] = adj_offsettable_operand (operands[0], 8); } else { middlehalf[0] = operands[0]; latehalf[0] = operands[0]; } if (optype1 == REGOP) { middlehalf[1] = gen_rtx (REG, SImode, REGNO (operands[1]) + 1); latehalf[1] = gen_rtx (REG, SImode, REGNO (operands[1]) + 2); } else if (optype1 == OFFSOP) { middlehalf[1] = adj_offsettable_operand (operands[1], 4); latehalf[1] = adj_offsettable_operand (operands[1], 8); } else if (optype1 == CNSTOP) { if (GET_CODE (operands[1]) == CONST_DOUBLE) { REAL_VALUE_TYPE r; long l[3]; REAL_VALUE_FROM_CONST_DOUBLE (r, operands[1]); REAL_VALUE_TO_TARGET_LONG_DOUBLE (r, l); operands[1] = GEN_INT (l[0]); middlehalf[1] = GEN_INT (l[1]); latehalf[1] = GEN_INT (l[2]); } else if (CONSTANT_P (operands[1])) /* No non-CONST_DOUBLE constant should ever appear here. */ abort (); } else { middlehalf[1] = operands[1]; latehalf[1] = operands[1]; } } else { /* Size is not 12. */ if (optype0 == REGOP) latehalf[0] = gen_rtx (REG, SImode, REGNO (operands[0]) + 1); else if (optype0 == OFFSOP) latehalf[0] = adj_offsettable_operand (operands[0], 4); else latehalf[0] = operands[0]; if (optype1 == REGOP) latehalf[1] = gen_rtx (REG, SImode, REGNO (operands[1]) + 1); else if (optype1 == OFFSOP) latehalf[1] = adj_offsettable_operand (operands[1], 4); else if (optype1 == CNSTOP) split_double (operands[1], &operands[1], &latehalf[1]); else latehalf[1] = operands[1]; } /* If insn is effectively movd N (sp),-(sp) then we will do the high word first. We should use the adjusted operand 1 (which is N+4 (sp) or N+8 (sp)) for the low word and middle word as well, to compensate for the first decrement of sp. */ if (optype0 == PUSHOP && REGNO (XEXP (XEXP (operands[0], 0), 0)) == STACK_POINTER_REGNUM && reg_overlap_mentioned_p (stack_pointer_rtx, operands[1])) middlehalf[1] = operands[1] = latehalf[1]; /* For (set (reg:DI N) (mem:DI ... (reg:SI N) ...)), if the upper part of reg N does not appear in the MEM, arrange to emit the move late-half first. Otherwise, compute the MEM address into the upper part of N and use that as a pointer to the memory operand. */ if (optype0 == REGOP && (optype1 == OFFSOP || optype1 == MEMOP)) { if (reg_mentioned_p (operands[0], XEXP (operands[1], 0)) && reg_mentioned_p (latehalf[0], XEXP (operands[1], 0))) { /* If both halves of dest are used in the src memory address, compute the address into latehalf of dest. */ compadr: xops[0] = latehalf[0]; xops[1] = XEXP (operands[1], 0); output_asm_insn (AS2 (lea%L0,%a1,%0), xops); if (GET_MODE (operands[1]) == XFmode) { operands[1] = gen_rtx (MEM, XFmode, latehalf[0]); middlehalf[1] = adj_offsettable_operand (operands[1], size-8); latehalf[1] = adj_offsettable_operand (operands[1], size-4); } else { operands[1] = gen_rtx (MEM, DImode, latehalf[0]); latehalf[1] = adj_offsettable_operand (operands[1], size-4); } } else if (size == 12 && reg_mentioned_p (middlehalf[0], XEXP (operands[1], 0))) { /* Check for two regs used by both source and dest. */ if (reg_mentioned_p (operands[0], XEXP (operands[1], 0)) || reg_mentioned_p (latehalf[0], XEXP (operands[1], 0))) goto compadr; /* JRV says this can't happen: */ if (addreg0 || addreg1) abort (); /* Only the middle reg conflicts; simply put it last. */ output_asm_insn (singlemove_string (operands), operands); output_asm_insn (singlemove_string (latehalf), latehalf); output_asm_insn (singlemove_string (middlehalf), middlehalf); return ""; } else if (reg_mentioned_p (operands[0], XEXP (operands[1], 0))) /* If the low half of dest is mentioned in the source memory address, the arrange to emit the move late half first. */ dest_overlapped_low = 1; } /* If one or both operands autodecrementing, do the two words, high-numbered first. */ /* Likewise, the first move would clobber the source of the second one, do them in the other order. This happens only for registers; such overlap can't happen in memory unless the user explicitly sets it up, and that is an undefined circumstance. */#if 0 if (optype0 == PUSHOP || optype1 == PUSHOP || (optype0 == REGOP && optype1 == REGOP && REGNO (operands[0]) == REGNO (latehalf[1])) || dest_overlapped_low)#endif if (optype0 == PUSHOP || optype1 == PUSHOP || (optype0 == REGOP && optype1 == REGOP && ((middlehalf[1] && REGNO (operands[0]) == REGNO (middlehalf[1])) || REGNO (operands[0]) == REGNO (latehalf[1]))) || dest_overlapped_low) { /* Make any unoffsettable addresses point at high-numbered word. */ if (addreg0) asm_add (size-4, addreg0); if (addreg1) asm_add (size-4, addreg1); /* Do that word. */ output_asm_insn (singlemove_string (latehalf), latehalf); /* Undo the adds we just did. */ if (addreg0) asm_add (-4, addreg0); if (addreg1) asm_add (-4, addreg1); if (size == 12) { output_asm_insn (singlemove_string (middlehalf), middlehalf); if (addreg0) asm_add (-4, addreg0); if (addreg1) asm_add (-4, addreg1); } /* Do low-numbered word. */ return singlemove_string (operands); } /* Normal case: do the two words, low-numbered first. */ output_asm_insn (singlemove_string (operands), operands); /* Do the middle one of the three words for long double */ if (size == 12) { if (addreg0) asm_add (4, addreg0); if (addreg1) asm_add (4, addreg1); output_asm_insn (singlemove_string (middlehalf), middlehalf); } /* Make any unoffsettable addresses point at high-numbered word. */ if (addreg0) asm_add (4, addreg0); if (addreg1) asm_add (4, addreg1); /* Do that word. */ output_asm_insn (singlemove_string (latehalf), latehalf); /* Undo the adds we just did. */ if (addreg0) asm_add (4-size, addreg0); if (addreg1) asm_add (4-size, addreg1); return "";}#define MAX_TMPS 2 /* max temporary registers used *//* Output the appropriate code to move push memory on the stack */char *output_move_pushmem (operands, insn, length, tmp_start, n_operands) rtx operands[]; rtx insn; int length; int tmp_start; int n_operands;{ struct { char *load; char *push; rtx xops[2]; } tmp_info[MAX_TMPS]; rtx src = operands[1]; int max_tmps = 0; int offset = 0; int stack_p = reg_overlap_mentioned_p (stack_pointer_rtx, src); int stack_offset = 0; int i, num_tmps; rtx xops[1]; if (! offsettable_memref_p (src)) fatal_insn ("Source is not offsettable", insn); if ((length & 3) != 0) fatal_insn ("Pushing non-word aligned size", insn); /* Figure out which temporary registers we have available */ for (i = tmp_start; i < n_operands; i++) { if (GET_CODE (operands[i]) == REG) { if (reg_overlap_mentioned_p (operands[i], src)) continue; tmp_info[ max_tmps++ ].xops[1] = operands[i]; if (max_tmps == MAX_TMPS) break; } } if (max_tmps == 0) for (offset = length - 4; offset >= 0; offset -= 4) { xops[0] = adj_offsettable_operand (src, offset + stack_offset); output_asm_insn (AS1(push%L0,%0), xops); if (stack_p) stack_offset += 4; } else for (offset = length - 4; offset >= 0; ) { for (num_tmps = 0; num_tmps < max_tmps && offset >= 0; num_tmps++) { tmp_info[num_tmps].load = AS2(mov%L0,%0,%1); tmp_info[num_tmps].push = AS1(push%L0,%1); tmp_info[num_tmps].xops[0] = adj_offsettable_operand (src, offset + stack_offset); offset -= 4; } for (i = 0; i < num_tmps; i++) output_asm_insn (tmp_info[i].load, tmp_info[i].xops); for (i = 0; i < num_tmps; i++) output_asm_insn (tmp_info[i].push, tmp_info[i].xops); if (stack_p) stack_offset += 4*num_tmps; } return "";}/* Output the appropriate code to move data between two memory locations */char *output_move_memory (operands, insn, length, tmp_start, n_operands) rtx operands[]; rtx insn; int length; int tmp_start; int n_operands;{ struct { char *load; char *store; rtx xops[3]; } tmp_info[MAX_TMPS]; rtx dest = operands[0]; rtx src = operands[1]; rtx qi_tmp = NULL_RTX; int max_tmps = 0; int offset = 0; int i, num_tmps; rtx xops[3]; if (GET_CODE (dest) == MEM && GET_CODE (XEXP (dest, 0)) == PRE_INC && XEXP (XEXP (dest, 0), 0) == stack_pointer_rtx) return output_move_pushmem (operands, insn, length, tmp_start, n_operands); if (! offsettable_memref_p (src)) fatal_insn ("Source is not offsettable", insn); if (! offsettable_memref_p (dest)) fatal_insn ("Destination is not offsettable", insn); /* Figure out which temporary registers we have available */ for (i = tmp_start; i < n_operands; i++) { if (GET_CODE (operands[i]) == REG) { if ((length & 1) != 0 && qi_tmp == 0 && QI_REG_P (operands[i])) qi_tmp = operands[i]; if (reg_overlap_mentioned_p (operands[i], dest)) fatal_insn ("Temporary register overlaps the destination", insn); if (reg_overlap_mentioned_p (operands[i], src)) fatal_insn ("Temporary register overlaps the source", insn); tmp_info[max_tmps++].xops[2] = operands[i]; if (max_tmps == MAX_TMPS) break; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -