📄 i386.c
字号:
FILE *file; int size;{ register int regno; int limit; rtx xops[4]; int pic_reg_used = flag_pic && (current_function_uses_pic_offset_table || current_function_uses_const_pool); xops[0] = stack_pointer_rtx; xops[1] = frame_pointer_rtx; xops[2] = GEN_INT (size); if (frame_pointer_needed) { output_asm_insn ("push%L1 %1", xops); output_asm_insn (AS2 (mov%L0,%0,%1), xops); } if (size) output_asm_insn (AS2 (sub%L0,%2,%0), xops); /* Note If use enter it is NOT reversed args. This one is not reversed from intel!! I think enter is slower. Also sdb doesn't like it. But if you want it the code is: { xops[3] = const0_rtx; output_asm_insn ("enter %2,%3", xops); } */ limit = (frame_pointer_needed ? FRAME_POINTER_REGNUM : STACK_POINTER_REGNUM); for (regno = limit - 1; regno >= 0; regno--) if ((regs_ever_live[regno] && ! call_used_regs[regno]) || (regno == PIC_OFFSET_TABLE_REGNUM && pic_reg_used)) { xops[0] = gen_rtx (REG, SImode, regno); output_asm_insn ("push%L0 %0", xops); } if (pic_reg_used) { xops[0] = pic_offset_table_rtx; xops[1] = (rtx) gen_label_rtx (); output_asm_insn (AS1 (call,%P1), xops); ASM_OUTPUT_INTERNAL_LABEL (file, "L", CODE_LABEL_NUMBER (xops[1])); output_asm_insn (AS1 (pop%L0,%0), xops); output_asm_insn ("addl $_GLOBAL_OFFSET_TABLE_+[.-%P1],%0", xops); }}/* Return 1 if it is appropriate to emit `ret' instructions in the body of a function. Do this only if the epilogue is simple, needing a couple of insns. Prior to reloading, we can't tell how many registers must be saved, so return 0 then. If NON_SAVING_SETJMP is defined and true, then it is not possible for the epilogue to be simple, so return 0. This is a special case since NON_SAVING_SETJMP will not cause regs_ever_live to change until final, but jump_optimize may need to know sooner if a `return' is OK. */intsimple_386_epilogue (){ int regno; int nregs = 0; int reglimit = (frame_pointer_needed ? FRAME_POINTER_REGNUM : STACK_POINTER_REGNUM); int pic_reg_used = flag_pic && (current_function_uses_pic_offset_table || current_function_uses_const_pool);#ifdef NON_SAVING_SETJMP if (NON_SAVING_SETJMP && current_function_calls_setjmp) return 0;#endif if (! reload_completed) return 0; for (regno = reglimit - 1; regno >= 0; regno--) if ((regs_ever_live[regno] && ! call_used_regs[regno]) || (regno == PIC_OFFSET_TABLE_REGNUM && pic_reg_used)) nregs++; return nregs == 0 || ! frame_pointer_needed;}/* This function generates the assembly code for function exit. FILE is an stdio stream to output the code to. SIZE is an int: how many units of temporary storage to deallocate. */voidfunction_epilogue (file, size) FILE *file; int size;{ register int regno; register int nregs, limit; int offset; rtx xops[3]; int pic_reg_used = flag_pic && (current_function_uses_pic_offset_table || current_function_uses_const_pool); /* Compute the number of registers to pop */ limit = (frame_pointer_needed ? FRAME_POINTER_REGNUM : STACK_POINTER_REGNUM); nregs = 0; for (regno = limit - 1; regno >= 0; regno--) if ((regs_ever_live[regno] && ! call_used_regs[regno]) || (regno == PIC_OFFSET_TABLE_REGNUM && pic_reg_used)) nregs++; /* sp is often unreliable so we must go off the frame pointer, */ /* In reality, we may not care if sp is unreliable, because we can restore the register relative to the frame pointer. In theory, since each move is the same speed as a pop, and we don't need the leal, this is faster. For now restore multiple registers the old way. */ offset = -size - (nregs * UNITS_PER_WORD); xops[2] = stack_pointer_rtx; if (nregs > 1 || ! frame_pointer_needed) { if (frame_pointer_needed) { xops[0] = adj_offsettable_operand (AT_BP (Pmode), offset); output_asm_insn (AS2 (lea%L2,%0,%2), xops); } for (regno = 0; regno < limit; regno++) if ((regs_ever_live[regno] && ! call_used_regs[regno]) || (regno == PIC_OFFSET_TABLE_REGNUM && pic_reg_used)) { xops[0] = gen_rtx (REG, SImode, regno); output_asm_insn ("pop%L0 %0", xops); } } else for (regno = 0; regno < limit; regno++) if ((regs_ever_live[regno] && ! call_used_regs[regno]) || (regno == PIC_OFFSET_TABLE_REGNUM && pic_reg_used)) { xops[0] = gen_rtx (REG, SImode, regno); xops[1] = adj_offsettable_operand (AT_BP (Pmode), offset); output_asm_insn (AS2 (mov%L0,%1,%0), xops); offset += 4; } if (frame_pointer_needed) { /* On i486, mov & pop is faster than "leave". */ if (!TARGET_386) { xops[0] = frame_pointer_rtx; output_asm_insn (AS2 (mov%L2,%0,%2), xops); output_asm_insn ("pop%L0 %0", xops); } else output_asm_insn ("leave", xops); } else if (size) { /* If there is no frame pointer, we must still release the frame. */ xops[0] = GEN_INT (size); output_asm_insn (AS2 (add%L2,%0,%2), xops); } if (current_function_pops_args && current_function_args_size) { xops[1] = GEN_INT (current_function_pops_args); /* i386 can only pop 32K bytes (maybe 64K? Is it signed?). If asked to pop more, pop return address, do explicit add, and jump indirectly to the caller. */ if (current_function_pops_args >= 32768) { /* ??? Which register to use here? */ xops[0] = gen_rtx (REG, SImode, 2); output_asm_insn ("pop%L0 %0", xops); output_asm_insn (AS2 (add%L2,%1,%2), xops); output_asm_insn ("jmp %*%0", xops); } else output_asm_insn ("ret %1", xops); } else output_asm_insn ("ret", xops);}/* GO_IF_LEGITIMATE_ADDRESS recognizes an RTL expression that is a valid memory address for an instruction. The MODE argument is the machine mode for the MEM expression that wants to use this address. On x86, legitimate addresses are: base movl (base),reg displacement movl disp,reg base + displacement movl disp(base),reg index + base movl (base,index),reg (index + base) + displacement movl disp(base,index),reg index*scale movl (,index,scale),reg index*scale + disp movl disp(,index,scale),reg index*scale + base movl (base,index,scale),reg (index*scale + base) + disp movl disp(base,index,scale),reg In each case, scale can be 1, 2, 4, 8. *//* This is exactly the same as print_operand_addr, except that it recognizes addresses instead of printing them. It only recognizes address in canonical form. LEGITIMIZE_ADDRESS should convert common non-canonical forms to canonical form so that they will be recognized. */#define ADDR_INVALID(msg,insn) \do { \ if (TARGET_DEBUG_ADDR) \ { \ fprintf (stderr, msg); \ debug_rtx (insn); \ } \} while (0)intlegitimate_address_p (mode, addr, strict) enum machine_mode mode; register rtx addr; int strict;{ rtx base = NULL_RTX; rtx indx = NULL_RTX; rtx scale = NULL_RTX; rtx disp = NULL_RTX; if (TARGET_DEBUG_ADDR) { fprintf (stderr, "\n==========\nGO_IF_LEGITIMATE_ADDRESS, mode = %s, strict = %d\n", GET_MODE_NAME (mode), strict); debug_rtx (addr); } if (GET_CODE (addr) == REG || GET_CODE (addr) == SUBREG) base = addr; /* base reg */ else if (GET_CODE (addr) == PLUS) { rtx op0 = XEXP (addr, 0); rtx op1 = XEXP (addr, 1); enum rtx_code code0 = GET_CODE (op0); enum rtx_code code1 = GET_CODE (op1); if (code0 == REG || code0 == SUBREG) { if (code1 == REG || code1 == SUBREG) { indx = op0; /* index + base */ base = op1; } else { base = op0; /* base + displacement */ disp = op1; } } else if (code0 == MULT) { indx = XEXP (op0, 0); scale = XEXP (op0, 1); if (code1 == REG || code1 == SUBREG) base = op1; /* index*scale + base */ else disp = op1; /* index*scale + disp */ } else if (code0 == PLUS && GET_CODE (XEXP (op0, 0)) == MULT) { indx = XEXP (XEXP (op0, 0), 0); /* index*scale + base + disp */ scale = XEXP (XEXP (op0, 0), 1); base = XEXP (op0, 1); disp = op1; } else if (code0 == PLUS) { indx = XEXP (op0, 0); /* index + base + disp */ base = XEXP (op0, 1); disp = op1; } else { ADDR_INVALID ("PLUS subcode is not valid.\n", op0); return FALSE; } } else if (GET_CODE (addr) == MULT) { indx = XEXP (addr, 0); /* index*scale */ scale = XEXP (addr, 1); } else disp = addr; /* displacement */ /* Allow arg pointer and stack pointer as index if there is not scaling */ if (base && indx && !scale && (indx == arg_pointer_rtx || indx == stack_pointer_rtx)) { rtx tmp = base; base = indx; indx = tmp; } /* Validate base register */ /* Don't allow SUBREG's here, it can lead to spill failures when the base is one word out of a two word structure, which is represented internally as a DImode int. */ if (base) { if (GET_CODE (base) != REG) { ADDR_INVALID ("Base is not a register.\n", base); return FALSE; } if ((strict && !REG_OK_FOR_BASE_STRICT_P (base)) || (!strict && !REG_OK_FOR_BASE_NONSTRICT_P (base))) { ADDR_INVALID ("Base is not valid.\n", base); return FALSE; } } /* Validate index register */ /* Don't allow SUBREG's here, it can lead to spill failures when the index is one word out of a two word structure, which is represented internally as a DImode int. */ if (indx) { if (GET_CODE (indx) != REG) { ADDR_INVALID ("Index is not a register.\n", indx); return FALSE; } if ((strict && !REG_OK_FOR_INDEX_STRICT_P (indx)) || (!strict && !REG_OK_FOR_INDEX_NONSTRICT_P (indx))) { ADDR_INVALID ("Index is not valid.\n", indx); return FALSE; } } else if (scale) abort (); /* scale w/o index invalid */ /* Validate scale factor */ if (scale) { HOST_WIDE_INT value; if (GET_CODE (scale) != CONST_INT) { ADDR_INVALID ("Scale is not valid.\n", scale); return FALSE; } value = INTVAL (scale); if (value != 1 && value != 2 && value != 4 && value != 8) { ADDR_INVALID ("Scale is not a good multiplier.\n", scale); return FALSE; } } /* Validate displacement */ if (disp) { if (!CONSTANT_ADDRESS_P (disp)) { ADDR_INVALID ("Displacement is not valid.\n", disp); return FALSE; } if (GET_CODE (disp) == CONST_DOUBLE) { ADDR_INVALID ("Displacement is a const_double.\n", disp); return FALSE; } if (flag_pic && SYMBOLIC_CONST (disp) && base != pic_offset_table_rtx && (indx != pic_offset_table_rtx || scale != NULL_RTX)) { ADDR_INVALID ("Displacement is an invalid pic reference.\n", disp); return FALSE; } if (HALF_PIC_P () && HALF_PIC_ADDRESS_P (disp) && (base != NULL_RTX || indx != NULL_RTX)) { ADDR_INVALID ("Displacement is an invalid half-pic reference.\n", disp); return FALSE; } } if (TARGET_DEBUG_ADDR) fprintf (stderr, "Address is valid.\n"); /* Everything looks valid, return true */ return TRUE;}/* Return a legitimate reference for ORIG (an address) using the register REG. If REG is 0, a new pseudo is generated. There are three types of references that must be handled: 1. Global data references must load the address from the GOT, via the PIC reg. An insn is emitted to do this load, and the reg is returned. 2. Static data references must compute the address as an offset from the GOT, whose base is in the PIC reg. An insn is emitted to compute the address into a reg, and the reg is returned. Static data objects have SYMBOL_REF_FLAG set to differentiate them from global data objects. 3. Constant pool addresses must be handled special. They are considered legitimate addresses, but only if not used with regs. When printed, the output routines know to print the reference with the PIC reg, even though the PIC reg doesn't appear in the RTL. GO_IF_LEGITIMATE_ADDRESS rejects symbolic references unless the PIC reg also appears in the address (except for constant pool references, noted above). "switch" statements also require special handling when generating PIC code. See comments by the `casesi' insn in i386.md for details. */rtxlegitimize_pic_address (orig, reg) rtx orig; rtx reg;{ rtx addr = orig; rtx new = orig; if (GET_CODE (addr) == SYMBOL_REF || GET_CODE (addr) == LABEL_REF) { if (GET_CODE (addr) == SYMBOL_REF && CONSTANT_POOL_ADDRESS_P (addr)) reg = new = orig; else { if (reg == 0) reg = gen_reg_rtx (Pmode); if ((GET_CODE (addr) == SYMBOL_REF && SYMBOL_REF_FLAG (addr)) || GET_CODE (addr) == LABEL_REF) new = gen_rtx (PLUS, Pmode, pic_offset_table_rtx, orig); else new = gen_rtx (MEM, Pmode, gen_rtx (PLUS, Pmode, pic_offset_table_rtx, orig)); emit_move_insn (reg, new);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -