📄 arm.c
字号:
if (fp_needed) { live_regs_mask |= 0xA800; if (frame_pointer_needed) live_regs_mask |= (1 << FRAME_POINTER_REGNUM); } else if (regs_ever_live[14]) live_regs_mask |= 0x4000; for (reg = 20; reg < 24; reg++) if (regs_ever_live[reg]) { fprintf (f, "\tldfe\t%s, [%s], #12\n", reg_names[reg], frame_pointer_needed ? "rfp" : "sp"); code_size += 4; } if (fp_needed) { print_multi_reg (f, "ldmea\tfp", live_regs_mask, TRUE); code_size += 4; } else { /* Restore stack pointer if necessary. */ if (frame_size) { operands[0] = operands[1] = stack_pointer_rtx; operands[2] = gen_rtx (CONST_INT, VOIDmode, frame_size); output_add_immediate (operands); } if (current_function_pretend_args_size == 0 && regs_ever_live[14]) { print_multi_reg (f, "ldmfd\tsp!", (live_regs_mask & ~0x4000) | 0x8000, TRUE); code_size += 4; } else { if (live_regs_mask) { print_multi_reg (f, "ldmfd\tsp!", live_regs_mask, FALSE); code_size += 4; } if (current_function_pretend_args_size) { operands[0] = operands[1] = stack_pointer_rtx; operands[2] = gen_rtx (CONST_INT, VOIDmode, current_function_pretend_args_size); output_add_immediate (operands); } fputs ("\tmovs\tpc, lr\n", f); code_size += 4; } } arm_increase_location (code_size); current_function_anonymous_args = 0;} /* output_epilogue *//* Increase the `arm_text_location' by AMOUNT if we're in the text segment. */voidarm_increase_location (amount) int amount;{ if (in_text_section ()) arm_text_location += amount;} /* arm_increase_location *//* Like output_asm_insn (), but also increases the arm_text_location (if in the .text segment, of course, even though this will always be true). Returns the empty string. */char *arm_output_asm_insn (template, operands) char *template; rtx *operands;{ extern FILE *asm_out_file; output_asm_insn (template, operands); if (in_text_section ()) arm_text_location += 4; fflush (asm_out_file); return ("");} /* arm_output_asm_insn *//* Output a label definition. If this label is within the .text segment, it is stored in OFFSET_TABLE, to be used when building `llc' instructions. Maybe GCC remembers names not starting with a `*' for a long time, but this is a minority anyway, so we just make a copy. Do not store the leading `*' if the name starts with one. */voidarm_asm_output_label (stream, name) FILE *stream; char *name;{ char *real_name, *s; struct label_offset *cur; int hash = 0; assemble_name (stream, name); fputs (":\n", stream); if (! in_text_section ()) return; if (name[0] == '*') { real_name = xmalloc (1 + strlen (&name[1])); strcpy (real_name, &name[1]); } else { real_name = xmalloc (2 + strlen (name)); strcpy (real_name, "_"); strcat (real_name, name); } for (s = real_name; *s; s++) hash += *s; hash = hash % LABEL_HASH_SIZE; cur = (struct label_offset *) xmalloc (sizeof (struct label_offset)); cur->name = real_name; cur->offset = arm_text_location; cur->cdr = offset_table[hash]; offset_table[hash] = cur;} /* arm_asm_output_label *//* Output the instructions needed to perform what Martin's /bin/as called llc: load an SImode thing from the function's constant pool. XXX This could be enhanced in that we do not really need a pointer in the constant pool pointing to the real thing. If we can address this pointer, we can also address what it is pointing at, in fact, anything in the text segment which has been defined already within this .s file. */char *arm_output_llc (operands) rtx *operands;{ char *s, *name = XSTR (XEXP (operands[1], 0), 0); struct label_offset *he; int hash = 0, conditional = (arm_ccfsm_state == 3 || arm_ccfsm_state == 4); if (*name != '*') abort (); for (s = &name[1]; *s; s++) hash += *s; hash = hash % LABEL_HASH_SIZE; he = offset_table[hash]; while (he && strcmp (he->name, &name[1])) he = he->cdr; if (!he) abort (); if (arm_text_location + 8 - he->offset < 4095) { fprintf (asm_out_file, "\tldr%s\t%s, [pc, #%s - . - 8]\n", conditional ? arm_condition_codes[arm_current_cc] : "", reg_names[REGNO (operands[0])], &name[1]); arm_increase_location (4); return (""); } else { int offset = - (arm_text_location + 8 - he->offset); char *reg_name = reg_names[REGNO (operands[0])]; /* ??? This is a hack, assuming the constant pool never is more than (1 + 255) * 4096 == 1Meg away from the PC. */ if (offset > 1000000) abort (); fprintf (asm_out_file, "\tsub%s\t%s, pc, #(8 + . - %s) & ~4095\n", conditional ? arm_condition_codes[arm_current_cc] : "", reg_name, &name[1]); fprintf (asm_out_file, "\tldr%s\t%s, [%s, #- ((4 + . - %s) & 4095)]\n", conditional ? arm_condition_codes[arm_current_cc] : "", reg_name, reg_name, &name[1]); arm_increase_location (8); } return ("");} /* arm_output_llc *//* Output code resembling an .lcomm directive. /bin/as doesn't have this directive hence this hack, which works by reserving some `.space' in the bss segment directly. XXX This is a severe hack, which is guaranteed NOT to work since it doesn't define STATIC COMMON space but merely STATIC BSS space. */voidoutput_lcomm_directive (stream, name, size, rounded) FILE *stream; char *name; int size, rounded;{ fputs ("\n\t.bss\t@ .lcomm\n", stream); assemble_name (stream, name); fprintf (stream, ":\t.space\t%d\n", rounded); if (in_text_section ()) fputs ("\n\t.text\n", stream); else fputs ("\n\t.data\n", stream);} /* output_lcomm_directive *//* A finite state machine takes care of noticing whether or not instructions can be conditionally executed, and thus decrease execution time and code size by deleting branch instructions. The fsm is controlled by final_prescan_insn, and controls the actions of ASM_OUTPUT_OPCODE. *//* The state of the fsm controlling condition codes are: 0: normal, do nothing special 1: make ASM_OUTPUT_OPCODE not output this instruction 2: make ASM_OUTPUT_OPCODE not output this instruction 3: make instructions conditional 4: make instructions conditional State transitions (state->state by whom under condition): 0 -> 1 final_prescan_insn if the `target' is a label 0 -> 2 final_prescan_insn if the `target' is an unconditional branch 1 -> 3 ASM_OUTPUT_OPCODE after not having output the conditional branch 2 -> 4 ASM_OUTPUT_OPCODE after not having output the conditional branch 3 -> 0 ASM_OUTPUT_INTERNAL_LABEL if the `target' label is reached (the target label has CODE_LABEL_NUMBER equal to arm_target_label). 4 -> 0 final_prescan_insn if the `target' unconditional branch is reached (the target insn is arm_target_insn). XXX In case the `target' is an unconditional branch, this conditionalising of the instructions always reduces code size, but not always execution time. But then, I want to reduce the code size to somewhere near what /bin/cc produces. *//* The condition codes of the ARM, and the inverse function. */char *arm_condition_codes[] ={ "eq", "ne", "cs", "cc", "mi", "pl", "vs", "vc", "hi", "ls", "ge", "lt", "gt", "le", "al", "nv"};#define ARM_INVERSE_CONDITION_CODE(X) ((X) ^ 1)/* Returns the index of the ARM condition code string in `arm_condition_codes'. COMPARISON should be an rtx like `(eq (...) (...))'. */intget_arm_condition_code (comparison) rtx comparison;{ switch (GET_CODE (comparison)) { case NE: return (1); case EQ: return (0); case GE: return (10); case GT: return (12); case LE: return (13); case LT: return (11); case GEU: return (2); case GTU: return (8); case LEU: return (9); case LTU: return (3); default: abort (); } /*NOTREACHED*/ return (42);} /* get_arm_condition_code */voidfinal_prescan_insn (insn, opvec, noperands) rtx insn; rtx *opvec; int noperands;{ /* BODY will hold the body of INSN. */ register rtx body = PATTERN (insn); /* This will be 1 if trying to repeat the trick, and things need to be reversed if it appears to fail. */ int reverse = 0; /* START_INSN will hold the insn from where we start looking. This is the first insn after the following code_label if REVERSE is true. */ rtx start_insn = insn; /* If in state 4, check if the target branch is reached, in order to change back to state 0. */ if (arm_ccfsm_state == 4) { if (insn == arm_target_insn) arm_ccfsm_state = 0; return; } /* If in state 3, it is possible to repeat the trick, if this insn is an unconditional branch to a label, and immediately following this branch is the previous target label which is only used once, and the label this branch jumps to is not too far off. */ if (arm_ccfsm_state == 3) { if (simplejump_p (insn)) { start_insn = next_nonnote_insn (start_insn); if (GET_CODE (start_insn) == BARRIER) { /* XXX Isn't this always a barrier? */ start_insn = next_nonnote_insn (start_insn); } if (GET_CODE (start_insn) == CODE_LABEL && CODE_LABEL_NUMBER (start_insn) == arm_target_label && LABEL_NUSES (start_insn) == 1) reverse = TRUE; else return; } else return; } if (arm_ccfsm_state != 0 && !reverse) abort (); if (GET_CODE (insn) != JUMP_INSN) return; if (reverse || (GET_CODE (body) == SET && GET_CODE (SET_DEST (body)) == PC && GET_CODE (SET_SRC (body)) == IF_THEN_ELSE)) { int insns_skipped = 0, fail = FALSE, succeed = FALSE; /* Flag which part of the IF_THEN_ELSE is the LABEL_REF. */ int then_not_else = TRUE; rtx this_insn = start_insn, label; /* Register the insn jumped to. */ if (reverse) label = XEXP (SET_SRC (body), 0); else if (GET_CODE (XEXP (SET_SRC (body), 1)) == LABEL_REF) label = XEXP (XEXP (SET_SRC (body), 1), 0); else if (GET_CODE (XEXP (SET_SRC (body), 2)) == LABEL_REF) { label = XEXP (XEXP (SET_SRC (body), 2), 0); then_not_else = FALSE; } else abort (); /* See how many insns this branch skips, and what kind of insns. If all insns are okay, and the label or unconditional branch to the same label is not too far away, succeed. */ for (insns_skipped = 0; !fail && !succeed && insns_skipped < MAX_INSNS_SKIPPED; insns_skipped++) { rtx scanbody; this_insn = next_nonnote_insn (this_insn); if (!this_insn) break; scanbody = PATTERN (this_insn); switch (GET_CODE (this_insn)) { case CODE_LABEL: /* Succeed if it is the target label, otherwise fail since control falls in from somewhere else. */ if (this_insn == label) { arm_ccfsm_state = 1; succeed = TRUE; } else fail = TRUE; break; case BARRIER: /* XXX Is this case necessary? */ /* Succeed if the following insn is the target label. Otherwise fail. */ this_insn = next_nonnote_insn (this_insn); if (this_insn == label) { arm_ccfsm_state = 1; succeed = TRUE; } else fail = TRUE; break; case JUMP_INSN: /* If this is an unconditional branch to the same label, succeed. If it is to another label, do nothing. If it is conditional, fail. */ /* XXX Probably, the test for the SET and the PC are unnecessary. */ if (GET_CODE (scanbody) == SET && GET_CODE (SET_DEST (scanbody)) == PC) { if (GET_CODE (SET_SRC (scanbody)) == LABEL_REF && XEXP (SET_SRC (scanbody), 0) == label && !reverse) { arm_ccfsm_state = 2; succeed = TRUE; } else if (GET_CODE (SET_SRC (scanbody)) == IF_THEN_ELSE) fail = TRUE; } break; case INSN: /* Instructions affecting the condition codes make it fail. */ if (sets_cc0_p (scanbody)) fail = TRUE; break; default: break; } } if (succeed) { if (arm_ccfsm_state == 1 || reverse) arm_target_label = CODE_LABEL_NUMBER (label); else if (arm_ccfsm_state == 2) arm_target_insn = this_insn; else abort (); /* If REVERSE is true, ARM_CURRENT_CC needs to be inverted from what it was. */ if (!reverse) arm_current_cc = get_arm_condition_code (XEXP (SET_SRC (body), 0)); if (reverse || then_not_else) arm_current_cc = ARM_INVERSE_CONDITION_CODE (arm_current_cc); } }} /* final_prescan_insn *//* EOF */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -