📄 i386.c
字号:
int named; /* whether or not the argument was named */{ int bytes = (mode == BLKmode) ? int_size_in_bytes (type) : GET_MODE_SIZE (mode); int words = (bytes + UNITS_PER_WORD - 1) / UNITS_PER_WORD; if (TARGET_DEBUG_ARG) fprintf (stderr, "function_adv( size=%d, words=%2d, nregs=%d, mode=%4s, named=%d )\n\n", words, cum->words, cum->nregs, GET_MODE_NAME (mode), named); cum->words += words; cum->nregs -= words; cum->regno += words; if (cum->nregs <= 0) { cum->nregs = 0; cum->regno = 0; } return;}/* Define where to put the arguments to a function. Value is zero to push the argument on the stack, or a hard register in which to store the argument. MODE is the argument's machine mode. TYPE is the data type of the argument (as a tree). This is null for libcalls where that information may not be available. CUM is a variable of type CUMULATIVE_ARGS which gives info about the preceding args and about the function being called. NAMED is nonzero if this argument is a named parameter (otherwise it is an extra parameter matching an ellipsis). */struct rtx_def *function_arg (cum, mode, type, named) CUMULATIVE_ARGS *cum; /* current arg information */ enum machine_mode mode; /* current arg mode */ tree type; /* type of the argument or 0 if lib support */ int named; /* != 0 for normal args, == 0 for ... args */{ rtx ret = NULL_RTX; int bytes = (mode == BLKmode) ? int_size_in_bytes (type) : GET_MODE_SIZE (mode); int words = (bytes + UNITS_PER_WORD - 1) / UNITS_PER_WORD; switch (mode) { default: /* for now, pass fp/complex values on the stack */ break; case BLKmode: case DImode: case SImode: case HImode: case QImode: if (words <= cum->nregs) ret = gen_rtx (REG, mode, cum->regno); break; } if (TARGET_DEBUG_ARG) { fprintf (stderr, "function_arg( size=%d, words=%2d, nregs=%d, mode=%4s, named=%d", words, cum->words, cum->nregs, GET_MODE_NAME (mode), named); if (ret) fprintf (stderr, ", reg=%%e%s", reg_names[ REGNO(ret) ]); else fprintf (stderr, ", stack"); fprintf (stderr, " )\n"); } return ret;}/* For an arg passed partly in registers and partly in memory, this is the number of registers used. For args passed entirely in registers or entirely in memory, zero. */intfunction_arg_partial_nregs (cum, mode, type, named) CUMULATIVE_ARGS *cum; /* current arg information */ enum machine_mode mode; /* current arg mode */ tree type; /* type of the argument or 0 if lib support */ int named; /* != 0 for normal args, == 0 for ... args */{ return 0;}/* Output an insn whose source is a 386 integer register. SRC is the rtx for the register, and TEMPLATE is the op-code template. SRC may be either SImode or DImode. The template will be output with operands[0] as SRC, and operands[1] as a pointer to the top of the 386 stack. So a call from floatsidf2 would look like this: output_op_from_reg (operands[1], AS1 (fild%z0,%1)); where %z0 corresponds to the caller's operands[1], and is used to emit the proper size suffix. ??? Extend this to handle HImode - a 387 can load and store HImode values directly. */voidoutput_op_from_reg (src, template) rtx src; char *template;{ rtx xops[4]; int size = GET_MODE_SIZE (GET_MODE (src)); xops[0] = src; xops[1] = AT_SP (Pmode); xops[2] = GEN_INT (size); xops[3] = stack_pointer_rtx; if (size > UNITS_PER_WORD) { rtx high; if (size > 2 * UNITS_PER_WORD) { high = gen_rtx (REG, SImode, REGNO (src) + 2); output_asm_insn (AS1 (push%L0,%0), &high); } high = gen_rtx (REG, SImode, REGNO (src) + 1); output_asm_insn (AS1 (push%L0,%0), &high); } output_asm_insn (AS1 (push%L0,%0), &src); output_asm_insn (template, xops); output_asm_insn (AS2 (add%L3,%2,%3), xops);}/* Output an insn to pop an value from the 387 top-of-stack to 386 register DEST. The 387 register stack is popped if DIES is true. If the mode of DEST is an integer mode, a `fist' integer store is done, otherwise a `fst' float store is done. */voidoutput_to_reg (dest, dies) rtx dest; int dies;{ rtx xops[4]; int size = GET_MODE_SIZE (GET_MODE (dest)); xops[0] = AT_SP (Pmode); xops[1] = stack_pointer_rtx; xops[2] = GEN_INT (size); xops[3] = dest; output_asm_insn (AS2 (sub%L1,%2,%1), xops); if (GET_MODE_CLASS (GET_MODE (dest)) == MODE_INT) { if (dies) output_asm_insn (AS1 (fistp%z3,%y0), xops); else output_asm_insn (AS1 (fist%z3,%y0), xops); } else if (GET_MODE_CLASS (GET_MODE (dest)) == MODE_FLOAT) { if (dies) output_asm_insn (AS1 (fstp%z3,%y0), xops); else { if (GET_MODE (dest) == XFmode) { output_asm_insn (AS1 (fstp%z3,%y0), xops); output_asm_insn (AS1 (fld%z3,%y0), xops); } else output_asm_insn (AS1 (fst%z3,%y0), xops); } } else abort (); output_asm_insn (AS1 (pop%L0,%0), &dest); if (size > UNITS_PER_WORD) { dest = gen_rtx (REG, SImode, REGNO (dest) + 1); output_asm_insn (AS1 (pop%L0,%0), &dest); if (size > 2 * UNITS_PER_WORD) { dest = gen_rtx (REG, SImode, REGNO (dest) + 1); output_asm_insn (AS1 (pop%L0,%0), &dest); } }}char *singlemove_string (operands) rtx *operands;{ rtx x; if (GET_CODE (operands[0]) == MEM && GET_CODE (x = XEXP (operands[0], 0)) == PRE_DEC) { if (XEXP (x, 0) != stack_pointer_rtx) abort (); return "push%L1 %1"; } else if (GET_CODE (operands[1]) == CONST_DOUBLE) { return output_move_const_single (operands); } else if (GET_CODE (operands[0]) == REG || GET_CODE (operands[1]) == REG) return AS2 (mov%L0,%1,%0); else if (CONSTANT_P (operands[1])) return AS2 (mov%L0,%1,%0); else { output_asm_insn ("push%L1 %1", operands); return "pop%L0 %0"; }}/* Return a REG that occurs in ADDR with coefficient 1. ADDR can be effectively incremented by incrementing REG. */static rtxfind_addr_reg (addr) rtx addr;{ while (GET_CODE (addr) == PLUS) { if (GET_CODE (XEXP (addr, 0)) == REG) addr = XEXP (addr, 0); else if (GET_CODE (XEXP (addr, 1)) == REG) addr = XEXP (addr, 1); else if (CONSTANT_P (XEXP (addr, 0))) addr = XEXP (addr, 1); else if (CONSTANT_P (XEXP (addr, 1))) addr = XEXP (addr, 0); else abort (); } if (GET_CODE (addr) == REG) 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) { 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 ) {/* abort (); */ operands[1] = gen_rtx (MEM, XFmode, latehalf[0]);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -