[PATCH: PR/40314] extract common address for memory access to fields of large struct
Carrot Wei
carrot@google.com
Sun May 31 09:31:00 GMT 2009
This patch is to implement the optimization suggested by:
http://gcc.gnu.org/bugzilla/show_bug.cgi?id=40314
It adds a new pass extract_common_addr to detect cases of memory accesses to
fields of large struct, add insns to compute a new base address which is
nearer these fields, then modify memory accesses to use the new base and
offset. Details can be found in the comment of extract-common-addr.c.
The optimization is enabled to ARM only in this patch. It can also be
applicable to other architectures which have addressing mode of
(base + offset) and offset has a limited value range. This optimization can
be enabled by providing an implementation of targetm.get_address_offset_range.
ChangeLog:
2009-05-31 Carrot Wei <carrot@google.com>
PR/40314
* config/arm/arm.c: add functions arm_address_offset_range,
thumb1_address_offset_range, thumb2_address_offset_range,
arm_address_offset_range_1.
* config/arm/arm-protos.h: add prototype of arm_address_offset_range.
* doc/tm.texi: add entry for TARGET_ADDRESS_OFFSET_RANGE.
* Makefile.in: add rule for extract_common_addr.
* target.h: add a hook function get_address_offset_range.
* target-def.h: add a hook function TARGET_ADDRESS_OFFSET_RANGE.
* tree-pass.h: add a pass_extract_common_addr.
* passes.c: add a pass_extract_common_addr.
* extract-common-addr.c: implementation of the new
pass_extract_common_addr.
Test:
This patch was applied to trunk GCC and tested on the arm emulator with newlib.
No new failures.
thanks
Carrot
Index: tree-pass.h
===================================================================
--- tree-pass.h (revision 148009)
+++ tree-pass.h (working copy)
@@ -433,6 +433,7 @@ extern struct rtl_opt_pass pass_rtl_fwpr
extern struct rtl_opt_pass pass_rtl_fwprop_addr;
extern struct rtl_opt_pass pass_jump2;
extern struct rtl_opt_pass pass_lower_subreg;
+extern struct rtl_opt_pass pass_extract_common_addr;
extern struct rtl_opt_pass pass_cse;
extern struct rtl_opt_pass pass_fast_rtl_dce;
extern struct rtl_opt_pass pass_ud_rtl_dce;
Index: target.h
===================================================================
--- target.h (revision 148009)
+++ target.h (working copy)
@@ -619,6 +619,10 @@ struct gcc_target
/* Given an address RTX, say whether it is valid. */
bool (* legitimate_address_p) (enum machine_mode, rtx, bool);
+ /* Given a base reg, return the min and max address offset. */
+ void (* get_address_offset_range)(enum machine_mode, const_rtx,
+ HOST_WIDE_INT*, HOST_WIDE_INT*, int*);
+
/* True if the given constant can be put into an object_block. */
bool (* use_blocks_for_constant_p) (enum machine_mode, const_rtx);
Index: target-def.h
===================================================================
--- target-def.h (revision 148009)
+++ target-def.h (working copy)
@@ -490,6 +490,7 @@
#define TARGET_LEGITIMIZE_ADDRESS default_legitimize_address
#define TARGET_DELEGITIMIZE_ADDRESS hook_rtx_rtx_identity
#define TARGET_LEGITIMATE_ADDRESS_P default_legitimate_address_p
+#define TARGET_ADDRESS_OFFSET_RANGE NULL
#define TARGET_USE_BLOCKS_FOR_CONSTANT_P hook_bool_mode_const_rtx_false
#define TARGET_MIN_ANCHOR_OFFSET 0
#define TARGET_MAX_ANCHOR_OFFSET 0
@@ -879,6 +880,7 @@
TARGET_LEGITIMIZE_ADDRESS, \
TARGET_DELEGITIMIZE_ADDRESS, \
TARGET_LEGITIMATE_ADDRESS_P, \
+ TARGET_ADDRESS_OFFSET_RANGE, \
TARGET_USE_BLOCKS_FOR_CONSTANT_P, \
TARGET_MIN_ANCHOR_OFFSET, \
TARGET_MAX_ANCHOR_OFFSET, \
Index: passes.c
===================================================================
--- passes.c (revision 148009)
+++ passes.c (working copy)
@@ -724,6 +724,7 @@ init_optimization_passes (void)
NEXT_PASS (pass_into_cfg_layout_mode);
NEXT_PASS (pass_jump2);
NEXT_PASS (pass_lower_subreg);
+ NEXT_PASS (pass_extract_common_addr);
NEXT_PASS (pass_df_initialize_opt);
NEXT_PASS (pass_cse);
NEXT_PASS (pass_rtl_fwprop);
Index: Makefile.in
===================================================================
--- Makefile.in (revision 148009)
+++ Makefile.in (working copy)
@@ -1129,6 +1129,7 @@ OBJS-common = \
explow.o \
expmed.o \
expr.o \
+ extract-common-addr.o \
final.o \
fixed-value.o \
fold-const.o \
@@ -2704,6 +2705,9 @@ ipa-struct-reorg.o: ipa-struct-reorg.c i
$(TREE_PASS_H) opts.h $(IPA_TYPE_ESCAPE_H) $(TREE_DUMP_H) \
$(GIMPLE_H)
+extract-common-addr.o : extract-common-addr.c $(CONFIG_H) $(SYSTEM_H) \
+ coretypes.h $(TM_H) $(RTL_H) $(FLAGS_H) insn-config.h $(RECOG_H) $(EXPR_H) \
+ $(BASIC_BLOCK_H) alloc-pool.h $(TARGET_H) tree-pass.h
coverage.o : coverage.c $(GCOV_IO_H) $(CONFIG_H) $(SYSTEM_H) coretypes.h \
$(TM_H) $(RTL_H) $(TREE_H) $(FLAGS_H) output.h $(REGS_H) $(EXPR_H) \
$(FUNCTION_H) $(TOPLEV_H) $(GGC_H) langhooks.h $(COVERAGE_H) gt-coverage.h \
Index: extract-common-addr.c
===================================================================
--- extract-common-addr.c (revision 0)
+++ extract-common-addr.c (revision 0)
@@ -0,0 +1,584 @@
+/* Extract common address calculation for accesses to
+ fields of large struct.
+ Copyright (C) 2009
+ Free Software Foundation, Inc.
+ Contributed by Carrot Wei <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 complex address calculation for
+ large structure.
+
+ Suppose we have a struct defined as:
+ typedef struct large_struct
+ {
+ char dummy[400];
+ int field1;
+ int field2;
+ } large_struct_t;
+
+ Now we want to access invidual fields through a pointer:
+
+ large_struct_t *p = ...;
+ ... = p->field1;
+ ... = p->field2;
+
+ If the load instruction of the target architecture can't support such
+ a large offset, gcc usually generats following insns to access struct
+ fields:
+
+ (set r200 400) # 400 is offset of field1
+ (set r201 (mem (plus r100 r200))) # r100 contains pointer p
+ ...
+ (set r300 404) # 404 is offset of field2
+ (set r301 (mem (plus r100 r300))) # r100 contains pointer p
+
+ Sometimes the large constant(offset) loading needs more than one
+ instruction, it make things worse.
+
+ One possible optimization for multiple accesses is first compute a new
+ base that is nearer to the fields that we want to access, then use the
+ normal (base + offset) addressing mode to access them. So ideally the
+ previous insns should be transformed to:
+
+ (set r101 (plus r100 400))
+ (set r201 (mem (plus r101 0)))
+ ...
+ (set r301 (mem (plus r101 4)))
+
+ The actual transformation done by this pass is:
+
+ (set r200 400) # keep the original insn
+ (set r250 (plus r100 400)) # r250 is new base
+ (set r201 (mem (plus r250 0)))
+ ...
+ (set r300 404)
+ (set r251 (plus r100 400)) # r251 contains same value as r250
+ (set r301 (mem (plus r251 4)))
+
+ And let dce and cse do the rest.
+*/
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "tm.h"
+#include "rtl.h"
+#include "basic-block.h"
+#include "alloc-pool.h"
+#include "flags.h"
+#include "insn-config.h"
+#include "recog.h"
+#include "expr.h"
+#include "target.h"
+#include "tree-pass.h"
+
+/* Memory offset information. */
+struct offset_info
+{
+ rtx mem_insn; /* The insn does memory access. */
+ rtx mem; /* The mem rtx which uses this offset. */
+ unsigned int offset_regno; /* The regno of the dest of the set. */
+ enum machine_mode mode; /* The dest machine mode. */
+ HOST_WIDE_INT offset; /* The offset constant. */
+ /* Next offset information, they are used with the same base register. */
+ struct offset_info *next;
+};
+
+/* Memory base address information. */
+struct base_reg_info
+{
+ /* All offset information used with this base register. */
+ struct offset_info *offsets;
+ unsigned int base_regno; /* Base register number. */
+ rtx base_reg; /* Base register rtx. */
+ enum machine_mode mode; /* Base register machine mode. */
+};
+
+/* Offset table, indexed by the offset register number. */
+static struct offset_info **offset_table;
+
+/* Base register table, indexed by the base register number. */
+static struct base_reg_info *base_table;
+
+/* Memory pool for offset_info elements. */
+static alloc_pool offset_element_pool;
+
+/* Given a register rtx, look up the offset_table. If we found it and they
+ have same machine mode returns the pointer to the offset information.
+ Otherwise returns NULL. */
+
+static inline struct offset_info *
+find_reg (rtx x)
+{
+ struct offset_info *offset_item;
+
+ gcc_assert (REG_P (x));
+ offset_item = offset_table[REGNO (x)];
+ if (offset_item == NULL)
+ return NULL;
+ if (offset_item->mode != GET_MODE (x))
+ return NULL;
+ return offset_item;
+}
+
+/* If x is MEM, and the address is in the form of
+ (plus base_reg offset_reg)
+ remove the corresponding item from offset table and
+ insert it into a base_reg_info object. */
+
+static int
+find_mem (rtx x, void *data)
+{
+ HOST_WIDE_INT min_offset, max_offset;
+ int alignment;
+ struct base_reg_info *base_reg_item;
+ struct offset_info *offset_item;
+ rtx reg1, reg2, address;
+
+ if (!MEM_P (x))
+ return 0;
+
+ /* Look for (plus reg1 reg2) pattern. */
+ address = XEXP (x, 0);
+ if (GET_CODE (address) != PLUS)
+ return 0;
+ reg1 = XEXP (address, 0);
+ reg2 = XEXP (address, 1);
+ if (!REG_P (reg1) || !REG_P (reg2))
+ return 0;
+
+ /* Look for addressing offset. If necessary swap reg1 and reg2
+ so that reg1 always contains base address and reg2 always
+ contains offset. */
+ offset_item = find_reg (reg2);
+ if (offset_item == NULL)
+ {
+ rtx t = reg1;
+ reg1 = reg2;
+ reg2 = t;
+ offset_item = find_reg (reg2);
+ if (offset_item == NULL)
+ return -1;
+ }
+
+ /* Currently only deals with pseudo base register. Other register
+ classes are also possible but need more complicated
+ implementation of targetm.get_address_offset_range. */
+ if (HARD_REGISTER_P (reg1))
+ return -1;
+
+ /* If the offset is not large enough, ignore it. */
+ targetm.get_address_offset_range (GET_MODE (x), reg1,
+ &min_offset, &max_offset, &alignment);
+ if (offset_item->offset >= min_offset
+ && offset_item->offset <= max_offset)
+ return -1;
+
+ /* If the base register and offset register don't have the same
+ machine mode, ignore this offset. */
+ if (GET_MODE (reg1) != offset_item->mode)
+ return -1;
+
+ /* Look up the corresponding base register information.
+ If there is no one, create it. */
+ base_reg_item = &base_table[REGNO (reg1)];
+ if (base_reg_item->offsets == NULL)
+ {
+ base_reg_item->mode = GET_MODE (reg1);
+ base_reg_item->base_regno = REGNO (reg1);
+ base_reg_item->base_reg = reg1;
+ }
+
+ /* Associate this offset information with the base information. */
+ offset_item->mem = x;
+ offset_item->mem_insn = (rtx) data;
+ offset_item->next = base_reg_item->offsets;
+ base_reg_item->offsets = offset_item;
+
+ /* Remove the offset information from offset table, since we don't
+ expect it will be used more than once. */
+ offset_table[offset_item->offset_regno] = NULL;
+ return -1;
+}
+
+/* If x is a register and has an entry in the offset table, the offset
+ information should be invalidated and removed from the table. */
+
+static inline void
+clobber_reg (rtx x)
+{
+ if (GET_CODE (x) == SUBREG)
+ x = SUBREG_REG (x);
+
+ if (REG_P (x))
+ {
+ if (offset_table[REGNO (x)] == NULL)
+ return;
+ pool_free (offset_element_pool, offset_table[REGNO (x)]);
+ offset_table[REGNO (x)] = NULL;
+ }
+}
+
+/* If a reg is clobbered in insn, remove it from offset table. */
+
+static void
+clobber_insn_reg (rtx insn)
+{
+ rtx x = PATTERN (insn);
+
+ switch (GET_CODE (x))
+ {
+ case SET:
+ clobber_reg (SET_DEST (x));
+ break;
+
+ case CLOBBER:
+ clobber_reg (XEXP (x, 0));
+ break;
+
+ case PARALLEL:
+ {
+ int i;
+ int len = XVECLEN (x, 0);
+ for (i = 0; i < len; i++)
+ {
+ rtx y = XVECEXP (x, 0, i);
+ if (GET_CODE (y) == SET)
+ clobber_reg (SET_DEST (y));
+ else if (GET_CODE (y) == CLOBBER)
+ clobber_reg (XEXP (y, 0));
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+}
+
+/* If this insn is in the form of
+ (set reg offset)
+ insert this offset info into offset table. */
+
+static void
+find_offset (rtx insn)
+{
+ rtx src, dest;
+ unsigned int regno;
+ HOST_WIDE_INT offset_val;
+ struct offset_info *offset;
+ rtx x = single_set (insn);
+ if (!x)
+ return;
+
+ dest = SET_DEST (x);
+ if (!REG_P (dest))
+ return;
+ regno = REGNO (dest);
+ if (regno < FIRST_PSEUDO_REGISTER)
+ return;
+
+ src = SET_SRC (x);
+ if (GET_CODE (src) != CONST_INT)
+ return;
+ offset_val = INTVAL (src);
+ if (offset_val <= 0)
+ return;
+
+ offset = (struct offset_info *) pool_alloc (offset_element_pool);
+ offset->mem = NULL;
+ offset->offset_regno = regno;
+ offset->mode = GET_MODE (dest);
+ offset->offset = offset_val;
+ offset->next = NULL;
+ offset_table[regno] = offset;
+}
+
+/* Look for optimization opportunities. */
+
+static void
+collect_candidates (void)
+{
+ basic_block block;
+
+ FOR_EACH_BB (block)
+ {
+ rtx insn;
+ memset (offset_table, 0, sizeof (struct offset_info *) * max_reg_num ());
+
+ FOR_BB_INSNS (block, insn)
+ {
+ if (!INSN_P (insn))
+ continue;
+
+ for_each_rtx (&PATTERN (insn), find_mem, insn);
+
+ clobber_insn_reg (insn);
+
+ find_offset (insn);
+ }
+ }
+}
+
+/* Compare function used by qsort. */
+
+static int
+compare_offset (const void *pa, const void *pb)
+{
+ struct offset_info * const *offset1 = (struct offset_info* const *)pa;
+ struct offset_info * const *offset2 = (struct offset_info* const *)pb;
+ if ((*offset1)->offset != (*offset2)->offset)
+ return (*offset1)->offset - (*offset2)->offset;
+ return GET_MODE ((*offset1)->mem) - GET_MODE ((*offset2)->mem);
+}
+
+/* Do the actual modification. */
+
+static void
+rewrite_memory_access (struct base_reg_info *base_reg,
+ struct offset_info **offset_array,
+ int count,
+ HOST_WIDE_INT base_offset)
+{
+ int i;
+ for (i = 0; i < count; i++)
+ {
+ rtx new_base_reg, new_base, new_base_insn, new_offset, x;
+ struct offset_info *offset = offset_array[i];
+
+ /* Insert an insn to compute the new base reg. */
+ new_base_reg = gen_reg_rtx (offset->mode);
+ new_base = gen_rtx_PLUS (offset->mode,
+ base_reg->base_reg, GEN_INT (base_offset));
+ new_base_insn = gen_move_insn (new_base_reg, new_base);
+ emit_insn_before (new_base_insn, offset->mem_insn);
+
+ /* Modify the original MEM rtx to use new base and offset address. */
+ new_offset = GEN_INT (offset->offset - base_offset);
+ x = XEXP (offset->mem, 0);
+ if (REGNO (XEXP (x, 0)) == base_reg->base_regno)
+ {
+ validate_change (offset->mem, &XEXP (x, 0), new_base_reg, 1);
+ validate_change (offset->mem, &XEXP (x, 1), new_offset, 1);
+ }
+ else
+ {
+ validate_change (offset->mem, &XEXP (x, 1), new_base_reg, 1);
+ validate_change (offset->mem, &XEXP (x, 0), new_offset, 1);
+ }
+
+ if (!apply_change_group ())
+ remove_insn(new_base_insn);
+ }
+}
+
+/* Optimize a list of memory addressing relative to one base. */
+
+static void
+process_one_base (struct base_reg_info *base_reg)
+{
+ HOST_WIDE_INT max_offset, min_offset;
+ HOST_WIDE_INT base_offset, max_base;
+ int alignment, new_alignment;
+
+ struct offset_info **offset_array;
+ struct offset_info *offset = base_reg->offsets;
+ int count = 0;
+ int i, group_count;
+
+ /* Count the number of memory accesses using the same base register. */
+ while (offset)
+ {
+ count++;
+ offset = offset->next;
+ }
+ if (count < 2)
+ return;
+
+ /* Sort the memory accesses according to their offset. */
+ offset_array = XNEWVEC (struct offset_info *, count);
+ offset = base_reg->offsets;
+ for (i = 0; i < count; i++)
+ {
+ offset_array[i] = offset;
+ offset = offset->next;
+ }
+ qsort (offset_array, count, sizeof(struct offset_info *), compare_offset);
+
+ /* group_count contains the number of MEM accesses should be relative to
+ the same new base address.
+ (base_offset + old_base) is the new base address of current group.
+ max_base is the maximum possible value of base_offset.
+ alignment is the offset requirement, not the memory alignment. */
+ group_count = 1;
+ targetm.get_address_offset_range (GET_MODE (offset_array[0]->mem),
+ NULL, &min_offset, &max_offset,
+ &new_alignment);
+ base_offset = offset_array[0]->offset - max_offset;
+ max_base = offset_array[0]->offset - min_offset;
+ alignment = new_alignment;
+
+ /* Add the memory access to current group one by one until it is not
+ feasible to add the new memory access. */
+ for (i = 1; i < count; i++)
+ {
+ HOST_WIDE_INT base2, max_base2;
+ int alignment2;
+
+ targetm.get_address_offset_range (GET_MODE (offset_array[i]->mem),
+ NULL, &min_offset, &max_offset, &new_alignment);
+ base2 = offset_array[i]->offset - max_offset;
+ alignment2 = alignment;
+
+ /* Adjust the new base_offset and alignment. */
+ if (base2 < base_offset)
+ {
+ if (new_alignment > alignment)
+ {
+ HOST_WIDE_INT delta = offset_array[i]->offset - base_offset;
+ alignment2 = new_alignment;
+ delta = delta & ~(alignment2 - 1);
+ base2 = offset_array[i]->offset - delta;
+ }
+ else
+ base2 = base_offset;
+ }
+ else
+ {
+ if (new_alignment > alignment)
+ alignment2 = new_alignment;
+ else
+ {
+ HOST_WIDE_INT delta = base2 - base_offset;
+ delta = (delta + alignment - 1) & ~(alignment - 1);
+ base2 = base_offset + delta;
+ }
+ }
+
+ /* Adjust max_base. */
+ max_base2 = offset_array[i]->offset - min_offset;
+ if (new_alignment < alignment)
+ {
+ HOST_WIDE_INT mask = alignment - 1;
+ HOST_WIDE_INT aligned_min = (min_offset + alignment - 1) & mask;
+ max_base2 = offset_array[i]->offset - aligned_min;
+ if (max_base2 < max_base)
+ max_base = max_base2;
+ }
+ else
+ {
+ if (max_base2 > max_base)
+ {
+ HOST_WIDE_INT delta = max_base2 - max_base;
+ delta = (delta + new_alignment - 1) & ~(new_alignment - 1);
+ max_base = max_base2 - delta;
+ }
+ else
+ max_base = max_base2;
+ }
+
+ /* If the new base_offset is greater than the new max_base, this
+ memory access can't be put into current group. */
+ if (base2 > max_base)
+ {
+ /* Only when there are two or more memory accesses in one group,
+ this optimization can be beneficial. */
+ if (group_count > 1)
+ {
+ struct offset_info **start = &offset_array[i - group_count];
+ rewrite_memory_access (base_reg, start, group_count, base_offset);
+ }
+
+ /* Start a new group. */
+ base_offset = offset_array[i]->offset - max_offset;
+ max_base = offset_array[i]->offset - min_offset;
+ alignment = new_alignment;
+ group_count = 0;
+ }
+ else
+ {
+ alignment = alignment2;
+ base_offset = base2;
+ }
+
+ group_count++;
+ }
+
+ /* The final group. */
+ if (group_count > 1)
+ {
+ struct offset_info **start = &offset_array[i - group_count];
+ rewrite_memory_access (base_reg, start, group_count, base_offset);
+ }
+
+ free (offset_array);
+}
+
+/* This optimization can be enabled by providing an implementation of
+ targetm.get_address_offset_range. */
+
+static bool
+gate_handle_common_addr (void)
+{
+ return targetm.get_address_offset_range != NULL && optimize > 0;
+}
+
+static unsigned int
+rest_of_handle_extract_common_addr (void)
+{
+ int i;
+ int reg_count = max_reg_num();
+ offset_table = XNEWVEC (struct offset_info *, reg_count);
+ base_table = XNEWVEC (struct base_reg_info, reg_count);
+ memset (offset_table, 0, sizeof (struct offset_info *) * reg_count);
+ memset (base_table, 0, sizeof (struct base_reg_info) * reg_count);
+ offset_element_pool = create_alloc_pool ("offset pool",
+ sizeof (struct offset_info), 64);
+
+ collect_candidates ();
+
+ for (i = 0; i < reg_count; i++)
+ {
+ if (base_table[i].offsets != NULL)
+ process_one_base(&base_table[i]);
+ }
+
+ free_alloc_pool (offset_element_pool);
+ free (base_table);
+ free (offset_table);
+ return 0;
+}
+
+struct rtl_opt_pass pass_extract_common_addr =
+{
+ {
+ RTL_PASS,
+ "extract_common_addr", /* name */
+ gate_handle_common_addr, /* gate */
+ rest_of_handle_extract_common_addr, /* execute */
+ NULL, /* sub */
+ NULL, /* next */
+ 0, /* static_pass_number */
+ TV_NONE, /* tv_id */
+ 0, /* properties_required */
+ 0, /* properties_provided */
+ 0, /* properties_destroyed */
+ 0, /* todo_flags_start */
+ TODO_dump_func /* todo_flags_finish */
+ }
+};
Index: tm.texi
===================================================================
--- tm.texi (revision 148009)
+++ tm.texi (working copy)
@@ -5564,6 +5564,11 @@ holding the constant. This restriction
of TLS symbols for various targets.
@end deftypefn
+@deftypefn {Target Hook} void TARGET_ADDRESS_OFFSET_RANGE (enum
machine_mode @var{mode}, const_rtx @var{x}, HOST_WIDE_INT
*@var{min_offset}, HOST_WIDE_INT *@var{max_offset}, int
*@var{alignment})
+Given a memory access @var{mode} and base register @var{x}, this hook should
+return the offset range and alignment in a legal (base + offset) addressing.
+@end deftypefn
+
@deftypefn {Target Hook} bool TARGET_USE_BLOCKS_FOR_CONSTANT_P (enum
machine_mode @var{mode}, rtx @var{x})
This hook should return true if pool entries for constant @var{x} can
be placed in an @code{object_block} structure. @var{mode} is the mode
Index: arm-protos.h
===================================================================
--- arm-protos.h (revision 148009)
+++ arm-protos.h (working copy)
@@ -58,6 +58,8 @@ extern int arm_legitimate_address_outer_
extern int thumb_legitimate_offset_p (enum machine_mode, HOST_WIDE_INT);
extern rtx thumb_legitimize_reload_address (rtx *, enum machine_mode, int, int,
int);
+extern void arm_address_offset_range (enum machine_mode, const_rtx,
+ HOST_WIDE_INT *, HOST_WIDE_INT *, int *);
extern int arm_const_double_rtx (rtx);
extern int neg_const_double_rtx_ok_for_fpa (rtx);
extern int vfp3_const_double_rtx (rtx);
Index: arm.c
===================================================================
--- arm.c (revision 148009)
+++ arm.c (working copy)
@@ -407,6 +407,9 @@ static bool arm_allocate_stack_slots_for
#undef TARGET_LEGITIMATE_ADDRESS_P
#define TARGET_LEGITIMATE_ADDRESS_P arm_legitimate_address_p
+#undef TARGET_ADDRESS_OFFSET_RANGE
+#define TARGET_ADDRESS_OFFSET_RANGE arm_address_offset_range
+
struct gcc_target targetm = TARGET_INITIALIZER;
/* Obstack for minipool constant handling. */
@@ -4476,6 +4479,180 @@ arm_legitimate_address_p (enum machine_m
return thumb1_legitimate_address_p (mode, x, strict_p);
}
+/* In arm mode, returns the min and max address offset relative to the
+ specified base register. */
+
+static void
+arm_address_offset_range_1 (enum machine_mode mode, const_rtx base_reg,
+ HOST_WIDE_INT *min_offset,
+ HOST_WIDE_INT *max_offset,
+ int *alignment)
+{
+ if (TARGET_HARD_FLOAT && (TARGET_FPA || TARGET_MAVERICK)
+ && (GET_MODE_CLASS (mode) == MODE_FLOAT
+ || (TARGET_MAVERICK && mode == DImode)))
+ {
+ *min_offset = -1023;
+ *max_offset = 1023;
+ *alignment = 4;
+ return;
+ }
+
+ if (TARGET_NEON
+ && (VALID_NEON_DREG_MODE (mode) || VALID_NEON_QREG_MODE (mode)))
+ {
+ *min_offset = -1023;
+ *max_offset = 1015;
+ *alignment = 4;
+ return;
+ }
+
+ if (TARGET_REALLY_IWMMXT && VALID_IWMMXT_REG_MODE (mode))
+ {
+ *min_offset = -1023;
+ *max_offset = 1023;
+ *alignment = 4;
+ return;
+ }
+
+ if (mode == DImode || mode == DFmode)
+ {
+ if (TARGET_LDRD)
+ {
+ *min_offset = -255;
+ *max_offset = 255;
+ }
+ else
+ {
+ *min_offset = -4095;
+ *max_offset = 4091;
+ }
+ *alignment = 1;
+ return;
+ }
+
+ if (arm_arch4)
+ {
+ if (mode == HImode || mode == QImode)
+ *max_offset = 255;
+ else
+ *max_offset = 4095;
+ }
+ else
+ *max_offset = (mode == HImode) ? 4094 : 4095;
+ *min_offset = - (*max_offset);
+ *alignment = 1;
+}
+
+/* In thumb2 mode, returns the min and max address offset relative to the
+ specified base register. */
+
+static void
+thumb2_address_offset_range (enum machine_mode mode, const_rtx base_reg,
+ HOST_WIDE_INT *min_offset,
+ HOST_WIDE_INT *max_offset,
+ int *alignment)
+{
+ if (TARGET_HARD_FLOAT && (TARGET_FPA || TARGET_MAVERICK)
+ && (GET_MODE_CLASS (mode) == MODE_FLOAT
+ || (TARGET_MAVERICK && mode == DImode)))
+ {
+ *min_offset = -1023;
+ *max_offset = 1023;
+ *alignment = 4;
+ return;
+ }
+
+ if (TARGET_REALLY_IWMMXT && VALID_IWMMXT_REG_MODE (mode))
+ {
+ if (!TARGET_LDRD || mode != DImode)
+ {
+ *min_offset = -1023;
+ *max_offset = 1023;
+ *alignment = 4;
+ return;
+ }
+ }
+
+ if (TARGET_NEON
+ && (VALID_NEON_DREG_MODE (mode) || VALID_NEON_QREG_MODE (mode)))
+ {
+ *min_offset = -1023;
+ *max_offset = 1015;
+ *alignment = 4;
+ return;
+ }
+
+ if (mode == DImode || mode == DFmode)
+ {
+ *min_offset = -255;
+ *max_offset = 255;
+ *alignment = 4;
+ return;
+ }
+
+ *min_offset = -255;
+ *max_offset = 4095;
+ *alignment = 1;
+}
+
+/* In thumb1 mode, returns the min and max address offset relative to the
+ specified base register. */
+
+static void
+thumb1_address_offset_range (enum machine_mode mode, const_rtx base_reg,
+ HOST_WIDE_INT *min_offset,
+ HOST_WIDE_INT *max_offset,
+ int *alignment)
+{
+ *min_offset = 0;
+ switch (GET_MODE_SIZE (mode))
+ {
+ case 1:
+ *max_offset = 31;
+ *alignment = 1;
+ break;
+
+ case 2:
+ *max_offset = 63;
+ *alignment = 2;
+ break;
+
+ default:
+ *max_offset = 127 - GET_MODE_SIZE (mode);
+ *alignment = 4;
+ }
+}
+
+/* Returns the min and max address offset relative to the specified
+ base register. */
+
+void
+arm_address_offset_range (enum machine_mode mode, const_rtx base_reg,
+ HOST_WIDE_INT *min_offset,
+ HOST_WIDE_INT *max_offset,
+ int *alignment)
+{
+ HOST_WIDE_INT mask;
+
+ /* Currently only deal with pseudo base register. */
+ gcc_assert (!base_reg || !HARD_REGISTER_P (base_reg));
+
+ if (TARGET_ARM)
+ arm_address_offset_range_1 (mode, base_reg,
+ min_offset, max_offset, alignment);
+ else if (TARGET_THUMB2)
+ thumb2_address_offset_range (mode, base_reg,
+ min_offset, max_offset, alignment);
+ else /* (TARGET_THUMB1) */
+ thumb1_address_offset_range (mode, base_reg,
+ min_offset, max_offset, alignment);
+
+ mask = ~(*alignment - 1);
+ *max_offset = *max_offset & mask;
+ *min_offset = (*min_offset + *alignment - 1) & mask;
+}
+
/* Build the SYMBOL_REF for __tls_get_addr. */
static GTY(()) rtx tls_get_addr_libfunc;
More information about the Gcc-patches
mailing list