📄 arc.c
字号:
const char *sp_str = reg_names[STACK_POINTER_REGNUM]; const char *fp_str = reg_names[FRAME_POINTER_REGNUM]; /* ??? There are lots of optimizations that can be done here. EG: Use fp to restore regs if it's closer. Maybe in time we'll do them all. For now, always restore regs from sp, but don't restore sp if we don't have to. */ if (!can_trust_sp_p) { if (!frame_pointer_needed) abort (); fprintf (file,"\tsub %s,%s,%d\t\t%s sp not trusted here\n", sp_str, fp_str, frame_size, ASM_COMMENT_START); } /* Restore any saved registers. */ arc_save_restore (file, sp_str, current_frame_info.reg_offset, /* The zeroing of these two bits is unnecessary, but leave this in for clarity. */ current_frame_info.gmask & ~(FRAME_POINTER_MASK | RETURN_ADDR_MASK), "ld"); if (MUST_SAVE_RETURN_ADDR) fprintf (file, "\tld %s,[%s,%d]\n", reg_names[RETURN_ADDR_REGNUM], frame_pointer_needed ? fp_str : sp_str, UNITS_PER_WORD + (frame_pointer_needed ? 0 : frame_size)); /* Keep track of how much of the stack pointer we've restored. It makes the following a lot more readable. */ restored = 0; fp_restored_p = 0; /* We try to emit the epilogue delay slot insn right after the load of the return address register so that it can execute with the stack intact. Secondly, loads are delayed. */ /* ??? If stack intactness is important, always emit now. */ if (MUST_SAVE_RETURN_ADDR && epilogue_delay != NULL_RTX) { final_scan_insn (XEXP (epilogue_delay, 0), file, 1, -2, 1, NULL); epilogue_delay = NULL_RTX; } if (frame_pointer_needed) { /* Try to restore the frame pointer in the delay slot. We can't, however, if any of these is true. */ if (epilogue_delay != NULL_RTX || !SMALL_INT (frame_size) || pretend_size || ARC_INTERRUPT_P (fn_type)) { /* Note that we restore fp and sp here! */ fprintf (file, "\tld.a %s,[%s,%d]\n", fp_str, sp_str, frame_size); restored += frame_size; fp_restored_p = 1; } } else if (!SMALL_INT (size /* frame_size + pretend_size */) || ARC_INTERRUPT_P (fn_type)) { fprintf (file, "\tadd %s,%s,%d\n", sp_str, sp_str, frame_size); restored += frame_size; } /* These must be done before the return insn because the delay slot does the final stack restore. */ if (ARC_INTERRUPT_P (fn_type)) { if (epilogue_delay) { final_scan_insn (XEXP (epilogue_delay, 0), file, 1, -2, 1, NULL); } } /* Emit the return instruction. */ { static const int regs[4] = { 0, RETURN_ADDR_REGNUM, ILINK1_REGNUM, ILINK2_REGNUM }; fprintf (file, "\tj.d %s\n", reg_names[regs[fn_type]]); } /* If the only register saved is the return address, we need a nop, unless we have an instruction to put into it. Otherwise we don't since reloading multiple registers doesn't reference the register being loaded. */ if (ARC_INTERRUPT_P (fn_type)) fprintf (file, "\tadd %s,%s,16\n", sp_str, sp_str); else if (epilogue_delay != NULL_RTX) { if (frame_pointer_needed && !fp_restored_p) abort (); if (restored < size) abort (); final_scan_insn (XEXP (epilogue_delay, 0), file, 1, -2, 1, NULL); } else if (frame_pointer_needed && !fp_restored_p) { if (!SMALL_INT (frame_size)) abort (); /* Note that we restore fp and sp here! */ fprintf (file, "\tld.a %s,[%s,%d]\n", fp_str, sp_str, frame_size); } else if (restored < size) { if (!SMALL_INT (size - restored)) abort (); fprintf (file, "\tadd %s,%s," HOST_WIDE_INT_PRINT_DEC "\n", sp_str, sp_str, size - restored); } else fprintf (file, "\tnop\n"); } /* Reset state info for each function. */ current_frame_info = zero_frame_info; arc_compute_function_type (NULL_TREE);}/* Define the number of delay slots needed for the function epilogue. Interrupt handlers can't have any epilogue delay slots (it's always needed for something else, I think). For normal functions, we have to worry about using call-saved regs as they'll be restored before the delay slot insn. Functions with non-empty frames already have enough choices for the epilogue delay slot so for now we only consider functions with empty frames. */intarc_delay_slots_for_epilogue (void){ if (arc_compute_function_type (current_function_decl) != ARC_FUNCTION_NORMAL) return 0; if (!current_frame_info.initialized) (void) arc_compute_frame_size (get_frame_size ()); if (current_frame_info.total_size == 0) return 1; return 0;}/* Return true if TRIAL is a valid insn for the epilogue delay slot. Any single length instruction which doesn't reference the stack or frame pointer or any call-saved register is OK. SLOT will always be 0. */intarc_eligible_for_epilogue_delay (rtx trial, int slot){ if (slot != 0) abort (); if (get_attr_length (trial) == 1 /* If registers where saved, presumably there's more than enough possibilities for the delay slot. The alternative is something more complicated (of course, if we expanded the epilogue as rtl this problem would go away). */ /* ??? Note that this will always be true since only functions with empty frames have epilogue delay slots. See arc_delay_slots_for_epilogue. */ && current_frame_info.gmask == 0 && ! reg_mentioned_p (stack_pointer_rtx, PATTERN (trial)) && ! reg_mentioned_p (frame_pointer_rtx, PATTERN (trial))) return 1; return 0;}/* PIC *//* Emit special PIC prologues and epilogues. */voidarc_finalize_pic (void){ /* nothing to do */}/* Return true if OP is a shift operator. */intshift_operator (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED){ switch (GET_CODE (op)) { case ASHIFTRT: case LSHIFTRT: case ASHIFT: return 1; default: return 0; }}/* Output the assembler code for doing a shift. We go to a bit of trouble to generate efficient code as the ARC only has single bit shifts. This is taken from the h8300 port. We only have one mode of shifting and can't access individual bytes like the h8300 can, so this is greatly simplified (at the expense of not generating hyper- efficient code). This function is not used if the variable shift insns are present. *//* ??? We assume the output operand is the same as operand 1. This can be optimized (deleted) in the case of 1 bit shifts. *//* ??? We use the loop register here. We don't use it elsewhere (yet) and using it here will give us a chance to play with it. */const char *output_shift (rtx *operands){ rtx shift = operands[3]; enum machine_mode mode = GET_MODE (shift); enum rtx_code code = GET_CODE (shift); const char *shift_one; if (mode != SImode) abort (); switch (code) { case ASHIFT: shift_one = "asl %0,%0"; break; case ASHIFTRT: shift_one = "asr %0,%0"; break; case LSHIFTRT: shift_one = "lsr %0,%0"; break; default: abort (); } if (GET_CODE (operands[2]) != CONST_INT) { if (optimize) output_asm_insn ("mov lp_count,%2", operands); else output_asm_insn ("mov %4,%2", operands); goto shiftloop; } else { int n = INTVAL (operands[2]); /* If the count is negative, make it 0. */ if (n < 0) n = 0; /* If the count is too big, truncate it. ANSI says shifts of GET_MODE_BITSIZE are undefined - we choose to do the intuitive thing. */ else if (n > GET_MODE_BITSIZE (mode)) n = GET_MODE_BITSIZE (mode); /* First see if we can do them inline. */ if (n <= 8) { while (--n >= 0) output_asm_insn (shift_one, operands); } /* See if we can use a rotate/and. */ else if (n == BITS_PER_WORD - 1) { switch (code) { case ASHIFT : output_asm_insn ("and %0,%0,1\n\tror %0,%0", operands); break; case ASHIFTRT : /* The ARC doesn't have a rol insn. Use something else. */ output_asm_insn ("asl.f 0,%0\n\tsbc %0,0,0", operands); break; case LSHIFTRT : /* The ARC doesn't have a rol insn. Use something else. */ output_asm_insn ("asl.f 0,%0\n\tadc %0,0,0", operands); break; default: break; } } /* Must loop. */ else { char buf[100]; if (optimize) output_asm_insn ("mov lp_count,%c2", operands); else output_asm_insn ("mov %4,%c2", operands); shiftloop: if (optimize) { if (flag_pic) sprintf (buf, "lr %%4,[status]\n\tadd %%4,%%4,6\t%s single insn loop start", ASM_COMMENT_START); else sprintf (buf, "mov %%4,%%%%st(1f)\t%s (single insn loop start) >> 2", ASM_COMMENT_START); output_asm_insn (buf, operands); output_asm_insn ("sr %4,[lp_start]", operands); output_asm_insn ("add %4,%4,1", operands); output_asm_insn ("sr %4,[lp_end]", operands); output_asm_insn ("nop\n\tnop", operands); if (flag_pic) fprintf (asm_out_file, "\t%s single insn loop\n", ASM_COMMENT_START); else fprintf (asm_out_file, "1:\t%s single insn loop\n", ASM_COMMENT_START); output_asm_insn (shift_one, operands); } else { fprintf (asm_out_file, "1:\t%s begin shift loop\n", ASM_COMMENT_START); output_asm_insn ("sub.f %4,%4,1", operands); output_asm_insn ("nop", operands); output_asm_insn ("bn.nd 2f", operands); output_asm_insn (shift_one, operands); output_asm_insn ("b.nd 1b", operands); fprintf (asm_out_file, "2:\t%s end shift loop\n", ASM_COMMENT_START); } } } return "";}/* Nested function support. *//* Emit RTL insns to initialize the variable parts of a trampoline. FNADDR is an RTX for the address of the function's pure code. CXT is an RTX for the static chain value for the function. */voidarc_initialize_trampoline (rtx tramp ATTRIBUTE_UNUSED, rtx fnaddr ATTRIBUTE_UNUSED, rtx cxt ATTRIBUTE_UNUSED){}/* Set the cpu type and print out other fancy things, at the top of the file. */static voidarc_file_start (void){ default_file_start (); fprintf (asm_out_file, "\t.cpu %s\n", arc_cpu_string);}/* Print operand X (an rtx) in assembler syntax to file FILE. CODE is a letter or dot (`z' in `%z0') or 0 if no letter was specified. For `%' followed by punctuation, CODE is the punctuation and X is null. */voidarc_print_operand (FILE *file, rtx x, int code){ switch (code) { case '#' : /* Conditional branches. For now these are equivalent. */ case '*' : /* Unconditional branches. Output the appropriate delay slot suffix. */ if (!final_sequence || XVECLEN (final_sequence, 0) == 1) { /* There's nothing in the delay slot. */ fputs (".nd", file); } else { rtx jump = XVECEXP (final_sequence, 0, 0); rtx delay = XVECEXP (final_sequence, 0, 1); if (INSN_ANNULLED_BRANCH_P (jump)) fputs (INSN_FROM_TARGET_P (delay) ? ".jd" : ".nd", file); else fputs (".d", file); } return; case '?' : /* with leading "." */ case '!' : /* without leading "." */ /* This insn can be conditionally executed. See if the ccfsm machinery says it should be conditionalized. */ if (arc_ccfsm_state == 3 || arc_ccfsm_state == 4) { /* Is this insn in a delay slot? */ if (final_sequence && XVECLEN (final_sequence, 0) == 2) { rtx insn = XVECEXP (final_sequence, 0, 1); /* If the insn is annulled and is from the target path, we need to inverse the condition test. */ if (INSN_ANNULLED_BRANCH_P (insn)) { if (INSN_FROM_TARGET_P (insn)) fprintf (file, "%s%s", code == '?' ? "." : "", arc_condition_codes[ARC_INVERSE_CONDITION_CODE (arc_ccfsm_current_cc)]); else fprintf (file, "%s%s", code == '?' ? "." : "", arc_condition_codes[arc_ccfsm_current_cc]); } else { /* This insn is executed for either path, so don't conditionalize it at all. */ ; /* nothing to do */ } } else { /* This insn isn't in a delay slot. */ fprintf (file, "%s%s", code == '?' ? "." : "", arc_condition_codes[arc_ccfsm_current_cc]); } } return; case '~' : /* Output a nop if we're between a set of the condition codes, and a conditional branch. */ if (last_insn_set_cc_p) fputs ("nop\n\t", file); return; case 'd' : fputs (arc_condition_codes[get_arc_condition_code (x)], file); return; case 'D' : fputs (arc_condition_codes[ARC_INVERSE_CONDITION_CODE (get_arc_condition_code (x))], file); return; case 'R' : /* Write second word of DImode or DFmode reference,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -