#include "config.h"
#include "system.h"
#include "coretypes.h"
#include "tm.h"
#include "tree.h"
#include "rtl.h"
#include "tm_p.h"
#include "basic-block.h"
#include "function.h"
#include "expr.h"
#include "langhooks.h"
#include "tree-flow.h"
#include "timevar.h"
#include "tree-dump.h"
#include "tree-pass.h"
#include "except.h"
#include "flags.h"
#include "diagnostic.h"
#include "toplev.h"
#include "debug.h"
#include "params.h"
static void
add_reg_br_prob_note (rtx last, int probability)
{
if (profile_status == PROFILE_ABSENT)
return;
for (last = NEXT_INSN (last); last && NEXT_INSN (last); last = NEXT_INSN (last))
if (JUMP_P (last))
{
if (!any_condjump_p (last)
|| !JUMP_P (NEXT_INSN (last))
|| !simplejump_p (NEXT_INSN (last))
|| !NEXT_INSN (NEXT_INSN (last))
|| !BARRIER_P (NEXT_INSN (NEXT_INSN (last)))
|| !NEXT_INSN (NEXT_INSN (NEXT_INSN (last)))
|| !LABEL_P (NEXT_INSN (NEXT_INSN (NEXT_INSN (last))))
|| NEXT_INSN (NEXT_INSN (NEXT_INSN (NEXT_INSN (last)))))
goto failed;
gcc_assert (!find_reg_note (last, REG_BR_PROB, 0));
REG_NOTES (last)
= gen_rtx_EXPR_LIST (REG_BR_PROB,
GEN_INT (REG_BR_PROB_BASE - probability),
REG_NOTES (last));
return;
}
if (!last || !JUMP_P (last) || !any_condjump_p (last))
goto failed;
gcc_assert (!find_reg_note (last, REG_BR_PROB, 0));
REG_NOTES (last)
= gen_rtx_EXPR_LIST (REG_BR_PROB,
GEN_INT (probability), REG_NOTES (last));
return;
failed:
if (dump_file)
fprintf (dump_file, "Failed to add probability note\n");
}
#ifndef LOCAL_ALIGNMENT
#define LOCAL_ALIGNMENT(TYPE, ALIGNMENT) ALIGNMENT
#endif
#ifndef STACK_ALIGNMENT_NEEDED
#define STACK_ALIGNMENT_NEEDED 1
#endif
struct stack_var
{
tree decl;
HOST_WIDE_INT offset;
HOST_WIDE_INT size;
unsigned int alignb;
size_t representative;
size_t next;
};
#define EOC ((size_t)-1)
static struct stack_var *stack_vars;
static size_t stack_vars_alloc;
static size_t stack_vars_num;
static size_t *stack_vars_sorted;
static bool *stack_vars_conflict;
static size_t stack_vars_conflict_alloc;
static int frame_phase;
static bool has_protected_decls;
static bool has_short_buffer;
static unsigned int
get_decl_align_unit (tree decl)
{
unsigned int align;
align = DECL_ALIGN (decl);
align = LOCAL_ALIGNMENT (TREE_TYPE (decl), align);
if (align > PREFERRED_STACK_BOUNDARY)
align = PREFERRED_STACK_BOUNDARY;
if (cfun->stack_alignment_needed < align)
cfun->stack_alignment_needed = align;
return align / BITS_PER_UNIT;
}
static HOST_WIDE_INT
alloc_stack_frame_space (HOST_WIDE_INT size, HOST_WIDE_INT align)
{
HOST_WIDE_INT offset, new_frame_offset;
new_frame_offset = frame_offset;
if (FRAME_GROWS_DOWNWARD)
{
new_frame_offset -= size + frame_phase;
new_frame_offset &= -align;
new_frame_offset += frame_phase;
offset = new_frame_offset;
}
else
{
new_frame_offset -= frame_phase;
new_frame_offset += align - 1;
new_frame_offset &= -align;
new_frame_offset += frame_phase;
offset = new_frame_offset;
new_frame_offset += size;
}
frame_offset = new_frame_offset;
if (frame_offset_overflow (frame_offset, cfun->decl))
frame_offset = offset = 0;
return offset;
}
static void
add_stack_var (tree decl)
{
if (stack_vars_num >= stack_vars_alloc)
{
if (stack_vars_alloc)
stack_vars_alloc = stack_vars_alloc * 3 / 2;
else
stack_vars_alloc = 32;
stack_vars
= XRESIZEVEC (struct stack_var, stack_vars, stack_vars_alloc);
}
stack_vars[stack_vars_num].decl = decl;
stack_vars[stack_vars_num].offset = 0;
stack_vars[stack_vars_num].size = tree_low_cst (DECL_SIZE_UNIT (decl), 1);
stack_vars[stack_vars_num].alignb = get_decl_align_unit (decl);
stack_vars[stack_vars_num].representative = stack_vars_num;
stack_vars[stack_vars_num].next = EOC;
SET_DECL_RTL (decl, pc_rtx);
stack_vars_num++;
}
static size_t
triangular_index (size_t i, size_t j)
{
if (i < j)
{
size_t t;
t = i, i = j, j = t;
}
return (i * (i + 1)) / 2 + j;
}
static void
resize_stack_vars_conflict (size_t n)
{
size_t size = triangular_index (n-1, n-1) + 1;
if (size <= stack_vars_conflict_alloc)
return;
stack_vars_conflict = XRESIZEVEC (bool, stack_vars_conflict, size);
memset (stack_vars_conflict + stack_vars_conflict_alloc, 0,
(size - stack_vars_conflict_alloc) * sizeof (bool));
stack_vars_conflict_alloc = size;
}
static void
add_stack_var_conflict (size_t x, size_t y)
{
size_t index = triangular_index (x, y);
gcc_assert (index < stack_vars_conflict_alloc);
stack_vars_conflict[index] = true;
}
static bool
stack_var_conflict_p (size_t x, size_t y)
{
size_t index = triangular_index (x, y);
gcc_assert (index < stack_vars_conflict_alloc);
return stack_vars_conflict[index];
}
static bool
aggregate_contains_union_type (tree type)
{
tree field;
if (TREE_CODE (type) == UNION_TYPE
|| TREE_CODE (type) == QUAL_UNION_TYPE)
return true;
if (TREE_CODE (type) == ARRAY_TYPE)
return aggregate_contains_union_type (TREE_TYPE (type));
if (TREE_CODE (type) != RECORD_TYPE)
return false;
for (field = TYPE_FIELDS (type); field; field = TREE_CHAIN (field))
if (TREE_CODE (field) == FIELD_DECL)
if (aggregate_contains_union_type (TREE_TYPE (field)))
return true;
return false;
}
static void
add_alias_set_conflicts (void)
{
size_t i, j, n = stack_vars_num;
for (i = 0; i < n; ++i)
{
tree type_i = TREE_TYPE (stack_vars[i].decl);
bool aggr_i = AGGREGATE_TYPE_P (type_i);
bool contains_union;
contains_union = aggregate_contains_union_type (type_i);
for (j = 0; j < i; ++j)
{
tree type_j = TREE_TYPE (stack_vars[j].decl);
bool aggr_j = AGGREGATE_TYPE_P (type_j);
if (aggr_i != aggr_j
|| !objects_must_conflict_p (type_i, type_j)
|| contains_union)
add_stack_var_conflict (i, j);
}
}
}
static int
stack_var_size_cmp (const void *a, const void *b)
{
HOST_WIDE_INT sa = stack_vars[*(const size_t *)a].size;
HOST_WIDE_INT sb = stack_vars[*(const size_t *)b].size;
unsigned int uida = DECL_UID (stack_vars[*(const size_t *)a].decl);
unsigned int uidb = DECL_UID (stack_vars[*(const size_t *)b].decl);
if (sa < sb)
return -1;
if (sa > sb)
return 1;
if (uida < uidb)
return -1;
if (uida > uidb)
return 1;
return 0;
}
static void
union_stack_vars (size_t a, size_t b, HOST_WIDE_INT offset)
{
size_t i, last;
for (last = i = b; i != EOC; last = i, i = stack_vars[i].next)
{
stack_vars[i].offset += offset;
stack_vars[i].representative = a;
}
stack_vars[last].next = stack_vars[a].next;
stack_vars[a].next = b;
if (stack_vars[a].alignb < stack_vars[b].alignb)
stack_vars[a].alignb = stack_vars[b].alignb;
for (last = stack_vars_num, i = 0; i < last; ++i)
if (stack_var_conflict_p (b, i))
add_stack_var_conflict (a, i);
}
static void
partition_stack_vars (void)
{
size_t si, sj, n = stack_vars_num;
stack_vars_sorted = XNEWVEC (size_t, stack_vars_num);
for (si = 0; si < n; ++si)
stack_vars_sorted[si] = si;
if (n == 1)
return;
qsort (stack_vars_sorted, n, sizeof (size_t), stack_var_size_cmp);
gcc_assert (sizeof(bool) == sizeof(char));
if (memchr (stack_vars_conflict, false, stack_vars_conflict_alloc) == NULL)
return;
for (si = 0; si < n; ++si)
{
size_t i = stack_vars_sorted[si];
HOST_WIDE_INT isize = stack_vars[i].size;
HOST_WIDE_INT offset = 0;
for (sj = si; sj-- > 0; )
{
size_t j = stack_vars_sorted[sj];
HOST_WIDE_INT jsize = stack_vars[j].size;
unsigned int jalign = stack_vars[j].alignb;
if (stack_vars[j].representative != j)
continue;
if (isize < jsize)
continue;
if (stack_var_conflict_p (i, j))
continue;
if (offset & (jalign - 1))
{
HOST_WIDE_INT toff = offset;
toff += jalign - 1;
toff &= -(HOST_WIDE_INT)jalign;
if (isize - (toff - offset) < jsize)
continue;
isize -= toff - offset;
offset = toff;
}
union_stack_vars (i, j, offset);
isize -= jsize;
if (isize == 0)
break;
}
}
}
static void
dump_stack_var_partition (void)
{
size_t si, i, j, n = stack_vars_num;
for (si = 0; si < n; ++si)
{
i = stack_vars_sorted[si];
if (stack_vars[i].representative != i)
continue;
fprintf (dump_file, "Partition %lu: size " HOST_WIDE_INT_PRINT_DEC
" align %u\n", (unsigned long) i, stack_vars[i].size,
stack_vars[i].alignb);
for (j = i; j != EOC; j = stack_vars[j].next)
{
fputc ('\t', dump_file);
print_generic_expr (dump_file, stack_vars[j].decl, dump_flags);
fprintf (dump_file, ", offset " HOST_WIDE_INT_PRINT_DEC "\n",
stack_vars[i].offset);
}
}
}
static void
expand_one_stack_var_at (tree decl, HOST_WIDE_INT offset)
{
HOST_WIDE_INT align;
rtx x;
gcc_assert (offset == trunc_int_for_mode (offset, Pmode));
x = plus_constant (virtual_stack_vars_rtx, offset);
x = gen_rtx_MEM (DECL_MODE (decl), x);
offset -= frame_phase;
align = offset & -offset;
align *= BITS_PER_UNIT;
if (align > STACK_BOUNDARY || align == 0)
align = STACK_BOUNDARY;
DECL_ALIGN (decl) = align;
DECL_USER_ALIGN (decl) = 0;
set_mem_attributes (x, decl, true);
SET_DECL_RTL (decl, x);
}
static void
expand_stack_vars (bool (*pred) (tree))
{
size_t si, i, j, n = stack_vars_num;
for (si = 0; si < n; ++si)
{
HOST_WIDE_INT offset;
i = stack_vars_sorted[si];
if (stack_vars[i].representative != i)
continue;
if (DECL_RTL (stack_vars[i].decl) != pc_rtx)
continue;
if (pred && !pred (stack_vars[i].decl))
continue;
offset = alloc_stack_frame_space (stack_vars[i].size,
stack_vars[i].alignb);
for (j = i; j != EOC; j = stack_vars[j].next)
expand_one_stack_var_at (stack_vars[j].decl,
stack_vars[j].offset + offset);
}
}
static void
expand_one_stack_var (tree var)
{
HOST_WIDE_INT size, offset, align;
size = tree_low_cst (DECL_SIZE_UNIT (var), 1);
align = get_decl_align_unit (var);
offset = alloc_stack_frame_space (size, align);
expand_one_stack_var_at (var, offset);
}
static void
expand_one_static_var (tree var)
{
if (flag_unit_at_a_time)
return;
var = DECL_ORIGIN (var);
if (TREE_ASM_WRITTEN (var))
return;
if (lang_hooks.expand_decl (var))
return;
rest_of_decl_compilation (var, 0, 0);
}
static void
expand_one_hard_reg_var (tree var)
{
rest_of_decl_compilation (var, 0, 0);
}
static void
expand_one_register_var (tree var)
{
tree type = TREE_TYPE (var);
int unsignedp = TYPE_UNSIGNED (type);
enum machine_mode reg_mode
= promote_mode (type, DECL_MODE (var), &unsignedp, 0);
rtx x = gen_reg_rtx (reg_mode);
SET_DECL_RTL (var, x);
if (!DECL_ARTIFICIAL (var))
{
mark_user_reg (x);
if (POINTER_TYPE_P (type))
mark_reg_pointer (x, TYPE_ALIGN (TREE_TYPE (TREE_TYPE (var))));
}
}
static void
expand_one_error_var (tree var)
{
enum machine_mode mode = DECL_MODE (var);
rtx x;
if (mode == BLKmode)
x = gen_rtx_MEM (BLKmode, const0_rtx);
else if (mode == VOIDmode)
x = const0_rtx;
else
x = gen_reg_rtx (mode);
SET_DECL_RTL (var, x);
}
static bool
defer_stack_allocation (tree var, bool toplevel)
{
if (flag_stack_protect)
return true;
if (toplevel && optimize < 2)
return false;
if (optimize == 0 && tree_low_cst (DECL_SIZE_UNIT (var), 1) < 32)
return false;
return true;
}
static void
expand_one_var (tree var, bool toplevel)
{
if (TREE_CODE (var) != VAR_DECL)
lang_hooks.expand_decl (var);
else if (DECL_EXTERNAL (var))
;
else if (DECL_HAS_VALUE_EXPR_P (var))
;
else if (TREE_STATIC (var))
expand_one_static_var (var);
else if (DECL_RTL_SET_P (var))
;
else if (TREE_TYPE (var) == error_mark_node)
expand_one_error_var (var);
else if (DECL_HARD_REGISTER (var))
expand_one_hard_reg_var (var);
else if (use_register_for_decl (var))
expand_one_register_var (var);
else if (defer_stack_allocation (var, toplevel))
add_stack_var (var);
else
expand_one_stack_var (var);
}
static void
expand_used_vars_for_block (tree block, bool toplevel)
{
size_t i, j, old_sv_num, this_sv_num, new_sv_num;
tree t;
old_sv_num = toplevel ? 0 : stack_vars_num;
for (t = BLOCK_VARS (block); t ; t = TREE_CHAIN (t))
if (TREE_USED (t)
|| (!flag_unit_at_a_time && TREE_STATIC (t)
&& DECL_PRESERVE_P (t)))
expand_one_var (t, toplevel);
this_sv_num = stack_vars_num;
for (t = BLOCK_SUBBLOCKS (block); t ; t = BLOCK_CHAIN (t))
expand_used_vars_for_block (t, false);
if (old_sv_num < this_sv_num)
{
new_sv_num = stack_vars_num;
resize_stack_vars_conflict (new_sv_num);
for (i = old_sv_num; i < new_sv_num; ++i)
for (j = i < this_sv_num ? i+1 : this_sv_num; j-- > old_sv_num ;)
add_stack_var_conflict (i, j);
}
}
static void
clear_tree_used (tree block)
{
tree t;
for (t = BLOCK_VARS (block); t ; t = TREE_CHAIN (t))
TREE_USED (t) = 0;
for (t = BLOCK_SUBBLOCKS (block); t ; t = BLOCK_CHAIN (t))
clear_tree_used (t);
}
#define SPCT_HAS_LARGE_CHAR_ARRAY 1
#define SPCT_HAS_SMALL_CHAR_ARRAY 2
#define SPCT_HAS_ARRAY 4
#define SPCT_HAS_AGGREGATE 8
static unsigned int
stack_protect_classify_type (tree type)
{
unsigned int ret = 0;
tree t;
switch (TREE_CODE (type))
{
case ARRAY_TYPE:
t = TYPE_MAIN_VARIANT (TREE_TYPE (type));
if (t == char_type_node
|| t == signed_char_type_node
|| t == unsigned_char_type_node)
{
unsigned HOST_WIDE_INT max = PARAM_VALUE (PARAM_SSP_BUFFER_SIZE);
unsigned HOST_WIDE_INT len;
if (!TYPE_SIZE_UNIT (type)
|| !host_integerp (TYPE_SIZE_UNIT (type), 1))
len = max;
else
len = tree_low_cst (TYPE_SIZE_UNIT (type), 1);
if (len < max)
ret = SPCT_HAS_SMALL_CHAR_ARRAY | SPCT_HAS_ARRAY;
else
ret = SPCT_HAS_LARGE_CHAR_ARRAY | SPCT_HAS_ARRAY;
}
else
ret = SPCT_HAS_ARRAY;
break;
case UNION_TYPE:
case QUAL_UNION_TYPE:
case RECORD_TYPE:
ret = SPCT_HAS_AGGREGATE;
for (t = TYPE_FIELDS (type); t ; t = TREE_CHAIN (t))
if (TREE_CODE (t) == FIELD_DECL)
ret |= stack_protect_classify_type (TREE_TYPE (t));
break;
default:
break;
}
return ret;
}
static int
stack_protect_decl_phase (tree decl)
{
unsigned int bits = stack_protect_classify_type (TREE_TYPE (decl));
int ret = 0;
if (bits & SPCT_HAS_SMALL_CHAR_ARRAY)
has_short_buffer = true;
if (flag_stack_protect == 2)
{
if ((bits & (SPCT_HAS_SMALL_CHAR_ARRAY | SPCT_HAS_LARGE_CHAR_ARRAY))
&& !(bits & SPCT_HAS_AGGREGATE))
ret = 1;
else if (bits & SPCT_HAS_ARRAY)
ret = 2;
}
else
ret = (bits & SPCT_HAS_LARGE_CHAR_ARRAY) != 0;
if (ret)
has_protected_decls = true;
return ret;
}
static bool
stack_protect_decl_phase_1 (tree decl)
{
return stack_protect_decl_phase (decl) == 1;
}
static bool
stack_protect_decl_phase_2 (tree decl)
{
return stack_protect_decl_phase (decl) == 2;
}
static void
add_stack_protection_conflicts (void)
{
size_t i, j, n = stack_vars_num;
unsigned char *phase;
phase = XNEWVEC (unsigned char, n);
for (i = 0; i < n; ++i)
phase[i] = stack_protect_decl_phase (stack_vars[i].decl);
for (i = 0; i < n; ++i)
{
unsigned char ph_i = phase[i];
for (j = 0; j < i; ++j)
if (ph_i != phase[j])
add_stack_var_conflict (i, j);
}
XDELETEVEC (phase);
}
static void
create_stack_guard (void)
{
tree guard = build_decl (VAR_DECL, NULL, ptr_type_node);
TREE_THIS_VOLATILE (guard) = 1;
TREE_USED (guard) = 1;
expand_one_stack_var (guard);
cfun->stack_protect_guard = guard;
}
static void
expand_used_vars (void)
{
tree t, outer_block = DECL_INITIAL (current_function_decl);
{
int align = PREFERRED_STACK_BOUNDARY / BITS_PER_UNIT;
int off = STARTING_FRAME_OFFSET % align;
frame_phase = off ? align - off : 0;
}
for (t = cfun->unexpanded_var_list; t; t = TREE_CHAIN (t))
TREE_USED (TREE_VALUE (t)) = 1;
clear_tree_used (outer_block);
has_protected_decls = false;
has_short_buffer = false;
for (t = cfun->unexpanded_var_list; t; t = TREE_CHAIN (t))
{
tree var = TREE_VALUE (t);
bool expand_now = false;
if (TREE_STATIC (var) || DECL_EXTERNAL (var))
expand_now = true;
else if (is_gimple_reg (var))
expand_now = true;
else if (TREE_USED (var))
expand_now = true;
TREE_USED (var) = 1;
if (expand_now)
expand_one_var (var, true);
}
cfun->unexpanded_var_list = NULL_TREE;
expand_used_vars_for_block (outer_block, true);
if (stack_vars_num > 0)
{
add_alias_set_conflicts ();
if (flag_stack_protect)
add_stack_protection_conflicts ();
partition_stack_vars ();
if (dump_file)
dump_stack_var_partition ();
}
if ((flag_stack_protect == 2
|| (flag_stack_protect
&& (current_function_calls_alloca || has_protected_decls)))
&& !cfun->iasm_asm_function)
create_stack_guard ();
if (stack_vars_num > 0)
{
if (has_protected_decls)
{
expand_stack_vars (stack_protect_decl_phase_1);
if (flag_stack_protect == 2)
expand_stack_vars (stack_protect_decl_phase_2);
}
expand_stack_vars (NULL);
XDELETEVEC (stack_vars);
XDELETEVEC (stack_vars_sorted);
XDELETEVEC (stack_vars_conflict);
stack_vars = NULL;
stack_vars_alloc = stack_vars_num = 0;
stack_vars_conflict = NULL;
stack_vars_conflict_alloc = 0;
}
if (STACK_ALIGNMENT_NEEDED)
{
HOST_WIDE_INT align = PREFERRED_STACK_BOUNDARY / BITS_PER_UNIT;
if (!FRAME_GROWS_DOWNWARD)
frame_offset += align - 1;
frame_offset &= -align;
}
}
static void
maybe_dump_rtl_for_tree_stmt (tree stmt, rtx since)
{
if (dump_file && (dump_flags & TDF_DETAILS))
{
fprintf (dump_file, "\n;; ");
print_generic_expr (dump_file, stmt, TDF_SLIM);
fprintf (dump_file, "\n");
print_rtl (dump_file, since ? NEXT_INSN (since) : since);
}
}
static basic_block
expand_gimple_cond_expr (basic_block bb, tree stmt)
{
basic_block new_bb, dest;
edge new_edge;
edge true_edge;
edge false_edge;
tree pred = COND_EXPR_COND (stmt);
tree then_exp = COND_EXPR_THEN (stmt);
tree else_exp = COND_EXPR_ELSE (stmt);
rtx last2, last;
last2 = last = get_last_insn ();
extract_true_false_edges_from_block (bb, &true_edge, &false_edge);
if (EXPR_LOCUS (stmt))
{
emit_line_note (*(EXPR_LOCUS (stmt)));
record_block_change (TREE_BLOCK (stmt));
}
true_edge->flags &= ~EDGE_TRUE_VALUE;
false_edge->flags &= ~EDGE_FALSE_VALUE;
if (TREE_CODE (then_exp) == GOTO_EXPR && IS_EMPTY_STMT (else_exp))
{
jumpif (pred, label_rtx (GOTO_DESTINATION (then_exp)));
add_reg_br_prob_note (last, true_edge->probability);
maybe_dump_rtl_for_tree_stmt (stmt, last);
if (EXPR_LOCUS (then_exp))
emit_line_note (*(EXPR_LOCUS (then_exp)));
return NULL;
}
if (TREE_CODE (else_exp) == GOTO_EXPR && IS_EMPTY_STMT (then_exp))
{
jumpifnot (pred, label_rtx (GOTO_DESTINATION (else_exp)));
add_reg_br_prob_note (last, false_edge->probability);
maybe_dump_rtl_for_tree_stmt (stmt, last);
if (EXPR_LOCUS (else_exp))
emit_line_note (*(EXPR_LOCUS (else_exp)));
return NULL;
}
gcc_assert (TREE_CODE (then_exp) == GOTO_EXPR
&& TREE_CODE (else_exp) == GOTO_EXPR);
jumpif (pred, label_rtx (GOTO_DESTINATION (then_exp)));
add_reg_br_prob_note (last, true_edge->probability);
last = get_last_insn ();
expand_expr (else_exp, const0_rtx, VOIDmode, 0);
BB_END (bb) = last;
if (BARRIER_P (BB_END (bb)))
BB_END (bb) = PREV_INSN (BB_END (bb));
update_bb_for_insn (bb);
new_bb = create_basic_block (NEXT_INSN (last), get_last_insn (), bb);
dest = false_edge->dest;
redirect_edge_succ (false_edge, new_bb);
false_edge->flags |= EDGE_FALLTHRU;
new_bb->count = false_edge->count;
new_bb->frequency = EDGE_FREQUENCY (false_edge);
new_edge = make_edge (new_bb, dest, 0);
new_edge->probability = REG_BR_PROB_BASE;
new_edge->count = new_bb->count;
if (BARRIER_P (BB_END (new_bb)))
BB_END (new_bb) = PREV_INSN (BB_END (new_bb));
update_bb_for_insn (new_bb);
maybe_dump_rtl_for_tree_stmt (stmt, last2);
if (EXPR_LOCUS (else_exp))
emit_line_note (*(EXPR_LOCUS (else_exp)));
return new_bb;
}
static basic_block
expand_gimple_tailcall (basic_block bb, tree stmt, bool *can_fallthru)
{
rtx last2, last;
edge e;
edge_iterator ei;
int probability;
gcov_type count;
last2 = last = get_last_insn ();
expand_expr_stmt (stmt);
for (last = NEXT_INSN (last); last; last = NEXT_INSN (last))
if (CALL_P (last) && SIBLING_CALL_P (last))
goto found;
maybe_dump_rtl_for_tree_stmt (stmt, last2);
*can_fallthru = true;
return NULL;
found:
do_pending_stack_adjust ();
probability = 0;
count = 0;
for (ei = ei_start (bb->succs); (e = ei_safe_edge (ei)); )
{
if (!(e->flags & (EDGE_ABNORMAL | EDGE_EH)))
{
if (e->dest != EXIT_BLOCK_PTR)
{
e->dest->count -= e->count;
e->dest->frequency -= EDGE_FREQUENCY (e);
if (e->dest->count < 0)
e->dest->count = 0;
if (e->dest->frequency < 0)
e->dest->frequency = 0;
}
count += e->count;
probability += e->probability;
remove_edge (e);
}
else
ei_next (&ei);
}
last = NEXT_INSN (last);
gcc_assert (BARRIER_P (last));
*can_fallthru = false;
while (NEXT_INSN (last))
{
if (LABEL_P (NEXT_INSN (last)))
{
*can_fallthru = true;
break;
}
delete_insn (NEXT_INSN (last));
}
e = make_edge (bb, EXIT_BLOCK_PTR, EDGE_ABNORMAL | EDGE_SIBCALL);
e->probability += probability;
e->count += count;
BB_END (bb) = last;
update_bb_for_insn (bb);
if (NEXT_INSN (last))
{
bb = create_basic_block (NEXT_INSN (last), get_last_insn (), bb);
last = BB_END (bb);
if (BARRIER_P (last))
BB_END (bb) = PREV_INSN (last);
}
maybe_dump_rtl_for_tree_stmt (stmt, last2);
return bb;
}
static basic_block
expand_gimple_basic_block (basic_block bb)
{
block_stmt_iterator bsi = bsi_start (bb);
tree stmt = NULL;
rtx note, last;
edge e;
edge_iterator ei;
if (dump_file)
{
fprintf (dump_file,
"\n;; Generating RTL for tree basic block %d\n",
bb->index);
}
init_rtl_bb_info (bb);
bb->flags |= BB_RTL;
if (!bsi_end_p (bsi))
stmt = bsi_stmt (bsi);
if (stmt && TREE_CODE (stmt) == LABEL_EXPR)
{
last = get_last_insn ();
expand_expr_stmt (stmt);
BB_HEAD (bb) = NEXT_INSN (last);
if (NOTE_P (BB_HEAD (bb)))
BB_HEAD (bb) = NEXT_INSN (BB_HEAD (bb));
bsi_next (&bsi);
note = emit_note_after (NOTE_INSN_BASIC_BLOCK, BB_HEAD (bb));
maybe_dump_rtl_for_tree_stmt (stmt, last);
}
else
note = BB_HEAD (bb) = emit_note (NOTE_INSN_BASIC_BLOCK);
NOTE_BASIC_BLOCK (note) = bb;
for (ei = ei_start (bb->succs); (e = ei_safe_edge (ei)); )
{
e->flags &= ~EDGE_EXECUTABLE;
if (e->flags & EDGE_ABNORMAL)
remove_edge (e);
else
ei_next (&ei);
}
for (; !bsi_end_p (bsi); bsi_next (&bsi))
{
tree stmt = bsi_stmt (bsi);
basic_block new_bb;
if (!stmt)
continue;
if (TREE_CODE (stmt) == COND_EXPR)
{
new_bb = expand_gimple_cond_expr (bb, stmt);
if (new_bb)
return new_bb;
}
else
{
tree call = get_call_expr_in (stmt);
if (call && CALL_EXPR_TAILCALL (call))
{
bool can_fallthru;
new_bb = expand_gimple_tailcall (bb, stmt, &can_fallthru);
if (new_bb)
{
if (can_fallthru)
bb = new_bb;
else
return new_bb;
}
}
else
{
last = get_last_insn ();
expand_expr_stmt (stmt);
maybe_dump_rtl_for_tree_stmt (stmt, last);
}
}
}
do_pending_stack_adjust ();
last = get_last_insn ();
if (BARRIER_P (last))
last = PREV_INSN (last);
if (JUMP_TABLE_DATA_P (last))
last = PREV_INSN (PREV_INSN (last));
BB_END (bb) = last;
update_bb_for_insn (bb);
return bb;
}
static basic_block
construct_init_block (void)
{
basic_block init_block, first_block;
edge e = NULL;
int flags;
gcc_assert (EDGE_COUNT (ENTRY_BLOCK_PTR->succs) == 1);
init_rtl_bb_info (ENTRY_BLOCK_PTR);
init_rtl_bb_info (EXIT_BLOCK_PTR);
ENTRY_BLOCK_PTR->flags |= BB_RTL;
EXIT_BLOCK_PTR->flags |= BB_RTL;
e = EDGE_SUCC (ENTRY_BLOCK_PTR, 0);
if (e && e->dest != ENTRY_BLOCK_PTR->next_bb)
{
tree label = tree_block_label (e->dest);
emit_jump (label_rtx (label));
flags = 0;
}
else
flags = EDGE_FALLTHRU;
init_block = create_basic_block (NEXT_INSN (get_insns ()),
get_last_insn (),
ENTRY_BLOCK_PTR);
init_block->frequency = ENTRY_BLOCK_PTR->frequency;
init_block->count = ENTRY_BLOCK_PTR->count;
if (e)
{
first_block = e->dest;
redirect_edge_succ (e, init_block);
e = make_edge (init_block, first_block, flags);
}
else
e = make_edge (init_block, EXIT_BLOCK_PTR, EDGE_FALLTHRU);
e->probability = REG_BR_PROB_BASE;
e->count = ENTRY_BLOCK_PTR->count;
update_bb_for_insn (init_block);
return init_block;
}
static void
construct_exit_block (void)
{
rtx head = get_last_insn ();
rtx end;
basic_block exit_block;
edge e, e2;
unsigned ix;
edge_iterator ei;
#ifdef USE_MAPPED_LOCATION
if (cfun->function_end_locus != UNKNOWN_LOCATION)
#else
if (cfun->function_end_locus.file)
#endif
input_location = cfun->function_end_locus;
record_block_change (DECL_INITIAL (current_function_decl));
expand_function_end ();
end = get_last_insn ();
if (head == end)
return;
while (NEXT_INSN (head) && NOTE_P (NEXT_INSN (head)))
head = NEXT_INSN (head);
exit_block = create_basic_block (NEXT_INSN (head), end,
EXIT_BLOCK_PTR->prev_bb);
exit_block->frequency = EXIT_BLOCK_PTR->frequency;
exit_block->count = EXIT_BLOCK_PTR->count;
ix = 0;
while (ix < EDGE_COUNT (EXIT_BLOCK_PTR->preds))
{
e = EDGE_PRED (EXIT_BLOCK_PTR, ix);
if (!(e->flags & EDGE_ABNORMAL))
redirect_edge_succ (e, exit_block);
else
ix++;
}
e = make_edge (exit_block, EXIT_BLOCK_PTR, EDGE_FALLTHRU);
e->probability = REG_BR_PROB_BASE;
e->count = EXIT_BLOCK_PTR->count;
FOR_EACH_EDGE (e2, ei, EXIT_BLOCK_PTR->preds)
if (e2 != e)
{
e->count -= e2->count;
exit_block->count -= e2->count;
exit_block->frequency -= EDGE_FREQUENCY (e2);
}
if (e->count < 0)
e->count = 0;
if (exit_block->count < 0)
exit_block->count = 0;
if (exit_block->frequency < 0)
exit_block->frequency = 0;
update_bb_for_insn (exit_block);
}
static tree
discover_nonconstant_array_refs_r (tree * tp, int *walk_subtrees,
void *data ATTRIBUTE_UNUSED)
{
tree t = *tp;
if (IS_TYPE_OR_DECL_P (t))
*walk_subtrees = 0;
else if (TREE_CODE (t) == ARRAY_REF || TREE_CODE (t) == ARRAY_RANGE_REF)
{
while (((TREE_CODE (t) == ARRAY_REF || TREE_CODE (t) == ARRAY_RANGE_REF)
&& is_gimple_min_invariant (TREE_OPERAND (t, 1))
&& (!TREE_OPERAND (t, 2)
|| is_gimple_min_invariant (TREE_OPERAND (t, 2))))
|| (TREE_CODE (t) == COMPONENT_REF
&& (!TREE_OPERAND (t,2)
|| is_gimple_min_invariant (TREE_OPERAND (t, 2))))
|| TREE_CODE (t) == BIT_FIELD_REF
|| TREE_CODE (t) == REALPART_EXPR
|| TREE_CODE (t) == IMAGPART_EXPR
|| TREE_CODE (t) == VIEW_CONVERT_EXPR
|| TREE_CODE (t) == NOP_EXPR
|| TREE_CODE (t) == CONVERT_EXPR)
t = TREE_OPERAND (t, 0);
if (TREE_CODE (t) == ARRAY_REF || TREE_CODE (t) == ARRAY_RANGE_REF)
{
t = get_base_address (t);
if (t && DECL_P (t))
TREE_ADDRESSABLE (t) = 1;
}
*walk_subtrees = 0;
}
return NULL_TREE;
}
static void
discover_nonconstant_array_refs (void)
{
basic_block bb;
block_stmt_iterator bsi;
FOR_EACH_BB (bb)
{
for (bsi = bsi_start (bb); !bsi_end_p (bsi); bsi_next (&bsi))
walk_tree (bsi_stmt_ptr (bsi), discover_nonconstant_array_refs_r,
NULL , NULL);
}
}
static unsigned int
tree_expand_cfg (void)
{
basic_block bb, init_block;
sbitmap blocks;
edge_iterator ei;
edge e;
currently_expanding_to_rtl = 1;
reset_block_changes ();
discover_nonconstant_array_refs ();
expand_used_vars ();
if (warn_stack_protect)
{
if (current_function_calls_alloca)
warning (0, "not protecting local variables: variable length buffer");
if (has_short_buffer && !cfun->stack_protect_guard)
warning (0, "not protecting function: no buffer at least %d bytes long",
(int) PARAM_VALUE (PARAM_SSP_BUFFER_SIZE));
}
expand_function_start (current_function_decl);
if (DECL_NAME (current_function_decl)
&& MAIN_NAME_P (DECL_NAME (current_function_decl))
&& DECL_FILE_SCOPE_P (current_function_decl))
expand_main_function ();
if (cfun->stack_protect_guard)
stack_protect_prologue ();
rtl_register_cfg_hooks ();
init_block = construct_init_block ();
FOR_EACH_EDGE (e, ei, ENTRY_BLOCK_PTR->succs)
e->flags &= ~EDGE_EXECUTABLE;
FOR_BB_BETWEEN (bb, init_block->next_bb, EXIT_BLOCK_PTR, next_bb)
bb = expand_gimple_basic_block (bb);
construct_exit_block ();
currently_expanding_to_rtl = 0;
convert_from_eh_region_ranges ();
rebuild_jump_labels (get_insns ());
find_exception_handler_labels ();
blocks = sbitmap_alloc (last_basic_block);
sbitmap_ones (blocks);
find_many_sub_basic_blocks (blocks);
purge_all_dead_edges ();
sbitmap_free (blocks);
compact_blocks ();
#ifdef ENABLE_CHECKING
verify_flow_info();
#endif
DECL_DEFER_OUTPUT (current_function_decl) = 0;
generating_concat_p = 0;
finalize_block_changes ();
if (dump_file)
{
fprintf (dump_file,
"\n\n;;\n;; Full RTL generated for this function:\n;;\n");
}
{
tree parent;
for (parent = DECL_CONTEXT (current_function_decl);
parent != NULL_TREE;
parent = get_containing_scope (parent))
if (TREE_CODE (parent) == FUNCTION_DECL)
TREE_SYMBOL_REFERENCED (DECL_ASSEMBLER_NAME (parent)) = 1;
}
if (cgraph_function_possibly_inlined_p (current_function_decl))
(*debug_hooks->outlining_inline_function) (current_function_decl);
TREE_ASM_WRITTEN (current_function_decl) = 1;
return_label = NULL;
naked_return_label = NULL;
return 0;
}
struct tree_opt_pass pass_expand =
{
"expand",
NULL,
tree_expand_cfg,
NULL,
NULL,
0,
TV_EXPAND,
PROP_gimple_leh | PROP_cfg,
PROP_rtl,
PROP_trees,
0,
TODO_dump_func,
'r'
};