📄 arc.c
字号:
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 (){ 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 (trial, slot) 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 (){ /* nothing to do */}/* Return true if OP is a shift operator. */intshift_operator (op, mode) rtx op; enum machine_mode mode;{ 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. */char *output_shift (operands) rtx *operands;{ static int loopend_lab; rtx shift = operands[3]; enum machine_mode mode = GET_MODE (shift); enum rtx_code code = GET_CODE (shift); 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; } } /* 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 ("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) asm_fprintf (asm_out_file, "\t%s single insn loop\n", ASM_COMMENT_START); else asm_fprintf (asm_out_file, "1:\t%s single insn loop\n", ASM_COMMENT_START); output_asm_insn (shift_one, operands); } else { asm_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); asm_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 (tramp, fnaddr, cxt) rtx tramp, fnaddr, cxt;{}/* Set the cpu type and print out other fancy things, at the top of the file. */voidarc_asm_file_start (file) FILE *file;{ fprintf (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, x, code) 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, register or memory. */ if (GET_CODE (x) == REG) fputs (reg_names[REGNO (x)+1], file); else if (GET_CODE (x) == MEM) { fputc ('[', file); /* Handle possible auto-increment. Since it is pre-increment and we have already done it, we can just use an offset of four. */ /* ??? This is taken from rs6000.c I think. I don't think it is currently necessary, but keep it around. */ if (GET_CODE (XEXP (x, 0)) == PRE_INC || GET_CODE (XEXP (x, 0)) == PRE_DEC) output_address (plus_constant (XEXP (XEXP (x, 0), 0), 4)); else output_address (plus_constant (XEXP (x, 0), 4)); fputc (']', file); } else output_operand_lossage ("invalid operand to %R code"); return; case 'S' : if ((GET_CODE (x) == SYMBOL_REF && SYMBOL_REF_FLAG (x)) || GET_CODE (x) == LABEL_REF) { fprintf (file, "%%st("); output_addr_const (file, x); fprintf (file, ")"); return; } break; case 'H' : case 'L' : if (GET_CODE (x) == REG) { /* L = least significant word, H = most significant word */ if ((TARGET_BIG_ENDIAN != 0) ^ (code == 'L')) fputs (reg_names[REGNO (x)], file); else fputs (reg_names[REGNO (x)+1], file); } else if (GET_CODE (x) == CONST_INT || GET_CODE (x) == CONST_DOUBLE) { rtx first, second; split_double (x, &first, &second); fprintf (file, "0x%08lx", code == 'L' ? INTVAL (first) : INTVAL (second)); } else output_operand_lossage ("invalid operand to %H/%L code"); return; case 'A' : { REAL_VALUE_TYPE d; char str[30]; if (GET_CODE (x) != CONST_DOUBLE || GET_MODE_CLASS (GET_MODE (x)) != MODE_FLOAT) abort (); REAL_VALUE_FROM_CONST_DOUBLE (d, x); REAL_VALUE_TO_DECIMAL (d, "%.20e", str); fprintf (file, "%s", str); return; } case 'U' : /* Output a load/store with update indicator if appropriate. */ if (GET_CODE (x) == MEM) { if (GET_CODE (XEXP (x, 0)) == PRE_INC || GET_CODE (XEXP (x, 0)) == PRE_DEC) fputs (".a", file); } else output_operand_lossage ("invalid operand to %U code"); return; case 'V' : /* Output cache bypass indicator for a load/store insn. Volatile memory refs are defined to use the cache bypass mechanism. */ if (GET_CODE (x) == MEM) { if (MEM_VOLATILE_P (x)) fputs (".di", file); } else output_operand_lossage ("invalid operand to %V code"); return; case 0 : /* Do nothing special. */ break; default : /* Unknown flag. */ output_operand_lossage ("invalid operand output code"); } switch (GET_CODE (x)) { case REG : fputs (reg_names[REGNO (x)], file); break; case MEM : fputc ('[', file); if (GET_CODE (XEXP (x, 0)) == PRE_INC) output_address (plus_constant (XEXP (XEXP (x, 0), 0), GET_MODE_SIZE (GET_MODE (x)))); else if (GET_CODE (XEXP (x, 0)) == PRE_DEC) output_address (plus_constant (XEXP (XEXP (x, 0), 0), - GET_MODE_SIZE (GET_MODE (x)))); else output_address (XEXP (x, 0)); fputc (']', file); break; case CONST_DOUBLE : /* We handle SFmode constants here as output_addr_const doesn't. */ if (GET_MODE (x) == SFmode) { REAL_VALUE_TYPE d; long l; REAL_VALUE_FROM_CONST_DOUBLE (d, x); REAL_VALUE_TO_TARGET_SINGLE (d, l); fprintf (file, "0x%08lx", l); break; } /* Fall through. Let output_addr_const deal with it. */ default : output_addr_const (file, x); break; }}/* Print a memory address as an operand to reference that memory location. */void
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -