📄 m68k.c
字号:
reg_names[FRAME_POINTER_REGNUM]); else if (fsize) { if (fsize + 4 < 0x8000) {#ifdef MOTOROLA asm_fprintf (stream, "\tadd.w %0I%d,%Rsp\n", fsize + 4);#else asm_fprintf (stream, "\taddw %0I%d,%Rsp\n", fsize + 4);#endif } else {#ifdef MOTOROLA asm_fprintf (stream, "\tadd.l %0I%d,%Rsp\n", fsize + 4);#else asm_fprintf (stream, "\taddl %0I%d,%Rsp\n", fsize + 4);#endif } } if (current_function_pops_args) asm_fprintf (stream, "\trtd %0I%d\n", current_function_pops_args); else fprintf (stream, "\trts\n");}/* Similar to general_operand, but exclude stack_pointer_rtx. */intnot_sp_operand (op, mode) register rtx op; enum machine_mode mode;{ return op != stack_pointer_rtx && general_operand (op, mode);}/* Return TRUE if X is a valid comparison operator for the dbcc instruction. Note it rejects floating point comparison operators. (In the future we could use Fdbcc). It also rejects some comparisons when CC_NO_OVERFLOW is set. */ intvalid_dbcc_comparison_p (x, mode) rtx x; enum machine_mode mode;{ /* We could add support for these in the future */ if (cc_prev_status.flags & CC_IN_68881) return 0; switch (GET_CODE (x)) { case EQ: case NE: case GTU: case LTU: case GEU: case LEU: return 1; /* Reject some when CC_NO_OVERFLOW is set. This may be over conservative */ case GT: case LT: case GE: case LE: return ! (cc_prev_status.flags & CC_NO_OVERFLOW); default: return 0; }}/* Output a dbCC; jCC sequence. Note we do not handle the floating point version of this sequence (Fdbcc). We also do not handle alternative conditions when CC_NO_OVERFLOW is set. It is assumed that valid_dbcc_comparison_p will kick those out before we get here. */output_dbcc_and_branch (operands) rtx *operands;{ switch (GET_CODE (operands[3])) { case EQ:#ifdef MOTOROLA output_asm_insn ("dbeq %0,%l1\n\tjbeq %l2", operands);#else output_asm_insn ("dbeq %0,%l1\n\tjeq %l2", operands);#endif break; case NE:#ifdef MOTOROLA output_asm_insn ("dbne %0,%l1\n\tjbne %l2", operands);#else output_asm_insn ("dbne %0,%l1\n\tjne %l2", operands);#endif break; case GT:#ifdef MOTOROLA output_asm_insn ("dbgt %0,%l1\n\tjbgt %l2", operands);#else output_asm_insn ("dbgt %0,%l1\n\tjgt %l2", operands);#endif break; case GTU:#ifdef MOTOROLA output_asm_insn ("dbhi %0,%l1\n\tjbhi %l2", operands);#else output_asm_insn ("dbhi %0,%l1\n\tjhi %l2", operands);#endif break; case LT:#ifdef MOTOROLA output_asm_insn ("dblt %0,%l1\n\tjblt %l2", operands);#else output_asm_insn ("dblt %0,%l1\n\tjlt %l2", operands);#endif break; case LTU:#ifdef MOTOROLA output_asm_insn ("dbcs %0,%l1\n\tjbcs %l2", operands);#else output_asm_insn ("dbcs %0,%l1\n\tjcs %l2", operands);#endif break; case GE:#ifdef MOTOROLA output_asm_insn ("dbge %0,%l1\n\tjbge %l2", operands);#else output_asm_insn ("dbge %0,%l1\n\tjge %l2", operands);#endif break; case GEU:#ifdef MOTOROLA output_asm_insn ("dbcc %0,%l1\n\tjbcc %l2", operands);#else output_asm_insn ("dbcc %0,%l1\n\tjcc %l2", operands);#endif break; case LE:#ifdef MOTOROLA output_asm_insn ("dble %0,%l1\n\tjble %l2", operands);#else output_asm_insn ("dble %0,%l1\n\tjle %l2", operands);#endif break; case LEU:#ifdef MOTOROLA output_asm_insn ("dbls %0,%l1\n\tjbls %l2", operands);#else output_asm_insn ("dbls %0,%l1\n\tjls %l2", operands);#endif break; default: abort (); } /* If the decrement is to be done in SImode, then we have to compensate for the fact that dbcc decrements in HImode. */ switch (GET_MODE (operands[0])) { case SImode:#ifdef MOTOROLA output_asm_insn ("clr%.w %0\n\tsubq%.l %#1,%0\n\tjbpl %l1", operands);#else output_asm_insn ("clr%.w %0\n\tsubq%.l %#1,%0\n\tjpl %l1", operands);#endif break; case HImode: break; default: abort (); }}char *output_btst (operands, countop, dataop, insn, signpos) rtx *operands; rtx countop, dataop; rtx insn; int signpos;{ operands[0] = countop; operands[1] = dataop; if (GET_CODE (countop) == CONST_INT) { register int count = INTVAL (countop); /* If COUNT is bigger than size of storage unit in use, advance to the containing unit of same size. */ if (count > signpos) { int offset = (count & ~signpos) / 8; count = count & signpos; operands[1] = dataop = adj_offsettable_operand (dataop, offset); } if (count == signpos) cc_status.flags = CC_NOT_POSITIVE | CC_Z_IN_NOT_N; else cc_status.flags = CC_NOT_NEGATIVE | CC_Z_IN_NOT_N; /* These three statements used to use next_insns_test_no... but it appears that this should do the same job. */ if (count == 31 && next_insn_tests_no_inequality (insn)) return "tst%.l %1"; if (count == 15 && next_insn_tests_no_inequality (insn)) return "tst%.w %1"; if (count == 7 && next_insn_tests_no_inequality (insn)) return "tst%.b %1"; cc_status.flags = CC_NOT_NEGATIVE; } return "btst %0,%1";}/* Returns 1 if OP is either a symbol reference or a sum of a symbol reference and a constant. */intsymbolic_operand (op, mode) register rtx op; enum machine_mode mode;{ switch (GET_CODE (op)) { case SYMBOL_REF: case LABEL_REF: return 1; case CONST: op = XEXP (op, 0); return ((GET_CODE (XEXP (op, 0)) == SYMBOL_REF || GET_CODE (XEXP (op, 0)) == LABEL_REF) && GET_CODE (XEXP (op, 1)) == CONST_INT);#if 0 /* Deleted, with corresponding change in m68k.h, so as to fit the specs. No CONST_DOUBLE is ever symbolic. */ case CONST_DOUBLE: return GET_MODE (op) == mode;#endif default: return 0; }}/* Legitimize PIC addresses. If the address is already position-independent, we return ORIG. Newly generated position-independent addresses go to REG. If we need more than one register, we lose. An address is legitimized by making an indirect reference through the Global Offset Table with the name of the symbol used as an offset. The assembler and linker are responsible for placing the address of the symbol in the GOT. The function prologue is responsible for initializing a5 to the starting address of the GOT. The assembler is also responsible for translating a symbol name into a constant displacement from the start of the GOT. A quick example may make things a little clearer: When not generating PIC code to store the value 12345 into _foo we would generate the following code: movel #12345, _foo When generating PIC two transformations are made. First, the compiler loads the address of foo into a register. So the first transformation makes: lea _foo, a0 movel #12345, a0@ The code in movsi will intercept the lea instruction and call this routine which will transform the instructions into: movel a5@(_foo:w), a0 movel #12345, a0@ That (in a nutshell) is how *all* symbol and label references are handled. */rtxlegitimize_pic_address (orig, mode, reg) rtx orig, reg; enum machine_mode mode;{ rtx pic_ref = orig; /* First handle a simple SYMBOL_REF or LABEL_REF */ if (GET_CODE (orig) == SYMBOL_REF || GET_CODE (orig) == LABEL_REF) { if (reg == 0) abort (); pic_ref = gen_rtx (MEM, Pmode, gen_rtx (PLUS, Pmode, pic_offset_table_rtx, orig)); current_function_uses_pic_offset_table = 1; RTX_UNCHANGING_P (pic_ref) = 1; emit_move_insn (reg, pic_ref); return reg; } else if (GET_CODE (orig) == CONST) { rtx base, offset; /* Make sure this is CONST has not already been legitimized */ if (GET_CODE (XEXP (orig, 0)) == PLUS && XEXP (XEXP (orig, 0), 0) == pic_offset_table_rtx) return orig; if (reg == 0) abort (); /* legitimize both operands of the PLUS */ if (GET_CODE (XEXP (orig, 0)) == PLUS) { base = legitimize_pic_address (XEXP (XEXP (orig, 0), 0), Pmode, reg); orig = legitimize_pic_address (XEXP (XEXP (orig, 0), 1), Pmode, base == reg ? 0 : reg); } else abort (); if (GET_CODE (orig) == CONST_INT) return plus_constant_for_output (base, INTVAL (orig)); pic_ref = gen_rtx (PLUS, Pmode, base, orig); /* Likewise, should we set special REG_NOTEs here? */ } return pic_ref;}/* Return the best assembler insn template for moving operands[1] into operands[0] as a fullword. */static char *singlemove_string (operands) rtx *operands;{#ifdef SUPPORT_SUN_FPA if (FPA_REG_P (operands[0]) || FPA_REG_P (operands[1])) return "fpmoves %1,%0";#endif if (DATA_REG_P (operands[0]) && GET_CODE (operands[1]) == CONST_INT && INTVAL (operands[1]) < 128 && INTVAL (operands[1]) >= -128) {#if defined (MOTOROLA) && !defined (CRDS) return "moveq%.l %1,%0";#else return "moveq %1,%0";#endif } if (operands[1] != const0_rtx) return "move%.l %1,%0"; if (! ADDRESS_REG_P (operands[0])) return "clr%.l %0"; return "sub%.l %0,%0";}/* Output assembler code to perform a doubleword move insn with operands OPERANDS. */char *output_move_double (operands) rtx *operands;{ enum { REGOP, OFFSOP, MEMOP, PUSHOP, POPOP, CNSTOP, RNDOP } optype0, optype1; rtx latehalf[2]; rtx addreg0 = 0, addreg1 = 0; /* First classify both operands. */ if (REG_P (operands[0])) optype0 = REGOP; else if (offsettable_memref_p (operands[0])) optype0 = OFFSOP; else if (GET_CODE (XEXP (operands[0], 0)) == POST_INC) optype0 = POPOP; else if (GET_CODE (XEXP (operands[0], 0)) == PRE_DEC) optype0 = PUSHOP; else if (GET_CODE (operands[0]) == MEM) optype0 = MEMOP; else optype0 = RNDOP; if (REG_P (operands[1])) optype1 = REGOP; else if (CONSTANT_P (operands[1])) optype1 = CNSTOP; else if (offsettable_memref_p (operands[1])) optype1 = OFFSOP; else if (GET_CODE (XEXP (operands[1], 0)) == POST_INC) optype1 = POPOP; else if (GET_CODE (XEXP (operands[1], 0)) == PRE_DEC) optype1 = PUSHOP; else if (GET_CODE (operands[1]) == MEM) optype1 = MEMOP; else optype1 = RNDOP; /* Check for the cases that the operand constraints are not supposed to allow to happen. Abort if we get one, because generating code for these cases is painful. */ if (optype0 == RNDOP || optype1 == RNDOP) abort (); /* If one operand is decrementing and one is incrementing decrement the former register explicitly and change that operand into ordinary indexing. */ if (optype0 == PUSHOP && optype1 == POPOP) { operands[0] = XEXP (XEXP (operands[0], 0), 0); output_asm_insn ("subq%.l %#8,%0", operands); operands[0] = gen_rtx (MEM, DImode, operands[0]); optype0 = OFFSOP; } if (optype0 == POPOP && optype1 == PUSHOP) { operands[1] = XEXP (XEXP (operands[1], 0), 0); output_asm_insn ("subq%.l %#8,%1", operands); operands[1] = gen_rtx (MEM, DImode, operands[1]); optype1 = OFFSOP; } /* If an operand is an unoffsettable memory ref, find a register we can increment temporarily to make it refer to the second word. */ if (optype0 == MEMOP) addreg0 = find_addr_reg (XEXP (operands[0], 0)); if (optype1 == MEMOP) addreg1 = find_addr_reg (XEXP (operands[1], 0)); /* Ok, we can do one word at a time. Normally we do the low-numbered word first, but if either operand is autodecrementing then we do the high-numbered word first. In either case, set up in LATEHALF the operands to use for the high-numbered word and in some cases alter the operands in OPERANDS to be suitable for the low-numbered word. */ if (optype0 == REGOP) latehalf[0] = gen_rtx (REG, SImode, REGNO (operands[0]) + 1); else if (optype0 == OFFSOP) latehalf[0] = adj_offsettable_operand (operands[0], 4); else latehalf[0] = operands[0]; if (optype1 == REGOP) latehalf[1] = gen_rtx (REG, SImode, REGNO (operands[1]) + 1); else if (optype1 == OFFSOP) latehalf[1] = adj_offsettable_operand (operands[1], 4); else if (optype1 == CNSTOP) split_double (operands[1], &operands[1], &latehalf[1]); else latehalf[1] = operands[1]; /* If insn is effectively movd N(sp),-(sp) then we will do the high word first. We should use the adjusted operand 1 (which is N+4(sp)) for the low word as well, to compensate for the first decrement of sp. */ if (optype0 == PUSHOP && REGNO (XEXP (XEXP (operands[0], 0), 0)) == STACK_POINTER_REGNUM && reg_overlap_mentioned_p (stack_pointer_rtx, operands[1])) operands[1] = latehalf[1]; /* If one or both operands autodecrementing, do the two words, high-numbered first. */ /* Likewise, the first move would clobber the source of the second one, do them in the other order. This happens only for registers; such overlap can't happen in memory unless the user explicitly
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -