#include "config.h"
#include "system.h"
#include "coretypes.h"
#include "tm.h"
#include "errors.h"
#include "ggc.h"
#include "tree.h"
#include "rtl.h"
#include "tm_p.h"
#include "hard-reg-set.h"
#include "basic-block.h"
#include "diagnostic.h"
#include "langhooks.h"
#include "tree-inline.h"
#include "tree-flow.h"
#include "tree-gimple.h"
#include "tree-dump.h"
#include "tree-pass.h"
#include "timevar.h"
#include "flags.h"
#include "bitmap.h"
#define MAX_NFIELDS_FOR_SRA 5
enum sra_copy_mode { SCALAR_SCALAR, FIELD_SCALAR, SCALAR_FIELD };
static inline bool can_be_scalarized_p (tree);
static inline void insert_edge_copies (tree stmt, basic_block bb);
static tree create_scalar_copies (tree lhs, tree rhs, enum sra_copy_mode mode);
static inline void scalarize_component_ref (tree, tree *tp);
static void scalarize_structures (void);
static void scalarize_stmt (block_stmt_iterator *);
static void scalarize_modify_expr (block_stmt_iterator *);
static void scalarize_call_expr (block_stmt_iterator *);
static void scalarize_asm_expr (block_stmt_iterator *);
static void scalarize_return_expr (block_stmt_iterator *);
static bitmap sra_candidates;
static bitmap needs_copy_in;
struct sra_elt
{
enum tree_code kind;
tree base;
tree field;
tree replace;
};
static htab_t sra_map;
static hashval_t
sra_elt_hash (const void *x)
{
const struct sra_elt *e = x;
hashval_t h = (size_t) e->base * e->kind;
if (e->kind == COMPONENT_REF)
h ^= (size_t) e->field;
return h;
}
static int
sra_elt_eq (const void *x, const void *y)
{
const struct sra_elt *a = x;
const struct sra_elt *b = y;
if (a->kind != b->kind)
return false;
if (a->base != b->base)
return false;
if (a->kind == COMPONENT_REF)
if (a->field != b->field)
return false;
return true;
}
static void
mark_all_vdefs (tree stmt)
{
vdef_optype vdefs;
size_t i, n;
get_stmt_operands (stmt);
vdefs = VDEF_OPS (stmt_ann (stmt));
n = NUM_VDEFS (vdefs);
for (i = 0; i < n; i++)
{
tree sym = VDEF_RESULT (vdefs, i);
bitmap_set_bit (vars_to_rename, var_ann (sym)->uid);
}
}
static bool
is_sra_candidate_decl (tree decl)
{
return DECL_P (decl) && bitmap_bit_p (sra_candidates, var_ann (decl)->uid);
}
static bool
is_sra_candidate_ref (tree exp, bool allow_bit_field_ref)
{
switch (TREE_CODE (exp))
{
case BIT_FIELD_REF:
if (!allow_bit_field_ref)
break;
case COMPONENT_REF:
case REALPART_EXPR:
case IMAGPART_EXPR:
return is_sra_candidate_decl (TREE_OPERAND (exp, 0));
default:
break;
}
return false;
}
static tree
lookup_scalar (struct sra_elt *key, tree type)
{
struct sra_elt **slot, *res;
slot = (struct sra_elt **) htab_find_slot (sra_map, key, INSERT);
res = *slot;
if (!res)
{
res = xmalloc (sizeof (*res));
*slot = res;
*res = *key;
res->replace = make_rename_temp (type, "SR");
if (DECL_NAME (key->base) && !DECL_IGNORED_P (key->base))
{
char *name = NULL;
switch (key->kind)
{
case COMPONENT_REF:
if (!DECL_NAME (key->field))
break;
name = concat (IDENTIFIER_POINTER (DECL_NAME (key->base)),
"$",
IDENTIFIER_POINTER (DECL_NAME (key->field)),
NULL);
break;
case REALPART_EXPR:
name = concat (IDENTIFIER_POINTER (DECL_NAME (key->base)),
"$real", NULL);
break;
case IMAGPART_EXPR:
name = concat (IDENTIFIER_POINTER (DECL_NAME (key->base)),
"$imag", NULL);
break;
default:
abort ();
}
if (name)
{
DECL_NAME (res->replace) = get_identifier (name);
free (name);
}
}
DECL_SOURCE_LOCATION (res->replace) = DECL_SOURCE_LOCATION (key->base);
TREE_NO_WARNING (res->replace) = TREE_NO_WARNING (key->base);
DECL_ARTIFICIAL (res->replace) = DECL_ARTIFICIAL (key->base);
}
return res->replace;
}
static tree
get_scalar_for_field (tree var, tree field)
{
struct sra_elt key;
#ifdef ENABLE_CHECKING
{
tree f;
for (f = TYPE_FIELDS (TREE_TYPE (var)); f ; f = TREE_CHAIN (f))
if (f == field)
goto found;
abort ();
found:;
}
#endif
key.kind = COMPONENT_REF;
key.base = var;
key.field = field;
return lookup_scalar (&key, TREE_TYPE (field));
}
static tree
get_scalar_for_complex_part (tree var, enum tree_code part)
{
struct sra_elt key;
key.kind = part;
key.base = var;
return lookup_scalar (&key, TREE_TYPE (TREE_TYPE (var)));
}
static inline bool
can_be_scalarized_p (tree var)
{
tree field, type;
int nfields;
if (!is_gimple_non_addressable (var))
{
if (dump_file && (dump_flags & TDF_DETAILS))
{
fprintf (dump_file, "Cannot scalarize variable ");
print_generic_expr (dump_file, var, dump_flags);
fprintf (dump_file, " because it must live in memory\n");
}
return false;
}
if (TREE_THIS_VOLATILE (var))
{
if (dump_file && (dump_flags & TDF_DETAILS))
{
fprintf (dump_file, "Cannot scalarize variable ");
print_generic_expr (dump_file, var, dump_flags);
fprintf (dump_file, " because it is declared volatile\n");
}
return false;
}
if (TREE_CODE (TREE_TYPE (var)) == COMPLEX_TYPE)
return true;
type = TREE_TYPE (var);
nfields = 0;
for (field = TYPE_FIELDS (type); field; field = TREE_CHAIN (field))
{
if (TREE_CODE (field) != FIELD_DECL)
continue;
if (AGGREGATE_TYPE_P (TREE_TYPE (field)))
{
if (dump_file && (dump_flags & TDF_DETAILS))
{
fprintf (dump_file, "Cannot scalarize variable ");
print_generic_expr (dump_file, var, dump_flags);
fprintf (dump_file,
" because it contains an aggregate type field, ");
print_generic_expr (dump_file, field, dump_flags);
fprintf (dump_file, "\n");
}
return false;
}
if (TREE_CODE (TREE_TYPE (field)) == COMPLEX_TYPE)
{
if (dump_file && (dump_flags & TDF_DETAILS))
{
fprintf (dump_file, "Cannot scalarize variable ");
print_generic_expr (dump_file, var, dump_flags);
fprintf (dump_file,
" because it contains a __complex__ field, ");
print_generic_expr (dump_file, field, dump_flags);
fprintf (dump_file, "\n");
}
return false;
}
if (DECL_BIT_FIELD (field))
{
if (dump_file && (dump_flags & TDF_DETAILS))
{
fprintf (dump_file, "Cannot scalarize variable ");
print_generic_expr (dump_file, var, dump_flags);
fprintf (dump_file,
" because it contains a bit-field, ");
print_generic_expr (dump_file, field, dump_flags);
fprintf (dump_file, "\n");
}
return false;
}
nfields++;
if (nfields > MAX_NFIELDS_FOR_SRA)
{
if (dump_file && (dump_flags & TDF_DETAILS))
{
fprintf (dump_file, "Cannot scalarize variable ");
print_generic_expr (dump_file, var, dump_flags);
fprintf (dump_file,
" because it contains more than %d fields\n",
MAX_NFIELDS_FOR_SRA);
}
return false;
}
}
return nfields > 0;
}
static inline void
scalarize_component_ref (tree stmt, tree *tp)
{
tree t = *tp, obj = TREE_OPERAND (t, 0);
if (TREE_CODE (obj) == PARM_DECL)
bitmap_set_bit (needs_copy_in, var_ann (obj)->uid);
switch (TREE_CODE (t))
{
case COMPONENT_REF:
t = get_scalar_for_field (obj, TREE_OPERAND (t, 1));
break;
case REALPART_EXPR:
case IMAGPART_EXPR:
t = get_scalar_for_complex_part (obj, TREE_CODE (t));
break;
default:
abort ();
}
*tp = t;
modify_stmt (stmt);
}
static void
scalarize_structure_assignment (block_stmt_iterator *si_p)
{
var_ann_t lhs_ann, rhs_ann;
tree lhs, rhs, list, orig_stmt;
bool lhs_can, rhs_can;
orig_stmt = bsi_stmt (*si_p);
lhs = TREE_OPERAND (orig_stmt, 0);
rhs = TREE_OPERAND (orig_stmt, 1);
list = NULL_TREE;
#if defined ENABLE_CHECKING
if (TREE_CODE (orig_stmt) != MODIFY_EXPR)
abort ();
#endif
STRIP_NOPS (rhs);
lhs_ann = DECL_P (lhs) ? var_ann (lhs) : NULL;
rhs_ann = DECL_P (rhs) ? var_ann (rhs) : NULL;
#if defined ENABLE_CHECKING
if (lhs_ann
&& rhs_ann
&& lhs != rhs
&& lhs_ann->uid == rhs_ann->uid)
abort ();
#endif
lhs_can = lhs_ann && bitmap_bit_p (sra_candidates, lhs_ann->uid);
rhs_can = rhs_ann && bitmap_bit_p (sra_candidates, rhs_ann->uid);
if (lhs_can && rhs_can)
list = create_scalar_copies (lhs, rhs, SCALAR_SCALAR);
else if (rhs_can)
list = create_scalar_copies (lhs, rhs, FIELD_SCALAR);
else if (lhs_can)
list = create_scalar_copies (lhs, rhs, SCALAR_FIELD);
else
return;
if (EXPR_HAS_LOCATION (orig_stmt))
annotate_all_with_locus (&list, EXPR_LOCATION (orig_stmt));
{
tree_stmt_iterator tsi = tsi_start (list);
bsi_replace (si_p, tsi_stmt (tsi), true);
tsi_delink (&tsi);
if (stmt_ends_bb_p (orig_stmt))
insert_edge_copies (list, bb_for_stmt (orig_stmt));
else
bsi_insert_after (si_p, list, BSI_CONTINUE_LINKING);
}
}
static bool
find_candidates_for_sra (void)
{
size_t i;
bool any_set = false;
for (i = 0; i < num_referenced_vars; i++)
{
tree var = referenced_var (i);
if ((TREE_CODE (TREE_TYPE (var)) == RECORD_TYPE
|| TREE_CODE (TREE_TYPE (var)) == COMPLEX_TYPE)
&& can_be_scalarized_p (var))
{
bitmap_set_bit (sra_candidates, var_ann (var)->uid);
any_set = true;
}
}
return any_set;
}
static inline void
insert_edge_copies (tree stmt, basic_block bb)
{
edge e;
bool first_copy;
first_copy = true;
for (e = bb->succ; e; e = e->succ_next)
{
if (!(e->flags & EDGE_ABNORMAL))
{
if (first_copy)
{
bsi_insert_on_edge (e, stmt);
first_copy = false;
}
else
bsi_insert_on_edge (e, lhd_unsave_expr_now (stmt));
}
}
}
static tree
csc_assign (tree_stmt_iterator *tsi, tree lhs, tree rhs)
{
tree stmt = build (MODIFY_EXPR, TREE_TYPE (lhs), lhs, rhs);
modify_stmt (stmt);
tsi_link_after (tsi, stmt, TSI_NEW_STMT);
return stmt;
}
static tree
csc_build_component_ref (tree base, tree field)
{
switch (TREE_CODE (base))
{
case CONSTRUCTOR:
if (CONSTRUCTOR_ELTS (base))
abort ();
return convert (TREE_TYPE (field), integer_zero_node);
default:
if (field != TYPE_FIELDS (TREE_TYPE (base)))
base = unshare_expr (base);
break;
case VAR_DECL:
case PARM_DECL:
break;
}
return build (COMPONENT_REF, TREE_TYPE (field), base, field);
}
static tree
csc_build_complex_part (tree base, enum tree_code part)
{
switch (TREE_CODE (base))
{
case COMPLEX_CST:
if (part == REALPART_EXPR)
return TREE_REALPART (base);
else
return TREE_IMAGPART (base);
case COMPLEX_EXPR:
if (part == REALPART_EXPR)
return TREE_OPERAND (base, 0);
else
return TREE_OPERAND (base, 1);
default:
if (part != REALPART_EXPR)
base = unshare_expr (base);
break;
case VAR_DECL:
case PARM_DECL:
break;
}
return build1 (part, TREE_TYPE (TREE_TYPE (base)), base);
}
static tree
create_scalar_copies (tree lhs, tree rhs, enum sra_copy_mode mode)
{
tree type, list;
tree_stmt_iterator tsi;
#if defined ENABLE_CHECKING
if (!DECL_P (lhs) && (mode == SCALAR_FIELD || mode == SCALAR_SCALAR))
abort ();
if (!DECL_P (rhs) && (mode == FIELD_SCALAR || mode == SCALAR_SCALAR))
abort ();
#endif
type = TREE_TYPE (lhs);
list = alloc_stmt_list ();
tsi = tsi_start (list);
if (TREE_CODE (rhs) == VA_ARG_EXPR)
{
tree stmt, tmp;
tmp = make_rename_temp (TREE_TYPE (rhs), NULL);
stmt = csc_assign (&tsi, tmp, rhs);
mark_all_vdefs (stmt);
rhs = tmp;
}
if ((mode == SCALAR_SCALAR || mode == FIELD_SCALAR)
&& TREE_CODE (rhs) == PARM_DECL)
bitmap_set_bit (needs_copy_in, var_ann (rhs)->uid);
if (TREE_CODE (type) == COMPLEX_TYPE)
{
tree real_lhs, real_rhs, imag_lhs, imag_rhs;
if (mode == SCALAR_FIELD)
{
real_rhs = csc_build_complex_part (rhs, REALPART_EXPR);
imag_rhs = csc_build_complex_part (rhs, IMAGPART_EXPR);
}
else
{
real_rhs = get_scalar_for_complex_part (rhs, REALPART_EXPR);
imag_rhs = get_scalar_for_complex_part (rhs, IMAGPART_EXPR);
}
if (mode == FIELD_SCALAR)
{
if (TREE_CONSTANT (real_rhs) && TREE_CONSTANT (imag_rhs))
rhs = build_complex (type, real_rhs, imag_rhs);
else
rhs = build (COMPLEX_EXPR, type, real_rhs, imag_rhs);
csc_assign (&tsi, lhs, rhs);
}
else
{
real_lhs = get_scalar_for_complex_part (lhs, REALPART_EXPR);
imag_lhs = get_scalar_for_complex_part (lhs, IMAGPART_EXPR);
csc_assign (&tsi, real_lhs, real_rhs);
csc_assign (&tsi, imag_lhs, imag_rhs);
}
}
else
{
tree lf, rf;
for (lf = TYPE_FIELDS (type), rf = TYPE_FIELDS (TREE_TYPE (rhs));
lf;
lf = TREE_CHAIN (lf), rf = TREE_CHAIN (rf))
{
tree lhs_var, rhs_var;
if (TREE_CODE (lf) != FIELD_DECL)
continue;
if (mode == FIELD_SCALAR)
lhs_var = csc_build_component_ref (lhs, lf);
else
lhs_var = get_scalar_for_field (lhs, lf);
if (mode == SCALAR_FIELD)
rhs_var = csc_build_component_ref (rhs, rf);
else
rhs_var = get_scalar_for_field (rhs, rf);
csc_assign (&tsi, lhs_var, rhs_var);
}
}
if (TREE_SIDE_EFFECTS (list))
{
if (mode == SCALAR_FIELD || mode == SCALAR_SCALAR)
{
bitmap_set_bit (vars_to_rename, var_ann (lhs)->uid);
}
else if (mode == FIELD_SCALAR)
{
mark_all_vdefs (tsi_stmt (tsi));
}
else
abort ();
}
return list;
}
static void
emit_scalar_copies (block_stmt_iterator *bsi, tree lhs, tree rhs,
enum sra_copy_mode mode)
{
tree list = create_scalar_copies (lhs, rhs, mode);
tree stmt = bsi_stmt (*bsi);
if (EXPR_HAS_LOCATION (stmt))
annotate_all_with_locus (&list, EXPR_LOCATION (stmt));
bsi_insert_before (bsi, list, BSI_SAME_STMT);
}
static void
scalarize_structures (void)
{
basic_block bb;
block_stmt_iterator si;
size_t i;
FOR_EACH_BB (bb)
for (si = bsi_start (bb); !bsi_end_p (si); bsi_next (&si))
{
tree stmt;
stmt_ann_t ann;
stmt = bsi_stmt (si);
ann = stmt_ann (stmt);
if (NUM_VDEFS (VDEF_OPS (ann)) == 0
&& NUM_VUSES (VUSE_OPS (ann)) == 0)
continue;
if (TREE_CODE (stmt) != MODIFY_EXPR
&& TREE_CODE (stmt) != CALL_EXPR
&& TREE_CODE (stmt) != RETURN_EXPR
&& TREE_CODE (stmt) != ASM_EXPR)
continue;
scalarize_stmt (&si);
}
EXECUTE_IF_SET_IN_BITMAP (needs_copy_in, 0, i,
{
tree var = referenced_var (i);
tree list = create_scalar_copies (var, var, SCALAR_FIELD);
bsi_insert_on_edge (ENTRY_BLOCK_PTR->succ, list);
});
bsi_commit_edge_inserts (NULL);
}
static void
scalarize_stmt (block_stmt_iterator *si_p)
{
tree stmt = bsi_stmt (*si_p);
if (TREE_CODE (stmt) == MODIFY_EXPR
&& TREE_CODE (TREE_OPERAND (stmt, 1)) != CALL_EXPR)
scalarize_modify_expr (si_p);
else if (TREE_CODE (stmt) == RETURN_EXPR)
scalarize_return_expr (si_p);
else if (get_call_expr_in (stmt) != NULL_TREE)
scalarize_call_expr (si_p);
else if (TREE_CODE (stmt) == ASM_EXPR)
scalarize_asm_expr (si_p);
}
static void
scalarize_modify_expr (block_stmt_iterator *si_p)
{
tree stmt = bsi_stmt (*si_p);
tree lhs = TREE_OPERAND (stmt, 0);
tree rhs = TREE_OPERAND (stmt, 1);
if (is_sra_candidate_ref (lhs, false))
{
tree sym;
vdef_optype vdefs;
scalarize_component_ref (stmt, &TREE_OPERAND (stmt, 0));
vdefs = STMT_VDEF_OPS (stmt);
if (NUM_VDEFS (vdefs) != 1)
abort ();
sym = SSA_NAME_VAR (VDEF_RESULT (vdefs, 0));
bitmap_set_bit (vars_to_rename, var_ann (sym)->uid);
}
else if (is_sra_candidate_ref (rhs, false))
scalarize_component_ref (stmt, &TREE_OPERAND (stmt, 1));
else if (is_sra_candidate_ref (rhs, true))
{
tree var = TREE_OPERAND (rhs, 0);
emit_scalar_copies (si_p, var, var, FIELD_SCALAR);
}
else if (DECL_P (lhs) || DECL_P (rhs))
scalarize_structure_assignment (si_p);
}
static inline void
scalarize_tree_list (tree list, block_stmt_iterator *si_p, bitmap done)
{
tree op;
for (op = list; op; op = TREE_CHAIN (op))
{
tree arg = TREE_VALUE (op);
if (is_sra_candidate_decl (arg))
{
int index = var_ann (arg)->uid;
if (!bitmap_bit_p (done, index))
{
emit_scalar_copies (si_p, arg, arg, FIELD_SCALAR);
bitmap_set_bit (done, index);
}
}
else if (is_sra_candidate_ref (arg, false))
{
tree stmt = bsi_stmt (*si_p);
scalarize_component_ref (stmt, &TREE_VALUE (op));
}
}
}
static void
scalarize_call_expr (block_stmt_iterator *si_p)
{
tree stmt = bsi_stmt (*si_p);
tree call = (TREE_CODE (stmt) == MODIFY_EXPR) ? TREE_OPERAND (stmt, 1) : stmt;
struct bitmap_head_def done_head;
bitmap_initialize (&done_head, 1);
scalarize_tree_list (TREE_OPERAND (call, 1), si_p, &done_head);
bitmap_clear (&done_head);
if (TREE_CODE (stmt) == MODIFY_EXPR)
{
tree var = TREE_OPERAND (stmt, 0);
if (is_sra_candidate_decl (var))
{
tree list = create_scalar_copies (var, var, SCALAR_FIELD);
if (EXPR_HAS_LOCATION (stmt))
annotate_all_with_locus (&list, EXPR_LOCATION (stmt));
if (stmt_ends_bb_p (stmt))
insert_edge_copies (list, bb_for_stmt (stmt));
else
bsi_insert_after (si_p, list, BSI_NEW_STMT);
}
}
}
static void
scalarize_asm_expr (block_stmt_iterator *si_p)
{
tree stmt = bsi_stmt (*si_p);
struct bitmap_head_def done_head;
bitmap_initialize (&done_head, 1);
scalarize_tree_list (ASM_INPUTS (stmt), si_p, &done_head);
scalarize_tree_list (ASM_OUTPUTS (stmt), si_p, &done_head);
bitmap_clear (&done_head);
}
static void
scalarize_return_expr (block_stmt_iterator *si_p)
{
tree stmt = bsi_stmt (*si_p);
tree op = TREE_OPERAND (stmt, 0);
if (op == NULL_TREE)
return;
if (is_sra_candidate_decl (op))
emit_scalar_copies (si_p, op, op, FIELD_SCALAR);
else if (TREE_CODE (op) == MODIFY_EXPR)
{
tree *rhs_p = &TREE_OPERAND (op, 1);
tree rhs = *rhs_p;
if (is_sra_candidate_decl (rhs))
emit_scalar_copies (si_p, rhs, rhs, FIELD_SCALAR);
else if (is_sra_candidate_ref (rhs, false))
scalarize_component_ref (stmt, rhs_p);
else if (TREE_CODE (rhs) == CALL_EXPR)
{
struct bitmap_head_def done_head;
bitmap_initialize (&done_head, 1);
scalarize_tree_list (TREE_OPERAND (rhs, 1), si_p, &done_head);
bitmap_clear (&done_head);
}
}
}
static int
dump_sra_map_trav (void **slot, void *data)
{
struct sra_elt *e = *slot;
FILE *f = data;
switch (e->kind)
{
case REALPART_EXPR:
fputs ("__real__ ", f);
print_generic_expr (dump_file, e->base, dump_flags);
fprintf (f, " -> %s\n", get_name (e->replace));
break;
case IMAGPART_EXPR:
fputs ("__imag__ ", f);
print_generic_expr (dump_file, e->base, dump_flags);
fprintf (f, " -> %s\n", get_name (e->replace));
break;
case COMPONENT_REF:
print_generic_expr (dump_file, e->base, dump_flags);
fprintf (f, ".%s -> %s\n", get_name (e->field), get_name (e->replace));
break;
default:
abort ();
}
return 1;
}
static void
dump_sra_map (FILE *f)
{
fputs ("Scalar replacements:\n", f);
htab_traverse_noresize (sra_map, dump_sra_map_trav, f);
fputs ("\n\n", f);
}
static void
tree_sra (void)
{
sra_candidates = BITMAP_XMALLOC ();
sra_map = NULL;
needs_copy_in = NULL;
if (!find_candidates_for_sra ())
{
BITMAP_XFREE (sra_candidates);
return;
}
sra_map = htab_create (101, sra_elt_hash, sra_elt_eq, free);
needs_copy_in = BITMAP_XMALLOC ();
scalarize_structures ();
if (dump_file)
dump_sra_map (dump_file);
htab_delete (sra_map);
sra_map = NULL;
BITMAP_XFREE (needs_copy_in);
BITMAP_XFREE (sra_candidates);
}
static bool
gate_sra (void)
{
return flag_tree_sra != 0;
}
struct tree_opt_pass pass_sra =
{
"sra",
gate_sra,
tree_sra,
NULL,
NULL,
0,
TV_TREE_SRA,
PROP_cfg | PROP_ssa,
0,
0,
0,
TODO_dump_func | TODO_rename_vars
| TODO_ggc_collect | TODO_verify_ssa
};