From df62c1c110e8532b995b23540b7e3695729c0779 Mon Sep 17 00:00:00 2001 From: Jing Yu Date: Thu, 5 Nov 2009 15:11:04 -0800 Subject: Check in gcc sources for prebuilt toolchains in Eclair. --- gcc-4.2.1/gcc/cfgexpand.c | 1732 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1732 insertions(+) create mode 100644 gcc-4.2.1/gcc/cfgexpand.c (limited to 'gcc-4.2.1/gcc/cfgexpand.c') diff --git a/gcc-4.2.1/gcc/cfgexpand.c b/gcc-4.2.1/gcc/cfgexpand.c new file mode 100644 index 000000000..b688917cc --- /dev/null +++ b/gcc-4.2.1/gcc/cfgexpand.c @@ -0,0 +1,1732 @@ +/* A pass for lowering trees to RTL. + Copyright (C) 2004, 2005 Free Software Foundation, Inc. + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GCC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING. If not, write to +the Free Software Foundation, 51 Franklin Street, Fifth Floor, +Boston, MA 02110-1301, USA. */ + +#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" + +/* Verify that there is exactly single jump instruction since last and attach + REG_BR_PROB note specifying probability. + ??? We really ought to pass the probability down to RTL expanders and let it + re-distribute it when the conditional expands into multiple conditionals. + This is however difficult to do. */ +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)) + { + /* It is common to emit condjump-around-jump sequence when we don't know + how to reverse the conditional. Special case this. */ + 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 + + +/* This structure holds data relevant to one variable that will be + placed in a stack slot. */ +struct stack_var +{ + /* The Variable. */ + tree decl; + + /* The offset of the variable. During partitioning, this is the + offset relative to the partition. After partitioning, this + is relative to the stack frame. */ + HOST_WIDE_INT offset; + + /* Initially, the size of the variable. Later, the size of the partition, + if this variable becomes it's partition's representative. */ + HOST_WIDE_INT size; + + /* The *byte* alignment required for this variable. Or as, with the + size, the alignment for this partition. */ + unsigned int alignb; + + /* The partition representative. */ + size_t representative; + + /* The next stack variable in the partition, or EOC. */ + size_t next; +}; + +#define EOC ((size_t)-1) + +/* We have an array of such objects while deciding allocation. */ +static struct stack_var *stack_vars; +static size_t stack_vars_alloc; +static size_t stack_vars_num; + +/* An array of indicies such that stack_vars[stack_vars_sorted[i]].size + is non-decreasing. */ +static size_t *stack_vars_sorted; + +/* We have an interference graph between such objects. This graph + is lower triangular. */ +static bool *stack_vars_conflict; +static size_t stack_vars_conflict_alloc; + +/* The phase of the stack frame. This is the known misalignment of + virtual_stack_vars_rtx from PREFERRED_STACK_BOUNDARY. That is, + (frame_offset+frame_phase) % PREFERRED_STACK_BOUNDARY == 0. */ +static int frame_phase; + +/* Used during expand_used_vars to remember if we saw any decls for + which we'd like to enable stack smashing protection. */ +static bool has_protected_decls; + +/* Used during expand_used_vars. Remember if we say a character buffer + smaller than our cutoff threshold. Used for -Wstack-protector. */ +static bool has_short_buffer; + +/* Discover the byte alignment to use for DECL. Ignore alignment + we can't do with expected alignment of the stack boundary. */ + +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; +} + +/* Allocate SIZE bytes at byte alignment ALIGN from the stack frame. + Return the frame offset. */ + +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; +} + +/* Accumulate DECL into STACK_VARS. */ + +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); + + /* All variables are initially in their own partition. */ + stack_vars[stack_vars_num].representative = stack_vars_num; + stack_vars[stack_vars_num].next = EOC; + + /* Ensure that this decl doesn't get put onto the list twice. */ + SET_DECL_RTL (decl, pc_rtx); + + stack_vars_num++; +} + +/* Compute the linear index of a lower-triangular coordinate (I, J). */ + +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; +} + +/* Ensure that STACK_VARS_CONFLICT is large enough for N objects. */ + +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; +} + +/* Make the decls associated with luid's X and Y conflict. */ + +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; +} + +/* Check whether the decls associated with luid's X and Y conflict. */ + +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]; +} + +/* Returns true if TYPE is or contains a union type. */ + +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; +} + +/* A subroutine of expand_used_vars. If two variables X and Y have alias + sets that do not conflict, then do add a conflict for these variables + in the interference graph. We also need to make sure to add conflicts + for union containing structures. Else RTL alias analysis comes along + and due to type based aliasing rules decides that for two overlapping + union temporaries { short s; int i; } accesses to the same mem through + different types may not alias and happily reorders stores across + life-time boundaries of the temporaries (See PR25654). + We also have to mind MEM_IN_STRUCT_P and MEM_SCALAR_P. */ + +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 + /* Either the objects conflict by means of type based + aliasing rules, or we need to add a conflict. */ + || !objects_must_conflict_p (type_i, type_j) + /* In case the types do not conflict ensure that access + to elements will conflict. In case of unions we have + to be careful as type based aliasing rules may say + access to the same memory does not conflict. So play + safe and add a conflict in this case. */ + || contains_union) + add_stack_var_conflict (i, j); + } + } +} + +/* A subroutine of partition_stack_vars. A comparison function for qsort, + sorting an array of indicies by the size of the object. */ + +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; + /* For stack variables of the same size use the uid of the decl + to make the sort stable. */ + if (uida < uidb) + return -1; + if (uida > uidb) + return 1; + return 0; +} + +/* A subroutine of partition_stack_vars. The UNION portion of a UNION/FIND + partitioning algorithm. Partitions A and B are known to be non-conflicting. + Merge them into a single partition A. + + At the same time, add OFFSET to all variables in partition B. At the end + of the partitioning process we've have a nice block easy to lay out within + the stack frame. */ + +static void +union_stack_vars (size_t a, size_t b, HOST_WIDE_INT offset) +{ + size_t i, last; + + /* Update each element of partition B with the given offset, + and merge them into partition A. */ + 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; + + /* Update the required alignment of partition A to account for B. */ + if (stack_vars[a].alignb < stack_vars[b].alignb) + stack_vars[a].alignb = stack_vars[b].alignb; + + /* Update the interference graph and merge the conflicts. */ + for (last = stack_vars_num, i = 0; i < last; ++i) + if (stack_var_conflict_p (b, i)) + add_stack_var_conflict (a, i); +} + +/* A subroutine of expand_used_vars. Binpack the variables into + partitions constrained by the interference graph. The overall + algorithm used is as follows: + + Sort the objects by size. + For each object A { + S = size(A) + O = 0 + loop { + Look for the largest non-conflicting object B with size <= S. + UNION (A, B) + offset(B) = O + O += size(B) + S -= size(B) + } + } +*/ + +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); + + /* Special case: detect when all variables conflict, and thus we can't + do anything during the partitioning loop. It isn't uncommon (with + C code at least) to declare all variables at the top of the function, + and if we're not inlining, then all variables will be in the same scope. + Take advantage of very fast libc routines for this scan. */ + 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; + + /* Ignore objects that aren't partition representatives. */ + if (stack_vars[j].representative != j) + continue; + + /* Ignore objects too large for the remaining space. */ + if (isize < jsize) + continue; + + /* Ignore conflicting objects. */ + if (stack_var_conflict_p (i, j)) + continue; + + /* Refine the remaining space check to include alignment. */ + 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 the objects, placing J at OFFSET. */ + union_stack_vars (i, j, offset); + + isize -= jsize; + if (isize == 0) + break; + } + } +} + +/* A debugging aid for expand_used_vars. Dump the generated partitions. */ + +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]; + + /* Skip variables that aren't partition representatives, for now. */ + 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); + } + } +} + +/* Assign rtl to DECL at frame offset OFFSET. */ + +static void +expand_one_stack_var_at (tree decl, HOST_WIDE_INT offset) +{ + HOST_WIDE_INT align; + rtx x; + + /* If this fails, we've overflowed the stack frame. Error nicely? */ + 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); + + /* Set alignment we actually gave this decl. */ + 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); +} + +/* A subroutine of expand_used_vars. Give each partition representative + a unique location within the stack frame. Update each partition member + with that location. */ + +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]; + + /* Skip variables that aren't partition representatives, for now. */ + if (stack_vars[i].representative != i) + continue; + + /* Skip variables that have already had rtl assigned. See also + add_stack_var where we perpetrate this pc_rtx hack. */ + if (DECL_RTL (stack_vars[i].decl) != pc_rtx) + continue; + + /* Check the predicate to see whether this variable should be + allocated in this pass. */ + if (pred && !pred (stack_vars[i].decl)) + continue; + + offset = alloc_stack_frame_space (stack_vars[i].size, + stack_vars[i].alignb); + + /* Create rtl for each variable based on their location within the + partition. */ + for (j = i; j != EOC; j = stack_vars[j].next) + expand_one_stack_var_at (stack_vars[j].decl, + stack_vars[j].offset + offset); + } +} + +/* A subroutine of expand_one_var. Called to immediately assign rtl + to a variable to be allocated in the stack frame. */ + +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); +} + +/* A subroutine of expand_one_var. Called to assign rtl + to a TREE_STATIC VAR_DECL. */ + +static void +expand_one_static_var (tree var) +{ + /* In unit-at-a-time all the static variables are expanded at the end + of compilation process. */ + if (flag_unit_at_a_time) + return; + /* If this is an inlined copy of a static local variable, + look up the original. */ + var = DECL_ORIGIN (var); + + /* If we've already processed this variable because of that, do nothing. */ + if (TREE_ASM_WRITTEN (var)) + return; + + /* Give the front end a chance to do whatever. In practice, this is + resolving duplicate names for IMA in C. */ + if (lang_hooks.expand_decl (var)) + return; + + /* Otherwise, just emit the variable. */ + rest_of_decl_compilation (var, 0, 0); +} + +/* A subroutine of expand_one_var. Called to assign rtl to a VAR_DECL + that will reside in a hard register. */ + +static void +expand_one_hard_reg_var (tree var) +{ + rest_of_decl_compilation (var, 0, 0); +} + +/* A subroutine of expand_one_var. Called to assign rtl to a VAR_DECL + that will reside in a pseudo register. */ + +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); + + /* Note if the object is a user variable. */ + if (!DECL_ARTIFICIAL (var)) + { + mark_user_reg (x); + + /* Trust user variables which have a pointer type to really + be pointers. Do not trust compiler generated temporaries + as our type system is totally busted as it relates to + pointer arithmetic which translates into lots of compiler + generated objects with pointer types, but which are not really + pointers. */ + if (POINTER_TYPE_P (type)) + mark_reg_pointer (x, TYPE_ALIGN (TREE_TYPE (TREE_TYPE (var)))); + } +} + +/* A subroutine of expand_one_var. Called to assign rtl to a VAR_DECL that + has some associated error, e.g. its type is error-mark. We just need + to pick something that won't crash the rest of the compiler. */ + +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); +} + +/* A subroutine of expand_one_var. VAR is a variable that will be + allocated to the local stack frame. Return true if we wish to + add VAR to STACK_VARS so that it will be coalesced with other + variables. Return false to allocate VAR immediately. + + This function is used to reduce the number of variables considered + for coalescing, which reduces the size of the quadratic problem. */ + +static bool +defer_stack_allocation (tree var, bool toplevel) +{ + /* If stack protection is enabled, *all* stack variables must be deferred, + so that we can re-order the strings to the top of the frame. */ + if (flag_stack_protect) + return true; + + /* Variables in the outermost scope automatically conflict with + every other variable. The only reason to want to defer them + at all is that, after sorting, we can more efficiently pack + small variables in the stack frame. Continue to defer at -O2. */ + if (toplevel && optimize < 2) + return false; + + /* Without optimization, *most* variables are allocated from the + stack, which makes the quadratic problem large exactly when we + want compilation to proceed as quickly as possible. On the + other hand, we don't want the function's stack frame size to + get completely out of hand. So we avoid adding scalars and + "small" aggregates to the list at all. */ + if (optimize == 0 && tree_low_cst (DECL_SIZE_UNIT (var), 1) < 32) + return false; + + return true; +} + +/* A subroutine of expand_used_vars. Expand one variable according to + its flavor. Variables to be placed on the stack are not actually + expanded yet, merely recorded. */ + +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); +} + +/* A subroutine of expand_used_vars. Walk down through the BLOCK tree + expanding variables. Those variables that can be put into registers + are allocated pseudos; those that can't are put on the stack. + + TOPLEVEL is true if this is the outermost BLOCK. */ + +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; + + /* Expand all variables at this level. */ + for (t = BLOCK_VARS (block); t ; t = TREE_CHAIN (t)) + if (TREE_USED (t) + /* Force local static variables to be output when marked by + used attribute. For unit-at-a-time, cgraph code already takes + care of this. */ + || (!flag_unit_at_a_time && TREE_STATIC (t) + && DECL_PRESERVE_P (t))) + expand_one_var (t, toplevel); + + this_sv_num = stack_vars_num; + + /* Expand all variables at containing levels. */ + for (t = BLOCK_SUBBLOCKS (block); t ; t = BLOCK_CHAIN (t)) + expand_used_vars_for_block (t, false); + + /* Since we do not track exact variable lifetimes (which is not even + possible for variables whose address escapes), we mirror the block + tree in the interference graph. Here we cause all variables at this + level, and all sublevels, to conflict. Do make certain that a + variable conflicts with itself. */ + 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); + } +} + +/* A subroutine of expand_used_vars. Walk down through the BLOCK tree + and clear TREE_USED on all local variables. */ + +static void +clear_tree_used (tree block) +{ + tree t; + + for (t = BLOCK_VARS (block); t ; t = TREE_CHAIN (t)) + /* if (!TREE_STATIC (t) && !DECL_EXTERNAL (t)) */ + TREE_USED (t) = 0; + + for (t = BLOCK_SUBBLOCKS (block); t ; t = BLOCK_CHAIN (t)) + clear_tree_used (t); +} + +/* Examine TYPE and determine a bit mask of the following features. */ + +#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; +} + +/* Return nonzero if DECL should be segregated into the "vulnerable" upper + part of the local stack frame. Remember if we ever return nonzero for + any variable in this function. The return value is the phase number in + which the variable should be allocated. */ + +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; +} + +/* Two helper routines that check for phase 1 and phase 2. These are used + as callbacks for expand_stack_vars. */ + +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; +} + +/* Ensure that variables in different stack protection phases conflict + so that they are not merged and share the same stack slot. */ + +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); +} + +/* Create a decl for the guard at the top of the stack frame. */ + +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; +} + +/* Expand all variables used in the function. */ + +static void +expand_used_vars (void) +{ + tree t, outer_block = DECL_INITIAL (current_function_decl); + + /* Compute the phase of the stack frame for this function. */ + { + int align = PREFERRED_STACK_BOUNDARY / BITS_PER_UNIT; + int off = STARTING_FRAME_OFFSET % align; + frame_phase = off ? align - off : 0; + } + + /* Set TREE_USED on all variables in the unexpanded_var_list. */ + for (t = cfun->unexpanded_var_list; t; t = TREE_CHAIN (t)) + TREE_USED (TREE_VALUE (t)) = 1; + + /* Clear TREE_USED on all variables associated with a block scope. */ + clear_tree_used (outer_block); + + /* Initialize local stack smashing state. */ + has_protected_decls = false; + has_short_buffer = false; + + /* At this point all variables on the unexpanded_var_list with TREE_USED + set are not associated with any block scope. Lay them out. */ + for (t = cfun->unexpanded_var_list; t; t = TREE_CHAIN (t)) + { + tree var = TREE_VALUE (t); + bool expand_now = false; + + /* We didn't set a block for static or extern because it's hard + to tell the difference between a global variable (re)declared + in a local scope, and one that's really declared there to + begin with. And it doesn't really matter much, since we're + not giving them stack space. Expand them now. */ + if (TREE_STATIC (var) || DECL_EXTERNAL (var)) + expand_now = true; + + /* Any variable that could have been hoisted into an SSA_NAME + will have been propagated anywhere the optimizers chose, + i.e. not confined to their original block. Allocate them + as if they were defined in the outermost scope. */ + else if (is_gimple_reg (var)) + expand_now = true; + + /* If the variable is not associated with any block, then it + was created by the optimizers, and could be live anywhere + in the function. */ + else if (TREE_USED (var)) + expand_now = true; + + /* Finally, mark all variables on the list as used. We'll use + this in a moment when we expand those associated with scopes. */ + TREE_USED (var) = 1; + + if (expand_now) + expand_one_var (var, true); + } + cfun->unexpanded_var_list = NULL_TREE; + + /* At this point, all variables within the block tree with TREE_USED + set are actually used by the optimized function. Lay them out. */ + expand_used_vars_for_block (outer_block, true); + + if (stack_vars_num > 0) + { + /* Due to the way alias sets work, no variables with non-conflicting + alias sets may be assigned the same address. Add conflicts to + reflect this. */ + add_alias_set_conflicts (); + + /* If stack protection is enabled, we don't share space between + vulnerable data and non-vulnerable data. */ + if (flag_stack_protect) + add_stack_protection_conflicts (); + + /* Now that we have collected all stack variables, and have computed a + minimal interference graph, attempt to save some stack space. */ + partition_stack_vars (); + if (dump_file) + dump_stack_var_partition (); + } + + /* There are several conditions under which we should create a + stack guard: protect-all, alloca used, protected decls present. */ + if (flag_stack_protect == 2 + || (flag_stack_protect + && (current_function_calls_alloca || has_protected_decls))) + create_stack_guard (); + + /* Assign rtl to each variable based on these partitions. */ + if (stack_vars_num > 0) + { + /* Reorder decls to be protected by iterating over the variables + array multiple times, and allocating out of each phase in turn. */ + /* ??? We could probably integrate this into the qsort we did + earlier, such that we naturally see these variables first, + and thus naturally allocate things in the right order. */ + if (has_protected_decls) + { + /* Phase 1 contains only character arrays. */ + expand_stack_vars (stack_protect_decl_phase_1); + + /* Phase 2 contains other kinds of arrays. */ + if (flag_stack_protect == 2) + expand_stack_vars (stack_protect_decl_phase_2); + } + + expand_stack_vars (NULL); + + /* Free up stack variable graph data. */ + 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 the target requires that FRAME_OFFSET be aligned, do it. */ + 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; + } +} + + +/* If we need to produce a detailed dump, print the tree representation + for STMT to the dump file. SINCE is the last RTX after which the RTL + generated for STMT should have been appended. */ + +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); + } +} + +/* A subroutine of expand_gimple_basic_block. Expand one COND_EXPR. + Returns a new basic block if we've terminated the current basic + block and created a new one. */ + +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)); + } + + /* These flags have no purpose in RTL land. */ + true_edge->flags &= ~EDGE_TRUE_VALUE; + false_edge->flags &= ~EDGE_FALSE_VALUE; + + /* We can either have a pure conditional jump with one fallthru edge or + two-way jump that needs to be decomposed into two basic blocks. */ + 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; +} + +/* A subroutine of expand_gimple_basic_block. Expand one CALL_EXPR + that has CALL_EXPR_TAILCALL set. Returns non-null if we actually + generated a tail call (something that might be denied by the ABI + rules governing the call; see calls.c). + + Sets CAN_FALLTHRU if we generated a *conditional* tail call, and + can still reach the rest of BB. The case here is __builtin_sqrt, + where the NaN result goes through the external function (with a + tailcall) and the normal result happens via a sqrt instruction. */ + +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: + /* ??? Wouldn't it be better to just reset any pending stack adjust? + Any instructions emitted here are about to be deleted. */ + do_pending_stack_adjust (); + + /* Remove any non-eh, non-abnormal edges that don't go to exit. */ + /* ??? I.e. the fallthrough edge. HOWEVER! If there were to be + EH or abnormal edges, we shouldn't have created a tail call in + the first place. So it seems to me we should just be removing + all edges here, or redirecting the existing fallthru edge to + the exit block. */ + + 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); + } + + /* This is somewhat ugly: the call_expr expander often emits instructions + after the sibcall (to perform the function return). These confuse the + find_many_sub_basic_blocks code, so we need to get rid of these. */ + last = NEXT_INSN (last); + gcc_assert (BARRIER_P (last)); + + *can_fallthru = false; + while (NEXT_INSN (last)) + { + /* For instance an sqrt builtin expander expands if with + sibcall in the then and label for `else`. */ + 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; +} + +/* Expand basic block BB from GIMPLE trees to RTL. */ + +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); + + /* Java emits line number notes in the top of labels. + ??? Make this go away once line number notes are obsoleted. */ + 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)); ) + { + /* Clear EDGE_EXECUTABLE. This flag is never used in the backend. */ + e->flags &= ~EDGE_EXECUTABLE; + + /* At the moment not all abnormal edges match the RTL representation. + It is safe to remove them here as find_many_sub_basic_blocks will + rediscover them. In the future we should get this fixed properly. */ + 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; + + /* Expand this statement, then evaluate the resulting RTL and + fixup the CFG accordingly. */ + 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 (); + + /* Find the block tail. The last insn in the block is the insn + before a barrier and/or table jump insn. */ + 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; +} + + +/* Create a basic block for initialization code. */ + +static basic_block +construct_init_block (void) +{ + basic_block init_block, first_block; + edge e = NULL; + int flags; + + /* Multiple entry points not supported yet. */ + 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); + + /* When entry edge points to first basic block, we don't need jump, + otherwise we have to jump into proper target. */ + 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; +} + + +/* Create a block containing landing pads and similar stuff. */ + +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; + + /* Make sure the locus is set to the end of the function, so that + epilogue line numbers and warnings are set properly. */ +#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; + + /* The following insns belong to the top scope. */ + record_block_change (DECL_INITIAL (current_function_decl)); + + /* Generate rtl for function exit. */ + 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); +} + +/* Helper function for discover_nonconstant_array_refs. + Look for ARRAY_REF nodes with non-constant indexes and mark them + addressable. */ + +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; +} + +/* RTL expansion is not able to compile array references with variable + offsets for arrays stored in single register. Discover such + expressions and mark variables as addressable to avoid this + scenario. */ + +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); + } +} + +/* Translate the intermediate representation contained in the CFG + from GIMPLE trees to RTL. + + We do conversion per basic block and preserve/update the tree CFG. + This implies we have to do some magic as the CFG can simultaneously + consist of basic blocks containing RTL and GIMPLE trees. This can + confuse the CFG hooks, so be careful to not manipulate CFG during + the expansion. */ + +static unsigned int +tree_expand_cfg (void) +{ + basic_block bb, init_block; + sbitmap blocks; + edge_iterator ei; + edge e; + + /* Some backends want to know that we are expanding to RTL. */ + currently_expanding_to_rtl = 1; + + /* Prepare the rtl middle end to start recording block changes. */ + reset_block_changes (); + + /* Mark arrays indexed with non-constant indices with TREE_ADDRESSABLE. */ + discover_nonconstant_array_refs (); + + /* Expand the variables recorded during gimple lowering. */ + expand_used_vars (); + + /* Honor stack protection warnings. */ + 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)); + } + + /* Set up parameters and prepare for return, for the function. */ + expand_function_start (current_function_decl); + + /* If this function is `main', emit a call to `__main' + to run global initializers, etc. */ + if (DECL_NAME (current_function_decl) + && MAIN_NAME_P (DECL_NAME (current_function_decl)) + && DECL_FILE_SCOPE_P (current_function_decl)) + expand_main_function (); + + /* Initialize the stack_protect_guard field. This must happen after the + call to __main (if any) so that the external decl is initialized. */ + if (cfun->stack_protect_guard) + stack_protect_prologue (); + + /* Register rtl specific functions for cfg. */ + rtl_register_cfg_hooks (); + + init_block = construct_init_block (); + + /* Clear EDGE_EXECUTABLE on the entry edge(s). It is cleaned from the + remaining edges in expand_gimple_basic_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 (); + + /* We're done expanding trees to RTL. */ + currently_expanding_to_rtl = 0; + + /* Convert tree EH labels to RTL EH labels, and clean out any unreachable + EH regions. */ + 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 + + /* There's no need to defer outputting this function any more; we + know we want to output it. */ + DECL_DEFER_OUTPUT (current_function_decl) = 0; + + /* Now that we're done expanding trees to RTL, we shouldn't have any + more CONCATs anywhere. */ + generating_concat_p = 0; + + finalize_block_changes (); + + if (dump_file) + { + fprintf (dump_file, + "\n\n;;\n;; Full RTL generated for this function:\n;;\n"); + /* And the pass manager will dump RTL for us. */ + } + + /* If we're emitting a nested function, make sure its parent gets + emitted as well. Doing otherwise confuses debug info. */ + { + 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; + } + + /* We are now committed to emitting code for this function. Do any + preparation, such as emitting abstract debug info for the inline + before it gets mangled by optimization. */ + if (cgraph_function_possibly_inlined_p (current_function_decl)) + (*debug_hooks->outlining_inline_function) (current_function_decl); + + TREE_ASM_WRITTEN (current_function_decl) = 1; + + /* After expanding, the return labels are no longer needed. */ + return_label = NULL; + naked_return_label = NULL; + return 0; +} + +struct tree_opt_pass pass_expand = +{ + "expand", /* name */ + NULL, /* gate */ + tree_expand_cfg, /* execute */ + NULL, /* sub */ + NULL, /* next */ + 0, /* static_pass_number */ + TV_EXPAND, /* tv_id */ + /* ??? If TER is enabled, we actually receive GENERIC. */ + PROP_gimple_leh | PROP_cfg, /* properties_required */ + PROP_rtl, /* properties_provided */ + PROP_trees, /* properties_destroyed */ + 0, /* todo_flags_start */ + TODO_dump_func, /* todo_flags_finish */ + 'r' /* letter */ +}; -- cgit v1.2.3