| Submitter | Jan Hubicka |
|---|---|
| Date | Sept. 4, 2010, 11:16 p.m. |
| Message ID | <20100904231611.GA17338@atrey.karlin.mff.cuni.cz> |
| Download | mbox | patch |
| Permalink | /patch/63811/ |
| State | New |
| Headers | show |
Comments
On Sun, 5 Sep 2010, Jan Hubicka wrote: > Hi, > while analyzing the remianing problems with folding in gfortran testsuite I noticed > that there is actual bug in way fold_const_aggregate_ref handle folding of > array refs into STRING_CST because it ignores lower bound. > Testaces in testsuite are lucky that with ignoring lower bound we get out of > string and thus do not fold, but this should get into real wrong code bugs > if gfortran was not forgetting to set TREE_STATIC flags on initializers and thus > folding happent at all. > > Fixed thus (by copying code from expr.c) As said, please split out the lower-bound fix, we might want to backport it. More comments below. > Honza > > /* { dg-do compile } */ > /* { dg-options "-O2 -fdump-tree-optimized" } */ > enum > { > ERROR_OK, ERROR_UNKNOWN, > ERROR_NUM > }; > enum > { __LC_CTYPE = 0, __LC_NUMERIC = 1, __LC_TIME = 2, __LC_COLLATE = > 3, __LC_MONETARY = 4, __LC_MESSAGES = 5, __LC_ALL = 6, __LC_PAPER = > 10, __LC_MEASUREMENT = 11, __LC_IDENTIFICATION = 12 }; > static const char *const _messages[] = { > "no error", "unknown error", "Internal error: unknown reason", > }; > elf_errmsg (int err) > { > if (err < 0 || err >= ERROR_NUM || _messages[err] == ((void *) 0)) > { > err = ERROR_UNKNOWN; > } > return _messages[err]; > } > > /* We should fold _messages[1]. */ > /* { dg-final { scan-tree-dump "unknown error" "optimized"} } */ > /* { dg-final { cleanup-tree-dump "optimized" } } */ > > Index: tree-ssa-sccvn.c > * gimple.h (fold_const_array_ref): Declare. > * tree-ssa-sccvn.c (fully_constant_vn_reference_p): Fold initialized > readonly static vars and array references into them. > * tree-ssa-ccp.c (fold_const_array_ref): Break out from ...; fix handling > of lower_bound. > (fold_const_aggregate_ref): ... here. > =================================================================== > --- tree-ssa-sccvn.c (revision 163862) > +++ tree-ssa-sccvn.c (working copy) > @@ -1109,24 +1109,57 @@ fully_constant_vn_reference_p (vn_refere > return folded; > } > } > - > - /* Simplify reads from constant strings. */ > + else if (op->opcode == VAR_DECL > + || op->opcode == CONST_DECL) > + return get_symbol_constant_value (op->op0); This needs a check for is_gimple_min_invariant and an unshare_expr. > else if (op->opcode == ARRAY_REF > - && TREE_CODE (op->op0) == INTEGER_CST > - && integer_zerop (op->op1) > && VEC_length (vn_reference_op_s, operands) == 2) > { > vn_reference_op_t arg0; > + tree ctor = NULL_TREE; > + tree op0; > + > arg0 = VEC_index (vn_reference_op_s, operands, 1); > - if (arg0->opcode == STRING_CST > - && (TYPE_MODE (op->type) > - == TYPE_MODE (TREE_TYPE (TREE_TYPE (arg0->op0)))) > - && GET_MODE_CLASS (TYPE_MODE (op->type)) == MODE_INT > - && GET_MODE_SIZE (TYPE_MODE (op->type)) == 1 > - && compare_tree_int (op->op0, TREE_STRING_LENGTH (arg0->op0)) < 0) > - return build_int_cst_type (op->type, > - (TREE_STRING_POINTER (arg0->op0) > - [TREE_INT_CST_LOW (op->op0)])); > + op0 = arg0->op0; > + > + switch (arg0->opcode) > + { > + case MEM_REF: > + /* ??? We could handle this case. */ > + if (!integer_zerop (TREE_OPERAND (op0, 1))) > + return NULL_TREE; > + op0 = get_base_address (op0); > + if (!op0 > + || TREE_CODE (op0) != VAR_DECL) > + return NULL_TREE; > + > + /* Fallthru. */ > + case VAR_DECL: > + if (!TREE_READONLY (op0) > + || TREE_CODE (TREE_TYPE (op0)) != ARRAY_TYPE > + || ((TREE_STATIC (op0) || DECL_EXTERNAL (op0)) > + && !varpool_get_node (op0)->const_value_known)) well, also for !(TREE_STATIC (op0) || DECL_EXTERNAL (op0)), right? Thus, > + if (!TREE_READONLY (op0) > + || TREE_CODE (TREE_TYPE (op0)) != ARRAY_TYPE || !(TREE_STATIC (op0) || DECL_EXTERNAL (op0)) || !varpool_get_node (op0)->const_value_known) > + return NULL_TREE; > + > + ctor = DECL_INITIAL (op0); > + break; > + > + case ARRAY_REF: > + case COMPONENT_REF: > + gcc_unreachable (); Please no unreachables here, the default case should simply return NULL_TREE. > + break; > + > + case STRING_CST: > + case CONSTRUCTOR: > + ctor = op0; > + break; > + > + default: return NULL_TREE; > + break; > + } > + gcc_assert (op->op1); No need to assert that. > + if (ctor) > + return fold_const_array_ref (op->type, ctor, op->op0, op->op1); > } > > return NULL_TREE; > Index: tree-ssa-ccp.c > =================================================================== > --- tree-ssa-ccp.c (revision 163862) > +++ tree-ssa-ccp.c (working copy) > @@ -1310,6 +1310,65 @@ ccp_fold (gimple stmt) > } > } > > +/* Fold array reference of TYPE into a variable with known value CTOR > + with known index IDX. */ > +tree > +fold_const_array_ref (tree type, tree ctor, tree idx, tree low_bound) > +{ > + unsigned HOST_WIDE_INT cnt; > + tree cfield, cval; > + tree tem; > + > + if (ctor == NULL_TREE > + || (TREE_CODE (ctor) != CONSTRUCTOR > + && TREE_CODE (ctor) != STRING_CST) > + || !TREE_STATIC (ctor)) > + return NULL_TREE; > + > + /* Get the index. If we have an SSA_NAME, try to resolve it > + with the current lattice value for the SSA_NAME. */ > + switch (TREE_CODE (idx)) > + { > + case SSA_NAME: > + if ((tem = get_constant_value (idx)) > + && TREE_CODE (tem) == INTEGER_CST) > + idx = tem; > + else > + return NULL_TREE; > + break; > + > + case INTEGER_CST: > + break; > + > + default: > + return NULL_TREE; > + } > + > + /* Fold read from constant string. */ > + if (TREE_CODE (ctor) == STRING_CST) > + { > + tree index = fold_convert (sizetype, idx); > + if (low_bound && !integer_zerop (low_bound)) > + index = size_diffop (index, fold_convert (sizetype, low_bound)); Instead of playing games with size_diffop here please reject non-INTEGER_CST low_bound early, do the arithmetic with double-ints and .. > + if ((TYPE_MODE (type) > + == TYPE_MODE (TREE_TYPE (TREE_TYPE (ctor)))) > + && (GET_MODE_CLASS (TYPE_MODE (TREE_TYPE (TREE_TYPE (ctor)))) > + == MODE_INT) > + && GET_MODE_SIZE (TYPE_MODE (TREE_TYPE (TREE_TYPE (ctor)))) == 1 > + && compare_tree_int (index, TREE_STRING_LENGTH (ctor)) < 0) .. the comparison here as well. Avoids building trees and should speed up things as well. Otherwise the patch looks good. Thanks, Richard. > + return build_int_cst_type (type, > + (TREE_STRING_POINTER (ctor) > + [TREE_INT_CST_LOW (index)])); > + return NULL_TREE; > + } > + > + /* Whoo-hoo! I'll fold ya baby. Yeah! */ > + FOR_EACH_CONSTRUCTOR_ELT (CONSTRUCTOR_ELTS (ctor), cnt, cfield, cval) > + if (tree_int_cst_equal (cfield, idx)) > + return canonicalize_constructor_val (cval); > + return NULL_TREE; > +} > + > /* Return the tree representing the element referenced by T if T is an > ARRAY_REF or COMPONENT_REF into constant aggregates. Return > NULL_TREE otherwise. */ > @@ -1332,11 +1391,11 @@ fold_const_aggregate_ref (tree t) > switch (TREE_CODE (t)) > { > case ARRAY_REF: > + base = TREE_OPERAND (t, 0); > /* Get a CONSTRUCTOR. If BASE is a VAR_DECL, get its > DECL_INITIAL. If BASE is a nested reference into another > ARRAY_REF or COMPONENT_REF, make a recursive call to resolve > the inner reference. */ > - base = TREE_OPERAND (t, 0); > switch (TREE_CODE (base)) > { > case MEM_REF: > @@ -1373,51 +1432,10 @@ fold_const_aggregate_ref (tree t) > return NULL_TREE; > } > > - if (ctor == NULL_TREE > - || (TREE_CODE (ctor) != CONSTRUCTOR > - && TREE_CODE (ctor) != STRING_CST) > - || !TREE_STATIC (ctor)) > - return NULL_TREE; > - > - /* Get the index. If we have an SSA_NAME, try to resolve it > - with the current lattice value for the SSA_NAME. */ > - idx = TREE_OPERAND (t, 1); > - switch (TREE_CODE (idx)) > - { > - case SSA_NAME: > - if ((tem = get_constant_value (idx)) > - && TREE_CODE (tem) == INTEGER_CST) > - idx = tem; > - else > - return NULL_TREE; > - break; > - > - case INTEGER_CST: > - break; > - > - default: > - return NULL_TREE; > - } > - > - /* Fold read from constant string. */ > - if (TREE_CODE (ctor) == STRING_CST) > - { > - if ((TYPE_MODE (TREE_TYPE (t)) > - == TYPE_MODE (TREE_TYPE (TREE_TYPE (ctor)))) > - && (GET_MODE_CLASS (TYPE_MODE (TREE_TYPE (TREE_TYPE (ctor)))) > - == MODE_INT) > - && GET_MODE_SIZE (TYPE_MODE (TREE_TYPE (TREE_TYPE (ctor)))) == 1 > - && compare_tree_int (idx, TREE_STRING_LENGTH (ctor)) < 0) > - return build_int_cst_type (TREE_TYPE (t), > - (TREE_STRING_POINTER (ctor) > - [TREE_INT_CST_LOW (idx)])); > - return NULL_TREE; > - } > - > - /* Whoo-hoo! I'll fold ya baby. Yeah! */ > - FOR_EACH_CONSTRUCTOR_ELT (CONSTRUCTOR_ELTS (ctor), cnt, cfield, cval) > - if (tree_int_cst_equal (cfield, idx)) > - return canonicalize_constructor_val (cval); > + return fold_const_array_ref (TREE_TYPE (t), > + ctor, > + TREE_OPERAND (t, 1), > + array_ref_low_bound (t)); > break; > > case COMPONENT_REF: > >
> > - > > - /* Simplify reads from constant strings. */ > > + else if (op->opcode == VAR_DECL > > + || op->opcode == CONST_DECL) > > + return get_symbol_constant_value (op->op0); > > This needs a check for is_gimple_min_invariant and an unshare_expr. get_symbol_constant_value is already testing is_gimple_min_invariant. I guess if unshare_expr belong here, it would need to be around about every get_symbol_constant_value use? > > + if (!TREE_READONLY (op0) > > + || TREE_CODE (TREE_TYPE (op0)) != ARRAY_TYPE > > + || ((TREE_STATIC (op0) || DECL_EXTERNAL (op0)) > > + && !varpool_get_node (op0)->const_value_known)) > > well, also for !(TREE_STATIC (op0) || DECL_EXTERNAL (op0)), right? Thus, Hmm, yes. Perhaps this can be simplified at gimple, because local vars are not readonly? > > > + if (!TREE_READONLY (op0) > > + || TREE_CODE (TREE_TYPE (op0)) != ARRAY_TYPE > || !(TREE_STATIC (op0) || DECL_EXTERNAL (op0)) > || !varpool_get_node (op0)->const_value_known) > > + return NULL_TREE; > > + > > + ctor = DECL_INITIAL (op0); > > + break; > > + > > + case ARRAY_REF: > > + case COMPONENT_REF: > > + gcc_unreachable (); > > Please no unreachables here, the default case should simply > return NULL_TREE. I was basically interested if functio never need to recursive. Just returning NULL_TREE would result in missed optimization on nested references. > > + /* Fold read from constant string. */ > > + if (TREE_CODE (ctor) == STRING_CST) > > + { > > + tree index = fold_convert (sizetype, idx); > > + if (low_bound && !integer_zerop (low_bound)) > > + index = size_diffop (index, fold_convert (sizetype, low_bound)); > > Instead of playing games with size_diffop here please reject > non-INTEGER_CST low_bound early, do the arithmetic with > double-ints and .. > > > + if ((TYPE_MODE (type) > > + == TYPE_MODE (TREE_TYPE (TREE_TYPE (ctor)))) > > + && (GET_MODE_CLASS (TYPE_MODE (TREE_TYPE (TREE_TYPE (ctor)))) > > + == MODE_INT) > > + && GET_MODE_SIZE (TYPE_MODE (TREE_TYPE (TREE_TYPE (ctor)))) == 1 > > + && compare_tree_int (index, TREE_STRING_LENGTH (ctor)) < 0) > > .. the comparison here as well. Avoids building trees and should > speed up things as well. Hmm, OK. This is copied from code that appears in fold_constat_string_reference and few other places. I guess i will first break out the low bound check and update all the uses to do double int. Thanks, Honza
On Mon, 6 Sep 2010, Jan Hubicka wrote: > > > - > > > - /* Simplify reads from constant strings. */ > > > + else if (op->opcode == VAR_DECL > > > + || op->opcode == CONST_DECL) > > > + return get_symbol_constant_value (op->op0); > > > > This needs a check for is_gimple_min_invariant and an unshare_expr. > > get_symbol_constant_value is already testing is_gimple_min_invariant. Hm. I thought this is an implementation detail of the uses, but you are right. > I guess if unshare_expr belong here, it would need to be around about > every get_symbol_constant_value use? Yes. Or rather at the point the constants are put into a new use in the IL (for CCP this is at substitution time). It might be that eliminate () already does the unsharing. > > > + if (!TREE_READONLY (op0) > > > + || TREE_CODE (TREE_TYPE (op0)) != ARRAY_TYPE > > > + || ((TREE_STATIC (op0) || DECL_EXTERNAL (op0)) > > > + && !varpool_get_node (op0)->const_value_known)) > > > > well, also for !(TREE_STATIC (op0) || DECL_EXTERNAL (op0)), right? Thus, > > Hmm, yes. > Perhaps this can be simplified at gimple, because local vars are not readonly? What do you mean by simplified? > > > + if (!TREE_READONLY (op0) > > > + || TREE_CODE (TREE_TYPE (op0)) != ARRAY_TYPE > > || !(TREE_STATIC (op0) || DECL_EXTERNAL (op0)) > > || !varpool_get_node (op0)->const_value_known) > > > + return NULL_TREE; > > > + > > > + ctor = DECL_INITIAL (op0); > > > + break; > > > + > > > + case ARRAY_REF: > > > + case COMPONENT_REF: > > > + gcc_unreachable (); > > > > Please no unreachables here, the default case should simply > > return NULL_TREE. > > I was basically interested if functio never need to recursive. Just returning NULL_TREE > would result in missed optimization on nested references. But we don't want to crash. Maybe for your debugging, but not in the final patch. > > > + /* Fold read from constant string. */ > > > + if (TREE_CODE (ctor) == STRING_CST) > > > + { > > > + tree index = fold_convert (sizetype, idx); > > > + if (low_bound && !integer_zerop (low_bound)) > > > + index = size_diffop (index, fold_convert (sizetype, low_bound)); > > > > Instead of playing games with size_diffop here please reject > > non-INTEGER_CST low_bound early, do the arithmetic with > > double-ints and .. > > > > > + if ((TYPE_MODE (type) > > > + == TYPE_MODE (TREE_TYPE (TREE_TYPE (ctor)))) > > > + && (GET_MODE_CLASS (TYPE_MODE (TREE_TYPE (TREE_TYPE (ctor)))) > > > + == MODE_INT) > > > + && GET_MODE_SIZE (TYPE_MODE (TREE_TYPE (TREE_TYPE (ctor)))) == 1 > > > + && compare_tree_int (index, TREE_STRING_LENGTH (ctor)) < 0) > > > > .. the comparison here as well. Avoids building trees and should > > speed up things as well. > > Hmm, OK. This is copied from code that appears in fold_constat_string_reference > and few other places. I guess i will first break out the low bound check and update > all the uses to do double int. Thx. Richard.
Patch
Index: tree-ssa-sccvn.c * gimple.h (fold_const_array_ref): Declare. * tree-ssa-sccvn.c (fully_constant_vn_reference_p): Fold initialized readonly static vars and array references into them. * tree-ssa-ccp.c (fold_const_array_ref): Break out from ...; fix handling of lower_bound. (fold_const_aggregate_ref): ... here. =================================================================== --- tree-ssa-sccvn.c (revision 163862) +++ tree-ssa-sccvn.c (working copy) @@ -1109,24 +1109,57 @@ fully_constant_vn_reference_p (vn_refere return folded; } } - - /* Simplify reads from constant strings. */ + else if (op->opcode == VAR_DECL + || op->opcode == CONST_DECL) + return get_symbol_constant_value (op->op0); else if (op->opcode == ARRAY_REF - && TREE_CODE (op->op0) == INTEGER_CST - && integer_zerop (op->op1) && VEC_length (vn_reference_op_s, operands) == 2) { vn_reference_op_t arg0; + tree ctor = NULL_TREE; + tree op0; + arg0 = VEC_index (vn_reference_op_s, operands, 1); - if (arg0->opcode == STRING_CST - && (TYPE_MODE (op->type) - == TYPE_MODE (TREE_TYPE (TREE_TYPE (arg0->op0)))) - && GET_MODE_CLASS (TYPE_MODE (op->type)) == MODE_INT - && GET_MODE_SIZE (TYPE_MODE (op->type)) == 1 - && compare_tree_int (op->op0, TREE_STRING_LENGTH (arg0->op0)) < 0) - return build_int_cst_type (op->type, - (TREE_STRING_POINTER (arg0->op0) - [TREE_INT_CST_LOW (op->op0)])); + op0 = arg0->op0; + + switch (arg0->opcode) + { + case MEM_REF: + /* ??? We could handle this case. */ + if (!integer_zerop (TREE_OPERAND (op0, 1))) + return NULL_TREE; + op0 = get_base_address (op0); + if (!op0 + || TREE_CODE (op0) != VAR_DECL) + return NULL_TREE; + + /* Fallthru. */ + case VAR_DECL: + if (!TREE_READONLY (op0) + || TREE_CODE (TREE_TYPE (op0)) != ARRAY_TYPE + || ((TREE_STATIC (op0) || DECL_EXTERNAL (op0)) + && !varpool_get_node (op0)->const_value_known)) + return NULL_TREE; + + ctor = DECL_INITIAL (op0); + break; + + case ARRAY_REF: + case COMPONENT_REF: + gcc_unreachable (); + break; + + case STRING_CST: + case CONSTRUCTOR: + ctor = op0; + break; + + default: + break; + } + gcc_assert (op->op1); + if (ctor) + return fold_const_array_ref (op->type, ctor, op->op0, op->op1); } return NULL_TREE; Index: tree-ssa-ccp.c =================================================================== --- tree-ssa-ccp.c (revision 163862) +++ tree-ssa-ccp.c (working copy) @@ -1310,6 +1310,65 @@ ccp_fold (gimple stmt) } } +/* Fold array reference of TYPE into a variable with known value CTOR + with known index IDX. */ +tree +fold_const_array_ref (tree type, tree ctor, tree idx, tree low_bound) +{ + unsigned HOST_WIDE_INT cnt; + tree cfield, cval; + tree tem; + + if (ctor == NULL_TREE + || (TREE_CODE (ctor) != CONSTRUCTOR + && TREE_CODE (ctor) != STRING_CST) + || !TREE_STATIC (ctor)) + return NULL_TREE; + + /* Get the index. If we have an SSA_NAME, try to resolve it + with the current lattice value for the SSA_NAME. */ + switch (TREE_CODE (idx)) + { + case SSA_NAME: + if ((tem = get_constant_value (idx)) + && TREE_CODE (tem) == INTEGER_CST) + idx = tem; + else + return NULL_TREE; + break; + + case INTEGER_CST: + break; + + default: + return NULL_TREE; + } + + /* Fold read from constant string. */ + if (TREE_CODE (ctor) == STRING_CST) + { + tree index = fold_convert (sizetype, idx); + if (low_bound && !integer_zerop (low_bound)) + index = size_diffop (index, fold_convert (sizetype, low_bound)); + if ((TYPE_MODE (type) + == TYPE_MODE (TREE_TYPE (TREE_TYPE (ctor)))) + && (GET_MODE_CLASS (TYPE_MODE (TREE_TYPE (TREE_TYPE (ctor)))) + == MODE_INT) + && GET_MODE_SIZE (TYPE_MODE (TREE_TYPE (TREE_TYPE (ctor)))) == 1 + && compare_tree_int (index, TREE_STRING_LENGTH (ctor)) < 0) + return build_int_cst_type (type, + (TREE_STRING_POINTER (ctor) + [TREE_INT_CST_LOW (index)])); + return NULL_TREE; + } + + /* Whoo-hoo! I'll fold ya baby. Yeah! */ + FOR_EACH_CONSTRUCTOR_ELT (CONSTRUCTOR_ELTS (ctor), cnt, cfield, cval) + if (tree_int_cst_equal (cfield, idx)) + return canonicalize_constructor_val (cval); + return NULL_TREE; +} + /* Return the tree representing the element referenced by T if T is an ARRAY_REF or COMPONENT_REF into constant aggregates. Return NULL_TREE otherwise. */ @@ -1332,11 +1391,11 @@ fold_const_aggregate_ref (tree t) switch (TREE_CODE (t)) { case ARRAY_REF: + base = TREE_OPERAND (t, 0); /* Get a CONSTRUCTOR. If BASE is a VAR_DECL, get its DECL_INITIAL. If BASE is a nested reference into another ARRAY_REF or COMPONENT_REF, make a recursive call to resolve the inner reference. */ - base = TREE_OPERAND (t, 0); switch (TREE_CODE (base)) { case MEM_REF: @@ -1373,51 +1432,10 @@ fold_const_aggregate_ref (tree t) return NULL_TREE; } - if (ctor == NULL_TREE - || (TREE_CODE (ctor) != CONSTRUCTOR - && TREE_CODE (ctor) != STRING_CST) - || !TREE_STATIC (ctor)) - return NULL_TREE; - - /* Get the index. If we have an SSA_NAME, try to resolve it - with the current lattice value for the SSA_NAME. */ - idx = TREE_OPERAND (t, 1); - switch (TREE_CODE (idx)) - { - case SSA_NAME: - if ((tem = get_constant_value (idx)) - && TREE_CODE (tem) == INTEGER_CST) - idx = tem; - else - return NULL_TREE; - break; - - case INTEGER_CST: - break; - - default: - return NULL_TREE; - } - - /* Fold read from constant string. */ - if (TREE_CODE (ctor) == STRING_CST) - { - if ((TYPE_MODE (TREE_TYPE (t)) - == TYPE_MODE (TREE_TYPE (TREE_TYPE (ctor)))) - && (GET_MODE_CLASS (TYPE_MODE (TREE_TYPE (TREE_TYPE (ctor)))) - == MODE_INT) - && GET_MODE_SIZE (TYPE_MODE (TREE_TYPE (TREE_TYPE (ctor)))) == 1 - && compare_tree_int (idx, TREE_STRING_LENGTH (ctor)) < 0) - return build_int_cst_type (TREE_TYPE (t), - (TREE_STRING_POINTER (ctor) - [TREE_INT_CST_LOW (idx)])); - return NULL_TREE; - } - - /* Whoo-hoo! I'll fold ya baby. Yeah! */ - FOR_EACH_CONSTRUCTOR_ELT (CONSTRUCTOR_ELTS (ctor), cnt, cfield, cval) - if (tree_int_cst_equal (cfield, idx)) - return canonicalize_constructor_val (cval); + return fold_const_array_ref (TREE_TYPE (t), + ctor, + TREE_OPERAND (t, 1), + array_ref_low_bound (t)); break; case COMPONENT_REF:
Hi, while analyzing the remianing problems with folding in gfortran testsuite I noticed that there is actual bug in way fold_const_aggregate_ref handle folding of array refs into STRING_CST because it ignores lower bound. Testaces in testsuite are lucky that with ignoring lower bound we get out of string and thus do not fold, but this should get into real wrong code bugs if gfortran was not forgetting to set TREE_STATIC flags on initializers and thus folding happent at all. Fixed thus (by copying code from expr.c) Honza /* { dg-do compile } */ /* { dg-options "-O2 -fdump-tree-optimized" } */ enum { ERROR_OK, ERROR_UNKNOWN, ERROR_NUM }; enum { __LC_CTYPE = 0, __LC_NUMERIC = 1, __LC_TIME = 2, __LC_COLLATE = 3, __LC_MONETARY = 4, __LC_MESSAGES = 5, __LC_ALL = 6, __LC_PAPER = 10, __LC_MEASUREMENT = 11, __LC_IDENTIFICATION = 12 }; static const char *const _messages[] = { "no error", "unknown error", "Internal error: unknown reason", }; elf_errmsg (int err) { if (err < 0 || err >= ERROR_NUM || _messages[err] == ((void *) 0)) { err = ERROR_UNKNOWN; } return _messages[err]; } /* We should fold _messages[1]. */ /* { dg-final { scan-tree-dump "unknown error" "optimized"} } */ /* { dg-final { cleanup-tree-dump "optimized" } } */