📄 arm.c
字号:
if (TARGET_APCS_REENT && flag_pic) fatal ("-fpic and -mapcs-reent are incompatible"); if (TARGET_APCS_REENT) warning ("APCS reentrant code not supported. Ignored"); /* If stack checking is disabled, we can use r10 as the PIC register, which keeps r9 available. */ if (flag_pic && ! TARGET_APCS_STACK) arm_pic_register = 10; if (TARGET_APCS_FLOAT) warning ("Passing floating point arguments in fp regs not yet supported"); /* Initialise boolean versions of the flags, for use in the arm.md file. */ arm_fast_multiply = (insn_flags & FL_FAST_MULT) != 0; arm_arch4 = (insn_flags & FL_ARCH4) != 0; arm_ld_sched = (tune_flags & FL_LDSCHED) != 0; arm_is_strong = (tune_flags & FL_STRONG) != 0; arm_is_6_or_7 = ((tune_flags & (FL_MODE26 | FL_MODE32)) && !(tune_flags & FL_ARCH4)); /* Default value for floating point code... if no co-processor bus, then schedule for emulated floating point. Otherwise, assume the user has an FPA. Note: this does not prevent use of floating point instructions, -msoft-float does that. */ arm_fpu = (tune_flags & FL_CO_PROC) ? FP_HARD : FP_SOFT3; if (target_fp_name) { if (streq (target_fp_name, "2")) arm_fpu_arch = FP_SOFT2; else if (streq (target_fp_name, "3")) arm_fpu_arch = FP_SOFT3; else fatal ("Invalid floating point emulation option: -mfpe-%s", target_fp_name); } else arm_fpu_arch = FP_DEFAULT; if (TARGET_FPE && arm_fpu != FP_HARD) arm_fpu = FP_SOFT2; /* For arm2/3 there is no need to do any scheduling if there is only a floating point emulator, or we are doing software floating-point. */ if ((TARGET_SOFT_FLOAT || arm_fpu != FP_HARD) && (tune_flags & FL_MODE32) == 0) flag_schedule_insns = flag_schedule_insns_after_reload = 0; arm_prog_mode = TARGET_APCS_32 ? PROG_MODE_PROG32 : PROG_MODE_PROG26; if (structure_size_string != NULL) { int size = strtol (structure_size_string, NULL, 0); if (size == 8 || size == 32) arm_structure_size_boundary = size; else warning ("Structure size boundary can only be set to 8 or 32"); } /* If optimizing for space, don't synthesize constants. For processors with load scheduling, it never costs more than 2 cycles to load a constant, and the load scheduler may well reduce that to 1. */ if (optimize_size || (tune_flags & FL_LDSCHED)) arm_constant_limit = 1; /* If optimizing for size, bump the number of instructions that we are prepared to conditionally execute (even on a StrongARM). Otherwise for the StrongARM, which has early execution of branches, a sequence that is worth skipping is shorter. */ if (optimize_size) max_insns_skipped = 6; else if (arm_is_strong) max_insns_skipped = 3;}/* Return 1 if it is possible to return using a single instruction */intuse_return_insn (iscond) int iscond;{ int regno; if (!reload_completed || current_function_pretend_args_size || current_function_anonymous_args || ((get_frame_size () + current_function_outgoing_args_size != 0) && !(TARGET_APCS && frame_pointer_needed))) return 0; /* Can't be done if interworking with Thumb, and any registers have been stacked. Similarly, on StrongARM, conditional returns are expensive if they aren't taken and registers have been stacked. */ if (iscond && arm_is_strong && frame_pointer_needed) return 0; if ((iscond && arm_is_strong) || TARGET_THUMB_INTERWORK) { for (regno = 0; regno < 16; regno++) if (regs_ever_live[regno] && ! call_used_regs[regno]) return 0; if (flag_pic && regs_ever_live[PIC_OFFSET_TABLE_REGNUM]) return 0; } /* Can't be done if any of the FPU regs are pushed, since this also requires an insn */ for (regno = 16; regno < 24; regno++) if (regs_ever_live[regno] && ! call_used_regs[regno]) return 0; /* If a function is naked, don't use the "return" insn. */ if (arm_naked_function_p (current_function_decl)) return 0; return 1;}/* Return TRUE if int I is a valid immediate ARM constant. */intconst_ok_for_arm (i) HOST_WIDE_INT i;{ unsigned HOST_WIDE_INT mask = ~(unsigned HOST_WIDE_INT)0xFF; /* For machines with >32 bit HOST_WIDE_INT, the bits above bit 31 must be all zero, or all one. */ if ((i & ~(unsigned HOST_WIDE_INT) 0xffffffff) != 0 && ((i & ~(unsigned HOST_WIDE_INT) 0xffffffff) != ((~(unsigned HOST_WIDE_INT) 0) & ~(unsigned HOST_WIDE_INT) 0xffffffff))) return FALSE; /* Fast return for 0 and powers of 2 */ if ((i & (i - 1)) == 0) return TRUE; do { if ((i & mask & (unsigned HOST_WIDE_INT) 0xffffffff) == 0) return TRUE; mask = (mask << 2) | ((mask & (unsigned HOST_WIDE_INT) 0xffffffff) >> (32 - 2)) | ~((unsigned HOST_WIDE_INT) 0xffffffff); } while (mask != ~(unsigned HOST_WIDE_INT) 0xFF); return FALSE;}/* Return true if I is a valid constant for the operation CODE. */static intconst_ok_for_op (i, code) HOST_WIDE_INT i; enum rtx_code code;{ if (const_ok_for_arm (i)) return 1; switch (code) { case PLUS: return const_ok_for_arm (ARM_SIGN_EXTEND (-i)); case MINUS: /* Should only occur with (MINUS I reg) => rsb */ case XOR: case IOR: return 0; case AND: return const_ok_for_arm (ARM_SIGN_EXTEND (~i)); default: abort (); }}/* Emit a sequence of insns to handle a large constant. CODE is the code of the operation required, it can be any of SET, PLUS, IOR, AND, XOR, MINUS; MODE is the mode in which the operation is being performed; VAL is the integer to operate on; SOURCE is the other operand (a register, or a null-pointer for SET); SUBTARGETS means it is safe to create scratch registers if that will either produce a simpler sequence, or we will want to cse the values. Return value is the number of insns emitted. */intarm_split_constant (code, mode, val, target, source, subtargets) enum rtx_code code; enum machine_mode mode; HOST_WIDE_INT val; rtx target; rtx source; int subtargets;{ if (subtargets || code == SET || (GET_CODE (target) == REG && GET_CODE (source) == REG && REGNO (target) != REGNO (source))) { /* After arm_reorg has been called, we can't fix up expensive constants by pushing them into memory so we must synthesise them in-line, regardless of the cost. This is only likely to be more costly on chips that have load delay slots and we are compiling without running the scheduler (so no splitting occurred before the final instruction emission). Ref: gcc -O1 -mcpu=strongarm gcc.c-torture/compile/980506-2.c */ if (! after_arm_reorg && (arm_gen_constant (code, mode, val, target, source, 1, 0) > arm_constant_limit + (code != SET))) { if (code == SET) { /* Currently SET is the only monadic value for CODE, all the rest are diadic. */ emit_insn (gen_rtx_SET (VOIDmode, target, GEN_INT (val))); return 1; } else { rtx temp = subtargets ? gen_reg_rtx (mode) : target; emit_insn (gen_rtx_SET (VOIDmode, temp, GEN_INT (val))); /* For MINUS, the value is subtracted from, since we never have subtraction of a constant. */ if (code == MINUS) emit_insn (gen_rtx_SET (VOIDmode, target, gen_rtx (code, mode, temp, source))); else emit_insn (gen_rtx_SET (VOIDmode, target, gen_rtx (code, mode, source, temp))); return 2; } } } return arm_gen_constant (code, mode, val, target, source, subtargets, 1);}/* As above, but extra parameter GENERATE which, if clear, suppresses RTL generation. */intarm_gen_constant (code, mode, val, target, source, subtargets, generate) enum rtx_code code; enum machine_mode mode; HOST_WIDE_INT val; rtx target; rtx source; int subtargets; int generate;{ int can_invert = 0; int can_negate = 0; int can_negate_initial = 0; int can_shift = 0; int i; int num_bits_set = 0; int set_sign_bit_copies = 0; int clear_sign_bit_copies = 0; int clear_zero_bit_copies = 0; int set_zero_bit_copies = 0; int insns = 0; unsigned HOST_WIDE_INT temp1, temp2; unsigned HOST_WIDE_INT remainder = val & 0xffffffff; /* find out which operations are safe for a given CODE. Also do a quick check for degenerate cases; these can occur when DImode operations are split. */ switch (code) { case SET: can_invert = 1; can_shift = 1; can_negate = 1; break; case PLUS: can_negate = 1; can_negate_initial = 1; break; case IOR: if (remainder == 0xffffffff) { if (generate) emit_insn (gen_rtx_SET (VOIDmode, target, GEN_INT (ARM_SIGN_EXTEND (val)))); return 1; } if (remainder == 0) { if (reload_completed && rtx_equal_p (target, source)) return 0; if (generate) emit_insn (gen_rtx_SET (VOIDmode, target, source)); return 1; } break; case AND: if (remainder == 0) { if (generate) emit_insn (gen_rtx_SET (VOIDmode, target, const0_rtx)); return 1; } if (remainder == 0xffffffff) { if (reload_completed && rtx_equal_p (target, source)) return 0; if (generate) emit_insn (gen_rtx_SET (VOIDmode, target, source)); return 1; } can_invert = 1; break; case XOR: if (remainder == 0) { if (reload_completed && rtx_equal_p (target, source)) return 0; if (generate) emit_insn (gen_rtx_SET (VOIDmode, target, source)); return 1; } if (remainder == 0xffffffff) { if (generate) emit_insn (gen_rtx_SET (VOIDmode, target, gen_rtx_NOT (mode, source))); return 1; } /* We don't know how to handle this yet below. */ abort (); case MINUS: /* We treat MINUS as (val - source), since (source - val) is always passed as (source + (-val)). */ if (remainder == 0) { if (generate) emit_insn (gen_rtx_SET (VOIDmode, target, gen_rtx_NEG (mode, source))); return 1; } if (const_ok_for_arm (val)) { if (generate) emit_insn (gen_rtx_SET (VOIDmode, target, gen_rtx_MINUS (mode, GEN_INT (val), source))); return 1; } can_negate = 1; break; default: abort (); } /* If we can do it in one insn get out quickly */ if (const_ok_for_arm (val) || (can_negate_initial && const_ok_for_arm (-val)) || (can_invert && const_ok_for_arm (~val))) { if (generate) emit_insn (gen_rtx_SET (VOIDmode, target, (source ? gen_rtx (code, mode, source, GEN_INT (val)) : GEN_INT (val)))); return 1; } /* Calculate a few attributes that may be useful for specific optimizations. */ for (i = 31; i >= 0; i--) { if ((remainder & (1 << i)) == 0) clear_sign_bit_copies++; else break; } for (i = 31; i >= 0; i--) { if ((remainder & (1 << i)) != 0) set_sign_bit_copies++; else break; } for (i = 0; i <= 31; i++) { if ((remainder & (1 << i)) == 0) clear_zero_bit_copies++; else break; } for (i = 0; i <= 31; i++) { if ((remainder & (1 << i)) != 0) set_zero_bit_copies++; else break; } switch (code) { case SET: /* See if we can do this by sign_extending a constant that is known to be negative. This is a good, way of doing it, since the shift may well merge into a subsequent insn. */ if (set_sign_bit_copies > 1) { if (const_ok_for_arm (temp1 = ARM_SIGN_EXTEND (remainder << (set_sign_bit_copies - 1)))) { if (generate) { rtx new_src = subtargets ? gen_reg_rtx (mode) : target; emit_insn (gen_rtx_SET (VOIDmode, new_src, GEN_INT (temp1))); emit_insn (gen_ashrsi3 (target, new_src, GEN_INT (set_sign_bit_copies - 1))); } return 2; } /* For an inverted constant, we will need to set the low bits, these will be shifted out of harm's way. */ temp1 |= (1 << (set_sign_bit_copies - 1)) - 1; if (const_ok_for_arm (~temp1)) { if (generate) { rtx new_src = subtargets ? gen_reg_rtx (mode) : target; emit_insn (gen_rtx_SET (VOIDmode, new_src, GEN_INT (temp1))); emit_insn (gen_ashrsi3 (target, new_src, GEN_INT (set_sign_bit_copies - 1))); } return 2; } } /* See if we can generate this by setting the bottom (or the top) 16 bits, and then shifting these into the other half of the word. We only look for the simplest cases, to do more would cost too much. Be careful, however, not to generate this when the alternative would take fewer insns. */ if (val & 0xffff0000) { temp1 = remainder & 0xffff0000; temp2 = remainder & 0x0000ffff; /* Overlaps outside this range are best done using other methods. */ for (i = 9; i < 24; i++) { if ((((temp2 | (temp2 << i)) & 0xffffffff) == remainder) && ! const_ok_for_arm (temp2)) { rtx new_src = (subtargets ? (generate ? gen_reg_rtx (mode) : NULL_RTX)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -