📄 i960.c
字号:
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 leafproc or tail calls if argblock, because argblock uses g14 implicitly. */ if (current_function_args_size != 0 || VARARGS_STDARG_FUNCTION (fndecl)) { 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[0] == '*' ? &name[1] : 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\t%s.lf\n", (name[0] == '*' ? &name[1] : name)); fprintf (file, "\t.leafproc\t"); assemble_name (file, name); fprintf (file, ",%s.lf\n", (name[0] == '*' ? &name[1] : name)); ASM_OUTPUT_LABEL (file, name); fprintf (file, "\tlda LR%d,g14\n", ret_label); fprintf (file, "%s.lf:\n", (name[0] == '*' ? &name[1] : 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; /* 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;}/* Here register group is range of registers which can be moved by one i960 instruction. */struct reg_group{ char start_reg; char length;};/* The following functions forms the biggest as possible register groups with registers in STATE. REGS contain states of the registers in range [start, finish_reg). The function returns the number of groups formed. */static inti960_form_reg_groups (start_reg, finish_reg, regs, state, reg_groups) int start_reg; int finish_reg; int *regs; int state; struct reg_group *reg_groups;{ int i; int nw = 0; for (i = start_reg; i < finish_reg; ) { if (regs [i] != state) { i++; continue; } else if (i % 2 != 0 || regs [i + 1] != state) reg_groups [nw].length = 1; else if (i % 4 != 0 || regs [i + 2] != state) reg_groups [nw].length = 2; else if (regs [i + 3] != state) reg_groups [nw].length = 3; else reg_groups [nw].length = 4; reg_groups [nw].start_reg = i; i += reg_groups [nw].length; nw++; } return nw;}/* We sort register winodws in descending order by length. */static inti960_reg_group_compare (group1, group2) void *group1; void *group2;{ struct reg_group *w1 = group1; struct reg_group *w2 = group2; if (w1->length > w2->length) return -1; else if (w1->length < w2->length) return 1; else return 0;}/* Split the first register group in REG_GROUPS on subgroups one of which will contain SUBGROUP_LENGTH registers. The function returns new number of winodws. */static inti960_split_reg_group (reg_groups, nw, subgroup_length) struct reg_group *reg_groups; int nw; int subgroup_length;{ if (subgroup_length < reg_groups->length - subgroup_length) /* This guarantees correct alignments of the two subgroups for i960 (see spliting for the group length 2, 3, 4). More generalized algorithm would require splitting the group more two subgroups. */ subgroup_length = reg_groups->length - subgroup_length; /* More generalized algorithm would require to try merging subgroups here. But in case i960 it always results in failure because of register group alignment. */ reg_groups[nw].length = reg_groups->length - subgroup_length; reg_groups[nw].start_reg = reg_groups->start_reg + subgroup_length; nw++; reg_groups->length = subgroup_length; qsort (reg_groups, nw, sizeof (struct reg_group), i960_reg_group_compare); return nw;}/* 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; int gnw, lnw; struct reg_group *g, *l; char tmpstr[1000]; /* -1 if reg must be saved on proc entry, 0 if available, 1 if saved somewhere. */ int regs[FIRST_PSEUDO_REGISTER]; /* All global registers (which must be saved) divided by groups. */ struct reg_group global_reg_groups [16]; /* All local registers (which are available) divided by groups. */ struct reg_group local_reg_groups [16]; for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) if (regs_ever_live[i] && ((! call_used_regs[i]) || (i > 7 && i < 12)) /* No need to save the static chain pointer. */ && ! (i == STATIC_CHAIN_REGNUM && current_function_needs_context)) { regs[i] = -1; /* Count global registers that need saving. */ if (i < 16) n_iregs++; } else regs[i] = 0; epilogue_string[0] = '\0'; if (profile_flag || profile_block_flag) { /* When profiling, we may use registers 20 to 27 to save arguments, so they can't be used here for saving globals. J is the number of argument registers the mcount call will save. */ for (j = 7; j >= 0 && ! regs_ever_live[j]; j--) ; for (i = 20; i <= j + 20; i++) regs[i] = -1; } gnw = i960_form_reg_groups (0, 16, regs, -1, global_reg_groups); lnw = i960_form_reg_groups (19, 32, regs, 0, local_reg_groups); qsort (global_reg_groups, gnw, sizeof (struct reg_group), i960_reg_group_compare); qsort (local_reg_groups, lnw, sizeof (struct reg_group), i960_reg_group_compare); for (g = global_reg_groups, l = local_reg_groups; lnw != 0 && gnw != 0;) { if (g->length == l->length) { fprintf (file, "\tmov%s %s,%s\n", ((g->length == 4) ? "q" : (g->length == 3) ? "t" : (g->length == 2) ? "l" : ""), reg_names[g->start_reg], reg_names[l->start_reg]); sprintf (tmpstr, "\tmov%s %s,%s\n", ((g->length == 4) ? "q" : (g->length == 3) ? "t" : (g->length == 2) ? "l" : ""), reg_names[l->start_reg], reg_names[g->start_reg]); strcat (epilogue_string, tmpstr); n_iregs -= g->length; for (i = 0; i < g->length; i++) { regs [i + g->start_reg] = 1; regs [i + l->start_reg] = -1; regs_ever_live [i + l->start_reg] = 1; } g++; l++; gnw--; lnw--; } else if (g->length > l->length) gnw = i960_split_reg_group (g, gnw, l->length); else lnw = i960_split_reg_group (l, lnw, g->length); } /* 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, but store them before the argument block area. */ offset = 64 + actual_fsize - compute_frame_size (0) - rsize; /* Save registers on stack if needed. */ /* ??? Is it worth to use the same algorithm as one for saving global registers in local registers? */ 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 profiler. */voidoutput_function_profiler (file, labelno) FILE *file; int labelno;{ /* The last used parameter register. */ int last_parm_reg; int i, j, increment; int varargs_stdarg_function = VARARGS_STDARG_FUNCTION (current_function_decl); /* Figure out the last used parameter register. The proper thing to do is to walk incoming args of the function. A function might have live parameter registers even if it has no incoming args. Note that we don't have to save parameter registers g8 to g11 because they are call preserved. */ /* See also output_function_prologue, which tries to use local registers for preserved call-saved global registers. */ for (last_parm_reg = 7; last_parm_reg >= 0 && ! regs_ever_live[last_parm_reg]; last_parm_reg--) ; /* Save parameter registers in regs r4 (20) to r11 (27). */ for (i = 0, j = 4; i <= last_parm_reg; i += increment, j += increment) { if (i % 4 == 0 && (last_parm_reg - i) >= 3) increment = 4; else if (i % 4 == 0 && (last_parm_reg - i) >= 2) increment = 3; else if (i % 2 == 0 && (last_parm_reg - i) >= 1) increment = 2; else increment = 1; fprintf (file, "\tmov%s g%d,r%d\n", (increment == 4 ? "q" : increment == 3 ? "t" : increment == 2 ? "l": ""), i, j); } /* If this function uses the arg pointer, then save it in r3 and then set it to zero. */ if (current_function_args_size != 0 || varargs_stdarg_function) fprintf (file, "\tmov g14,r3\n\tmov 0,g14\n"); /* Load location address into g0 and call mcount. */ fprintf (file, "\tlda\tLP%d,g0\n\tcallx\tmcount\n", labelno); /* If this function uses the arg pointer, restore it. */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -