-/* This represents the used range of a variable. */
-
-typedef struct used_part
-{
- HOST_WIDE_INT minused;
- HOST_WIDE_INT maxused;
- /* True if we have an explicit use/def of some portion of this variable,
- even if it is all of it. i.e. a.b = 5 or temp = a.b. */
- bool explicit_uses;
- /* True if we have an implicit use/def of some portion of this
- variable. Implicit uses occur when we can't tell what part we
- are referencing, and have to make conservative assumptions. */
- bool implicit_uses;
- /* True if the structure is only written to or taken its address. */
- bool write_only;
-} *used_part_t;
-
-/* An array of used_part structures, indexed by variable uid. */
-
-static htab_t used_portions;
-
-struct used_part_map
-{
- unsigned int uid;
- used_part_t to;
-};
-
-/* Return true if the uid in the two used part maps are equal. */
-
-static int
-used_part_map_eq (const void *va, const void *vb)
-{
- const struct used_part_map *a = (const struct used_part_map *) va;
- const struct used_part_map *b = (const struct used_part_map *) vb;
- return (a->uid == b->uid);
-}
-
-/* Hash a from uid in a used_part_map. */
-
-static unsigned int
-used_part_map_hash (const void *item)
-{
- return ((const struct used_part_map *)item)->uid;
-}
-
-/* Free a used part map element. */
-
-static void
-free_used_part_map (void *item)
-{
- free (((struct used_part_map *)item)->to);
- free (item);
-}
-
-/* Lookup a used_part structure for a UID. */
-
-static used_part_t
-up_lookup (unsigned int uid)
-{
- struct used_part_map *h, in;
- in.uid = uid;
- h = (struct used_part_map *) htab_find_with_hash (used_portions, &in, uid);
- if (!h)
- return NULL;
- return h->to;
-}
-
-/* Insert the pair UID, TO into the used part hashtable. */
-
-static void
-up_insert (unsigned int uid, used_part_t to)
-{
- struct used_part_map *h;
- void **loc;
-
- h = XNEW (struct used_part_map);
- h->uid = uid;
- h->to = to;
- loc = htab_find_slot_with_hash (used_portions, h,
- uid, INSERT);
- if (*loc != NULL)
- free (*loc);
- *(struct used_part_map **) loc = h;
-}
-
-
-/* Given a variable uid, UID, get or create the entry in the used portions
- table for the variable. */
-
-static used_part_t
-get_or_create_used_part_for (size_t uid)
-{
- used_part_t up;
- if ((up = up_lookup (uid)) == NULL)
- {
- up = XCNEW (struct used_part);
- up->minused = INT_MAX;
- up->maxused = 0;
- up->explicit_uses = false;
- up->implicit_uses = false;
- up->write_only = true;
- }
-
- return up;
-}
-
-
-/* Create and return a structure sub-variable for field type FIELD at
- offset OFFSET, with size SIZE, of variable VAR. */
-
-static tree
-create_sft (tree var, tree field, unsigned HOST_WIDE_INT offset,
- unsigned HOST_WIDE_INT size)
-{
- var_ann_t ann;
- tree subvar = create_tag_raw (STRUCT_FIELD_TAG, field, "SFT");
-
- /* We need to copy the various flags from VAR to SUBVAR, so that
- they are is_global_var iff the original variable was. */
- DECL_CONTEXT (subvar) = DECL_CONTEXT (var);
- MTAG_GLOBAL (subvar) = DECL_EXTERNAL (var);
- TREE_PUBLIC (subvar) = TREE_PUBLIC (var);
- TREE_STATIC (subvar) = TREE_STATIC (var);
- TREE_READONLY (subvar) = TREE_READONLY (var);
- TREE_ADDRESSABLE (subvar) = TREE_ADDRESSABLE (var);
-
- /* Add the new variable to REFERENCED_VARS. */
- ann = get_var_ann (subvar);
- ann->symbol_mem_tag = NULL;
- add_referenced_var (subvar);
- SFT_PARENT_VAR (subvar) = var;
- SFT_OFFSET (subvar) = offset;
- SFT_SIZE (subvar) = size;
- return subvar;
-}
-
-
-/* Given an aggregate VAR, create the subvariables that represent its
- fields. */
-
-static void
-create_overlap_variables_for (tree var)
-{
- VEC(fieldoff_s,heap) *fieldstack = NULL;
- used_part_t up;
- size_t uid = DECL_UID (var);
-
- up = up_lookup (uid);
- if (!up
- || up->write_only)
- return;
-
- push_fields_onto_fieldstack (TREE_TYPE (var), &fieldstack, 0, NULL);
- if (VEC_length (fieldoff_s, fieldstack) != 0)
- {
- subvar_t *subvars;
- fieldoff_s *fo;
- bool notokay = false;
- int fieldcount = 0;
- int i;
- HOST_WIDE_INT lastfooffset = -1;
- HOST_WIDE_INT lastfosize = -1;
- tree lastfotype = NULL_TREE;
-
- /* Not all fields have DECL_SIZE set, and those that don't, we don't
- know their size, and thus, can't handle.
- The same is true of fields with DECL_SIZE that is not an integer
- constant (such as variable sized fields).
- Fields with offsets which are not constant will have an offset < 0
- We *could* handle fields that are constant sized arrays, but
- currently don't. Doing so would require some extra changes to
- tree-ssa-operands.c. */
-
- for (i = 0; VEC_iterate (fieldoff_s, fieldstack, i, fo); i++)
- {
- if (!fo->size
- || TREE_CODE (fo->size) != INTEGER_CST
- || fo->offset < 0)
- {
- notokay = true;
- break;
- }
- fieldcount++;
- }
-
- /* The current heuristic we use is as follows:
- If the variable has no used portions in this function, no
- structure vars are created for it.
- Otherwise,
- If the variable has less than SALIAS_MAX_IMPLICIT_FIELDS,
- we always create structure vars for them.
- If the variable has more than SALIAS_MAX_IMPLICIT_FIELDS, and
- some explicit uses, we create structure vars for them.
- If the variable has more than SALIAS_MAX_IMPLICIT_FIELDS, and
- no explicit uses, we do not create structure vars for them.
- */
-
- if (fieldcount >= SALIAS_MAX_IMPLICIT_FIELDS
- && !up->explicit_uses)
- {
- if (dump_file && (dump_flags & TDF_DETAILS))
- {
- fprintf (dump_file, "Variable ");
- print_generic_expr (dump_file, var, 0);
- fprintf (dump_file, " has no explicit uses in this function, and is > SALIAS_MAX_IMPLICIT_FIELDS, so skipping\n");
- }
- notokay = true;
- }
-
- /* Bail out, if we can't create overlap variables. */
- if (notokay)
- {
- VEC_free (fieldoff_s, heap, fieldstack);
- return;
- }
-
- /* Otherwise, create the variables. */
- subvars = lookup_subvars_for_var (var);
-
- sort_fieldstack (fieldstack);
-
- for (i = VEC_length (fieldoff_s, fieldstack);
- VEC_iterate (fieldoff_s, fieldstack, --i, fo);)
- {
- subvar_t sv;
- HOST_WIDE_INT fosize;
- tree currfotype;
-
- fosize = TREE_INT_CST_LOW (fo->size);
- currfotype = fo->type;
-
- /* If this field isn't in the used portion,
- or it has the exact same offset and size as the last
- field, skip it. */
-
- if (((fo->offset <= up->minused
- && fo->offset + fosize <= up->minused)
- || fo->offset >= up->maxused)
- || (fo->offset == lastfooffset
- && fosize == lastfosize
- && currfotype == lastfotype))
- continue;
- sv = GGC_NEW (struct subvar);
- sv->next = *subvars;
- sv->var = create_sft (var, fo->type, fo->offset, fosize);
-
- if (dump_file)
- {
- fprintf (dump_file, "structure field tag %s created for var %s",
- get_name (sv->var), get_name (var));
- fprintf (dump_file, " offset " HOST_WIDE_INT_PRINT_DEC,
- SFT_OFFSET (sv->var));
- fprintf (dump_file, " size " HOST_WIDE_INT_PRINT_DEC,
- SFT_SIZE (sv->var));
- fprintf (dump_file, "\n");
- }
-
- lastfotype = currfotype;
- lastfooffset = fo->offset;
- lastfosize = fosize;
- *subvars = sv;
- }
-
- /* Once we have created subvars, the original is no longer call
- clobbered on its own. Its call clobbered status depends
- completely on the call clobbered status of the subvars.
-
- add_referenced_var in the above loop will take care of
- marking subvars of global variables as call clobbered for us
- to start, since they are global as well. */
- clear_call_clobbered (var);
- }
-
- VEC_free (fieldoff_s, heap, fieldstack);
-}
-
-
-/* Find the conservative answer to the question of what portions of what
- structures are used by this statement. We assume that if we have a
- component ref with a known size + offset, that we only need that part
- of the structure. For unknown cases, or cases where we do something
- to the whole structure, we assume we need to create fields for the
- entire structure. */
-
-static tree
-find_used_portions (tree *tp, int *walk_subtrees, void *lhs_p)
-{
- switch (TREE_CODE (*tp))
- {
- case MODIFY_EXPR:
- /* Recurse manually here to track whether the use is in the
- LHS of an assignment. */
- find_used_portions (&TREE_OPERAND (*tp, 0), walk_subtrees, tp);
- return find_used_portions (&TREE_OPERAND (*tp, 1), walk_subtrees, NULL);
- case REALPART_EXPR:
- case IMAGPART_EXPR:
- case COMPONENT_REF:
- case ARRAY_REF:
- {
- HOST_WIDE_INT bitsize;
- HOST_WIDE_INT bitmaxsize;
- HOST_WIDE_INT bitpos;
- tree ref;
- ref = get_ref_base_and_extent (*tp, &bitpos, &bitsize, &bitmaxsize);
- if (DECL_P (ref)
- && var_can_have_subvars (ref)
- && bitmaxsize != -1)
- {
- size_t uid = DECL_UID (ref);
- used_part_t up;
-
- up = get_or_create_used_part_for (uid);
-
- if (bitpos <= up->minused)
- up->minused = bitpos;
- if ((bitpos + bitmaxsize >= up->maxused))
- up->maxused = bitpos + bitmaxsize;
-
- if (bitsize == bitmaxsize)
- up->explicit_uses = true;
- else
- up->implicit_uses = true;
- if (!lhs_p)
- up->write_only = false;
- up_insert (uid, up);
-
- *walk_subtrees = 0;
- return NULL_TREE;
- }
- }
- break;
- /* This is here to make sure we mark the entire base variable as used
- when you take its address. Because our used portion analysis is
- simple, we aren't looking at casts or pointer arithmetic to see what
- happens when you take the address. */
- case ADDR_EXPR:
- {
- tree var = get_base_address (TREE_OPERAND (*tp, 0));
-
- if (var
- && DECL_P (var)
- && DECL_SIZE (var)
- && var_can_have_subvars (var)
- && TREE_CODE (DECL_SIZE (var)) == INTEGER_CST)
- {
- used_part_t up;
- size_t uid = DECL_UID (var);
-
- up = get_or_create_used_part_for (uid);
-
- up->minused = 0;
- up->maxused = TREE_INT_CST_LOW (DECL_SIZE (var));
- up->implicit_uses = true;
- if (!lhs_p)
- up->write_only = false;
-
- up_insert (uid, up);
- *walk_subtrees = 0;
- return NULL_TREE;
- }
- }
- break;
- case CALL_EXPR:
- {
- tree *arg;
- for (arg = &TREE_OPERAND (*tp, 1); *arg; arg = &TREE_CHAIN (*arg))
- {
- if (TREE_CODE (TREE_VALUE (*arg)) != ADDR_EXPR)
- find_used_portions (&TREE_VALUE (*arg), walk_subtrees, NULL);
- }
- *walk_subtrees = 0;
- return NULL_TREE;
- }
- case VAR_DECL:
- case PARM_DECL:
- case RESULT_DECL:
- {
- tree var = *tp;
- if (DECL_SIZE (var)
- && var_can_have_subvars (var)
- && TREE_CODE (DECL_SIZE (var)) == INTEGER_CST)
- {
- used_part_t up;
- size_t uid = DECL_UID (var);
-
- up = get_or_create_used_part_for (uid);
-
- up->minused = 0;
- up->maxused = TREE_INT_CST_LOW (DECL_SIZE (var));
- up->implicit_uses = true;
-
- up_insert (uid, up);
- *walk_subtrees = 0;
- return NULL_TREE;
- }
- }
- break;
-
- default:
- break;
-
- }
- return NULL_TREE;
-}
-
-/* Create structure field variables for structures used in this function. */
-
-static unsigned int
-create_structure_vars (void)
-{
- basic_block bb;
- safe_referenced_var_iterator rvi;
- VEC (tree, heap) *varvec = NULL;
- tree var;
-
- used_portions = htab_create (10, used_part_map_hash, used_part_map_eq,
- free_used_part_map);
-
- FOR_EACH_BB (bb)
- {
- block_stmt_iterator bsi;
- for (bsi = bsi_start (bb); !bsi_end_p (bsi); bsi_next (&bsi))
- {
- walk_tree_without_duplicates (bsi_stmt_ptr (bsi),
- find_used_portions,
- NULL);
- }
- }
- FOR_EACH_REFERENCED_VAR_SAFE (var, varvec, rvi)
- {
- /* The C++ FE creates vars without DECL_SIZE set, for some reason. */
- if (var
- && DECL_SIZE (var)
- && var_can_have_subvars (var)
- && !MTAG_P (var)
- && TREE_CODE (DECL_SIZE (var)) == INTEGER_CST)
- create_overlap_variables_for (var);
- }
- htab_delete (used_portions);
- VEC_free (tree, heap, varvec);
- return 0;
-}
-
-static bool
-gate_structure_vars (void)
-{
- return flag_tree_salias != 0;
-}
-
-struct tree_opt_pass pass_create_structure_vars =
-{
- "salias", /* name */
- gate_structure_vars, /* gate */
- create_structure_vars, /* execute */
- NULL, /* sub */
- NULL, /* next */
- 0, /* static_pass_number */
- 0, /* tv_id */
- PROP_cfg, /* properties_required */
- 0, /* properties_provided */
- 0, /* properties_destroyed */
- 0, /* todo_flags_start */
- TODO_dump_func, /* todo_flags_finish */
- 0 /* letter */
-};