| /* 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. */ |
| /* APPLE LOCAL begin CW asm */ |
| /* Don't create a guard for iasm functions. */ |
| if ((flag_stack_protect == 2 |
| || (flag_stack_protect |
| && (current_function_calls_alloca || has_protected_decls))) |
| && !cfun->iasm_asm_function) |
| create_stack_guard (); |
| /* APPLE LOCAL end CW asm */ |
| |
| /* 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 */ |
| }; |