Hi, this patch makes it possible to fold through aliases. It may seem unimportant, but we run into those cases in C++ where extra name aliases may get used by devirtualization machinery. The patch also fixes the following long standing bug: jh@gcc10:~/trunk/build2/gcc$ cat t.c static int a=4; static int b __attribute__ ((alias("a"))); main() { return b+a; } jh@gcc10:~/trunk/build2/gcc$ gcc -O2 t.c -S jh@gcc10:~/trunk/build2/gcc$ more t.s .file "t.c" .text .p2align 4,,15 .globl main .type main, @function main: .LFB0: .cfi_startproc movl $4, %eax ret .cfi_endproc .LFE0: .size main, .-main .section .rodata .align 4 .type a, @object .size a, 4 a: .long 4 .set b,a .ident "GCC: (Debian 4.4.5-8) 4.4.5" .section .note.GNU-stack,"",@progbits jh@gcc10:~/trunk/build2/gcc$ gcc --version gcc (Debian 4.4.5-8) 4.4.5 Copyright (C) 2010 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
jh@gcc10:~/trunk/build2/gcc$ ./xgcc -B ./ -O2 t.c -S jh@gcc10:~/trunk/build2/gcc$ more t.s .file "t.c" .section .text.startup,"ax",@progbits .p2align 4,,15 .globl main .type main, @function main: .LFB0: .cfi_startproc movl $8, %eax ret .cfi_endproc .LFE0: .size main, .-main .ident "GCC: (GNU) 4.9.0 20130616 (experimental)" .section .note.GNU-stack,"",@progbits The main idea is to replace const_value_known_p predicate by ctor_for_folding that returns the ctor and is able to look through aliases via the symbol table. The huge change in expand_expr_real_1 is really just a reformating. I have bootstrapped/regtested the patch on x86_64-linux and tested with Firefox build. I am running now PPC64 test and will wait for Richard's renaming patch. Honza * cgraph.h (const_value_known_p): Replace by ... (ctor_for_folding): .. this one. * cgraphunit.c (process_function_and_variable_attributes): Use it. * lto-cgraph.c (compute_ltrans_boundary): Use ctor_for_folding. * expr.c (expand_expr_real_1): Likewise. (string_constant): Likewise. * tree-ssa-loop-ivcanon.c (constant_after_peeling): Likewise. * ipa.c (process_references): Likewise. (symtab_remove_unreachable_nodes): Likewise. * ipa-inline-analysis.c (param_change_prob): Likewise. * gimple-fold.c (canonicalize_constructor_val): Likewise. (get_base_constructor): Likwise. * varpool.c (varpool_remove_node): Likewise. (varpool_remove_initializer): LIkewise. (dump_varpool_node): LIkwise. (const_value_known_p): Rewrite to ... (ctor_for_folding): ... this one. * lto-partition.c (add_references_to_partition): Use ctor_for_folding. * gcc.dg/tree-ssa/attr-alias-2.c: New testcase. Index: cgraph.h =================================================================== *** cgraph.h (revision 200147) --- cgraph.h (working copy) *************** void varpool_analyze_node (struct varpoo *** 797,803 **** struct varpool_node * varpool_extra_name_alias (tree, tree); struct varpool_node * varpool_create_variable_alias (tree, tree); void varpool_reset_queue (void); ! bool const_value_known_p (tree); bool varpool_for_node_and_aliases (struct varpool_node *, bool (*) (struct varpool_node *, void *), void *, bool); --- 797,803 ---- struct varpool_node * varpool_extra_name_alias (tree, tree); struct varpool_node * varpool_create_variable_alias (tree, tree); void varpool_reset_queue (void); ! tree ctor_for_folding (tree); bool varpool_for_node_and_aliases (struct varpool_node *, bool (*) (struct varpool_node *, void *), void *, bool); Index: cgraphunit.c =================================================================== *** cgraphunit.c (revision 200147) --- cgraphunit.c (working copy) *************** process_function_and_variable_attributes *** 761,768 **** { tree decl = vnode->symbol.decl; if (DECL_EXTERNAL (decl) ! && DECL_INITIAL (decl) ! && const_value_known_p (decl)) varpool_finalize_decl (decl); if (DECL_PRESERVE_P (decl)) vnode->symbol.force_output = true; --- 761,767 ---- { tree decl = vnode->symbol.decl; if (DECL_EXTERNAL (decl) ! && DECL_INITIAL (decl)) varpool_finalize_decl (decl); if (DECL_PRESERVE_P (decl)) vnode->symbol.force_output = true; Index: testsuite/gcc.dg/tree-ssa/attr-alias-2.c =================================================================== *** testsuite/gcc.dg/tree-ssa/attr-alias-2.c (revision 0) --- testsuite/gcc.dg/tree-ssa/attr-alias-2.c (revision 0) *************** *** 0 **** --- 1,10 ---- + /* { dg-do compile } */ + /* { dg-require-alias "" } */ + /* { dg-options "-O2 -fdump-tree-optimized" } */ + static int a=4; + static int b __attribute__ ((alias("a"))); + main() + { + return b+a; + } + /* { dg-final { scan-tree-dump "return 8" "optimized" } } */ Index: lto-cgraph.c =================================================================== *** lto-cgraph.c (revision 200147) --- lto-cgraph.c (working copy) *************** compute_ltrans_boundary (lto_symtab_enco *** 766,775 **** symtab_node node = lto_symtab_encoder_deref (encoder, i); if (varpool_node *vnode = dyn_cast <varpool_node> (node)) { ! if (DECL_INITIAL (vnode->symbol.decl) ! && !lto_symtab_encoder_encode_initializer_p (encoder, ! vnode) ! && const_value_known_p (vnode->symbol.decl)) { lto_set_symtab_encoder_encode_initializer (encoder, vnode); add_references (encoder, &vnode->symbol.ref_list); --- 766,774 ---- symtab_node node = lto_symtab_encoder_deref (encoder, i); if (varpool_node *vnode = dyn_cast <varpool_node> (node)) { ! if (!lto_symtab_encoder_encode_initializer_p (encoder, ! vnode) ! && ctor_for_folding (vnode->symbol.decl) != error_mark_node) { lto_set_symtab_encoder_encode_initializer (encoder, vnode); add_references (encoder, &vnode->symbol.ref_list); Index: expr.c =================================================================== *** expr.c (revision 200147) --- expr.c (working copy) *************** expand_expr_real_1 (tree exp, rtx target *** 9698,9703 **** --- 9698,9704 ---- { tree array = treeop0; tree index = treeop1; + tree init; /* Fold an expression like: "foo"[2]. This is not done in fold so it won't happen inside &. *************** expand_expr_real_1 (tree exp, rtx target *** 9744,9819 **** && modifier != EXPAND_INITIALIZER && modifier != EXPAND_MEMORY && TREE_READONLY (array) && ! TREE_SIDE_EFFECTS (array) ! && TREE_CODE (array) == VAR_DECL && DECL_INITIAL (array) ! && TREE_CODE (DECL_INITIAL (array)) != ERROR_MARK ! && const_value_known_p (array)) { ! if (TREE_CODE (index) == INTEGER_CST) { ! tree init = DECL_INITIAL (array); ! if (TREE_CODE (init) == CONSTRUCTOR) ! { ! unsigned HOST_WIDE_INT ix; ! tree field, value; ! FOR_EACH_CONSTRUCTOR_ELT (CONSTRUCTOR_ELTS (init), ix, ! field, value) ! if (tree_int_cst_equal (field, index)) { ! if (TREE_SIDE_EFFECTS (value)) break; ! if (TREE_CODE (value) == CONSTRUCTOR) ! { ! /* If VALUE is a CONSTRUCTOR, this ! optimization is only useful if ! this doesn't store the CONSTRUCTOR ! into memory. If it does, it is more ! efficient to just load the data from ! the array directly. */ ! rtx ret = expand_constructor (value, target, ! modifier, true); ! if (ret == NULL_RTX) ! break; ! } ! return expand_expr (fold (value), target, tmode, ! modifier); ! } ! } ! else if(TREE_CODE (init) == STRING_CST) { ! tree index1 = index; ! tree low_bound = array_ref_low_bound (exp); ! index1 = fold_convert_loc (loc, sizetype, ! treeop1); ! ! /* Optimize the special-case of a zero lower bound. ! ! We convert the low_bound to sizetype to avoid some problems ! with constant folding. (E.g. suppose the lower bound is 1, ! and its mode is QI. Without the conversion,l (ARRAY ! +(INDEX-(unsigned char)1)) becomes ((ARRAY+(-(unsigned char)1)) ! +INDEX), which becomes (ARRAY+255+INDEX). Opps!) */ ! ! if (! integer_zerop (low_bound)) ! index1 = size_diffop_loc (loc, index1, ! fold_convert_loc (loc, sizetype, ! low_bound)); ! ! if (0 > compare_tree_int (index1, ! TREE_STRING_LENGTH (init))) ! { ! tree type = TREE_TYPE (TREE_TYPE (init)); ! enum machine_mode mode = TYPE_MODE (type); ! if (GET_MODE_CLASS (mode) == MODE_INT ! && GET_MODE_SIZE (mode) == 1) ! return gen_int_mode (TREE_STRING_POINTER (init) ! [TREE_INT_CST_LOW (index1)], ! mode); ! } } } } --- 9745,9816 ---- && modifier != EXPAND_INITIALIZER && modifier != EXPAND_MEMORY && TREE_READONLY (array) && ! TREE_SIDE_EFFECTS (array) ! && TREE_CODE (index) == INTEGER_CST ! && (TREE_CODE (array) == VAR_DECL ! || TREE_CODE (array) == CONST_DECL) ! && (init = ctor_for_folding (array)) != error_mark_node) { ! if (TREE_CODE (init) == CONSTRUCTOR) { ! unsigned HOST_WIDE_INT ix; ! tree field, value; ! FOR_EACH_CONSTRUCTOR_ELT (CONSTRUCTOR_ELTS (init), ix, ! field, value) ! if (tree_int_cst_equal (field, index)) ! { ! if (TREE_SIDE_EFFECTS (value)) ! break; ! if (TREE_CODE (value) == CONSTRUCTOR) { ! /* If VALUE is a CONSTRUCTOR, this ! optimization is only useful if ! this doesn't store the CONSTRUCTOR ! into memory. If it does, it is more ! efficient to just load the data from ! the array directly. */ ! rtx ret = expand_constructor (value, target, ! modifier, true); ! if (ret == NULL_RTX) break; + } ! return expand_expr (fold (value), target, tmode, ! modifier); ! } ! } ! else if(TREE_CODE (init) == STRING_CST) ! { ! tree index1 = index; ! tree low_bound = array_ref_low_bound (exp); ! index1 = fold_convert_loc (loc, sizetype, ! treeop1); ! ! /* Optimize the special-case of a zero lower bound. ! ! We convert the low_bound to sizetype to avoid some problems ! with constant folding. (E.g. suppose the lower bound is 1, ! and its mode is QI. Without the conversion,l (ARRAY ! +(INDEX-(unsigned char)1)) becomes ((ARRAY+(-(unsigned char)1)) ! +INDEX), which becomes (ARRAY+255+INDEX). Opps!) */ ! ! if (! integer_zerop (low_bound)) ! index1 = size_diffop_loc (loc, index1, ! fold_convert_loc (loc, sizetype, ! low_bound)); ! if (0 > compare_tree_int (index1, ! TREE_STRING_LENGTH (init))) { ! tree type = TREE_TYPE (TREE_TYPE (init)); ! enum machine_mode mode = TYPE_MODE (type); ! if (GET_MODE_CLASS (mode) == MODE_INT ! && GET_MODE_SIZE (mode) == 1) ! return gen_int_mode (TREE_STRING_POINTER (init) ! [TREE_INT_CST_LOW (index1)], ! mode); } } } *************** string_constant (tree arg, tree *ptr_off *** 10676,10692 **** || TREE_CODE (array) == CONST_DECL) { int length; /* Variables initialized to string literals can be handled too. */ ! if (!const_value_known_p (array) ! || !DECL_INITIAL (array) ! || TREE_CODE (DECL_INITIAL (array)) != STRING_CST) return 0; /* Avoid const char foo[4] = "abcde"; */ if (DECL_SIZE_UNIT (array) == NULL_TREE || TREE_CODE (DECL_SIZE_UNIT (array)) != INTEGER_CST ! || (length = TREE_STRING_LENGTH (DECL_INITIAL (array))) <= 0 || compare_tree_int (DECL_SIZE_UNIT (array), length) < 0) return 0; --- 10673,10690 ---- || TREE_CODE (array) == CONST_DECL) { int length; + tree init = ctor_for_folding (array); /* Variables initialized to string literals can be handled too. */ ! if (init == error_mark_node ! || !init ! || TREE_CODE (init) != STRING_CST) return 0; /* Avoid const char foo[4] = "abcde"; */ if (DECL_SIZE_UNIT (array) == NULL_TREE || TREE_CODE (DECL_SIZE_UNIT (array)) != INTEGER_CST ! || (length = TREE_STRING_LENGTH (init)) <= 0 || compare_tree_int (DECL_SIZE_UNIT (array), length) < 0) return 0; *************** string_constant (tree arg, tree *ptr_off *** 10699,10705 **** return 0; *ptr_offset = offset; ! return DECL_INITIAL (array); } return 0; --- 10697,10703 ---- return 0; *ptr_offset = offset; ! return init; } return 0; Index: tree-ssa-loop-ivcanon.c =================================================================== *** tree-ssa-loop-ivcanon.c (revision 200147) --- tree-ssa-loop-ivcanon.c (working copy) *************** constant_after_peeling (tree op, gimple *** 174,180 **** while (handled_component_p (base)) base = TREE_OPERAND (base, 0); if ((DECL_P (base) ! && const_value_known_p (base)) || CONSTANT_CLASS_P (base)) { /* If so, see if we understand all the indices. */ --- 174,180 ---- while (handled_component_p (base)) base = TREE_OPERAND (base, 0); if ((DECL_P (base) ! && ctor_for_folding (base) != error_mark_node) || CONSTANT_CLASS_P (base)) { /* If so, see if we understand all the indices. */ Index: ipa.c =================================================================== *** ipa.c (revision 200147) --- ipa.c (working copy) *************** process_references (struct ipa_ref_list *** 145,151 **** constant folding. Keep references alive so partitioning knows about potential references. */ || (TREE_CODE (node->symbol.decl) == VAR_DECL ! && flag_wpa && const_value_known_p (node->symbol.decl))))) pointer_set_insert (reachable, node); enqueue_node ((symtab_node) node, first, reachable); } --- 145,153 ---- constant folding. Keep references alive so partitioning knows about potential references. */ || (TREE_CODE (node->symbol.decl) == VAR_DECL ! && flag_wpa ! && ctor_for_folding (node->symbol.decl) ! != error_mark_node)))) pointer_set_insert (reachable, node); enqueue_node ((symtab_node) node, first, reachable); } *************** symtab_remove_unreachable_nodes (bool be *** 400,405 **** --- 402,408 ---- } else if (!pointer_set_contains (reachable, vnode)) { + tree init; if (vnode->symbol.definition) { if (file) *************** symtab_remove_unreachable_nodes (bool be *** 411,418 **** vnode->symbol.aux = NULL; /* Keep body if it may be useful for constant folding. */ ! if (!const_value_known_p (vnode->symbol.decl)) varpool_remove_initializer (vnode); ipa_remove_all_references (&vnode->symbol.ref_list); } else --- 414,423 ---- vnode->symbol.aux = NULL; /* Keep body if it may be useful for constant folding. */ ! if ((init = ctor_for_folding (vnode->symbol.decl)) == error_mark_node) varpool_remove_initializer (vnode); + else + DECL_INITIAL (vnode->symbol.decl) = init; ipa_remove_all_references (&vnode->symbol.ref_list); } else Index: ipa-inline-analysis.c =================================================================== *** ipa-inline-analysis.c (revision 200147) --- ipa-inline-analysis.c (working copy) *************** param_change_prob (gimple stmt, int i) *** 2106,2113 **** struct record_modified_bb_info info; bitmap_iterator bi; unsigned index; ! if (const_value_known_p (base)) return 0; if (!bb->frequency) return REG_BR_PROB_BASE; --- 2106,2114 ---- struct record_modified_bb_info info; bitmap_iterator bi; unsigned index; + tree init = ctor_for_folding (base); ! if (init != error_mark_node) return 0; if (!bb->frequency) return REG_BR_PROB_BASE; Index: gimple-fold.c =================================================================== *** gimple-fold.c (revision 200147) --- gimple-fold.c (working copy) *************** canonicalize_constructor_val (tree cval, *** 192,200 **** tree get_symbol_constant_value (tree sym) { ! if (const_value_known_p (sym)) { - tree val = DECL_INITIAL (sym); if (val) { val = canonicalize_constructor_val (unshare_expr (val), sym); --- 192,200 ---- tree get_symbol_constant_value (tree sym) { ! tree val = ctor_for_folding (sym); ! if (val != error_mark_node) { if (val) { val = canonicalize_constructor_val (unshare_expr (val), sym); *************** get_base_constructor (tree base, HOST_WI *** 2695,2713 **** switch (TREE_CODE (base)) { case VAR_DECL: - if (!const_value_known_p (base)) - return NULL_TREE; - - /* Fallthru. */ case CONST_DECL: ! if (!DECL_INITIAL (base) ! && (TREE_STATIC (base) || DECL_EXTERNAL (base))) ! return error_mark_node; ! /* Do not return an error_mark_node DECL_INITIAL. LTO uses this ! as special marker (_not_ zero ...) for its own purposes. */ ! if (DECL_INITIAL (base) == error_mark_node) ! return NULL_TREE; ! return DECL_INITIAL (base); case ARRAY_REF: case COMPONENT_REF: --- 2695,2712 ---- switch (TREE_CODE (base)) { case VAR_DECL: case CONST_DECL: ! { ! tree init = ctor_for_folding (base); ! ! /* Our semantic is exact oposite of ctor_for_folding; ! NULL means unknown, while error_mark_node is 0. */ ! if (init == error_mark_node) ! return NULL_TREE; ! if (!init) ! return error_mark_node; ! return init; ! } case ARRAY_REF: case COMPONENT_REF: Index: lto/lto-partition.c =================================================================== *** lto/lto-partition.c (revision 200147) --- lto/lto-partition.c (working copy) *************** add_references_to_partition (ltrans_part *** 146,152 **** Recursively look into the initializers of the constant variable and add references, too. */ else if (is_a <varpool_node> (ref->referred) ! && const_value_known_p (ref->referred->symbol.decl) && !lto_symtab_encoder_in_partition_p (part->encoder, ref->referred)) { if (!part->initializers_visited) --- 146,152 ---- Recursively look into the initializers of the constant variable and add references, too. */ else if (is_a <varpool_node> (ref->referred) ! && ctor_for_folding (ref->referred->symbol.decl) != error_mark_node && !lto_symtab_encoder_in_partition_p (part->encoder, ref->referred)) { if (!part->initializers_visited) Index: varpool.c =================================================================== *** varpool.c (revision 200147) --- varpool.c (working copy) *************** void *** 66,77 **** varpool_remove_node (struct varpool_node *node) { symtab_unregister_node ((symtab_node)node); /* Because we remove references from external functions before final compilation, we may end up removing useful constructors. FIXME: We probably want to trace boundaries better. */ ! if (!const_value_known_p (node->symbol.decl)) varpool_remove_initializer (node); ggc_free (node); } --- 66,80 ---- varpool_remove_node (struct varpool_node *node) { symtab_unregister_node ((symtab_node)node); + tree init; /* Because we remove references from external functions before final compilation, we may end up removing useful constructors. FIXME: We probably want to trace boundaries better. */ ! if ((init = ctor_for_folding (node->symbol.decl)) == error_mark_node) varpool_remove_initializer (node); + else + DECL_INITIAL (node->symbol.decl) = init; ggc_free (node); } *************** varpool_remove_initializer (struct varpo *** 84,90 **** /* Keep vtables for BINFO folding. */ && !DECL_VIRTUAL_P (node->symbol.decl) /* FIXME: http://gcc.gnu.org/PR55395 */ ! && debug_info_level == DINFO_LEVEL_NONE) DECL_INITIAL (node->symbol.decl) = error_mark_node; } --- 87,96 ---- /* Keep vtables for BINFO folding. */ && !DECL_VIRTUAL_P (node->symbol.decl) /* FIXME: http://gcc.gnu.org/PR55395 */ ! && debug_info_level == DINFO_LEVEL_NONE ! /* During LTO streaming we may have multiple nodes ! associated to a given decl. */ ! && cgraph_state != CGRAPH_LTO_STREAMING) DECL_INITIAL (node->symbol.decl) = error_mark_node; } *************** dump_varpool_node (FILE *f, struct varpo *** 104,110 **** fprintf (f, " output"); if (TREE_READONLY (node->symbol.decl)) fprintf (f, " read-only"); ! if (const_value_known_p (node->symbol.decl)) fprintf (f, " const-value-known"); fprintf (f, "\n"); } --- 110,116 ---- fprintf (f, " output"); if (TREE_READONLY (node->symbol.decl)) fprintf (f, " read-only"); ! if (ctor_for_folding (node->symbol.decl) != error_mark_node) fprintf (f, " const-value-known"); fprintf (f, "\n"); } *************** varpool_node_for_asm (tree asmname) *** 139,182 **** } /* Return if DECL is constant and its initial value is known (so we can do ! constant folding using DECL_INITIAL (decl)). */ ! bool ! const_value_known_p (tree decl) { if (TREE_CODE (decl) != VAR_DECL ! &&TREE_CODE (decl) != CONST_DECL) ! return false; if (TREE_CODE (decl) == CONST_DECL || DECL_IN_CONSTANT_POOL (decl)) ! return true; ! ! gcc_assert (TREE_CODE (decl) == VAR_DECL); ! if (!TREE_READONLY (decl) || TREE_THIS_VOLATILE (decl)) ! return false; ! /* Gimplifier takes away constructors of local vars */ if (!TREE_STATIC (decl) && !DECL_EXTERNAL (decl)) ! return DECL_INITIAL (decl) != NULL; ! gcc_assert (TREE_STATIC (decl) || DECL_EXTERNAL (decl)); /* Variables declared 'const' without an initializer have zero as the initializer if they may not be overridden at link or run time. */ ! if (!DECL_INITIAL (decl) ! && (DECL_EXTERNAL (decl) ! || decl_replaceable_p (decl))) ! return false; /* Variables declared `const' with an initializer are considered to not be overwritable with different initializer by default. ??? Previously we behaved so for scalar variables but not for array accesses. */ ! return true; } /* Add the variable DECL to the varpool. --- 145,237 ---- } /* Return if DECL is constant and its initial value is known (so we can do ! constant folding using DECL_INITIAL (decl)). ! Return ERROR_MARK_NODE when value is unknown. */ ! tree ! ctor_for_folding (tree decl) { + struct varpool_node *node, *real_node; + tree real_decl; + if (TREE_CODE (decl) != VAR_DECL ! && TREE_CODE (decl) != CONST_DECL) ! return error_mark_node; if (TREE_CODE (decl) == CONST_DECL || DECL_IN_CONSTANT_POOL (decl)) ! return DECL_INITIAL (decl); ! if (TREE_THIS_VOLATILE (decl)) ! return error_mark_node; ! /* Do not care about automatic variables. Those are never initialized ! anyway, because gimplifier exapnds the code*/ if (!TREE_STATIC (decl) && !DECL_EXTERNAL (decl)) ! { ! gcc_assert (!TREE_PUBLIC (decl)); ! return error_mark_node; ! } ! ! gcc_assert (TREE_CODE (decl) == VAR_DECL); ! ! node = varpool_get_node (decl); ! if (node) ! { ! real_node = varpool_variable_node (node); ! real_decl = real_node->symbol.decl; ! } ! else ! real_decl = decl; ! /* See if we are dealing with alias. ! In most cases alias is just alternative symbol pointing to a given ! constructor. This allows us to use interposition rules of DECL ! constructor of REAL_NODE. However weakrefs are special by being just ! alternative name of their target (if defined). */ ! if (decl != real_decl) ! { ! gcc_assert (!DECL_INITIAL (decl) ! || DECL_INITIAL (decl) == error_mark_node); ! if (lookup_attribute ("weakref", DECL_ATTRIBUTES (decl))) ! { ! node = varpool_alias_target (node); ! decl = node->symbol.decl; ! } ! } ! ! /* Vtables are defined by their types and must match no matter of interposition ! rules. */ ! if (DECL_VIRTUAL_P (real_decl)) ! { ! gcc_checking_assert (TREE_READONLY (real_decl)); ! return DECL_INITIAL (real_decl); ! } ! ! /* If thre is no constructor, we have nothing to do. */ ! if (DECL_INITIAL (real_decl) == error_mark_node) ! return error_mark_node; ! ! /* Non-readonly alias of readonly variable is also de-facto readonly, ! because the variable itself is in readonly section. ! We also honnor READONLY flag on alias assuming that user knows ! what he is doing. */ ! if (!TREE_READONLY (decl) && !TREE_READONLY (real_decl)) ! return error_mark_node; /* Variables declared 'const' without an initializer have zero as the initializer if they may not be overridden at link or run time. */ ! if (!DECL_INITIAL (real_decl) ! && (DECL_EXTERNAL (decl) || decl_replaceable_p (decl))) ! return error_mark_node; /* Variables declared `const' with an initializer are considered to not be overwritable with different initializer by default. ??? Previously we behaved so for scalar variables but not for array accesses. */ ! return DECL_INITIAL (real_decl); } /* Add the variable DECL to the varpool.