📄 pdp11.c
字号:
such overlap can't happen in memory unless the user explicitly sets it up, and that is an undefined circumstance. */ if (optype0 == PUSHOP || optype1 == PUSHOP || (optype0 == REGOP && optype1 == REGOP && REGNO (operands[0]) == REGNO (latehalf[1]))) { /* Make any unoffsettable addresses point at high-numbered word. */ if (addreg0) output_asm_insn ("add $2,%0", &addreg0); if (addreg1) output_asm_insn ("add $2,%0", &addreg1); /* Do that word. */ output_asm_insn (singlemove_string (latehalf), latehalf); /* Undo the adds we just did. */ if (addreg0) output_asm_insn ("sub $2,%0", &addreg0); if (addreg1) output_asm_insn ("sub $2,%0", &addreg1); /* Do low-numbered word. */ return singlemove_string (operands); } /* Normal case: do the two words, low-numbered first. */ output_asm_insn (singlemove_string (operands), operands); /* Make any unoffsettable addresses point at high-numbered word. */ if (addreg0) output_asm_insn ("add $2,%0", &addreg0); if (addreg1) output_asm_insn ("add $2,%0", &addreg1); /* Do that word. */ output_asm_insn (singlemove_string (latehalf), latehalf); /* Undo the adds we just did. */ if (addreg0) output_asm_insn ("sub $2,%0", &addreg0); if (addreg1) output_asm_insn ("sub $2,%0", &addreg1); return "";}/* Output assembler code to perform a quadword move insn with operands OPERANDS. */const char *output_move_quad (rtx *operands){ enum { REGOP, OFFSOP, MEMOP, PUSHOP, POPOP, CNSTOP, RNDOP } optype0, optype1; rtx latehalf[2]; rtx addreg0 = 0, addreg1 = 0; output_asm_insn(";/* movdi/df: %1 -> %0 */", 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]) || GET_CODE (operands[1]) == CONST_DOUBLE) 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 (); /* check if we move a CPU reg to an FPU reg, or vice versa! */ if (optype0 == REGOP && optype1 == REGOP) /* bogus - 64 bit cannot reside in CPU! */ if (CPU_REG_P(REGNO(operands[0])) || CPU_REG_P (REGNO(operands[1]))) abort(); if (optype0 == REGOP || optype1 == REGOP) { /* check for use of clrd???? if you ever allow ac4 and ac5 (now we require secondary load) you must check whether you want to load into them or store from them - then dump ac0 into $help$ movce ac4/5 to ac0, do the store from ac0, and restore ac0 - if you can find an unused ac[0-3], use that and you save a store and a load!*/ if (FPU_REG_P(REGNO(operands[0]))) { if (GET_CODE(operands[1]) == CONST_DOUBLE) { REAL_VALUE_TYPE r; REAL_VALUE_FROM_CONST_DOUBLE (r, operands[1]); if (REAL_VALUES_EQUAL (r, dconst0)) return "{clrd|clrf} %0"; } return "{ldd|movf} %1, %0"; } if (FPU_REG_P(REGNO(operands[1]))) return "{std|movf} %1, %0"; } /* 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 ("sub $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 ("sub $8,%1", operands); operands[1] = gen_rtx_MEM (SImode, 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]) + 2); else if (optype0 == OFFSOP) latehalf[0] = adjust_address (operands[0], SImode, 4); else latehalf[0] = operands[0]; if (optype1 == REGOP) latehalf[1] = gen_rtx_REG (SImode, REGNO (operands[1]) + 2); else if (optype1 == OFFSOP) latehalf[1] = adjust_address (operands[1], SImode, 4); else if (optype1 == CNSTOP) { if (GET_CODE (operands[1]) == CONST_DOUBLE) { REAL_VALUE_TYPE r; long dval[2]; REAL_VALUE_FROM_CONST_DOUBLE (r, operands[1]); REAL_VALUE_TO_TARGET_DOUBLE (r, dval); latehalf[1] = GEN_INT (dval[1]); operands[1] = GEN_INT (dval[0]); } else if (GET_CODE(operands[1]) == CONST_INT) { latehalf[1] = const0_rtx; } else abort(); } 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 sets it up, and that is an undefined circumstance. */ if (optype0 == PUSHOP || optype1 == PUSHOP || (optype0 == REGOP && optype1 == REGOP && REGNO (operands[0]) == REGNO (latehalf[1]))) { /* Make any unoffsettable addresses point at high-numbered word. */ if (addreg0) output_asm_insn ("add $4,%0", &addreg0); if (addreg1) output_asm_insn ("add $4,%0", &addreg1); /* Do that word. */ output_asm_insn(output_move_double(latehalf), latehalf); /* Undo the adds we just did. */ if (addreg0) output_asm_insn ("sub $4,%0", &addreg0); if (addreg1) output_asm_insn ("sub $4,%0", &addreg1); /* Do low-numbered word. */ return output_move_double (operands); } /* Normal case: do the two words, low-numbered first. */ output_asm_insn (output_move_double (operands), operands); /* Make any unoffsettable addresses point at high-numbered word. */ if (addreg0) output_asm_insn ("add $4,%0", &addreg0); if (addreg1) output_asm_insn ("add $4,%0", &addreg1); /* Do that word. */ output_asm_insn (output_move_double (latehalf), latehalf); /* Undo the adds we just did. */ if (addreg0) output_asm_insn ("sub $4,%0", &addreg0); if (addreg1) output_asm_insn ("sub $4,%0", &addreg1); return "";}/* Return a REG that occurs in ADDR with coefficient 1. ADDR can be effectively incremented by incrementing REG. */static rtxfind_addr_reg (rtx addr){ while (GET_CODE (addr) == PLUS) { if (GET_CODE (XEXP (addr, 0)) == REG) addr = XEXP (addr, 0); if (GET_CODE (XEXP (addr, 1)) == REG) addr = XEXP (addr, 1); if (CONSTANT_P (XEXP (addr, 0))) addr = XEXP (addr, 1); if (CONSTANT_P (XEXP (addr, 1))) addr = XEXP (addr, 0); } if (GET_CODE (addr) == REG) return addr; return 0;}/* Output an ascii string. */voidoutput_ascii (FILE *file, const char *p, int size){ int i; /* This used to output .byte "string", which doesn't work with the UNIX assembler and I think not with DEC ones either. */ fprintf (file, "\t.byte "); for (i = 0; i < size; i++) { register int c = p[i]; if (c < 0) c += 256; fprintf (file, "%#o", c); if (i < size - 1) putc (',', file); } putc ('\n', file);}/* --- stole from out-vax, needs changes */voidprint_operand_address (FILE *file, register rtx addr){ register rtx reg1, reg2, breg, ireg; rtx offset; retry: switch (GET_CODE (addr)) { case MEM: if (TARGET_UNIX_ASM) fprintf (file, "*"); else fprintf (file, "@"); addr = XEXP (addr, 0); goto retry; case REG: fprintf (file, "(%s)", reg_names[REGNO (addr)]); break; case PRE_MODIFY: case PRE_DEC: fprintf (file, "-(%s)", reg_names[REGNO (XEXP (addr, 0))]); break; case POST_MODIFY: case POST_INC: fprintf (file, "(%s)+", reg_names[REGNO (XEXP (addr, 0))]); break; case PLUS: reg1 = 0; reg2 = 0; ireg = 0; breg = 0; offset = 0; if (CONSTANT_ADDRESS_P (XEXP (addr, 0)) || GET_CODE (XEXP (addr, 0)) == MEM) { offset = XEXP (addr, 0); addr = XEXP (addr, 1); } else if (CONSTANT_ADDRESS_P (XEXP (addr, 1)) || GET_CODE (XEXP (addr, 1)) == MEM) { offset = XEXP (addr, 1); addr = XEXP (addr, 0); } if (GET_CODE (addr) != PLUS) ; else if (GET_CODE (XEXP (addr, 0)) == MULT) { reg1 = XEXP (addr, 0); addr = XEXP (addr, 1); } else if (GET_CODE (XEXP (addr, 1)) == MULT) { reg1 = XEXP (addr, 1); addr = XEXP (addr, 0); } else if (GET_CODE (XEXP (addr, 0)) == REG) { reg1 = XEXP (addr, 0); addr = XEXP (addr, 1); } else if (GET_CODE (XEXP (addr, 1)) == REG) { reg1 = XEXP (addr, 1); addr = XEXP (addr, 0); } if (GET_CODE (addr) == REG || GET_CODE (addr) == MULT) { if (reg1 == 0) reg1 = addr; else reg2 = addr; addr = 0; } if (offset != 0) { if (addr != 0) abort (); addr = offset; } if (reg1 != 0 && GET_CODE (reg1) == MULT) { breg = reg2; ireg = reg1; } else if (reg2 != 0 && GET_CODE (reg2) == MULT) { breg = reg1; ireg = reg2; } else if (reg2 != 0 || GET_CODE (addr) == MEM) { breg = reg2; ireg = reg1; } else { breg = reg1; ireg = reg2; } if (addr != 0) output_address (addr); if (breg != 0) { if (GET_CODE (breg) != REG) abort (); fprintf (file, "(%s)", reg_names[REGNO (breg)]); } if (ireg != 0) { if (GET_CODE (ireg) == MULT) ireg = XEXP (ireg, 0); if (GET_CODE (ireg) != REG) abort (); abort(); fprintf (file, "[%s]", reg_names[REGNO (ireg)]); } break; default: output_addr_const_pdp11 (file, addr); }}/* Target hook to assemble integer objects. We need to use the pdp-specific version of output_addr_const. */static boolpdp11_assemble_integer (rtx x, unsigned int size, int aligned_p){ if (aligned_p) switch (size) { case 1: fprintf (asm_out_file, "\t.byte\t"); output_addr_const_pdp11 (asm_out_file, x); fprintf (asm_out_file, " /* char */\n"); return true; case 2: fprintf (asm_out_file, TARGET_UNIX_ASM ? "\t" : "\t.word\t"); output_addr_const_pdp11 (asm_out_file, x); fprintf (asm_out_file, " /* short */\n"); return true; } return default_assemble_integer (x, size, aligned_p);}/* register move costs, indexed by regs */static const int move_costs[N_REG_CLASSES][N_REG_CLASSES] = { /* NO MUL GEN LFPU NLFPU FPU ALL *//* NO */ { 0, 0, 0, 0, 0, 0, 0},/* MUL */ { 0, 2, 2, 10, 22, 22, 22},/* GEN */ { 0, 2, 2, 10, 22, 22, 22},/* LFPU */ { 0, 10, 10, 2, 2, 2, 10},/* NLFPU */ { 0, 22, 22, 2, 2, 2, 22},/* FPU */ { 0, 22, 22, 2, 2, 2, 22},/* ALL */ { 0, 22, 22, 10, 22, 22, 22}} ;/* -- note that some moves are tremendously expensive, because they require lots of tricks! do we have to charge the costs incurred by secondary reload class -- as we do here with 22 -- or not ? */int register_move_cost(c1, c2) enum reg_class c1, c2;{ return move_costs[(int)c1][(int)c2];}static boolpdp11_rtx_costs (rtx x, int code, int outer_code ATTRIBUTE_UNUSED, int *total){ switch (code) { case CONST_INT: if (INTVAL (x) == 0 || INTVAL (x) == -1 || INTVAL (x) == 1) { *total = 0; return true; } /* FALLTHRU */ case CONST: case LABEL_REF: case SYMBOL_REF: /* Twice as expensive as REG. */ *total = 2; return true; case CONST_DOUBLE: /* Twice (or 4 times) as expensive as 16 bit. */ *total = 4; return true; case MULT: /* ??? There is something wrong in MULT because MULT is not as cheap as total = 2 even if we can shift! */ /* If optimizing for size make mult etc cheap, but not 1, so when in doubt the faster insn is chosen. */ if (optimize_size) *total = COSTS_N_INSNS (2); else *total = COSTS_N_INSNS (11); return false; case DIV: if (optimize_size) *total = COSTS_N_INSNS (2); else *total = COSTS_N_INSNS (25); return false; case MOD: if (optimize_size) *total = COSTS_N_INSNS (2); else *total = COSTS_N_INSNS (26); return false; case ABS: /* Equivalent to length, so same for optimize_size. */ *total = COSTS_N_INSNS (3); return false; case ZERO_EXTEND: /* Only used for qi->hi. */ *total = COSTS_N_INSNS (1); return false; case SIGN_EXTEND: if (GET_MODE (x) == HImode) *total = COSTS_N_INSNS (1); else if (GET_MODE (x) == SImode) *total = COSTS_N_INSNS (6); else *total = COSTS_N_INSNS (2); return false; case ASHIFT: case LSHIFTRT: case ASHIFTRT: if (optimize_size) *total = COSTS_N_INSNS (1); else if (GET_MODE (x) == QImode) { if (GET_CODE (XEXP (x, 1)) != CONST_INT) *total = COSTS_N_INSNS (8); /* worst case */ else *total = COSTS_N_INSNS (INTVAL (XEXP (x, 1))); } else if (GET_MODE (x) == HImode) { if (GET_CODE (XEXP (x, 1)) == CONST_INT) { if (abs (INTVAL (XEXP (x, 1))) == 1) *total = COSTS_N_INSNS (1); else *total = COSTS_N_INSNS (2.5 + 0.5 * INTVAL (XEXP (x, 1))); } else *total = COSTS_N_INSNS (10); /* worst case */ } else if (GET_MODE (x) == SImode) { if (GET_CODE (XEXP (x, 1)) == CONST_INT) *total = COSTS_N_INSNS (2.5 + 0.5 * INTVAL (XEXP (x, 1)));
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -