This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
[patch] Uniquization of constants at the Tree level
- From: Eric Botcazou <ebotcazou at adacore dot com>
- To: gcc-patches at gcc dot gnu dot org
- Date: Sun, 25 Apr 2010 13:40:56 +0200
- Subject: [patch] Uniquization of constants at the Tree level
Hi,
this is something we've been using in our 4.x compilers for quite some time.
Constant objects (*_CST and CONSTRUCTOR) are uniquized at the RTL level by
means of output_constant_def but there is no equivalent at the Tree level so,
if a constant is commited to read-only memory at this level, it might be
unnecessarily duplicated like in the following artificial C example:
int lookup1 (int i)
{
int a[] = { 0, 1, 2, 3, 4, 5, 6, 7 };
return a[i];
}
int lookup2 (int i)
{
int a[] = { 0, 1, 2, 3, 4, 5, 6, 7 };
return a[i+1];
}
Compiled at -Os for i686, the assembly file contains:
.section .rodata
.align 4
.type C.0.1957, @object
.size C.0.1957, 32
C.0.1957:
.long 0
.long 1
.long 2
.long 3
.long 4
.long 5
.long 6
.long 7
.align 4
.type C.1.1961, @object
.size C.1.1961, 32
C.1.1961:
.long 0
.long 1
.long 2
.long 3
.long 4
.long 5
.long 6
.long 7
The patch introduces the counterpart of output_constant_def at the Tree level,
namely tree_output_constant_def, which returns something directly usable in
the GENERIC/GIMPLE IL (a VAR_DECL) when passed a valid constant, doing the
uniquization and output work behind the scene automatically by means of the
varasm.c uniquization machinery and the varpool.
The new feature is used in gimplify_init_constructor, which eliminates the
duplication for the above C example, as well as in gnat_gimplify_expr when an
address of a constant constructor is gimplified (this happens often in Ada).
Other front-ends could presumably use it as well in equivalent cases.
The implementation is straightforward, modulo the following nits:
- the varasm.c uniquization machinery wasn't updated for VECTOR_CSTs so the
patch does that,
- same for CONSTANT_ALIGNMENT of VECTOR_TYPEs for most architectures so the
patch uses DATA_ALIGNMENT as well,
- config/arm/unknown-elf.h duplicates a macro of varasm.c and someone thought
it was clever to undefine the instance in varasm.c... so the patch untangles
that (no functional changes for the ARM target because of that).
Tested on x86_64-suse-linux and i586-suse-linux with mainline, but a backport
to our 4.3-based compiler is in use on a wide range of platforms without any
problems as of this writing. OK for mainline?
2010-04-25 Eric Botcazou <ebotcazou@adacore.com>
Uniquization of constants at the Tree level
* tree.h (tree_output_constant_def): Declare.
* gimplify.c (gimplify_init_constructor): When using block copy,
uniquize the constant constructor on the RHS.
* varasm.c (IN_NAMED_SECTION): Remove guard.
(tree_constant_section_name): New static variable.
(DECL_TREE_LEVEL_CONSTANT_P): New macro.
(make_decl_rtl): Deal with Tree level constants.
(assemble_variable): If the underlying symbol is attached to a
uniquized constant, output the constant.
(decode_addr_const): Handle special case of INDIRECT_REF.
(const_hash_1) <VECTOR_CST>: New case.
(compare_constant) <VECTOR_CST>: Likewise.
<ADDR_EXPR>: Deal with LABEL_REFs.
(copy_constant) <VECTOR_CST>: New case.
(get_constant_alignment): Delete.
(get_constant_section): Add ALIGN parameter and simplify.
(build_constant_desc): Build a VAR_DECL and attach it to the symbol.
(assemble_constant_contents): Use the expression of the VAR_DECL.
(output_constant_def_contents): Use the alignment of the VAR_DECL.
(tree_output_constant_def): New global function.
(mark_constant): Use the expression of the VAR_DECL.
(init_varasm_once): Initialize tree_constant_section_name.
(place_block_symbol): Use the alignment of the VAR_DECL and the size
of its expression.
(output_object_block): Likewise and assemble the expression.
* config/arm/unknown-elf.h (IN_NAMED_SECTION): Rename to...
(IN_NAMED_SECTION_P): ...this.
(ASM_OUTPUT_ALIGNED_BSS): Adjust for above renaming.
(ASM_OUTPUT_ALIGNED_DECL_LOCAL): Likewise.
ada/
* gcc-interface/trans.c (gnat_gimplify_expr) <ADDR_EXPR>: Uniquize
constant constructors before taking their address.
--
Eric Botcazou
Index: tree.h
===================================================================
--- tree.h (revision 158675)
+++ tree.h (working copy)
@@ -5125,6 +5125,7 @@ extern void internal_reference_types (vo
extern unsigned int update_alignment_for_field (record_layout_info, tree,
unsigned int);
/* varasm.c */
+extern tree tree_output_constant_def (tree);
extern void make_decl_rtl (tree);
extern rtx make_decl_rtl_for_debug (tree);
extern void make_decl_one_only (tree, tree);
Index: ada/gcc-interface/trans.c
===================================================================
--- ada/gcc-interface/trans.c (revision 158701)
+++ ada/gcc-interface/trans.c (working copy)
@@ -6036,16 +6036,8 @@ gnat_gimplify_expr (tree *expr_p, gimple
the reference is in an elaboration procedure. */
if (TREE_CONSTANT (op))
{
- tree new_var = create_tmp_var_raw (TREE_TYPE (op), "C");
- TREE_ADDRESSABLE (new_var) = 1;
- gimple_add_tmp_var (new_var);
-
- TREE_READONLY (new_var) = 1;
- TREE_STATIC (new_var) = 1;
- DECL_INITIAL (new_var) = op;
-
- TREE_OPERAND (expr, 0) = new_var;
- recompute_tree_invariant_for_addr_expr (expr);
+ tree addr = build_fold_addr_expr (tree_output_constant_def (op));
+ *expr_p = fold_convert (TREE_TYPE (expr), addr);
}
/* Otherwise explicitly create the local temporary. That's required
Index: gimplify.c
===================================================================
--- gimplify.c (revision 158675)
+++ gimplify.c (working copy)
@@ -3762,25 +3762,11 @@ gimplify_init_constructor (tree *expr_p,
&& num_nonzero_elements > 1
&& !can_move_by_pieces (size, align))
{
- tree new_tree;
-
if (notify_temp_creation)
return GS_ERROR;
- new_tree = create_tmp_var_raw (type, "C");
-
- gimple_add_tmp_var (new_tree);
- TREE_STATIC (new_tree) = 1;
- TREE_READONLY (new_tree) = 1;
- DECL_INITIAL (new_tree) = ctor;
- if (align > DECL_ALIGN (new_tree))
- {
- DECL_ALIGN (new_tree) = align;
- DECL_USER_ALIGN (new_tree) = 1;
- }
- walk_tree (&DECL_INITIAL (new_tree), force_labels_r, NULL, NULL);
-
- TREE_OPERAND (*expr_p, 1) = new_tree;
+ walk_tree (&ctor, force_labels_r, NULL, NULL);
+ TREE_OPERAND (*expr_p, 1) = tree_output_constant_def (ctor);
/* This is no longer an assignment of a CONSTRUCTOR, but
we still may have processing to do on the LHS. So
Index: varasm.c
===================================================================
--- varasm.c (revision 158675)
+++ varasm.c (working copy)
@@ -170,15 +170,20 @@ bool in_cold_section_p;
static GTY(()) section *unnamed_sections;
/* Return a nonzero value if DECL has a section attribute. */
-#ifndef IN_NAMED_SECTION
#define IN_NAMED_SECTION(DECL) \
((TREE_CODE (DECL) == FUNCTION_DECL || TREE_CODE (DECL) == VAR_DECL) \
&& DECL_SECTION_NAME (DECL) != NULL_TREE)
-#endif
/* Hash table of named sections. */
static GTY((param_is (section))) htab_t section_htab;
+/* Name of the virtual section assigned to tree level constants. */
+static GTY (()) tree tree_constant_section_name;
+
+/* Return a nonzero value if DECL is a tree level constant. */
+#define DECL_TREE_LEVEL_CONSTANT_P(DECL) \
+ (DECL_SECTION_NAME (DECL) == tree_constant_section_name)
+
/* A table of object_blocks, indexed by section. */
static GTY((param_is (struct object_block))) htab_t object_block_htab;
@@ -1356,6 +1361,13 @@ make_decl_rtl (tree decl)
return;
}
+ /* If this is a tree level constant, just set the pre-computed RTL. */
+ if (TREE_CODE (decl) == VAR_DECL && DECL_TREE_LEVEL_CONSTANT_P (decl))
+ {
+ SET_DECL_RTL (decl, output_constant_def (DECL_INITIAL (decl), 1));
+ return;
+ }
+
name = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl));
if (name[0] != '*' && TREE_CODE (decl) != FUNCTION_DECL
@@ -2200,8 +2212,6 @@ assemble_variable (tree decl, int top_le
if (flag_syntax_only)
return;
- app_disable ();
-
if (! dont_output_data
&& ! host_integerp (DECL_SIZE_UNIT (decl), 1))
{
@@ -2212,6 +2222,19 @@ assemble_variable (tree decl, int top_le
gcc_assert (MEM_P (decl_rtl));
gcc_assert (GET_CODE (XEXP (decl_rtl, 0)) == SYMBOL_REF);
symbol = XEXP (decl_rtl, 0);
+
+ /* If this is the symbol attached to an uniquized constant, just
+ output the constant if it hasn't already been written. */
+ if (TREE_CONSTANT_POOL_ADDRESS_P (symbol))
+ {
+ tree decl = SYMBOL_REF_DECL (symbol);
+ if (!TREE_ASM_WRITTEN (DECL_INITIAL (decl)))
+ output_constant_def_contents (symbol);
+ return;
+ }
+
+ app_disable ();
+
name = XSTR (symbol, 0);
if (TREE_PUBLIC (decl) && DECL_NAME (decl))
notice_global_symbol (decl);
@@ -2792,7 +2815,6 @@ decode_addr_const (tree exp, struct addr
{
if (TREE_CODE (target) == COMPONENT_REF
&& host_integerp (byte_position (TREE_OPERAND (target, 1)), 0))
-
{
offset += int_byte_position (TREE_OPERAND (target, 1));
target = TREE_OPERAND (target, 0);
@@ -2804,6 +2826,11 @@ decode_addr_const (tree exp, struct addr
* tree_low_cst (TREE_OPERAND (target, 1), 0));
target = TREE_OPERAND (target, 0);
}
+ else if (TREE_CODE (target) == INDIRECT_REF
+ && TREE_CODE (TREE_OPERAND (target, 0)) == NOP_EXPR
+ && TREE_CODE (TREE_OPERAND (TREE_OPERAND (target, 0), 0))
+ == ADDR_EXPR)
+ target = TREE_OPERAND (TREE_OPERAND (TREE_OPERAND (target, 0), 0), 0);
else
break;
}
@@ -2844,7 +2871,6 @@ decode_addr_const (tree exp, struct addr
static GTY((param_is (struct constant_descriptor_tree)))
htab_t const_desc_htab;
-static struct constant_descriptor_tree * build_constant_desc (tree);
static void maybe_output_constant_def_contents (struct constant_descriptor_tree *, int);
/* Constant pool accessor function. */
@@ -2896,6 +2922,18 @@ const_hash_1 (const tree exp)
return (const_hash_1 (TREE_REALPART (exp)) * 5
+ const_hash_1 (TREE_IMAGPART (exp)));
+ case VECTOR_CST:
+ {
+ tree link;
+
+ hi = 7 + TYPE_VECTOR_SUBPARTS (TREE_TYPE (exp));
+
+ for (link = TREE_VECTOR_CST_ELTS (exp); link; link = TREE_CHAIN (link))
+ hi = hi * 563 + const_hash_1 (TREE_VALUE (link));
+
+ return hi;
+ }
+
case CONSTRUCTOR:
{
unsigned HOST_WIDE_INT idx;
@@ -3024,6 +3062,27 @@ compare_constant (const tree t1, const t
return (compare_constant (TREE_REALPART (t1), TREE_REALPART (t2))
&& compare_constant (TREE_IMAGPART (t1), TREE_IMAGPART (t2)));
+ case VECTOR_CST:
+ {
+ tree link1, link2;
+
+ if (TYPE_VECTOR_SUBPARTS (TREE_TYPE (t1))
+ != TYPE_VECTOR_SUBPARTS (TREE_TYPE (t2)))
+ return 0;
+
+ link2 = TREE_VECTOR_CST_ELTS (t2);
+ for (link1 = TREE_VECTOR_CST_ELTS (t1);
+ link1;
+ link1 = TREE_CHAIN (link1))
+ {
+ if (!compare_constant (TREE_VALUE (link1), TREE_VALUE (link2)))
+ return 0;
+ link2 = TREE_CHAIN (link2);
+ }
+
+ return 1;
+ }
+
case CONSTRUCTOR:
{
VEC(constructor_elt, gc) *v1, *v2;
@@ -3084,11 +3143,34 @@ compare_constant (const tree t1, const t
case FDESC_EXPR:
{
struct addr_const value1, value2;
+ enum rtx_code code;
+ int ret;
decode_addr_const (t1, &value1);
decode_addr_const (t2, &value2);
- return (value1.offset == value2.offset
- && strcmp (XSTR (value1.base, 0), XSTR (value2.base, 0)) == 0);
+
+ if (value1.offset != value2.offset)
+ return 0;
+
+ code = GET_CODE (value1.base);
+ if (code != GET_CODE (value2.base))
+ return 0;
+
+ switch (code)
+ {
+ case SYMBOL_REF:
+ ret = (strcmp (XSTR (value1.base, 0), XSTR (value2.base, 0)) == 0);
+ break;
+
+ case LABEL_REF:
+ ret = (CODE_LABEL_NUMBER (XEXP (value1.base, 0))
+ == CODE_LABEL_NUMBER (XEXP (value2.base, 0)));
+ break;
+
+ default:
+ gcc_unreachable ();
+ }
+ return ret;
}
case PLUS_EXPR:
@@ -3149,6 +3231,10 @@ copy_constant (tree exp)
return build1 (TREE_CODE (exp), TREE_TYPE (exp),
copy_constant (TREE_OPERAND (exp, 0)));
+ case VECTOR_CST:
+ return build_vector (TREE_TYPE (exp),
+ copy_list (TREE_VECTOR_CST_ELTS (exp)));
+
case CONSTRUCTOR:
{
tree copy = copy_node (exp);
@@ -3173,31 +3259,14 @@ copy_constant (tree exp)
}
}
-/* Return the alignment of constant EXP in bits. */
-
-static unsigned int
-get_constant_alignment (tree exp)
-{
- unsigned int align;
-
- align = TYPE_ALIGN (TREE_TYPE (exp));
-#ifdef CONSTANT_ALIGNMENT
- align = CONSTANT_ALIGNMENT (exp, align);
-#endif
- return align;
-}
-
/* Return the section into which constant EXP should be placed. */
static section *
-get_constant_section (tree exp)
+get_constant_section (tree exp, unsigned int align)
{
- if (IN_NAMED_SECTION (exp))
- return get_named_section (exp, NULL, compute_reloc_for_constant (exp));
- else
- return targetm.asm_out.select_section (exp,
- compute_reloc_for_constant (exp),
- get_constant_alignment (exp));
+ return targetm.asm_out.select_section (exp,
+ compute_reloc_for_constant (exp),
+ align);
}
/* Return the size of constant EXP in bytes. */
@@ -3223,11 +3292,11 @@ get_constant_size (tree exp)
static struct constant_descriptor_tree *
build_constant_desc (tree exp)
{
- rtx symbol;
- rtx rtl;
+ struct constant_descriptor_tree *desc;
+ rtx symbol, rtl;
char label[256];
int labelno;
- struct constant_descriptor_tree *desc;
+ tree decl;
desc = GGC_NEW (struct constant_descriptor_tree);
desc->value = copy_constant (exp);
@@ -3240,17 +3309,41 @@ build_constant_desc (tree exp)
labelno = const_labelno++;
ASM_GENERATE_INTERNAL_LABEL (label, "LC", labelno);
- /* We have a symbol name; construct the SYMBOL_REF and the MEM. */
+ /* Construct the VAR_DECL associated with the constant. */
+ decl = build_decl (UNKNOWN_LOCATION, VAR_DECL, get_identifier (label),
+ TREE_TYPE (exp));
+ DECL_ARTIFICIAL (decl) = 1;
+ DECL_IGNORED_P (decl) = 1;
+ TREE_READONLY (decl) = 1;
+ TREE_STATIC (decl) = 1;
+ TREE_ADDRESSABLE (decl) = 1;
+ DECL_INITIAL (decl) = desc->value;
+ /* ??? We don't set the RTL yet as this would cause cgraph to assume that
+ the variable is referenced. Instead we set the section name to some
+ marker that will be recognized in make_decl_rtl. */
+ DECL_SECTION_NAME (decl) = tree_constant_section_name;
+ /* ??? CONSTANT_ALIGNMENT hasn't been updated for vector types on most
+ architectures so use DATA_ALIGNMENT as well, except for strings. */
+ if (TREE_CODE (exp) == STRING_CST)
+ {
+#ifdef CONSTANT_ALIGNMENT
+ DECL_ALIGN (decl) = CONSTANT_ALIGNMENT (exp, DECL_ALIGN (decl));
+#endif
+ }
+ else
+ align_variable (decl, 0);
+
+ /* Now construct the SYMBOL_REF and the MEM. */
if (use_object_blocks_p ())
{
- section *sect = get_constant_section (exp);
+ section *sect = get_constant_section (exp, DECL_ALIGN (decl));
symbol = create_block_symbol (ggc_strdup (label),
get_block_for_section (sect), -1);
}
else
symbol = gen_rtx_SYMBOL_REF (Pmode, ggc_strdup (label));
SYMBOL_REF_FLAGS (symbol) |= SYMBOL_FLAG_LOCAL;
- SET_SYMBOL_REF_DECL (symbol, desc->value);
+ SET_SYMBOL_REF_DECL (symbol, decl);
TREE_CONSTANT_POOL_ADDRESS_P (symbol) = 1;
rtl = gen_rtx_MEM (TYPE_MODE (TREE_TYPE (exp)), symbol);
@@ -3267,7 +3360,6 @@ build_constant_desc (tree exp)
ASM_OUTPUT_LABELREF will have to know how to strip this
information. This call might invalidate our local variable
SYMBOL; we can't use it afterward. */
-
targetm.encode_section_info (exp, rtl, true);
desc->rtl = rtl;
@@ -3374,7 +3466,8 @@ assemble_constant_contents (tree exp, co
static void
output_constant_def_contents (rtx symbol)
{
- tree exp = SYMBOL_REF_DECL (symbol);
+ tree decl = SYMBOL_REF_DECL (symbol);
+ tree exp = DECL_INITIAL (decl);
unsigned int align;
/* Make sure any other constants whose addresses appear in EXP
@@ -3391,8 +3484,8 @@ output_constant_def_contents (rtx symbol
place_block_symbol (symbol);
else
{
- switch_to_section (get_constant_section (exp));
- align = get_constant_alignment (exp);
+ align = DECL_ALIGN (decl);
+ switch_to_section (get_constant_section (exp, align));
if (align > BITS_PER_UNIT)
ASM_OUTPUT_ALIGN (asm_out_file, floor_log2 (align / BITS_PER_UNIT));
assemble_constant_contents (exp, XSTR (symbol, 0), align);
@@ -3417,6 +3510,37 @@ lookup_constant_def (tree exp)
return (desc ? desc->rtl : NULL_RTX);
}
+
+/* Return a tree representing a reference to constant data in memory
+ for the constant expression EXP.
+
+ This is the counterpart of output_constant_def at the tree level. */
+
+tree
+tree_output_constant_def (tree exp)
+{
+ struct constant_descriptor_tree *desc, key;
+ void **loc;
+ tree decl;
+
+ /* Look up EXP in the table of constant descriptors. If we didn't find
+ it, create a new one. */
+ key.value = exp;
+ key.hash = const_hash_1 (exp);
+ loc = htab_find_slot_with_hash (const_desc_htab, &key, key.hash, INSERT);
+
+ desc = (struct constant_descriptor_tree *) *loc;
+ if (desc == 0)
+ {
+ desc = build_constant_desc (exp);
+ desc->hash = key.hash;
+ *loc = desc;
+ }
+
+ decl = SYMBOL_REF_DECL (XEXP (desc->rtl, 0));
+ varpool_finalize_decl (decl);
+ return decl;
+}
/* Used in the hash tables to avoid outputting the same constant
twice. Unlike 'struct constant_descriptor_tree', RTX constants
@@ -3886,8 +4010,8 @@ mark_constant (rtx *current_rtx, void *d
}
else if (TREE_CONSTANT_POOL_ADDRESS_P (x))
{
- tree exp = SYMBOL_REF_DECL (x);
- if (!TREE_ASM_WRITTEN (exp))
+ tree decl = SYMBOL_REF_DECL (x);
+ if (!TREE_ASM_WRITTEN (DECL_INITIAL (decl)))
{
n_deferred_constants--;
output_constant_def_contents (x);
@@ -5893,6 +6017,7 @@ init_varasm_once (void)
const_desc_eq, NULL);
const_alias_set = new_alias_set ();
+ tree_constant_section_name = build_string (3, "tcs");
shared_constant_pool = create_constant_pool ();
#ifdef TEXT_SECTION_ASM_OP
@@ -6849,8 +6974,8 @@ place_block_symbol (rtx symbol)
else if (TREE_CONSTANT_POOL_ADDRESS_P (symbol))
{
decl = SYMBOL_REF_DECL (symbol);
- alignment = get_constant_alignment (decl);
- size = get_constant_size (decl);
+ alignment = DECL_ALIGN (decl);
+ size = get_constant_size (DECL_INITIAL (decl));
}
else
{
@@ -6996,9 +7121,9 @@ output_object_block (struct object_block
else if (TREE_CONSTANT_POOL_ADDRESS_P (symbol))
{
decl = SYMBOL_REF_DECL (symbol);
- assemble_constant_contents (decl, XSTR (symbol, 0),
- get_constant_alignment (decl));
- offset += get_constant_size (decl);
+ assemble_constant_contents (DECL_INITIAL (decl), XSTR (symbol, 0),
+ DECL_ALIGN (decl));
+ offset += get_constant_size (DECL_INITIAL (decl));
}
else
{
Index: config/arm/unknown-elf.h
===================================================================
--- config/arm/unknown-elf.h (revision 158675)
+++ config/arm/unknown-elf.h (working copy)
@@ -52,7 +52,7 @@
#define PREFERRED_DEBUGGING_TYPE DWARF2_DEBUG
/* Return a nonzero value if DECL has a section attribute. */
-#define IN_NAMED_SECTION(DECL) \
+#define IN_NAMED_SECTION_P(DECL) \
((TREE_CODE (DECL) == FUNCTION_DECL || TREE_CODE (DECL) == VAR_DECL) \
&& DECL_SECTION_NAME (DECL) != NULL_TREE)
@@ -60,7 +60,7 @@
#define ASM_OUTPUT_ALIGNED_BSS(FILE, DECL, NAME, SIZE, ALIGN) \
do \
{ \
- if (IN_NAMED_SECTION (DECL)) \
+ if (IN_NAMED_SECTION_P (DECL)) \
switch_to_section (get_named_section (DECL, NULL, 0)); \
else \
switch_to_section (bss_section); \
@@ -77,7 +77,7 @@
#define ASM_OUTPUT_ALIGNED_DECL_LOCAL(FILE, DECL, NAME, SIZE, ALIGN) \
do \
{ \
- if ((DECL) != NULL && IN_NAMED_SECTION (DECL)) \
+ if ((DECL) != NULL && IN_NAMED_SECTION_P (DECL)) \
switch_to_section (get_named_section (DECL, NULL, 0)); \
else \
switch_to_section (bss_section); \