📄 i386.c
字号:
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 are not supposed to happen either due to the operand constraints or the fact that all memory operands on the x86 are offsettable. Abort if we get one, because generating code for these cases is painful. */ if (optype0 == RNDOP || optype1 == RNDOP || optype0 == MEMOP || optype1 == MEMOP) 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; } /* 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) { 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; /* 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) { /* Do the high-numbered word. */ output_asm_insn (singlemove_string (latehalf), latehalf); if (size == 12) output_asm_insn (singlemove_string (middlehalf), middlehalf); /* 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) output_asm_insn (singlemove_string (middlehalf), middlehalf); /* Do the high-numbered word. */ output_asm_insn (singlemove_string (latehalf), latehalf); 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 "";}intstandard_80387_constant_p (x) rtx x;{#if ! defined (REAL_IS_NOT_DOUBLE) || defined (REAL_ARITHMETIC) REAL_VALUE_TYPE d; jmp_buf handler; int is0, is1; if (setjmp (handler)) return 0; set_float_handler (handler); REAL_VALUE_FROM_CONST_DOUBLE (d, x); is0 = REAL_VALUES_EQUAL (d, dconst0) && !REAL_VALUE_MINUS_ZERO (d); is1 = REAL_VALUES_EQUAL (d, dconst1); set_float_handler (NULL_PTR); if (is0) return 1; if (is1) return 2; /* Note that on the 80387, other constants, such as pi, are much slower to load as standard constants than to load from doubles in memory! */ /* ??? Not true on K6: all constants are equal cost. */#endif return 0;}char *output_move_const_single (operands) rtx *operands;{ if (FP_REG_P (operands[0])) { int conval = standard_80387_constant_p (operands[1]); if (conval == 1) return "fldz"; if (conval == 2) return "fld1"; } if (GET_CODE (operands[1]) == CONST_DOUBLE) { REAL_VALUE_TYPE r; long l; if (GET_MODE (operands[1]) == XFmode) abort (); REAL_VALUE_FROM_CONST_DOUBLE (r, operands[1]); REAL_VALUE_TO_TARGET_SINGLE (r, l); operands[1] = GEN_INT (l); } return singlemove_string (operands);}/* Returns 1 if OP is either a symbol reference or a sum of a symbol reference and a constant. */intsymbolic_operand (op, mode) register rtx op; enum machine_mode mode ATTRIBUTE_UNUSED;{ switch (GET_CODE (op)) { case SYMBOL_REF: case LABEL_REF: return 1; case CONST: op = XEXP (op, 0); return ((GET_CODE (XEXP (op, 0)) == SYMBOL_REF || GET_CODE (XEXP (op, 0)) == LABEL_REF) && GET_CODE (XEXP (op, 1)) == CONST_INT); default: return 0; }}/* Return nonzero if OP is a constant shift count small enough to encode into an lea instruction. */intsmall_shift_operand (op, mode) rtx op; enum machine_mode mode ATTRIBUTE_UNUSED;{ return (GET_CODE (op) == CONST_INT && INTVAL (op) > 0 && INTVAL (op) < 4);}/* Test for a valid operand for a call instruction. Don't allow the arg pointer register or virtual regs since they may change into reg + const, which the patterns can't handle yet. */intcall_insn_operand (op, mode) rtx op; enum machine_mode mode ATTRIBUTE_UNUSED;{ if (GET_CODE (op) == MEM && ((CONSTANT_ADDRESS_P (XEXP (op, 0)) /* This makes a difference for PIC. */ && general_operand (XEXP (op, 0), Pmode)) || (GET_CODE (XEXP (op, 0)) == REG && XEXP (op, 0) != arg_pointer_rtx && ! (REGNO (XEXP (op, 0)) >= FIRST_PSEUDO_REGISTER && REGNO (XEXP (op, 0)) <= LAST_VIRTUAL_REGISTER)))) return 1; return 0;}/* Like call_insn_operand but allow (mem (symbol_ref ...)) even if pic. */intexpander_call_insn_operand (op, mode) rtx op; enum machine_mode mode ATTRIBUTE_UNUSED;{ if (GET_CODE (op) == MEM && (CONSTANT_ADDRESS_P (XEXP (op, 0)) || (GET_CODE (XEXP (op, 0)) == REG && XEXP (op, 0) != arg_pointer_rtx && ! (REGNO (XEXP (op, 0)) >= FIRST_PSEUDO_REGISTER && REGNO (XEXP (op, 0)) <= LAST_VIRTUAL_REGISTER)))) return 1; return 0;}/* Return 1 if OP is a comparison operator that can use the condition code generated by an arithmetic operation. */intarithmetic_comparison_operator (op, mode) register rtx op; enum machine_mode mode;{ enum rtx_code code;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -