📄 ia64.c
字号:
Return the length of the field, or <= 0 on failure. */intia64_depz_field_mask (rop, rshift) rtx rop, rshift;{ unsigned HOST_WIDE_INT op = INTVAL (rop); unsigned HOST_WIDE_INT shift = INTVAL (rshift); /* Get rid of the zero bits we're shifting in. */ op >>= shift; /* We must now have a solid block of 1's at bit 0. */ return exact_log2 (op + 1);}/* Expand a symbolic constant load. *//* ??? Should generalize this, so that we can also support 32 bit pointers. */voidia64_expand_load_address (dest, src, scratch) rtx dest, src, scratch;{ rtx temp; /* The destination could be a MEM during initial rtl generation, which isn't a valid destination for the PIC load address patterns. */ if (! register_operand (dest, DImode)) temp = gen_reg_rtx (DImode); else temp = dest; if (TARGET_AUTO_PIC) emit_insn (gen_load_gprel64 (temp, src)); else if (GET_CODE (src) == SYMBOL_REF && SYMBOL_REF_FLAG (src)) emit_insn (gen_load_fptr (temp, src)); else if (sdata_symbolic_operand (src, DImode)) emit_insn (gen_load_gprel (temp, src)); else if (GET_CODE (src) == CONST && GET_CODE (XEXP (src, 0)) == PLUS && GET_CODE (XEXP (XEXP (src, 0), 1)) == CONST_INT && (INTVAL (XEXP (XEXP (src, 0), 1)) & 0x1fff) != 0) { rtx subtarget = no_new_pseudos ? temp : gen_reg_rtx (DImode); rtx sym = XEXP (XEXP (src, 0), 0); HOST_WIDE_INT ofs, hi, lo; /* Split the offset into a sign extended 14-bit low part and a complementary high part. */ ofs = INTVAL (XEXP (XEXP (src, 0), 1)); lo = ((ofs & 0x3fff) ^ 0x2000) - 0x2000; hi = ofs - lo; if (! scratch) scratch = no_new_pseudos ? subtarget : gen_reg_rtx (DImode); emit_insn (gen_load_symptr (subtarget, plus_constant (sym, hi), scratch)); emit_insn (gen_adddi3 (temp, subtarget, GEN_INT (lo))); } else { rtx insn; if (! scratch) scratch = no_new_pseudos ? temp : gen_reg_rtx (DImode); insn = emit_insn (gen_load_symptr (temp, src, scratch)); REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_EQUAL, src, REG_NOTES (insn)); } if (temp != dest) emit_move_insn (dest, temp);}rtxia64_gp_save_reg (setjmp_p) int setjmp_p;{ rtx save = cfun->machine->ia64_gp_save; if (save != NULL) { /* We can't save GP in a pseudo if we are calling setjmp, because pseudos won't be restored by longjmp. For now, we save it in r4. */ /* ??? It would be more efficient to save this directly into a stack slot. Unfortunately, the stack slot address gets cse'd across the setjmp call because the NOTE_INSN_SETJMP note is in the wrong place. */ /* ??? Get the barf bag, Virginia. We've got to replace this thing in place, since this rtx is used in exception handling receivers. Moreover, we must get this rtx out of regno_reg_rtx or reload will do the wrong thing. */ unsigned int old_regno = REGNO (save); if (setjmp_p && old_regno != GR_REG (4)) { REGNO (save) = GR_REG (4); regno_reg_rtx[old_regno] = gen_rtx_raw_REG (DImode, old_regno); } } else { if (setjmp_p) save = gen_rtx_REG (DImode, GR_REG (4)); else if (! optimize) save = gen_rtx_REG (DImode, LOC_REG (0)); else save = gen_reg_rtx (DImode); cfun->machine->ia64_gp_save = save; } return save;}/* Split a post-reload TImode reference into two DImode components. */rtxia64_split_timode (out, in, scratch) rtx out[2]; rtx in, scratch;{ switch (GET_CODE (in)) { case REG: out[0] = gen_rtx_REG (DImode, REGNO (in)); out[1] = gen_rtx_REG (DImode, REGNO (in) + 1); return NULL_RTX; case MEM: { rtx base = XEXP (in, 0); switch (GET_CODE (base)) { case REG: out[0] = adjust_address (in, DImode, 0); break; case POST_MODIFY: base = XEXP (base, 0); out[0] = adjust_address (in, DImode, 0); break; /* Since we're changing the mode, we need to change to POST_MODIFY as well to preserve the size of the increment. Either that or do the update in two steps, but we've already got this scratch register handy so let's use it. */ case POST_INC: base = XEXP (base, 0); out[0] = change_address (in, DImode, gen_rtx_POST_MODIFY (Pmode, base, plus_constant (base, 16))); break; case POST_DEC: base = XEXP (base, 0); out[0] = change_address (in, DImode, gen_rtx_POST_MODIFY (Pmode, base, plus_constant (base, -16))); break; default: abort (); } if (scratch == NULL_RTX) abort (); out[1] = change_address (in, DImode, scratch); return gen_adddi3 (scratch, base, GEN_INT (8)); } case CONST_INT: case CONST_DOUBLE: split_double (in, &out[0], &out[1]); return NULL_RTX; default: abort (); }}/* ??? Fixing GR->FR TFmode moves during reload is hard. You need to go through memory plus an extra GR scratch register. Except that you can either get the first from SECONDARY_MEMORY_NEEDED or the second from SECONDARY_RELOAD_CLASS, but not both. We got into problems in the first place by allowing a construct like (subreg:TF (reg:TI)), which we got from a union containing a long double. This solution attempts to prevent this situation from occurring. When we see something like the above, we spill the inner register to memory. */rtxspill_tfmode_operand (in, force) rtx in; int force;{ if (GET_CODE (in) == SUBREG && GET_MODE (SUBREG_REG (in)) == TImode && GET_CODE (SUBREG_REG (in)) == REG) { rtx mem = gen_mem_addressof (SUBREG_REG (in), NULL_TREE); return gen_rtx_MEM (TFmode, copy_to_reg (XEXP (mem, 0))); } else if (force && GET_CODE (in) == REG) { rtx mem = gen_mem_addressof (in, NULL_TREE); return gen_rtx_MEM (TFmode, copy_to_reg (XEXP (mem, 0))); } else if (GET_CODE (in) == MEM && GET_CODE (XEXP (in, 0)) == ADDRESSOF) return change_address (in, TFmode, copy_to_reg (XEXP (in, 0))); else return in;}/* Emit comparison instruction if necessary, returning the expression that holds the compare result in the proper mode. */rtxia64_expand_compare (code, mode) enum rtx_code code; enum machine_mode mode;{ rtx op0 = ia64_compare_op0, op1 = ia64_compare_op1; rtx cmp; /* If we have a BImode input, then we already have a compare result, and do not need to emit another comparison. */ if (GET_MODE (op0) == BImode) { if ((code == NE || code == EQ) && op1 == const0_rtx) cmp = op0; else abort (); } else { cmp = gen_reg_rtx (BImode); emit_insn (gen_rtx_SET (VOIDmode, cmp, gen_rtx_fmt_ee (code, BImode, op0, op1))); code = NE; } return gen_rtx_fmt_ee (code, mode, cmp, const0_rtx);}/* Emit the appropriate sequence for a call. */voidia64_expand_call (retval, addr, nextarg, sibcall_p) rtx retval; rtx addr; rtx nextarg; int sibcall_p;{ rtx insn, b0, pfs, gp_save, narg_rtx, dest; bool indirect_p; int narg; addr = XEXP (addr, 0); b0 = gen_rtx_REG (DImode, R_BR (0)); pfs = gen_rtx_REG (DImode, AR_PFS_REGNUM); if (! nextarg) narg = 0; else if (IN_REGNO_P (REGNO (nextarg))) narg = REGNO (nextarg) - IN_REG (0); else narg = REGNO (nextarg) - OUT_REG (0); narg_rtx = GEN_INT (narg); if (TARGET_NO_PIC || TARGET_AUTO_PIC) { if (sibcall_p) insn = gen_sibcall_nopic (addr, narg_rtx, b0, pfs); else if (! retval) insn = gen_call_nopic (addr, narg_rtx, b0); else insn = gen_call_value_nopic (retval, addr, narg_rtx, b0); emit_call_insn (insn); return; } indirect_p = ! symbolic_operand (addr, VOIDmode); if (sibcall_p || (TARGET_CONST_GP && !indirect_p)) gp_save = NULL_RTX; else gp_save = ia64_gp_save_reg (setjmp_operand (addr, VOIDmode)); if (gp_save) emit_move_insn (gp_save, pic_offset_table_rtx); /* If this is an indirect call, then we have the address of a descriptor. */ if (indirect_p) { dest = force_reg (DImode, gen_rtx_MEM (DImode, addr)); emit_move_insn (pic_offset_table_rtx, gen_rtx_MEM (DImode, plus_constant (addr, 8))); } else dest = addr; if (sibcall_p) insn = gen_sibcall_pic (dest, narg_rtx, b0, pfs); else if (! retval) insn = gen_call_pic (dest, narg_rtx, b0); else insn = gen_call_value_pic (retval, dest, narg_rtx, b0); emit_call_insn (insn); if (gp_save) emit_move_insn (pic_offset_table_rtx, gp_save);}/* Begin the assembly file. */voidemit_safe_across_calls (f) FILE *f;{ unsigned int rs, re; int out_state; rs = 1; out_state = 0; while (1) { while (rs < 64 && call_used_regs[PR_REG (rs)]) rs++; if (rs >= 64) break; for (re = rs + 1; re < 64 && ! call_used_regs[PR_REG (re)]; re++) continue; if (out_state == 0) { fputs ("\t.pred.safe_across_calls ", f); out_state = 1; } else fputc (',', f); if (re == rs + 1) fprintf (f, "p%u", rs); else fprintf (f, "p%u-p%u", rs, re - 1); rs = re + 1; } if (out_state) fputc ('\n', f);}/* Structure to be filled in by ia64_compute_frame_size with register save masks and offsets for the current function. */struct ia64_frame_info{ HOST_WIDE_INT total_size; /* size of the stack frame, not including the caller's scratch area. */ HOST_WIDE_INT spill_cfa_off; /* top of the reg spill area from the cfa. */ HOST_WIDE_INT spill_size; /* size of the gr/br/fr spill area. */ HOST_WIDE_INT extra_spill_size; /* size of spill area for others. */ HARD_REG_SET mask; /* mask of saved registers. */ unsigned int gr_used_mask; /* mask of registers in use as gr spill registers or long-term scratches. */ int n_spilled; /* number of spilled registers. */ int reg_fp; /* register for fp. */ int reg_save_b0; /* save register for b0. */ int reg_save_pr; /* save register for prs. */ int reg_save_ar_pfs; /* save register for ar.pfs. */ int reg_save_ar_unat; /* save register for ar.unat. */ int reg_save_ar_lc; /* save register for ar.lc. */ int n_input_regs; /* number of input registers used. */ int n_local_regs; /* number of local registers used. */ int n_output_regs; /* number of output registers used. */ int n_rotate_regs; /* number of rotating registers used. */ char need_regstk; /* true if a .regstk directive needed. */ char initialized; /* true if the data is finalized. */};/* Current frame information calculated by ia64_compute_frame_size. */static struct ia64_frame_info current_frame_info;/* Helper function for ia64_compute_frame_size: find an appropriate general register to spill some special register to. SPECIAL_SPILL_MASK contains bits in GR0 to GR31 that have already been allocated by this routine. TRY_LOCALS is true if we should attempt to locate a local regnum. */static intfind_gr_spill (try_locals) int try_locals;{ int regno; /* If this is a leaf function, first try an otherwise unused call-clobbered register. */ if (current_function_is_leaf) { for (regno = GR_REG (1); regno <= GR_REG (31); regno++) if (! regs_ever_live[regno] && call_used_regs[regno] && ! fixed_regs[regno] && ! global_regs[regno] && ((current_frame_info.gr_used_mask >> regno) & 1) == 0) { current_frame_info.gr_used_mask |= 1 << regno; return regno; } } if (try_locals) { regno = current_frame_info.n_local_regs; /* If there is a frame pointer, then we can't use loc79, because that is HARD_FRAME_POINTER_REGNUM. In particular, see the reg_name switching code in ia64_expand_prologue. */ if (regno < (80 - frame_pointer_needed)) { current_frame_info.n_local_regs = regno + 1; return LOC_REG (0) + regno; } } /* Failed to find a general register to spill to. Must use stack. */ return 0;}/* In order to make for nice schedules, we try to allocate every temporary to a different register. We must of course stay away from call-saved, fixed, and global registers. We must also stay away from registers allocated in current_frame_info.gr_used_mask, since those include regs used all through the prologue. Any register allocated here must be used immediately. The idea is to aid scheduling, not to solve data flow problems. */static int last_scratch_gr_reg;static intnext_scratch_gr_reg (){ int i, regno; for (i = 0; i < 32; ++i) { regno = (last_scratch_gr_reg + i + 1) & 31;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -