diff options
Diffstat (limited to 'gcc-4.8/gcc/simplify-got.c')
-rw-r--r-- | gcc-4.8/gcc/simplify-got.c | 197 |
1 files changed, 197 insertions, 0 deletions
diff --git a/gcc-4.8/gcc/simplify-got.c b/gcc-4.8/gcc/simplify-got.c new file mode 100644 index 000000000..e822fa736 --- /dev/null +++ b/gcc-4.8/gcc/simplify-got.c @@ -0,0 +1,197 @@ +/* Simplify the code to load global variable's address from GOT. + Copyright (C) 2011 + Free Software Foundation, Inc. + Contributed by Wei Guozhi <carrot@google.com>. + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 3, or (at your option) any later +version. + +GCC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING3. If not see +<http://www.gnu.org/licenses/>. */ + +/* This file contains optimization for global variable's address loading + from GOT. + + When generating PIC code, we need to load global variable's address from + GOT. Many targets do this as following: + + (set pic_reg ...) # load the base address of GOT into pic_reg. + ... + (set off_set ...) # load the offset from the base of GOT to + # a global variable's GOT entry. + (set address # load the address from GOT. + (mem (plus pic_reg off_set))) + ... + + If the target has an alternative method (usually uses a different + relocation) to load the global address and in some cases it has less + cost and avoid the pic_reg, we can use this pass to improve it. + + In order to employ this optimization the target must satisfy the + following constraints: + + 1. There should be at least 2 methods to load a global variable's + address from GOT. + + 2. By default all global variables accesses use the method described + above. + + 3. There is a target dependent situation that the alternative method is + better when considering the number of global variable accesses and + the number of accessed variables. + + 4. The alternative method doesn't use the base of GOT (pic_reg). +*/ + +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "tm.h" +#include "rtl.h" +#include "flags.h" +#include "target.h" +#include "tree-pass.h" +#include "df.h" +#include "timevar.h" + +#define VAR_TABLE_SIZE 10 + +/* Information needed when rewrite the GOT access insns. */ +struct got_access_info +{ + rtx symbol; /* The global variable. */ + rtx offset_reg; /* Register contains the GOT entry offset. */ + rtx address_reg; /* Register contains the final global address. */ + rtx offset_insn; /* The insn loads the offset. */ + rtx load_insn; /* The insn which loads the address from GOT. */ +}; + +/* This optimization is enabled only when the pic_reg is actually used. */ +static bool +gate_handle_simplify_got (void) +{ + return optimize && targetm.got_access.get_pic_reg (); +} + +static unsigned int +rest_of_handle_simplify_got (void) +{ + df_ref ref; + rtx use = NULL_RTX; + int i, n_symbol, n_access = 0; + struct got_access_info* got_accesses; + htab_t var_table = htab_create (VAR_TABLE_SIZE, + htab_hash_pointer, + htab_eq_pointer, + NULL); + rtx pic_reg = targetm.got_access.get_pic_reg (); + gcc_assert (pic_reg); + + ref = DF_REG_USE_CHAIN (REGNO (pic_reg)); + got_accesses = XNEWVEC(struct got_access_info, + DF_REG_USE_COUNT (REGNO (pic_reg))); + + /* Check if all uses of pic_reg are loading global address through the + default method. */ + while (ref) + { + rtx insn = DF_REF_INSN (ref); + + /* Check for the special USE insn, it is not a real usage of pic_reg. */ + if (GET_CODE (PATTERN (insn)) == USE) + use = insn; + else + { + /* If an insn both set and use pic_reg, it is in the process of + constructing the value of pic_reg. We should also ignore it. */ + rtx set = single_set (insn); + if (!(set && SET_DEST (set) == pic_reg)) + { + rtx offset_reg; + rtx offset_insn; + rtx symbol = targetm.got_access.loaded_global_var (insn, + &offset_reg, + &offset_insn); + if (symbol) + { + rtx* slot = (rtx*) htab_find_slot (var_table, symbol, INSERT); + if (*slot == HTAB_EMPTY_ENTRY) + *slot = symbol; + + gcc_assert (set); + got_accesses[n_access].symbol = symbol; + got_accesses[n_access].offset_reg = offset_reg; + got_accesses[n_access].address_reg = SET_DEST (set); + got_accesses[n_access].load_insn = insn; + got_accesses[n_access].offset_insn = offset_insn; + n_access++; + } + else + { + /* This insn doesn't load a global address, but it has + other unexpected usage of pic_reg, give up. */ + free (got_accesses); + htab_delete (var_table); + return 0; + } + } + } + ref = DF_REF_NEXT_REG(ref); + } + + /* Check if we can simplify it. */ + n_symbol = htab_elements (var_table); + gcc_assert (n_symbol <= n_access); + if (!targetm.got_access.can_simplify_got_access (n_symbol, n_access)) + { + free (got_accesses); + htab_delete (var_table); + return 0; + } + + /* Rewrite the global address loading insns. */ + for (i=0; i<n_access; i++) + targetm.got_access.load_global_address (got_accesses[i].symbol, + got_accesses[i].offset_reg, + got_accesses[i].address_reg, + got_accesses[i].load_insn, + got_accesses[i].offset_insn); + + /* Since there is no usage of pic_reg now, we can remove it. */ + if (use) + remove_insn (use); + targetm.got_access.clear_pic_reg (); + free (got_accesses); + htab_delete (var_table); + return 0; +} + +struct rtl_opt_pass pass_simplify_got = +{ + { + RTL_PASS, + "simplify_got", /* name */ + OPTGROUP_NONE, /* optinfo_flags */ + gate_handle_simplify_got, /* gate */ + rest_of_handle_simplify_got, /* execute */ + NULL, /* sub */ + NULL, /* next */ + 0, /* static_pass_number */ + TV_SIMPLIFY_GOT, /* tv_id */ + 0, /* properties_required */ + 0, /* properties_provided */ + 0, /* properties_destroyed */ + 0, /* todo_flags_start */ + 0 /* todo_flags_finish */ + } +}; |