📄 thumb.c
字号:
if (len >= 8) { emit_insn (gen_movmem8b (out, in)); len -= 8; } if (len >= 4) { rtx reg = gen_reg_rtx (SImode); emit_insn (gen_movsi (reg, gen_rtx (MEM, SImode, in))); emit_insn (gen_movsi (gen_rtx (MEM, SImode, out), reg)); len -= 4; offset += 4; } if (len >= 2) { rtx reg = gen_reg_rtx (HImode); emit_insn (gen_movhi (reg, gen_rtx (MEM, HImode, plus_constant (in, offset)))); emit_insn (gen_movhi (gen_rtx (MEM, HImode, plus_constant (out, offset)), reg)); len -= 2; offset += 2; } if (len) { rtx reg = gen_reg_rtx (QImode); emit_insn (gen_movqi (reg, gen_rtx (MEM, QImode, plus_constant (in, offset)))); emit_insn (gen_movqi (gen_rtx (MEM, QImode, plus_constant (out, offset)), reg)); }}/* Routines for reloading */voidthumb_reload_out_si (operands) rtx operands;{ abort ();}/* Return non-zero if FUNC must be entered in ARM mode. */intis_called_in_ARM_mode (func) tree func;{ if (TREE_CODE (func) != FUNCTION_DECL) abort (); /* Ignore the problem about functions whoes address is taken. */ if (TARGET_CALLEE_INTERWORKING && TREE_PUBLIC (func)) return TRUE; return FALSE;}/* Routines for emitting code */voidfinal_prescan_insn(insn) rtx insn;{ extern int *insn_addresses; if (flag_print_asm_name) fprintf (asm_out_file, "%s 0x%04x\n", ASM_COMMENT_START, insn_addresses[INSN_UID (insn)]);}static void thumb_pushpop ( FILE *, int, int ); /* Forward declaration. */#ifdef __GNUC__inline#endifstatic intnumber_of_first_bit_set (mask) int mask;{ int bit; for (bit = 0; (mask & (1 << bit)) == 0; ++ bit) continue; return bit;}#define ARG_1_REGISTER 0#define ARG_2_REGISTER 1#define ARG_3_REGISTER 2#define ARG_4_REGISTER 3#define WORK_REGISTER 7#define FRAME_POINTER 11#define IP_REGISTER 12#define STACK_POINTER STACK_POINTER_REGNUM#define LINK_REGISTER 14#define PROGRAM_COUNTER 15/* Generate code to return from a thumb function. If 'reg_containing_return_addr' is -1, then the return address is actually on the stack, at the stack pointer. */static voidthumb_exit (f, reg_containing_return_addr) FILE * f; int reg_containing_return_addr;{ int regs_available_for_popping; int regs_to_pop; int pops_needed; int reg; int available; int required; int mode; int size; int restore_a4 = FALSE; /* Compute the registers we need to pop. */ regs_to_pop = 0; pops_needed = 0; if (reg_containing_return_addr == -1) { regs_to_pop |= 1 << LINK_REGISTER; ++ pops_needed; } if (TARGET_BACKTRACE) { /* Restore frame pointer and stack pointer. */ regs_to_pop |= (1 << FRAME_POINTER) | (1 << STACK_POINTER); pops_needed += 2; } /* If there is nothing to pop then just emit the BX instruction and return.*/ if (pops_needed == 0) { asm_fprintf (f, "\tbx\t%s\n", reg_names [reg_containing_return_addr]); return; } /* Otherwise if we are not supporting interworking and we have not created a backtrace structure and the function was not entered in ARM mode then just pop the return address straight into the PC. */ else if ( ! TARGET_THUMB_INTERWORK && ! TARGET_BACKTRACE && ! is_called_in_ARM_mode (current_function_decl)) { asm_fprintf (f, "\tpop\t{pc}\n" ); return; } /* Find out how many of the (return) argument registers we can corrupt. */ regs_available_for_popping = 0; #ifdef RTX_CODE /* If we can deduce the registers used from the function's return value. This is more reliable that examining regs_ever_live[] because that will be set if the register is ever used in the function, not just if the register is used to hold a return value. */ if (current_function_return_rtx != 0) mode = GET_MODE (current_function_return_rtx); else#endif mode = DECL_MODE (DECL_RESULT (current_function_decl)); size = GET_MODE_SIZE (mode); if (size == 0) { /* In a void function we can use any argument register. In a function that returns a structure on the stack we can use the second and third argument registers. */ if (mode == VOIDmode) regs_available_for_popping = (1 << ARG_1_REGISTER) | (1 << ARG_2_REGISTER) | (1 << ARG_3_REGISTER); else regs_available_for_popping = (1 << ARG_2_REGISTER) | (1 << ARG_3_REGISTER); } else if (size <= 4) regs_available_for_popping = (1 << ARG_2_REGISTER) | (1 << ARG_3_REGISTER); else if (size <= 8) regs_available_for_popping = (1 << ARG_3_REGISTER); /* Match registers to be popped with registers into which we pop them. */ for (available = regs_available_for_popping, required = regs_to_pop; required != 0 && available != 0; available &= ~(available & - available), required &= ~(required & - required)) -- pops_needed; /* If we have any popping registers left over, remove them. */ if (available > 0) regs_available_for_popping &= ~ available; /* Otherwise if we need another popping register we can use the fourth argument register. */ else if (pops_needed) { /* If we have not found any free argument registers and reg a4 contains the return address, we must move it. */ if (regs_available_for_popping == 0 && reg_containing_return_addr == ARG_4_REGISTER) { asm_fprintf (f, "\tmov\t%s, %s\n", reg_names [LINK_REGISTER], reg_names [ARG_4_REGISTER]); reg_containing_return_addr = LINK_REGISTER; } else if (size > 12) { /* Register a4 is being used to hold part of the return value, but we have dire need of a free, low register. */ restore_a4 = TRUE; asm_fprintf (f, "\tmov\t%s, %s\n", reg_names [IP_REGISTER], reg_names [ARG_4_REGISTER]); } if (reg_containing_return_addr != ARG_4_REGISTER) { /* The fourth argument register is available. */ regs_available_for_popping |= 1 << ARG_4_REGISTER; -- pops_needed; } } /* Pop as many registers as we can. */ thumb_pushpop (f, regs_available_for_popping, FALSE); /* Process the registers we popped. */ if (reg_containing_return_addr == -1) { /* The return address was popped into the lowest numbered register. */ regs_to_pop &= ~ (1 << LINK_REGISTER); reg_containing_return_addr = number_of_first_bit_set (regs_available_for_popping); /* Remove this register for the mask of available registers, so that the return address will not be corrupted by futher pops. */ regs_available_for_popping &= ~ (1 << reg_containing_return_addr); } /* If we popped other registers then handle them here. */ if (regs_available_for_popping) { int frame_pointer; /* Work out which register currently contains the frame pointer. */ frame_pointer = number_of_first_bit_set (regs_available_for_popping); /* Move it into the correct place. */ asm_fprintf (f, "\tmov\tfp, %s\n", reg_names [frame_pointer]); /* (Temporarily) remove it from the mask of popped registers. */ regs_available_for_popping &= ~ (1 << frame_pointer); regs_to_pop &= ~ (1 << FRAME_POINTER); if (regs_available_for_popping) { int stack_pointer; /* We popped the stack pointer as well, find the register that contains it.*/ stack_pointer = number_of_first_bit_set (regs_available_for_popping); /* Move it into the stack register. */ asm_fprintf (f, "\tmov\tsp, %s\n", reg_names [stack_pointer]); /* At this point we have popped all necessary registers, so do not worry about restoring regs_available_for_popping to its correct value: assert (pops_needed == 0) assert (regs_available_for_popping == (1 << frame_pointer)) assert (regs_to_pop == (1 << STACK_POINTER)) */ } else { /* Since we have just move the popped value into the frame pointer, the popping register is available for reuse, and we know that we still have the stack pointer left to pop. */ regs_available_for_popping |= (1 << frame_pointer); } } /* If we still have registers left on the stack, but we no longer have any registers into which we can pop them, then we must move the return address into the link register and make available the register that contained it. */ if (regs_available_for_popping == 0 && pops_needed > 0) { regs_available_for_popping |= 1 << reg_containing_return_addr; asm_fprintf (f, "\tmov\t%s, %s\n", reg_names [LINK_REGISTER], reg_names [reg_containing_return_addr]); reg_containing_return_addr = LINK_REGISTER; } /* If we have registers left on the stack then pop some more. We know that at most we will want to pop FP and SP. */ if (pops_needed > 0) { int popped_into; int move_to; thumb_pushpop (f, regs_available_for_popping, FALSE); /* We have popped either FP or SP. Move whichever one it is into the correct register. */ popped_into = number_of_first_bit_set (regs_available_for_popping); move_to = number_of_first_bit_set (regs_to_pop); asm_fprintf (f, "\tmov\t%s, %s\n", reg_names [move_to], reg_names [popped_into]); regs_to_pop &= ~ (1 << move_to); -- pops_needed; } /* If we still have not popped everything then we must have only had one register available to us and we are now popping the SP. */ if (pops_needed > 0) { int popped_into; thumb_pushpop (f, regs_available_for_popping, FALSE); popped_into = number_of_first_bit_set (regs_available_for_popping); asm_fprintf (f, "\tmov\tsp, %s\n", reg_names [popped_into]); /* assert (regs_to_pop == (1 << STACK_POINTER)) assert (pops_needed == 1) */ } /* If necessary restore the a4 register. */ if (restore_a4) { if (reg_containing_return_addr != LINK_REGISTER) { asm_fprintf (f, "\tmov\t%s, %s\n", reg_names [LINK_REGISTER], reg_names [ARG_4_REGISTER]); reg_containing_return_addr = LINK_REGISTER; } asm_fprintf (f, "\tmov\t%s, %s\n", reg_names [ARG_4_REGISTER], reg_names [IP_REGISTER]); } /* Return to caller. */ asm_fprintf (f, "\tbx\t%s\n", reg_names [reg_containing_return_addr]);}/* Emit code to push or pop registers to or from the stack. */static voidthumb_pushpop (f, mask, push) FILE * f; int mask; int push;{ int regno; int lo_mask = mask & 0xFF; if (lo_mask == 0 && ! push && (mask & (1 << 15))) { /* Special case. Do not generate a POP PC statement here, do it in thumb_exit() */ thumb_exit (f, -1); return; } asm_fprintf (f, "\t%s\t{", push ? "push" : "pop"); /* Look at the low registers first. */ for (regno = 0; regno < 8; regno ++, lo_mask >>= 1) { if (lo_mask & 1) { asm_fprintf (f, reg_names[regno]); if ((lo_mask & ~1) != 0) asm_fprintf (f, ", "); } } if (push && (mask & (1 << 14))) { /* Catch pushing the LR. */ if (mask & 0xFF) asm_fprintf (f, ", "); asm_fprintf (f, reg_names[14]); } else if (!push && (mask & (1 << 15))) { /* Catch popping the PC. */ if (TARGET_THUMB_INTERWORK || TARGET_BACKTRACE) { /* The PC is never poped directly, instead it is popped into r3 and then BX is used. */ asm_fprintf (f, "}\n"); thumb_exit (f, -1); return; } else { if (mask & 0xFF) asm_fprintf (f, ", "); asm_fprintf (f, reg_names[15]); } } asm_fprintf (f, "}\n");}/* Returns non-zero if the current function contains a far jump */intfar_jump_used_p (void){ rtx insn; for (insn = get_insns (); insn; insn = NEXT_INSN (insn)) { if (GET_CODE (insn) == JUMP_INSN /* Ignore tablejump patterns. */ && GET_CODE (PATTERN (insn)) != ADDR_VEC && GET_CODE (PATTERN (insn)) != ADDR_DIFF_VEC && get_attr_far_jump (insn) == FAR_JUMP_YES) return 1; } return 0;}static int return_used_this_function = 0;char *output_return (){ int regno; int live_regs_mask = 0; return_used_this_function = 1; for (regno = 0; regno < 8; regno++) if (regs_ever_live[regno] && ! call_used_regs[regno]) live_regs_mask |= 1 << regno; if (live_regs_mask == 0) { if (leaf_function_p () && ! far_jump_used_p()) { thumb_exit (asm_out_file, 14); } else if ( TARGET_THUMB_INTERWORK || TARGET_BACKTRACE || is_called_in_ARM_mode (current_function_decl)) { thumb_exit (asm_out_file, -1); } else asm_fprintf (asm_out_file, "\tpop\t{pc}\n"); } else { asm_fprintf (asm_out_file, "\tpop\t{"); for (regno = 0; live_regs_mask; regno ++, live_regs_mask >>= 1) if (live_regs_mask & 1) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -