+/* 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. */
+
+static tree
+fold_const_aggregate_ref (tree t)
+{
+ prop_value_t *value;
+ tree base, ctor, idx, field;
+ unsigned HOST_WIDE_INT cnt;
+ tree cfield, cval;
+
+ switch (TREE_CODE (t))
+ {
+ case ARRAY_REF:
+ /* 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 VAR_DECL:
+ if (!TREE_READONLY (base)
+ || TREE_CODE (TREE_TYPE (base)) != ARRAY_TYPE
+ || !targetm.binds_local_p (base))
+ return NULL_TREE;
+
+ ctor = DECL_INITIAL (base);
+ break;
+
+ case ARRAY_REF:
+ case COMPONENT_REF:
+ ctor = fold_const_aggregate_ref (base);
+ break;
+
+ default:
+ 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 ((value = get_value (idx, true))
+ && value->lattice_val == CONSTANT
+ && TREE_CODE (value->value) == INTEGER_CST)
+ idx = value->value;
+ 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 (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 cval;
+ break;
+
+ case COMPONENT_REF:
+ /* 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 VAR_DECL:
+ if (!TREE_READONLY (base)
+ || TREE_CODE (TREE_TYPE (base)) != RECORD_TYPE
+ || !targetm.binds_local_p (base))
+ return NULL_TREE;
+
+ ctor = DECL_INITIAL (base);
+ break;
+
+ case ARRAY_REF:
+ case COMPONENT_REF:
+ ctor = fold_const_aggregate_ref (base);
+ break;
+
+ default:
+ return NULL_TREE;
+ }
+
+ if (ctor == NULL_TREE
+ || TREE_CODE (ctor) != CONSTRUCTOR
+ || !TREE_STATIC (ctor))
+ return NULL_TREE;
+
+ field = TREE_OPERAND (t, 1);
+
+ FOR_EACH_CONSTRUCTOR_ELT (CONSTRUCTOR_ELTS (ctor), cnt, cfield, cval)
+ if (cfield == field
+ /* FIXME: Handle bit-fields. */
+ && ! DECL_BIT_FIELD (cfield))
+ return cval;
+ break;
+
+ case REALPART_EXPR:
+ case IMAGPART_EXPR:
+ {
+ tree c = fold_const_aggregate_ref (TREE_OPERAND (t, 0));
+ if (c && TREE_CODE (c) == COMPLEX_CST)
+ return fold_build1 (TREE_CODE (t), TREE_TYPE (t), c);
+ break;
+ }
+
+ default:
+ break;
+ }
+
+ return NULL_TREE;
+}
+