aboutsummaryrefslogtreecommitdiffstats
path: root/gcc-4.8/gcc/config/arm/arm.c
diff options
context:
space:
mode:
Diffstat (limited to 'gcc-4.8/gcc/config/arm/arm.c')
-rw-r--r--gcc-4.8/gcc/config/arm/arm.c220
1 files changed, 220 insertions, 0 deletions
diff --git a/gcc-4.8/gcc/config/arm/arm.c b/gcc-4.8/gcc/config/arm/arm.c
index 8dd5107ed..e8d83e0f3 100644
--- a/gcc-4.8/gcc/config/arm/arm.c
+++ b/gcc-4.8/gcc/config/arm/arm.c
@@ -280,6 +280,12 @@ static unsigned arm_add_stmt_cost (void *data, int count,
static void arm_canonicalize_comparison (int *code, rtx *op0, rtx *op1,
bool op0_preserve_value);
+static rtx arm_get_pic_reg (void);
+static void arm_clear_pic_reg (void);
+static bool arm_can_simplify_got_access (int, int);
+static rtx arm_loaded_global_var (rtx, rtx *, rtx *);
+static void arm_load_global_address (rtx, rtx, rtx, rtx, rtx);
+
/* Table of machine attributes. */
static const struct attribute_spec arm_attribute_table[] =
@@ -645,6 +651,21 @@ static const struct attribute_spec arm_attribute_table[] =
#undef TARGET_VECTORIZE_ADD_STMT_COST
#define TARGET_VECTORIZE_ADD_STMT_COST arm_add_stmt_cost
+#undef TARGET_GET_PIC_REG
+#define TARGET_GET_PIC_REG arm_get_pic_reg
+
+#undef TARGET_CLEAR_PIC_REG
+#define TARGET_CLEAR_PIC_REG arm_clear_pic_reg
+
+#undef TARGET_LOADED_GLOBAL_VAR
+#define TARGET_LOADED_GLOBAL_VAR arm_loaded_global_var
+
+#undef TARGET_CAN_SIMPLIFY_GOT_ACCESS
+#define TARGET_CAN_SIMPLIFY_GOT_ACCESS arm_can_simplify_got_access
+
+#undef TARGET_LOAD_GLOBAL_ADDRESS
+#define TARGET_LOAD_GLOBAL_ADDRESS arm_load_global_address
+
#undef TARGET_CANONICALIZE_COMPARISON
#define TARGET_CANONICALIZE_COMPARISON \
arm_canonicalize_comparison
@@ -25682,6 +25703,14 @@ arm_output_addr_const_extra (FILE *fp, rtx x)
fputc (')', fp);
return TRUE;
}
+ else if (GET_CODE (x) == UNSPEC && XINT (x, 1) == UNSPEC_GOT_PREL_SYM)
+ {
+ output_addr_const (fp, XVECEXP (x, 0, 0));
+ fputs ("(GOT_PREL)+(", fp);
+ output_addr_const (fp, XVECEXP (x, 0, 1));
+ fputc (')', fp);
+ return TRUE;
+ }
else if (GET_CODE (x) == CONST_VECTOR)
return arm_emit_vector_const (fp, x);
@@ -27463,4 +27492,195 @@ arm_validize_comparison (rtx *comparison, rtx * op1, rtx * op2)
}
+rtx
+arm_get_pic_reg (void)
+{
+ return cfun->machine->pic_reg;
+}
+
+/* Clear the pic_reg to NULL. */
+void
+arm_clear_pic_reg (void)
+{
+ cfun->machine->pic_reg = NULL_RTX;
+}
+
+/* Determine if it is profitable to simplify GOT accesses.
+
+ The default global address loading instructions are:
+
+ ldr r3, .L2 # A
+ ldr r2, .L2+4 # B
+.LPIC0:
+ add r3, pc # A
+ ldr r4, [r3, r2] # B
+ ...
+.L2:
+ .word _GLOBAL_OFFSET_TABLE_-(.LPIC0+4) # A
+ .word i(GOT) # S
+
+ The new instruction sequence is:
+
+ ldr r3, .L2 # C
+.LPIC0:
+ add r3, pc # C
+ ldr r3, [r3] # C
+ ...
+.L2:
+ i(GOT_PREL)+(.-(.LPIC0+4)) # C
+
+ Suppose the number of global address loading is n, the number of
+ accessed global symbol is s, this function should return
+
+ cost(A) + cost(B) * n + cost(S) * s >= cost(C) * n
+
+ From the above code snippets, we can see that
+
+ cost(A) = INSN_LENGTH * 2 + WORD_LENGTH
+ cost(B) = INSN_LENGTH * 2
+ cost(S) = WORD_LENGTH
+ cost(C) = INSN_LENGTH * 3 + WORD_LENGTH
+
+ The length of instruction depends on the target instruction set. */
+
+#define N_INSNS_A 2
+#define N_INSNS_B 2
+#define N_INSNS_C 3
+
+bool
+arm_can_simplify_got_access (int n_symbol, int n_access)
+{
+ int insn_len = TARGET_THUMB ? 2 : 4;
+ int cost_A = insn_len * N_INSNS_A + UNITS_PER_WORD;
+ int cost_B = insn_len * N_INSNS_B;
+ int cost_S = UNITS_PER_WORD;
+ int cost_C = insn_len * N_INSNS_C + UNITS_PER_WORD;
+
+ return cost_A + cost_B * n_access + cost_S * n_symbol >= cost_C * n_access;
+}
+
+/* Detect if INSN loads a global address. If so returns the symbol.
+ If the GOT offset is loaded in a separate instruction, sets the
+ corresponding OFFSET_REG and OFFSET_INSN. Otherwise fills with NULL. */
+rtx
+arm_loaded_global_var (rtx insn, rtx *offset_reg, rtx *offset_insn)
+{
+ rtx set = single_set (insn);
+ rtx pic_reg = cfun->machine->pic_reg;
+ gcc_assert (pic_reg);
+
+ /* Global address loading instruction has the pattern:
+ (SET address_reg (MEM (PLUS pic_reg offset_reg))) */
+ if (set && MEM_P (SET_SRC (set))
+ && (GET_CODE (XEXP (SET_SRC (set),0)) == PLUS))
+ {
+ unsigned int regno;
+ df_ref def;
+ rtx def_insn;
+ rtx src;
+ rtx plus = XEXP (SET_SRC (set),0);
+ rtx op0 = XEXP (plus, 0);
+ rtx op1 = XEXP (plus, 1);
+ if (op1 == pic_reg)
+ {
+ rtx tmp = op0;
+ op0 = op1;
+ op1 = tmp;
+ }
+
+ if (op0 != pic_reg)
+ return NULL_RTX;
+
+ if (REG_P (op1))
+ {
+ regno = REGNO (op1);
+ if ((DF_REG_USE_COUNT (regno) != 1)
+ || (DF_REG_DEF_COUNT (regno) != 1))
+ return NULL_RTX;
+
+ /* The offset loading insn has the pattern:
+ (SET offset_reg (UNSPEC [symbol] UNSPEC_PIC_SYM)) */
+ def = DF_REG_DEF_CHAIN (regno);
+ def_insn = DF_REF_INSN (def);
+ set = single_set (def_insn);
+ if (SET_DEST (set) != op1)
+ return NULL_RTX;
+
+ src = SET_SRC (set);
+ *offset_reg = op1;
+ *offset_insn = def_insn;
+ }
+ else
+ {
+ src = op1;
+ *offset_reg = NULL;
+ *offset_insn = NULL;
+ }
+
+ if ((GET_CODE (src) != UNSPEC) || (XINT (src, 1) != UNSPEC_PIC_SYM))
+ return NULL_RTX;
+
+ return RTVEC_ELT (XVEC (src, 0), 0);
+ }
+
+ return NULL_RTX;
+}
+
+/* Rewrite the global address loading instructions.
+ SYMBOL is the global variable. OFFSET_REG contains the offset of the
+ GOT entry. ADDRESS_REG will receive the final global address.
+ LOAD_INSN is the original insn which loads the address from GOT.
+ OFFSET_INSN is the original insn which sets OFFSET_REG.
+ If the GOT offset is not loaded in a separate instruction, OFFSET_REG
+ and OFFSET_INSN should be NULL. */
+void
+arm_load_global_address (rtx symbol, rtx offset_reg,
+ rtx address_reg, rtx load_insn, rtx offset_insn)
+{
+ rtx offset, got_prel, new_insn;
+ rtx labelno = GEN_INT (pic_labelno++);
+ rtx l1 = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, labelno), UNSPEC_PIC_LABEL);
+ rtx set = single_set (load_insn);
+
+ rtx tmp_reg = offset_reg;
+ rtx insert_pos = offset_insn;
+ if (offset_reg == NULL)
+ {
+ tmp_reg = address_reg;
+ insert_pos = PREV_INSN (load_insn);
+ }
+
+ /* The first insn:
+ (SET tmp_reg (address_of_GOT_entry(symbol) - pc))
+ The expression (address_of_GOT_entry(symbol) - pc) is expressed by
+ got_prel, which is actually represented by R_ARM_GOT_PREL relocation. */
+ l1 = gen_rtx_CONST (VOIDmode, l1);
+ l1 = plus_constant (Pmode, l1, TARGET_ARM ? 8 : 4);
+ offset = gen_rtx_MINUS (VOIDmode, pc_rtx, l1);
+ got_prel = gen_rtx_UNSPEC (Pmode, gen_rtvec (2, symbol, offset),
+ UNSPEC_GOT_PREL_SYM);
+ got_prel = gen_rtx_CONST (Pmode, got_prel);
+ if (TARGET_32BIT)
+ new_insn = emit_insn_after (gen_pic_load_addr_32bit (tmp_reg, got_prel),
+ insert_pos);
+ else
+ new_insn = emit_insn_after (gen_pic_load_addr_thumb1 (tmp_reg, got_prel),
+ insert_pos);
+
+ /* The second insn:
+ (SET tmp_reg (PLUS tmp_reg pc_rtx)) */
+ if (TARGET_ARM)
+ emit_insn_after (gen_pic_add_dot_plus_eight (tmp_reg, tmp_reg, labelno),
+ new_insn);
+ else
+ emit_insn_after (gen_pic_add_dot_plus_four (tmp_reg, tmp_reg, labelno),
+ new_insn);
+
+ /* The last insn to access the GOT entry:
+ (SET address_reg (MEM tmp_reg))
+ We reuse the existed load instruction. */
+ XEXP (SET_SRC (set), 0) = tmp_reg;
+ df_insn_rescan (load_insn);
+}
+
#include "gt-arm.h"