📄 i386.c
字号:
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 (optype0 == PUSHOP || optype1 == PUSHOP || (optype0 == REGOP && optype1 == REGOP && REGNO (operands[0]) == REGNO (latehalf[1])) || dest_overlapped_low)*/ 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 && 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; } } if (max_tmps == 0) fatal_insn ("No scratch registers were found to do memory->memory moves", insn); if ((length & 1) != 0) { if (!qi_tmp) fatal_insn ("No byte register found when moving odd # of bytes.", insn); } while (length > 1) { for (num_tmps = 0; num_tmps < max_tmps; num_tmps++) { if (length >= 4) { tmp_info[num_tmps].load = AS2(mov%L0,%1,%2); tmp_info[num_tmps].store = AS2(mov%L0,%2,%0); tmp_info[num_tmps].xops[0] = adj_offsettable_operand (dest, offset); tmp_info[num_tmps].xops[1] = adj_offsettable_operand (src, offset); offset += 4; length -= 4; } else if (length >= 2) { tmp_info[num_tmps].load = AS2(mov%W0,%1,%2); tmp_info[num_tmps].store = AS2(mov%W0,%2,%0); tmp_info[num_tmps].xops[0] = adj_offsettable_operand (dest, offset); tmp_info[num_tmps].xops[1] = adj_offsettable_operand (src, offset); offset += 2; length -= 2; } else break; } 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].store, tmp_info[i].xops); } if (length == 1) { xops[0] = adj_offsettable_operand (dest, offset); xops[1] = adj_offsettable_operand (src, offset); xops[2] = qi_tmp; output_asm_insn (AS2(mov%B0,%1,%2), xops); output_asm_insn (AS2(mov%B0,%2,%0), xops); } 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); 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! */#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;{ 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; }}/* 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;{ 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;{ 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; if (mode != VOIDmode && mode != GET_MODE (op)) return 0; code = GET_CODE (op); if (GET_RTX_CLASS (code) != '<') return 0; return (code != GT && code != LE);}/* Returns 1 if OP contains a symbol reference */intsymbolic_reference_mentioned_p (op) rtx op;{ register char *fmt; register int i; if (GET_CODE (op) == SYMBOL_REF || GET_CODE (op) == LABEL_REF) return 1; fmt = GET_RTX_FORMAT (GET_CODE (op)); for (i = GET_RTX_LENGTH (GET_CODE (op)) - 1; i >= 0; i--) { if (fmt[i] == 'E') { register int j; for (j = XVECLEN (op, i) - 1; j >= 0; j--) if (symbolic_reference_mentioned_p (XVECEXP (op, i, j))) return 1; } else if (fmt[i] == 'e' && symbolic_reference_mentioned_p (XEXP (op, i))) return 1; } return 0;}/* This function generates the assembly code for function entry. FILE is an stdio stream to output the code to. SIZE is an int: how many units of temporary storage to allocate. */voidfunction_prologue (file, size)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -