📄 arm.c
字号:
/* Output a move between double words. It must be REG<-REG, REG<-CONST_DOUBLE, REG<-CONST_INT, REG<-MEM or MEM<-REG and all MEMs must be offsettable addresses. */char *output_move_double (operands) rtx *operands;{ enum rtx_code code0 = GET_CODE (operands[0]); enum rtx_code code1 = GET_CODE (operands[1]); rtx otherops[2]; if (code0 == REG) { int reg0 = REGNO (operands[0]); otherops[0] = gen_rtx (REG, SImode, 1 + reg0); if (code1 == REG) { int reg1 = REGNO (operands[1]); if (reg1 == 12) abort(); otherops[1] = gen_rtx (REG, SImode, 1 + reg1); /* Ensure the second source is not overwritten */ if (reg0 == 1 + reg1) { output_asm_insn("mov%?\t%0, %1", otherops); output_asm_insn("mov%?\t%0, %1", operands); } else { output_asm_insn("mov%?\t%0, %1", operands); output_asm_insn("mov%?\t%0, %1", otherops); } } else if (code1 == CONST_DOUBLE) { otherops[1] = gen_rtx (CONST_INT, VOIDmode, CONST_DOUBLE_HIGH (operands[1])); operands[1] = gen_rtx (CONST_INT, VOIDmode, CONST_DOUBLE_LOW (operands[1])); output_mov_immediate (operands, FALSE, ""); output_mov_immediate (otherops, FALSE, ""); } else if (code1 == CONST_INT) { otherops[1] = const0_rtx; /* sign extend the intval into the high-order word */ /* Note: output_mov_immediate may clobber operands[1], so we put this out first */ if (INTVAL (operands[1]) < 0) output_asm_insn ("mvn%?\t%0, %1", otherops); else output_asm_insn ("mov%?\t%0, %1", otherops); output_mov_immediate (operands, FALSE, ""); } else if (code1 == MEM) { switch (GET_CODE (XEXP (operands[1], 0))) { case REG: /* Handle the simple case where address is [r, #0] more efficient. */ output_asm_insn ("ldm%?ia\t%m1, %M0", operands); break; case PRE_INC: output_asm_insn ("add%?\t%m1, %m1, #8", operands); output_asm_insn ("ldm%?ia\t%m1, %M0", operands); break; case PRE_DEC: output_asm_insn ("sub%?\t%m1, %m1, #8", operands); output_asm_insn ("ldm%?ia\t%m1, %M0", operands); break; case POST_INC: output_asm_insn ("ldm%?ia\t%m1!, %M0", operands); break; case POST_DEC: output_asm_insn ("ldm%?ia\t%m1, %M0", operands); output_asm_insn ("sub%?\t%m1, %m1, #8", operands); break; default: otherops[1] = adj_offsettable_operand (operands[1], 4); /* Take care of overlapping base/data reg. */ if (reg_mentioned_p (operands[0], operands[1])) { output_asm_insn ("ldr%?\t%0, %1", otherops); output_asm_insn ("ldr%?\t%0, %1", operands); } else { output_asm_insn ("ldr%?\t%0, %1", operands); output_asm_insn ("ldr%?\t%0, %1", otherops); } } } else abort(); /* Constraints should prevent this */ } else if (code0 == MEM && code1 == REG) { if (REGNO (operands[1]) == 12) abort(); switch (GET_CODE (XEXP (operands[0], 0))) { case REG: output_asm_insn ("stm%?ia\t%m0, %M1", operands); break; case PRE_INC: output_asm_insn ("add%?\t%m0, %m0, #8", operands); output_asm_insn ("stm%?ia\t%m0, %M1", operands); break; case PRE_DEC: output_asm_insn ("sub%?\t%m0, %m0, #8", operands); output_asm_insn ("stm%?ia\t%m0, %M1", operands); break; case POST_INC: output_asm_insn ("stm%?ia\t%m0!, %M1", operands); break; case POST_DEC: output_asm_insn ("stm%?ia\t%m0, %M1", operands); output_asm_insn ("sub%?\t%m0, %m0, #8", operands); break; default: otherops[0] = adj_offsettable_operand (operands[0], 4); otherops[1] = gen_rtx (REG, SImode, 1 + REGNO (operands[1])); output_asm_insn ("str%?\t%1, %0", operands); output_asm_insn ("str%?\t%1, %0", otherops); } } else abort(); /* Constraints should prevent this */ return "";}/* Output an arbitrary MOV reg, #n. OPERANDS[0] is a register. OPERANDS[1] is a const_int. */char *output_mov_immediate (operands) rtx *operands;{ HOST_WIDE_INT n = INTVAL (operands[1]); int n_ones = 0; int i; /* Try to use one MOV */ if (const_ok_for_arm (n)) { output_asm_insn ("mov%?\t%0, %1", operands); return ""; } /* Try to use one MVN */ if (const_ok_for_arm (~n)) { operands[1] = GEN_INT (~n); output_asm_insn ("mvn%?\t%0, %1", operands); return ""; } /* If all else fails, make it out of ORRs or BICs as appropriate. */ for (i=0; i < 32; i++) if (n & 1 << i) n_ones++; if (n_ones > 16) /* Shorter to use MVN with BIC in this case. */ output_multi_immediate(operands, "mvn%?\t%0, %1", "bic%?\t%0, %0, %1", 1, ~n); else output_multi_immediate(operands, "mov%?\t%0, %1", "orr%?\t%0, %0, %1", 1, n); return "";}/* Output an ADD r, s, #n where n may be too big for one instruction. If adding zero to one register, output nothing. */char *output_add_immediate (operands) rtx *operands;{ HOST_WIDE_INT n = INTVAL (operands[2]); if (n != 0 || REGNO (operands[0]) != REGNO (operands[1])) { if (n < 0) output_multi_immediate (operands, "sub%?\t%0, %1, %2", "sub%?\t%0, %0, %2", 2, -n); else output_multi_immediate (operands, "add%?\t%0, %1, %2", "add%?\t%0, %0, %2", 2, n); } return "";}/* Output a multiple immediate operation. OPERANDS is the vector of operands referred to in the output patterns. INSTR1 is the output pattern to use for the first constant. INSTR2 is the output pattern to use for subsequent constants. IMMED_OP is the index of the constant slot in OPERANDS. N is the constant value. */char *output_multi_immediate (operands, instr1, instr2, immed_op, n) rtx *operands; char *instr1, *instr2; int immed_op; HOST_WIDE_INT n;{#if HOST_BITS_PER_WIDE_INT > 32 n &= 0xffffffff;#endif if (n == 0) { operands[immed_op] = const0_rtx; output_asm_insn (instr1, operands); /* Quick and easy output */ } else { int i; char *instr = instr1; /* Note that n is never zero here (which would give no output) */ for (i = 0; i < 32; i += 2) { if (n & (3 << i)) { operands[immed_op] = GEN_INT (n & (255 << i)); output_asm_insn (instr, operands); instr = instr2; i += 6; } } } return "";}/* Return the appropriate ARM instruction for the operation code. The returned result should not be overwritten. OP is the rtx of the operation. SHIFT_FIRST_ARG is TRUE if the first argument of the operator was shifted. */char *arithmetic_instr (op, shift_first_arg) rtx op; int shift_first_arg;{ switch (GET_CODE (op)) { case PLUS: return "add"; case MINUS: return shift_first_arg ? "rsb" : "sub"; case IOR: return "orr"; case XOR: return "eor"; case AND: return "and"; default: abort (); }}/* Ensure valid constant shifts and return the appropriate shift mnemonic for the operation code. The returned result should not be overwritten. OP is the rtx code of the shift. On exit, *AMOUNTP will be -1 if the shift is by a register, or a constant shift. */static char *shift_op (op, amountp) rtx op; HOST_WIDE_INT *amountp;{ char *mnem; enum rtx_code code = GET_CODE (op); if (GET_CODE (XEXP (op, 1)) == REG || GET_CODE (XEXP (op, 1)) == SUBREG) *amountp = -1; else if (GET_CODE (XEXP (op, 1)) == CONST_INT) *amountp = INTVAL (XEXP (op, 1)); else abort (); switch (code) { case ASHIFT: mnem = "asl"; break; case ASHIFTRT: mnem = "asr"; break; case LSHIFTRT: mnem = "lsr"; break; case ROTATERT: mnem = "ror"; break; case MULT: /* We never have to worry about the amount being other than a power of 2, since this case can never be reloaded from a reg. */ if (*amountp != -1) *amountp = int_log2 (*amountp); else abort (); return "asl"; default: abort (); } if (*amountp != -1) { /* This is not 100% correct, but follows from the desire to merge multiplication by a power of 2 with the recognizer for a shift. >=32 is not a valid shift for "asl", so we must try and output a shift that produces the correct arithmetical result. Using lsr #32 is identical except for the fact that the carry bit is not set correctly if we set the flags; but we never use the carry bit from such an operation, so we can ignore that. */ if (code == ROTATERT) *amountp &= 31; /* Rotate is just modulo 32 */ else if (*amountp != (*amountp & 31)) { if (code == ASHIFT) mnem = "lsr"; *amountp = 32; } /* Shifts of 0 are no-ops. */ if (*amountp == 0) return NULL; } return mnem;}/* Obtain the shift from the POWER of two. */HOST_WIDE_INTint_log2 (power) HOST_WIDE_INT power;{ HOST_WIDE_INT shift = 0; while (((1 << shift) & power) == 0) { if (shift > 31) abort (); shift++; } return shift;}/* Output a .ascii pseudo-op, keeping track of lengths. This is because /bin/as is horribly restrictive. */voidoutput_ascii_pseudo_op (stream, p, len) FILE *stream; unsigned char *p; int len;{ int i; int len_so_far = 1000; int chars_so_far = 0; for (i = 0; i < len; i++) { register int c = p[i]; if (len_so_far > 50) { if (chars_so_far) fputs ("\"\n", stream); fputs ("\t.ascii\t\"", stream); len_so_far = 0; arm_increase_location (chars_so_far); chars_so_far = 0; } if (c == '\"' || c == '\\') { putc('\\', stream); len_so_far++; } if (c >= ' ' && c < 0177) { putc (c, stream); len_so_far++; } else { fprintf (stream, "\\%03o", c); len_so_far +=4; } chars_so_far++; } fputs ("\"\n", stream); arm_increase_location (chars_so_far);}/* Try to determine whether a pattern really clobbers the link register. This information is useful when peepholing, so that lr need not be pushed if we combine a call followed by a return. NOTE: This code does not check for side-effect expressions in a SET_SRC: such a check should not be needed because these only update an existing value within a register; the register must still be set elsewhere within the function. */static intpattern_really_clobbers_lr (x) rtx x;{ int i; switch (GET_CODE (x)) { case SET: switch (GET_CODE (SET_DEST (x))) { case REG: return REGNO (SET_DEST (x)) == 14; case SUBREG: if (GET_CODE (XEXP (SET_DEST (x), 0)) == REG) return REGNO (XEXP (SET_DEST (x), 0)) == 14; if (GET_CODE (XEXP (SET_DEST (x), 0)) == MEM) return 0; abort (); default: return 0; } case PARALLEL: for (i = 0; i < XVECLEN (x, 0); i++) if (pattern_really_clobbers_lr (XVECEXP (x, 0, i))) return 1; return 0; case CLOBBER: switch (GET_CODE (XEXP (x, 0))) { case REG: return REGNO (XEXP (x, 0)) == 14; case SUBREG: if (GET_CODE (XEXP (XEXP (x, 0), 0)) == REG) return REGNO (XEXP (XEXP (x, 0), 0)) == 14; abort (); default: return 0; } case UNSPEC: return 1; default: return 0; }}static intfunction_really_clobbers_lr (first) rtx first;{ rtx insn, next; for (insn = first; insn; insn = next_nonnote_insn (insn)) { switch (GET_CODE (insn)) { case BARRIER: case NOTE: case CODE_LABEL: case JUMP_INSN: /* Jump insns only change the PC (and conds) */ case INLINE_HEADER: break; case INSN: if (pattern_really_clobbers_lr (PATTERN (insn))) return 1; break; case CALL_INSN: /* Don't yet know how to handle those calls that are not to a SYMBOL_REF */ if (GET_CODE (PATTERN (insn)) != PARALLEL) abort (); switch (GET_CODE
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -