📄 i960.c
字号:
output_asm_insn ("# ldconst %1,%0",operands); operands[0] = gen_rtx (REG, SImode, REGNO (dst)); operands[1] = first; output_asm_insn (i960_output_ldconst (operands[0], operands[1]), operands); operands[0] = gen_rtx (REG, SImode, REGNO (dst) + 1); operands[1] = second; output_asm_insn (i960_output_ldconst (operands[0], operands[1]), operands); return "";#else if (fp_literal_one (src, VOIDmode)) return "movrl 0f1.0,%0"; fatal ("inline double constants not supported on this host");#endif } else if (mode == TImode) { /* ??? This is currently not handled at all. */ abort (); /* Note: lowest order word goes in lowest numbered reg. */ rsrc1 = INTVAL (src); if (rsrc1 >= 0 && rsrc1 < 32) return "movq %1,%0"; else output_asm_insn ("movq\t0,%0\t# ldconstq %1,%0",operands); /* Go pick up the low-order word. */ } else if (mode == DImode) { rtx upperhalf, lowerhalf, xoperands[2]; char *string; if (GET_CODE (src) == CONST_DOUBLE) { upperhalf = gen_rtx (CONST_INT, VOIDmode, CONST_DOUBLE_HIGH (src)); lowerhalf = gen_rtx (CONST_INT, VOIDmode, CONST_DOUBLE_LOW (src)); } else if (GET_CODE (src) == CONST_INT) { lowerhalf = src; upperhalf = INTVAL (src) < 0 ? constm1_rtx : const0_rtx; } else abort (); /* Note: lowest order word goes in lowest numbered reg. */ /* Numbers from 0 to 31 can be handled with a single insn. */ rsrc1 = INTVAL (lowerhalf); if (upperhalf == const0_rtx && rsrc1 >= 0 && rsrc1 < 32) return "movl %1,%0"; /* Output the upper half with a recursive call. */ xoperands[0] = gen_rtx (REG, SImode, REGNO (dst) + 1); xoperands[1] = upperhalf; output_asm_insn (i960_output_ldconst (xoperands[0], xoperands[1]), xoperands); /* The lower word is emitted as normally. */ } else if (mode == SFmode) {#if HOST_FLOAT_FORMAT == TARGET_FLOAT_FORMAT REAL_VALUE_TYPE d; long value; REAL_VALUE_FROM_CONST_DOUBLE (d, src); REAL_VALUE_TO_TARGET_SINGLE (d, value); output_asm_insn ("# ldconst %1,%0",operands); operands[0] = gen_rtx (REG, SImode, REGNO (dst)); operands[1] = gen_rtx (CONST_INT, VOIDmode, value); output_asm_insn (i960_output_ldconst (operands[0], operands[1]), operands);#else if (fp_literal_zero (src, VOIDmode)) return "movr 0f0.0,%0"; if (fp_literal_one (src, VOIDmode)) return "movr 0f1.0,%0"; fatal ("inline float constants not supported on this host");#endif return ""; } else { rsrc1 = INTVAL (src); if (mode == QImode) { if (rsrc1 > 0xff) rsrc1 &= 0xff; } else if (mode == HImode) { if (rsrc1 > 0xffff) rsrc1 &= 0xffff; } } if (rsrc1 >= 0) { /* ldconst 0..31,X -> mov 0..31,X */ if (rsrc1 < 32) { if (i960_last_insn_type == I_TYPE_REG && TARGET_C_SERIES) return "lda %1,%0"; return "mov %1,%0"; } /* ldconst 32..63,X -> add 31,nn,X */ if (rsrc1 < 63) { if (i960_last_insn_type == I_TYPE_REG && TARGET_C_SERIES) return "lda %1,%0"; operands[1] = gen_rtx (CONST_INT, VOIDmode, rsrc1 - 31); output_asm_insn ("addo\t31,%1,%0\t# ldconst %3,%0", operands); return ""; } } else if (rsrc1 < 0) { /* ldconst -1..-31 -> sub 0,0..31,X */ if (rsrc1 >= -31) { /* return 'sub -(%1),0,%0' */ operands[1] = gen_rtx (CONST_INT, VOIDmode, - rsrc1); output_asm_insn ("subo\t%1,0,%0\t# ldconst %3,%0", operands); return ""; } /* ldconst -32 -> not 31,X */ if (rsrc1 == -32) { operands[1] = gen_rtx (CONST_INT, VOIDmode, ~rsrc1); output_asm_insn ("not\t%1,%0 # ldconst %3,%0", operands); return ""; } } /* If const is a single bit. */ if (bitpos (rsrc1) >= 0) { operands[1] = gen_rtx (CONST_INT, VOIDmode, bitpos (rsrc1)); output_asm_insn ("setbit\t%1,0,%0\t# ldconst %3,%0", operands); return ""; } /* If const is a bit string of less than 6 bits (1..31 shifted). */ if (is_mask (rsrc1)) { int s, e; if (bitstr (rsrc1, &s, &e) < 6) { rsrc2 = ((unsigned int) rsrc1) >> s; operands[1] = gen_rtx (CONST_INT, VOIDmode, rsrc2); operands[2] = gen_rtx (CONST_INT, VOIDmode, s); output_asm_insn ("shlo\t%2,%1,%0\t# ldconst %3,%0", operands); return ""; } } /* Unimplemented cases: const is in range 0..31 but rotated around end of word: ror 31,3,g0 -> ldconst 0xe0000003,g0 and any 2 instruction cases that might be worthwhile */ output_asm_insn ("ldconst %1,%0", operands); return "";}/* Determine if there is an opportunity for a bypass optimization. Bypass succeeds on the 960K* if the destination of the previous instruction is the second operand of the current instruction. Bypass always succeeds on the C*. Return 1 if the pattern should interchange the operands. CMPBR_FLAG is true if this is for a compare-and-branch insn. OP1 and OP2 are the two source operands of a 3 operand insn. */inti960_bypass (insn, op1, op2, cmpbr_flag) register rtx insn, op1, op2; int cmpbr_flag;{ register rtx prev_insn, prev_dest; if (TARGET_C_SERIES) return 0; /* Can't do this if op1 isn't a register. */ if (! REG_P (op1)) return 0; /* Can't do this for a compare-and-branch if both ops aren't regs. */ if (cmpbr_flag && ! REG_P (op2)) return 0; prev_insn = prev_real_insn (insn); if (prev_insn && GET_CODE (prev_insn) == INSN && GET_CODE (PATTERN (prev_insn)) == SET) { prev_dest = SET_DEST (PATTERN (prev_insn)); if ((GET_CODE (prev_dest) == REG && REGNO (prev_dest) == REGNO (op1)) || (GET_CODE (prev_dest) == SUBREG && GET_CODE (SUBREG_REG (prev_dest)) == REG && REGNO (SUBREG_REG (prev_dest)) == REGNO (op1))) return 1; } return 0;}/* Output the code which declares the function name. This also handles leaf routines, which have special requirements, and initializes some global variables. */voidi960_function_name_declare (file, name, fndecl) FILE *file; char *name; tree fndecl;{ register int i, j; int leaf_proc_ok; rtx insn; /* Increment global return label. */ ret_label++; /* Compute whether tail calls and leaf routine optimizations can be performed for this function. */ if (TARGET_TAILCALL) tail_call_ok = 1; else tail_call_ok = 0; if (TARGET_LEAFPROC) leaf_proc_ok = 1; else leaf_proc_ok = 0; /* Even if nobody uses extra parms, can't have leafroc or tail calls if argblock, because argblock uses g14 implicitly. */ if (current_function_args_size != 0) { tail_call_ok = 0; leaf_proc_ok = 0; } /* See if caller passes in an address to return value. */ if (aggregate_value_p (DECL_RESULT (fndecl))) { tail_call_ok = 0; leaf_proc_ok = 0; } /* Can not use tail calls or make this a leaf routine if there is a non zero frame size. */ if (get_frame_size () != 0) leaf_proc_ok = 0; /* I don't understand this condition, and do not think that it is correct. Apparently this is just checking whether the frame pointer is used, and we can't trust regs_ever_live[fp] since it is (almost?) always set. */ if (tail_call_ok) for (insn = get_insns (); insn; insn = NEXT_INSN (insn)) if (GET_CODE (insn) == INSN && reg_mentioned_p (frame_pointer_rtx, insn)) { tail_call_ok = 0; break; } /* Check for CALL insns. Can not be a leaf routine if there are any. */ if (leaf_proc_ok) for (insn = get_insns (); insn; insn = NEXT_INSN (insn)) if (GET_CODE (insn) == CALL_INSN) { leaf_proc_ok = 0; break; } /* Can not be a leaf routine if any non-call clobbered registers are used in this function. */ if (leaf_proc_ok) for (i = 0, j = 0; i < FIRST_PSEUDO_REGISTER; i++) if (regs_ever_live[i] && ((! call_used_regs[i]) || (i > 7 && i < 12))) { /* Global registers. */ if (i < 16 && i > 7 && i != 13) leaf_proc_ok = 0; /* Local registers. */ else if (i < 32) leaf_proc_ok = 0; } /* Now choose a leaf return register, if we can find one, and if it is OK for this to be a leaf routine. */ i960_leaf_ret_reg = -1; if (optimize && leaf_proc_ok) { for (i960_leaf_ret_reg = -1, i = 0; i < 8; i++) if (regs_ever_live[i] == 0) { i960_leaf_ret_reg = i; regs_ever_live[i] = 1; break; } } /* Do this after choosing the leaf return register, so it will be listed if one was chosen. */ fprintf (file, "\t# Function '%s'\n", name); fprintf (file, "\t# Registers used: "); for (i = 0, j = 0; i < FIRST_PSEUDO_REGISTER; i++) { if (regs_ever_live[i]) { fprintf (file, "%s%s ", reg_names[i], call_used_regs[i] ? "" : "*"); if (i > 15 && j == 0) { fprintf (file,"\n\t#\t\t "); j++; } } } fprintf (file, "\n"); if (i960_leaf_ret_reg >= 0) { /* Make it a leaf procedure. */ if (TREE_PUBLIC (fndecl)) fprintf (file,"\t.globl %s.lf\n", name); fprintf (file, "\t.leafproc\t_%s,%s.lf\n", name, name); fprintf (file, "_%s:\n", name); fprintf (file, "\tlda LR%d,g14\n", ret_label); fprintf (file, "%s.lf:\n", name); fprintf (file, "\tmov g14,g%d\n", i960_leaf_ret_reg); if (TARGET_C_SERIES) { fprintf (file, "\tlda 0,g14\n"); i960_last_insn_type = I_TYPE_MEM; } else { fprintf (file, "\tmov 0,g14\n"); i960_last_insn_type = I_TYPE_REG; } } else { ASM_OUTPUT_LABEL (file, name); i960_last_insn_type = I_TYPE_CTRL; }}/* Compute and return the frame size. */intcompute_frame_size (size) int size;{ int actual_fsize; int outgoing_args_size = current_function_outgoing_args_size + current_function_pretend_args_size; /* The STARTING_FRAME_OFFSET is totally hidden to us as far as size is concerned. */ actual_fsize = (size + 15) & -16; actual_fsize += (outgoing_args_size + 15) & -16; return actual_fsize;}/* Output code for the function prologue. */voidi960_function_prologue (file, size) FILE *file; unsigned int size;{ register int i, j, nr; int n_iregs = 0; int rsize = 0; int actual_fsize, offset; char tmpstr[1000]; /* -1 if reg must be saved on proc entry, 0 if available, 1 if saved somewhere. */ int regs[FIRST_PSEUDO_REGISTER]; for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) if (regs_ever_live[i] && ((! call_used_regs[i]) || (i > 7 && i < 12))) { regs[i] = -1; /* Count global registers that need saving. */ if (i < 16) n_iregs++; } else regs[i] = 0; epilogue_string[0] = '\0'; /* First look for local registers to save globals in. */ for (i = 0; i < 16; i++) { if (regs[i] == 0) continue; /* Start at r4, not r3. */ for (j = 20; j < 32; j++) { if (regs[j] != 0) continue; regs[i] = 1; regs[j] = -1; regs_ever_live[j] = 1; nr = 1; if (i <= 14 && i % 2 == 0 && j <= 30 && j % 2 == 0 && regs[i+1] != 0 && regs[j+1] == 0) { nr = 2; regs[i+1] = 1; regs[j+1] = -1; regs_ever_live[j+1] = 1; } if (nr == 2 && i <= 12 && i % 4 == 0 && j <= 28 && j % 4 == 0 && regs[i+2] != 0 && regs[j+2] == 0) { nr = 3; regs[i+2] = 1; regs[j+2] = -1; regs_ever_live[j+2] = 1; } if (nr == 3 && regs[i+3] != 0 && regs[j+3] == 0) { nr = 4; regs[i+3] = 1; regs[j+3] = -1; regs_ever_live[j+3] = 1; } fprintf (file, "\tmov%s %s,%s\n", ((nr == 4) ? "q" : (nr == 3) ? "t" : (nr == 2) ? "l" : ""), reg_names[i], reg_names[j]); sprintf (tmpstr, "\tmov%s %s,%s\n", ((nr == 4) ? "q" : (nr == 3) ? "t" : (nr == 2) ? "l" : ""), reg_names[j], reg_names[i]); strcat (epilogue_string, tmpstr); n_iregs -= nr; i += nr-1; break; } } /* N_iregs is now the number of global registers that haven't been saved yet. */ rsize = (n_iregs * 4); actual_fsize = compute_frame_size (size) + rsize;#if 0 /* ??? The 1.2.1 compiler does this also. This is meant to round the frame size up to the nearest multiple of 16. I don't know whether this is necessary, or even desirable. The frame pointer must be aligned, but the call instruction takes care of that. If we leave the stack pointer unaligned, we may save a little on dynamic stack allocation. And we don't lose, at least according to the i960CA manual. */ actual_fsize = (actual_fsize + 15) & ~0xF;#endif /* Allocate space for register save and locals. */ if (actual_fsize > 0) { if (actual_fsize < 32) fprintf (file, "\taddo %d,sp,sp\n", actual_fsize); else fprintf (file, "\tlda\t%d(sp),sp\n", actual_fsize); } /* Take hardware register save area created by the call instruction into account. */ offset = compute_frame_size (size) + 64; /* Save registers on stack if needed. */ for (i = 0, j = n_iregs; j > 0 && i < 16; i++) { if (regs[i] != -1) continue; nr = 1; if (i <= 14 && i % 2 == 0 && regs[i+1] == -1 && offset % 2 == 0) nr = 2; if (nr == 2 && i <= 12 && i % 4 == 0 && regs[i+2] == -1 && offset % 4 == 0) nr = 3; if (nr == 3 && regs[i+3] == -1) nr = 4; fprintf (file,"\tst%s %s,%d(fp)\n", ((nr == 4) ? "q" : (nr == 3) ? "t" : (nr == 2) ? "l" : ""), reg_names[i], offset); sprintf (tmpstr,"\tld%s %d(fp),%s\n", ((nr == 4) ? "q" : (nr == 3) ? "t" : (nr == 2) ? "l" : ""), offset, reg_names[i]); strcat (epilogue_string, tmpstr); i += nr-1; j -= nr; offset += nr * 4; } if (actual_fsize == 0 && size == 0 && rsize == 0) return; fprintf (file, "\t#Prologue stats:\n"); fprintf (file, "\t# Total Frame Size: %d bytes\n", actual_fsize); if (size) fprintf (file, "\t# Local Variable Size: %d bytes\n", size); if (rsize) fprintf (file, "\t# Register Save Size: %d regs, %d bytes\n", n_iregs, rsize); fprintf (file, "\t#End Prologue#\n");}/* Output code for the function epilogue. */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -