| /* A pass for lowering trees to RTL. |
| Copyright (C) 2004-2015 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 3, 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 COPYING3. If not see |
| <http://www.gnu.org/licenses/>. */ |
| |
| #include "config.h" |
| #include "system.h" |
| #include "coretypes.h" |
| #include "tm.h" |
| #include "rtl.h" |
| #include "hard-reg-set.h" |
| #include "hash-set.h" |
| #include "machmode.h" |
| #include "vec.h" |
| #include "double-int.h" |
| #include "input.h" |
| #include "alias.h" |
| #include "symtab.h" |
| #include "wide-int.h" |
| #include "inchash.h" |
| #include "tree.h" |
| #include "fold-const.h" |
| #include "stringpool.h" |
| #include "varasm.h" |
| #include "stor-layout.h" |
| #include "stmt.h" |
| #include "print-tree.h" |
| #include "tm_p.h" |
| #include "predict.h" |
| #include "hashtab.h" |
| #include "function.h" |
| #include "dominance.h" |
| #include "cfg.h" |
| #include "cfgrtl.h" |
| #include "cfganal.h" |
| #include "cfgbuild.h" |
| #include "cfgcleanup.h" |
| #include "basic-block.h" |
| #include "insn-codes.h" |
| #include "optabs.h" |
| #include "flags.h" |
| #include "statistics.h" |
| #include "real.h" |
| #include "fixed-value.h" |
| #include "insn-config.h" |
| #include "expmed.h" |
| #include "dojump.h" |
| #include "explow.h" |
| #include "calls.h" |
| #include "emit-rtl.h" |
| #include "expr.h" |
| #include "langhooks.h" |
| #include "bitmap.h" |
| #include "tree-ssa-alias.h" |
| #include "internal-fn.h" |
| #include "tree-eh.h" |
| #include "gimple-expr.h" |
| #include "is-a.h" |
| #include "gimple.h" |
| #include "gimple-iterator.h" |
| #include "gimple-walk.h" |
| #include "gimple-ssa.h" |
| #include "hash-map.h" |
| #include "plugin-api.h" |
| #include "ipa-ref.h" |
| #include "cgraph.h" |
| #include "tree-cfg.h" |
| #include "tree-phinodes.h" |
| #include "ssa-iterators.h" |
| #include "tree-ssanames.h" |
| #include "tree-dfa.h" |
| #include "tree-ssa.h" |
| #include "tree-pass.h" |
| #include "except.h" |
| #include "diagnostic.h" |
| #include "gimple-pretty-print.h" |
| #include "toplev.h" |
| #include "debug.h" |
| #include "params.h" |
| #include "tree-inline.h" |
| #include "value-prof.h" |
| #include "target.h" |
| #include "tree-ssa-live.h" |
| #include "tree-outof-ssa.h" |
| #include "sbitmap.h" |
| #include "cfgloop.h" |
| #include "regs.h" /* For reg_renumber. */ |
| #include "insn-attr.h" /* For INSN_SCHEDULING. */ |
| #include "asan.h" |
| #include "tree-ssa-address.h" |
| #include "recog.h" |
| #include "output.h" |
| #include "builtins.h" |
| #include "tree-chkp.h" |
| #include "rtl-chkp.h" |
| |
| /* Some systems use __main in a way incompatible with its use in gcc, in these |
| cases use the macros NAME__MAIN to give a quoted symbol and SYMBOL__MAIN to |
| give the same symbol without quotes for an alternative entry point. You |
| must define both, or neither. */ |
| #ifndef NAME__MAIN |
| #define NAME__MAIN "__main" |
| #endif |
| |
| /* This variable holds information helping the rewriting of SSA trees |
| into RTL. */ |
| struct ssaexpand SA; |
| |
| /* This variable holds the currently expanded gimple statement for purposes |
| of comminucating the profile info to the builtin expanders. */ |
| gimple currently_expanding_gimple_stmt; |
| |
| static rtx expand_debug_expr (tree); |
| |
| /* Return an expression tree corresponding to the RHS of GIMPLE |
| statement STMT. */ |
| |
| tree |
| gimple_assign_rhs_to_tree (gimple stmt) |
| { |
| tree t; |
| enum gimple_rhs_class grhs_class; |
| |
| grhs_class = get_gimple_rhs_class (gimple_expr_code (stmt)); |
| |
| if (grhs_class == GIMPLE_TERNARY_RHS) |
| t = build3 (gimple_assign_rhs_code (stmt), |
| TREE_TYPE (gimple_assign_lhs (stmt)), |
| gimple_assign_rhs1 (stmt), |
| gimple_assign_rhs2 (stmt), |
| gimple_assign_rhs3 (stmt)); |
| else if (grhs_class == GIMPLE_BINARY_RHS) |
| t = build2 (gimple_assign_rhs_code (stmt), |
| TREE_TYPE (gimple_assign_lhs (stmt)), |
| gimple_assign_rhs1 (stmt), |
| gimple_assign_rhs2 (stmt)); |
| else if (grhs_class == GIMPLE_UNARY_RHS) |
| t = build1 (gimple_assign_rhs_code (stmt), |
| TREE_TYPE (gimple_assign_lhs (stmt)), |
| gimple_assign_rhs1 (stmt)); |
| else if (grhs_class == GIMPLE_SINGLE_RHS) |
| { |
| t = gimple_assign_rhs1 (stmt); |
| /* Avoid modifying this tree in place below. */ |
| if ((gimple_has_location (stmt) && CAN_HAVE_LOCATION_P (t) |
| && gimple_location (stmt) != EXPR_LOCATION (t)) |
| || (gimple_block (stmt) |
| && currently_expanding_to_rtl |
| && EXPR_P (t))) |
| t = copy_node (t); |
| } |
| else |
| gcc_unreachable (); |
| |
| if (gimple_has_location (stmt) && CAN_HAVE_LOCATION_P (t)) |
| SET_EXPR_LOCATION (t, gimple_location (stmt)); |
| |
| return t; |
| } |
| |
| |
| #ifndef STACK_ALIGNMENT_NEEDED |
| #define STACK_ALIGNMENT_NEEDED 1 |
| #endif |
| |
| #define SSAVAR(x) (TREE_CODE (x) == SSA_NAME ? SSA_NAME_VAR (x) : x) |
| |
| /* Associate declaration T with storage space X. If T is no |
| SSA name this is exactly SET_DECL_RTL, otherwise make the |
| partition of T associated with X. */ |
| static inline void |
| set_rtl (tree t, rtx x) |
| { |
| if (TREE_CODE (t) == SSA_NAME) |
| { |
| SA.partition_to_pseudo[var_to_partition (SA.map, t)] = x; |
| if (x && !MEM_P (x)) |
| set_reg_attrs_for_decl_rtl (SSA_NAME_VAR (t), x); |
| /* For the benefit of debug information at -O0 (where vartracking |
| doesn't run) record the place also in the base DECL if it's |
| a normal variable (not a parameter). */ |
| if (x && x != pc_rtx && TREE_CODE (SSA_NAME_VAR (t)) == VAR_DECL) |
| { |
| tree var = SSA_NAME_VAR (t); |
| /* If we don't yet have something recorded, just record it now. */ |
| if (!DECL_RTL_SET_P (var)) |
| SET_DECL_RTL (var, x); |
| /* If we have it set already to "multiple places" don't |
| change this. */ |
| else if (DECL_RTL (var) == pc_rtx) |
| ; |
| /* If we have something recorded and it's not the same place |
| as we want to record now, we have multiple partitions for the |
| same base variable, with different places. We can't just |
| randomly chose one, hence we have to say that we don't know. |
| This only happens with optimization, and there var-tracking |
| will figure out the right thing. */ |
| else if (DECL_RTL (var) != x) |
| SET_DECL_RTL (var, pc_rtx); |
| } |
| } |
| else |
| SET_DECL_RTL (t, x); |
| } |
| |
| /* This structure holds data relevant to one variable that will be |
| placed in a stack slot. */ |
| struct stack_var |
| { |
| /* The Variable. */ |
| tree decl; |
| |
| /* 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; |
| |
| /* The numbers of conflicting stack variables. */ |
| bitmap conflicts; |
| }; |
| |
| #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; |
| static hash_map<tree, size_t> *decl_to_stack_part; |
| |
| /* Conflict bitmaps go on this obstack. This allows us to destroy |
| all of them in one big sweep. */ |
| static bitmap_obstack stack_var_bitmap_obstack; |
| |
| /* An array of indices such that stack_vars[stack_vars_sorted[i]].size |
| is non-decreasing. */ |
| static size_t *stack_vars_sorted; |
| |
| /* 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; |
| |
| /* Compute the byte alignment to use for DECL. Ignore alignment |
| we can't do with expected alignment of the stack boundary. */ |
| |
| static unsigned int |
| align_local_variable (tree decl) |
| { |
| unsigned int align = LOCAL_DECL_ALIGNMENT (decl); |
| DECL_ALIGN (decl) = align; |
| return align / BITS_PER_UNIT; |
| } |
| |
| /* Align given offset BASE with ALIGN. Truncate up if ALIGN_UP is true, |
| down otherwise. Return truncated BASE value. */ |
| |
| static inline unsigned HOST_WIDE_INT |
| align_base (HOST_WIDE_INT base, unsigned HOST_WIDE_INT align, bool align_up) |
| { |
| return align_up ? (base + align - 1) & -align : base & -align; |
| } |
| |
| /* 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, unsigned HOST_WIDE_INT align) |
| { |
| HOST_WIDE_INT offset, new_frame_offset; |
| |
| if (FRAME_GROWS_DOWNWARD) |
| { |
| new_frame_offset |
| = align_base (frame_offset - frame_phase - size, |
| align, false) + frame_phase; |
| offset = new_frame_offset; |
| } |
| else |
| { |
| new_frame_offset |
| = align_base (frame_offset - frame_phase, align, true) + 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) |
| { |
| struct stack_var *v; |
| |
| 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); |
| } |
| if (!decl_to_stack_part) |
| decl_to_stack_part = new hash_map<tree, size_t>; |
| |
| v = &stack_vars[stack_vars_num]; |
| decl_to_stack_part->put (decl, stack_vars_num); |
| |
| v->decl = decl; |
| v->size = tree_to_uhwi (DECL_SIZE_UNIT (SSAVAR (decl))); |
| /* Ensure that all variables have size, so that &a != &b for any two |
| variables that are simultaneously live. */ |
| if (v->size == 0) |
| v->size = 1; |
| v->alignb = align_local_variable (SSAVAR (decl)); |
| /* An alignment of zero can mightily confuse us later. */ |
| gcc_assert (v->alignb != 0); |
| |
| /* All variables are initially in their own partition. */ |
| v->representative = stack_vars_num; |
| v->next = EOC; |
| |
| /* All variables initially conflict with no other. */ |
| v->conflicts = NULL; |
| |
| /* Ensure that this decl doesn't get put onto the list twice. */ |
| set_rtl (decl, pc_rtx); |
| |
| stack_vars_num++; |
| } |
| |
| /* Make the decls associated with luid's X and Y conflict. */ |
| |
| static void |
| add_stack_var_conflict (size_t x, size_t y) |
| { |
| struct stack_var *a = &stack_vars[x]; |
| struct stack_var *b = &stack_vars[y]; |
| if (!a->conflicts) |
| a->conflicts = BITMAP_ALLOC (&stack_var_bitmap_obstack); |
| if (!b->conflicts) |
| b->conflicts = BITMAP_ALLOC (&stack_var_bitmap_obstack); |
| bitmap_set_bit (a->conflicts, y); |
| bitmap_set_bit (b->conflicts, x); |
| } |
| |
| /* Check whether the decls associated with luid's X and Y conflict. */ |
| |
| static bool |
| stack_var_conflict_p (size_t x, size_t y) |
| { |
| struct stack_var *a = &stack_vars[x]; |
| struct stack_var *b = &stack_vars[y]; |
| if (x == y) |
| return false; |
| /* Partitions containing an SSA name result from gimple registers |
| with things like unsupported modes. They are top-level and |
| hence conflict with everything else. */ |
| if (TREE_CODE (a->decl) == SSA_NAME || TREE_CODE (b->decl) == SSA_NAME) |
| return true; |
| |
| if (!a->conflicts || !b->conflicts) |
| return false; |
| return bitmap_bit_p (a->conflicts, y); |
| } |
| |
| /* Callback for walk_stmt_ops. If OP is a decl touched by add_stack_var |
| enter its partition number into bitmap DATA. */ |
| |
| static bool |
| visit_op (gimple, tree op, tree, void *data) |
| { |
| bitmap active = (bitmap)data; |
| op = get_base_address (op); |
| if (op |
| && DECL_P (op) |
| && DECL_RTL_IF_SET (op) == pc_rtx) |
| { |
| size_t *v = decl_to_stack_part->get (op); |
| if (v) |
| bitmap_set_bit (active, *v); |
| } |
| return false; |
| } |
| |
| /* Callback for walk_stmt_ops. If OP is a decl touched by add_stack_var |
| record conflicts between it and all currently active other partitions |
| from bitmap DATA. */ |
| |
| static bool |
| visit_conflict (gimple, tree op, tree, void *data) |
| { |
| bitmap active = (bitmap)data; |
| op = get_base_address (op); |
| if (op |
| && DECL_P (op) |
| && DECL_RTL_IF_SET (op) == pc_rtx) |
| { |
| size_t *v = decl_to_stack_part->get (op); |
| if (v && bitmap_set_bit (active, *v)) |
| { |
| size_t num = *v; |
| bitmap_iterator bi; |
| unsigned i; |
| gcc_assert (num < stack_vars_num); |
| EXECUTE_IF_SET_IN_BITMAP (active, 0, i, bi) |
| add_stack_var_conflict (num, i); |
| } |
| } |
| return false; |
| } |
| |
| /* Helper routine for add_scope_conflicts, calculating the active partitions |
| at the end of BB, leaving the result in WORK. We're called to generate |
| conflicts when FOR_CONFLICT is true, otherwise we're just tracking |
| liveness. */ |
| |
| static void |
| add_scope_conflicts_1 (basic_block bb, bitmap work, bool for_conflict) |
| { |
| edge e; |
| edge_iterator ei; |
| gimple_stmt_iterator gsi; |
| walk_stmt_load_store_addr_fn visit; |
| |
| bitmap_clear (work); |
| FOR_EACH_EDGE (e, ei, bb->preds) |
| bitmap_ior_into (work, (bitmap)e->src->aux); |
| |
| visit = visit_op; |
| |
| for (gsi = gsi_start_phis (bb); !gsi_end_p (gsi); gsi_next (&gsi)) |
| { |
| gimple stmt = gsi_stmt (gsi); |
| walk_stmt_load_store_addr_ops (stmt, work, NULL, NULL, visit); |
| } |
| for (gsi = gsi_after_labels (bb); !gsi_end_p (gsi); gsi_next (&gsi)) |
| { |
| gimple stmt = gsi_stmt (gsi); |
| |
| if (gimple_clobber_p (stmt)) |
| { |
| tree lhs = gimple_assign_lhs (stmt); |
| size_t *v; |
| /* Nested function lowering might introduce LHSs |
| that are COMPONENT_REFs. */ |
| if (TREE_CODE (lhs) != VAR_DECL) |
| continue; |
| if (DECL_RTL_IF_SET (lhs) == pc_rtx |
| && (v = decl_to_stack_part->get (lhs))) |
| bitmap_clear_bit (work, *v); |
| } |
| else if (!is_gimple_debug (stmt)) |
| { |
| if (for_conflict |
| && visit == visit_op) |
| { |
| /* If this is the first real instruction in this BB we need |
| to add conflicts for everything live at this point now. |
| Unlike classical liveness for named objects we can't |
| rely on seeing a def/use of the names we're interested in. |
| There might merely be indirect loads/stores. We'd not add any |
| conflicts for such partitions. */ |
| bitmap_iterator bi; |
| unsigned i; |
| EXECUTE_IF_SET_IN_BITMAP (work, 0, i, bi) |
| { |
| struct stack_var *a = &stack_vars[i]; |
| if (!a->conflicts) |
| a->conflicts = BITMAP_ALLOC (&stack_var_bitmap_obstack); |
| bitmap_ior_into (a->conflicts, work); |
| } |
| visit = visit_conflict; |
| } |
| walk_stmt_load_store_addr_ops (stmt, work, visit, visit, visit); |
| } |
| } |
| } |
| |
| /* Generate stack partition conflicts between all partitions that are |
| simultaneously live. */ |
| |
| static void |
| add_scope_conflicts (void) |
| { |
| basic_block bb; |
| bool changed; |
| bitmap work = BITMAP_ALLOC (NULL); |
| int *rpo; |
| int n_bbs; |
| |
| /* We approximate the live range of a stack variable by taking the first |
| mention of its name as starting point(s), and by the end-of-scope |
| death clobber added by gimplify as ending point(s) of the range. |
| This overapproximates in the case we for instance moved an address-taken |
| operation upward, without also moving a dereference to it upwards. |
| But it's conservatively correct as a variable never can hold values |
| before its name is mentioned at least once. |
| |
| We then do a mostly classical bitmap liveness algorithm. */ |
| |
| FOR_ALL_BB_FN (bb, cfun) |
| bb->aux = BITMAP_ALLOC (&stack_var_bitmap_obstack); |
| |
| rpo = XNEWVEC (int, last_basic_block_for_fn (cfun)); |
| n_bbs = pre_and_rev_post_order_compute (NULL, rpo, false); |
| |
| changed = true; |
| while (changed) |
| { |
| int i; |
| changed = false; |
| for (i = 0; i < n_bbs; i++) |
| { |
| bitmap active; |
| bb = BASIC_BLOCK_FOR_FN (cfun, rpo[i]); |
| active = (bitmap)bb->aux; |
| add_scope_conflicts_1 (bb, work, false); |
| if (bitmap_ior_into (active, work)) |
| changed = true; |
| } |
| } |
| |
| FOR_EACH_BB_FN (bb, cfun) |
| add_scope_conflicts_1 (bb, work, true); |
| |
| free (rpo); |
| BITMAP_FREE (work); |
| FOR_ALL_BB_FN (bb, cfun) |
| BITMAP_FREE (bb->aux); |
| } |
| |
| /* A subroutine of partition_stack_vars. A comparison function for qsort, |
| sorting an array of indices by the properties of the object. */ |
| |
| static int |
| stack_var_cmp (const void *a, const void *b) |
| { |
| size_t ia = *(const size_t *)a; |
| size_t ib = *(const size_t *)b; |
| unsigned int aligna = stack_vars[ia].alignb; |
| unsigned int alignb = stack_vars[ib].alignb; |
| HOST_WIDE_INT sizea = stack_vars[ia].size; |
| HOST_WIDE_INT sizeb = stack_vars[ib].size; |
| tree decla = stack_vars[ia].decl; |
| tree declb = stack_vars[ib].decl; |
| bool largea, largeb; |
| unsigned int uida, uidb; |
| |
| /* Primary compare on "large" alignment. Large comes first. */ |
| largea = (aligna * BITS_PER_UNIT > MAX_SUPPORTED_STACK_ALIGNMENT); |
| largeb = (alignb * BITS_PER_UNIT > MAX_SUPPORTED_STACK_ALIGNMENT); |
| if (largea != largeb) |
| return (int)largeb - (int)largea; |
| |
| /* Secondary compare on size, decreasing */ |
| if (sizea > sizeb) |
| return -1; |
| if (sizea < sizeb) |
| return 1; |
| |
| /* Tertiary compare on true alignment, decreasing. */ |
| if (aligna < alignb) |
| return -1; |
| if (aligna > alignb) |
| return 1; |
| |
| /* Final compare on ID for sort stability, increasing. |
| Two SSA names are compared by their version, SSA names come before |
| non-SSA names, and two normal decls are compared by their DECL_UID. */ |
| if (TREE_CODE (decla) == SSA_NAME) |
| { |
| if (TREE_CODE (declb) == SSA_NAME) |
| uida = SSA_NAME_VERSION (decla), uidb = SSA_NAME_VERSION (declb); |
| else |
| return -1; |
| } |
| else if (TREE_CODE (declb) == SSA_NAME) |
| return 1; |
| else |
| uida = DECL_UID (decla), uidb = DECL_UID (declb); |
| if (uida < uidb) |
| return 1; |
| if (uida > uidb) |
| return -1; |
| return 0; |
| } |
| |
| struct part_traits : default_hashmap_traits |
| { |
| template<typename T> |
| static bool |
| is_deleted (T &e) |
| { return e.m_value == reinterpret_cast<void *> (1); } |
| |
| template<typename T> static bool is_empty (T &e) { return e.m_value == NULL; } |
| template<typename T> |
| static void |
| mark_deleted (T &e) |
| { e.m_value = reinterpret_cast<T> (1); } |
| |
| template<typename T> |
| static void |
| mark_empty (T &e) |
| { e.m_value = NULL; } |
| }; |
| |
| typedef hash_map<size_t, bitmap, part_traits> part_hashmap; |
| |
| /* If the points-to solution *PI points to variables that are in a partition |
| together with other variables add all partition members to the pointed-to |
| variables bitmap. */ |
| |
| static void |
| add_partitioned_vars_to_ptset (struct pt_solution *pt, |
| part_hashmap *decls_to_partitions, |
| hash_set<bitmap> *visited, bitmap temp) |
| { |
| bitmap_iterator bi; |
| unsigned i; |
| bitmap *part; |
| |
| if (pt->anything |
| || pt->vars == NULL |
| /* The pointed-to vars bitmap is shared, it is enough to |
| visit it once. */ |
| || visited->add (pt->vars)) |
| return; |
| |
| bitmap_clear (temp); |
| |
| /* By using a temporary bitmap to store all members of the partitions |
| we have to add we make sure to visit each of the partitions only |
| once. */ |
| EXECUTE_IF_SET_IN_BITMAP (pt->vars, 0, i, bi) |
| if ((!temp |
| || !bitmap_bit_p (temp, i)) |
| && (part = decls_to_partitions->get (i))) |
| bitmap_ior_into (temp, *part); |
| if (!bitmap_empty_p (temp)) |
| bitmap_ior_into (pt->vars, temp); |
| } |
| |
| /* Update points-to sets based on partition info, so we can use them on RTL. |
| The bitmaps representing stack partitions will be saved until expand, |
| where partitioned decls used as bases in memory expressions will be |
| rewritten. */ |
| |
| static void |
| update_alias_info_with_stack_vars (void) |
| { |
| part_hashmap *decls_to_partitions = NULL; |
| size_t i, j; |
| tree var = NULL_TREE; |
| |
| for (i = 0; i < stack_vars_num; i++) |
| { |
| bitmap part = NULL; |
| tree name; |
| struct ptr_info_def *pi; |
| |
| /* Not interested in partitions with single variable. */ |
| if (stack_vars[i].representative != i |
| || stack_vars[i].next == EOC) |
| continue; |
| |
| if (!decls_to_partitions) |
| { |
| decls_to_partitions = new part_hashmap; |
| cfun->gimple_df->decls_to_pointers = new hash_map<tree, tree>; |
| } |
| |
| /* Create an SSA_NAME that points to the partition for use |
| as base during alias-oracle queries on RTL for bases that |
| have been partitioned. */ |
| if (var == NULL_TREE) |
| var = create_tmp_var (ptr_type_node); |
| name = make_ssa_name (var); |
| |
| /* Create bitmaps representing partitions. They will be used for |
| points-to sets later, so use GGC alloc. */ |
| part = BITMAP_GGC_ALLOC (); |
| for (j = i; j != EOC; j = stack_vars[j].next) |
| { |
| tree decl = stack_vars[j].decl; |
| unsigned int uid = DECL_PT_UID (decl); |
| bitmap_set_bit (part, uid); |
| decls_to_partitions->put (uid, part); |
| cfun->gimple_df->decls_to_pointers->put (decl, name); |
| if (TREE_ADDRESSABLE (decl)) |
| TREE_ADDRESSABLE (name) = 1; |
| } |
| |
| /* Make the SSA name point to all partition members. */ |
| pi = get_ptr_info (name); |
| pt_solution_set (&pi->pt, part, false); |
| } |
| |
| /* Make all points-to sets that contain one member of a partition |
| contain all members of the partition. */ |
| if (decls_to_partitions) |
| { |
| unsigned i; |
| hash_set<bitmap> visited; |
| bitmap temp = BITMAP_ALLOC (&stack_var_bitmap_obstack); |
| |
| for (i = 1; i < num_ssa_names; i++) |
| { |
| tree name = ssa_name (i); |
| struct ptr_info_def *pi; |
| |
| if (name |
| && POINTER_TYPE_P (TREE_TYPE (name)) |
| && ((pi = SSA_NAME_PTR_INFO (name)) != NULL)) |
| add_partitioned_vars_to_ptset (&pi->pt, decls_to_partitions, |
| &visited, temp); |
| } |
| |
| add_partitioned_vars_to_ptset (&cfun->gimple_df->escaped, |
| decls_to_partitions, &visited, temp); |
| |
| delete decls_to_partitions; |
| BITMAP_FREE (temp); |
| } |
| } |
| |
| /* 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. */ |
| |
| static void |
| union_stack_vars (size_t a, size_t b) |
| { |
| struct stack_var *vb = &stack_vars[b]; |
| bitmap_iterator bi; |
| unsigned u; |
| |
| gcc_assert (stack_vars[b].next == EOC); |
| /* Add B to A's partition. */ |
| stack_vars[b].next = stack_vars[a].next; |
| stack_vars[b].representative = a; |
| 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. */ |
| if (vb->conflicts) |
| { |
| EXECUTE_IF_SET_IN_BITMAP (vb->conflicts, 0, u, bi) |
| add_stack_var_conflict (a, stack_vars[u].representative); |
| BITMAP_FREE (vb->conflicts); |
| } |
| } |
| |
| /* 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 in descending order. |
| For each object A { |
| S = size(A) |
| O = 0 |
| loop { |
| Look for the largest non-conflicting object B with size <= S. |
| UNION (A, 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_cmp); |
| |
| for (si = 0; si < n; ++si) |
| { |
| size_t i = stack_vars_sorted[si]; |
| unsigned int ialign = stack_vars[i].alignb; |
| HOST_WIDE_INT isize = stack_vars[i].size; |
| |
| /* Ignore objects that aren't partition representatives. If we |
| see a var that is not a partition representative, it must |
| have been merged earlier. */ |
| if (stack_vars[i].representative != i) |
| continue; |
| |
| for (sj = si + 1; sj < n; ++sj) |
| { |
| size_t j = stack_vars_sorted[sj]; |
| unsigned int jalign = stack_vars[j].alignb; |
| HOST_WIDE_INT jsize = stack_vars[j].size; |
| |
| /* Ignore objects that aren't partition representatives. */ |
| if (stack_vars[j].representative != j) |
| continue; |
| |
| /* Do not mix objects of "small" (supported) alignment |
| and "large" (unsupported) alignment. */ |
| if ((ialign * BITS_PER_UNIT <= MAX_SUPPORTED_STACK_ALIGNMENT) |
| != (jalign * BITS_PER_UNIT <= MAX_SUPPORTED_STACK_ALIGNMENT)) |
| break; |
| |
| /* For Address Sanitizer do not mix objects with different |
| sizes, as the shorter vars wouldn't be adequately protected. |
| Don't do that for "large" (unsupported) alignment objects, |
| those aren't protected anyway. */ |
| if ((flag_sanitize & SANITIZE_ADDRESS) && ASAN_STACK && isize != jsize |
| && ialign * BITS_PER_UNIT <= MAX_SUPPORTED_STACK_ALIGNMENT) |
| break; |
| |
| /* Ignore conflicting objects. */ |
| if (stack_var_conflict_p (i, j)) |
| continue; |
| |
| /* UNION the objects, placing J at OFFSET. */ |
| union_stack_vars (i, j); |
| } |
| } |
| |
| update_alias_info_with_stack_vars (); |
| } |
| |
| /* 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); |
| } |
| fputc ('\n', dump_file); |
| } |
| } |
| |
| /* Assign rtl to DECL at BASE + OFFSET. */ |
| |
| static void |
| expand_one_stack_var_at (tree decl, rtx base, unsigned base_align, |
| HOST_WIDE_INT offset) |
| { |
| unsigned 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 (Pmode, base, offset); |
| x = gen_rtx_MEM (DECL_MODE (SSAVAR (decl)), x); |
| |
| if (TREE_CODE (decl) != SSA_NAME) |
| { |
| /* Set alignment we actually gave this decl if it isn't an SSA name. |
| If it is we generate stack slots only accidentally so it isn't as |
| important, we'll simply use the alignment that is already set. */ |
| if (base == virtual_stack_vars_rtx) |
| offset -= frame_phase; |
| align = offset & -offset; |
| align *= BITS_PER_UNIT; |
| if (align == 0 || align > base_align) |
| align = base_align; |
| |
| /* One would think that we could assert that we're not decreasing |
| alignment here, but (at least) the i386 port does exactly this |
| via the MINIMUM_ALIGNMENT hook. */ |
| |
| DECL_ALIGN (decl) = align; |
| DECL_USER_ALIGN (decl) = 0; |
| } |
| |
| set_mem_attributes (x, SSAVAR (decl), true); |
| set_rtl (decl, x); |
| } |
| |
| struct stack_vars_data |
| { |
| /* Vector of offset pairs, always end of some padding followed |
| by start of the padding that needs Address Sanitizer protection. |
| The vector is in reversed, highest offset pairs come first. */ |
| vec<HOST_WIDE_INT> asan_vec; |
| |
| /* Vector of partition representative decls in between the paddings. */ |
| vec<tree> asan_decl_vec; |
| |
| /* Base pseudo register for Address Sanitizer protected automatic vars. */ |
| rtx asan_base; |
| |
| /* Alignment needed for the Address Sanitizer protected automatic vars. */ |
| unsigned int asan_alignb; |
| }; |
| |
| /* 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) (size_t), struct stack_vars_data *data) |
| { |
| size_t si, i, j, n = stack_vars_num; |
| HOST_WIDE_INT large_size = 0, large_alloc = 0; |
| rtx large_base = NULL; |
| unsigned large_align = 0; |
| tree decl; |
| |
| /* Determine if there are any variables requiring "large" alignment. |
| Since these are dynamically allocated, we only process these if |
| no predicate involved. */ |
| large_align = stack_vars[stack_vars_sorted[0]].alignb * BITS_PER_UNIT; |
| if (pred == NULL && large_align > MAX_SUPPORTED_STACK_ALIGNMENT) |
| { |
| /* Find the total size of these variables. */ |
| for (si = 0; si < n; ++si) |
| { |
| unsigned alignb; |
| |
| i = stack_vars_sorted[si]; |
| alignb = stack_vars[i].alignb; |
| |
| /* All "large" alignment decls come before all "small" alignment |
| decls, but "large" alignment decls are not sorted based on |
| their alignment. Increase large_align to track the largest |
| required alignment. */ |
| if ((alignb * BITS_PER_UNIT) > large_align) |
| large_align = alignb * BITS_PER_UNIT; |
| |
| /* Stop when we get to the first decl with "small" alignment. */ |
| if (alignb * BITS_PER_UNIT <= MAX_SUPPORTED_STACK_ALIGNMENT) |
| break; |
| |
| /* Skip variables that aren't partition representatives. */ |
| 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. */ |
| decl = stack_vars[i].decl; |
| if ((TREE_CODE (decl) == SSA_NAME |
| ? SA.partition_to_pseudo[var_to_partition (SA.map, decl)] |
| : DECL_RTL (decl)) != pc_rtx) |
| continue; |
| |
| large_size += alignb - 1; |
| large_size &= -(HOST_WIDE_INT)alignb; |
| large_size += stack_vars[i].size; |
| } |
| |
| /* If there were any, allocate space. */ |
| if (large_size > 0) |
| large_base = allocate_dynamic_stack_space (GEN_INT (large_size), 0, |
| large_align, true); |
| } |
| |
| for (si = 0; si < n; ++si) |
| { |
| rtx base; |
| unsigned base_align, alignb; |
| 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. */ |
| decl = stack_vars[i].decl; |
| if ((TREE_CODE (decl) == SSA_NAME |
| ? SA.partition_to_pseudo[var_to_partition (SA.map, decl)] |
| : DECL_RTL (decl)) != pc_rtx) |
| continue; |
| |
| /* Check the predicate to see whether this variable should be |
| allocated in this pass. */ |
| if (pred && !pred (i)) |
| continue; |
| |
| alignb = stack_vars[i].alignb; |
| if (alignb * BITS_PER_UNIT <= MAX_SUPPORTED_STACK_ALIGNMENT) |
| { |
| base = virtual_stack_vars_rtx; |
| if ((flag_sanitize & SANITIZE_ADDRESS) && ASAN_STACK && pred) |
| { |
| HOST_WIDE_INT prev_offset |
| = align_base (frame_offset, |
| MAX (alignb, ASAN_RED_ZONE_SIZE), |
| FRAME_GROWS_DOWNWARD); |
| tree repr_decl = NULL_TREE; |
| offset |
| = alloc_stack_frame_space (stack_vars[i].size |
| + ASAN_RED_ZONE_SIZE, |
| MAX (alignb, ASAN_RED_ZONE_SIZE)); |
| |
| data->asan_vec.safe_push (prev_offset); |
| data->asan_vec.safe_push (offset + stack_vars[i].size); |
| /* Find best representative of the partition. |
| Prefer those with DECL_NAME, even better |
| satisfying asan_protect_stack_decl predicate. */ |
| for (j = i; j != EOC; j = stack_vars[j].next) |
| if (asan_protect_stack_decl (stack_vars[j].decl) |
| && DECL_NAME (stack_vars[j].decl)) |
| { |
| repr_decl = stack_vars[j].decl; |
| break; |
| } |
| else if (repr_decl == NULL_TREE |
| && DECL_P (stack_vars[j].decl) |
| && DECL_NAME (stack_vars[j].decl)) |
| repr_decl = stack_vars[j].decl; |
| if (repr_decl == NULL_TREE) |
| repr_decl = stack_vars[i].decl; |
| data->asan_decl_vec.safe_push (repr_decl); |
| data->asan_alignb = MAX (data->asan_alignb, alignb); |
| if (data->asan_base == NULL) |
| data->asan_base = gen_reg_rtx (Pmode); |
| base = data->asan_base; |
| |
| if (!STRICT_ALIGNMENT) |
| base_align = crtl->max_used_stack_slot_alignment; |
| else |
| base_align = MAX (crtl->max_used_stack_slot_alignment, |
| GET_MODE_ALIGNMENT (SImode) |
| << ASAN_SHADOW_SHIFT); |
| } |
| else |
| { |
| offset = alloc_stack_frame_space (stack_vars[i].size, alignb); |
| base_align = crtl->max_used_stack_slot_alignment; |
| } |
| } |
| else |
| { |
| /* Large alignment is only processed in the last pass. */ |
| if (pred) |
| continue; |
| gcc_assert (large_base != NULL); |
| |
| large_alloc += alignb - 1; |
| large_alloc &= -(HOST_WIDE_INT)alignb; |
| offset = large_alloc; |
| large_alloc += stack_vars[i].size; |
| |
| base = large_base; |
| base_align = large_align; |
| } |
| |
| /* 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, |
| base, base_align, |
| offset); |
| } |
| } |
| |
| gcc_assert (large_alloc == large_size); |
| } |
| |
| /* Take into account all sizes of partitions and reset DECL_RTLs. */ |
| static HOST_WIDE_INT |
| account_stack_vars (void) |
| { |
| size_t si, j, i, n = stack_vars_num; |
| HOST_WIDE_INT size = 0; |
| |
| 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; |
| |
| size += stack_vars[i].size; |
| for (j = i; j != EOC; j = stack_vars[j].next) |
| set_rtl (stack_vars[j].decl, NULL); |
| } |
| return size; |
| } |
| |
| /* 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; |
| unsigned byte_align; |
| |
| size = tree_to_uhwi (DECL_SIZE_UNIT (SSAVAR (var))); |
| byte_align = align_local_variable (SSAVAR (var)); |
| |
| /* We handle highly aligned variables in expand_stack_vars. */ |
| gcc_assert (byte_align * BITS_PER_UNIT <= MAX_SUPPORTED_STACK_ALIGNMENT); |
| |
| offset = alloc_stack_frame_space (size, byte_align); |
| |
| expand_one_stack_var_at (var, virtual_stack_vars_rtx, |
| crtl->max_used_stack_slot_alignment, offset); |
| } |
| |
| /* 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 decl = SSAVAR (var); |
| tree type = TREE_TYPE (decl); |
| machine_mode reg_mode = promote_decl_mode (decl, NULL); |
| rtx x = gen_reg_rtx (reg_mode); |
| |
| set_rtl (var, x); |
| |
| /* Note if the object is a user variable. */ |
| if (!DECL_ARTIFICIAL (decl)) |
| mark_user_reg (x); |
| |
| if (POINTER_TYPE_P (type)) |
| mark_reg_pointer (x, get_pointer_alignment (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) |
| { |
| 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) |
| { |
| /* Whether the variable is small enough for immediate allocation not to be |
| a problem with regard to the frame size. */ |
| bool smallish |
| = ((HOST_WIDE_INT) tree_to_uhwi (DECL_SIZE_UNIT (var)) |
| < PARAM_VALUE (PARAM_MIN_SIZE_FOR_STACK_SHARING)); |
| |
| /* 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. |
| Similarly for Address Sanitizer. */ |
| if (flag_stack_protect || ((flag_sanitize & SANITIZE_ADDRESS) && ASAN_STACK)) |
| return true; |
| |
| /* We handle "large" alignment via dynamic allocation. We want to handle |
| this extra complication in only one place, so defer them. */ |
| if (DECL_ALIGN (var) > MAX_SUPPORTED_STACK_ALIGNMENT) |
| return true; |
| |
| /* When optimization is enabled, DECL_IGNORED_P variables originally scoped |
| might be detached from their block and appear at toplevel when we reach |
| here. We want to coalesce them with variables from other blocks when |
| the immediate contribution to the frame size would be noticeable. */ |
| if (toplevel && optimize > 0 && DECL_IGNORED_P (var) && !smallish) |
| return true; |
| |
| /* Variables declared 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 && smallish) |
| 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. |
| When REALLY_EXPAND is false, only add stack values to be allocated. |
| Return stack usage this variable is supposed to take. |
| */ |
| |
| static HOST_WIDE_INT |
| expand_one_var (tree var, bool toplevel, bool really_expand) |
| { |
| unsigned int align = BITS_PER_UNIT; |
| tree origvar = var; |
| |
| var = SSAVAR (var); |
| |
| if (TREE_TYPE (var) != error_mark_node && TREE_CODE (var) == VAR_DECL) |
| { |
| /* Because we don't know if VAR will be in register or on stack, |
| we conservatively assume it will be on stack even if VAR is |
| eventually put into register after RA pass. For non-automatic |
| variables, which won't be on stack, we collect alignment of |
| type and ignore user specified alignment. Similarly for |
| SSA_NAMEs for which use_register_for_decl returns true. */ |
| if (TREE_STATIC (var) |
| || DECL_EXTERNAL (var) |
| || (TREE_CODE (origvar) == SSA_NAME && use_register_for_decl (var))) |
| align = MINIMUM_ALIGNMENT (TREE_TYPE (var), |
| TYPE_MODE (TREE_TYPE (var)), |
| TYPE_ALIGN (TREE_TYPE (var))); |
| else if (DECL_HAS_VALUE_EXPR_P (var) |
| || (DECL_RTL_SET_P (var) && MEM_P (DECL_RTL (var)))) |
| /* Don't consider debug only variables with DECL_HAS_VALUE_EXPR_P set |
| or variables which were assigned a stack slot already by |
| expand_one_stack_var_at - in the latter case DECL_ALIGN has been |
| changed from the offset chosen to it. */ |
| align = crtl->stack_alignment_estimated; |
| else |
| align = MINIMUM_ALIGNMENT (var, DECL_MODE (var), DECL_ALIGN (var)); |
| |
| /* If the variable alignment is very large we'll dynamicaly allocate |
| it, which means that in-frame portion is just a pointer. */ |
| if (align > MAX_SUPPORTED_STACK_ALIGNMENT) |
| align = POINTER_SIZE; |
| } |
| |
| if (SUPPORTS_STACK_ALIGNMENT |
| && crtl->stack_alignment_estimated < align) |
| { |
| /* stack_alignment_estimated shouldn't change after stack |
| realign decision made */ |
| gcc_assert (!crtl->stack_realign_processed); |
| crtl->stack_alignment_estimated = align; |
| } |
| |
| /* stack_alignment_needed > PREFERRED_STACK_BOUNDARY is permitted. |
| So here we only make sure stack_alignment_needed >= align. */ |
| if (crtl->stack_alignment_needed < align) |
| crtl->stack_alignment_needed = align; |
| if (crtl->max_used_stack_slot_alignment < align) |
| crtl->max_used_stack_slot_alignment = align; |
| |
| if (TREE_CODE (origvar) == SSA_NAME) |
| { |
| gcc_assert (TREE_CODE (var) != VAR_DECL |
| || (!DECL_EXTERNAL (var) |
| && !DECL_HAS_VALUE_EXPR_P (var) |
| && !TREE_STATIC (var) |
| && TREE_TYPE (var) != error_mark_node |
| && !DECL_HARD_REGISTER (var) |
| && really_expand)); |
| } |
| if (TREE_CODE (var) != VAR_DECL && TREE_CODE (origvar) != SSA_NAME) |
| ; |
| else if (DECL_EXTERNAL (var)) |
| ; |
| else if (DECL_HAS_VALUE_EXPR_P (var)) |
| ; |
| else if (TREE_STATIC (var)) |
| ; |
| else if (TREE_CODE (origvar) != SSA_NAME && DECL_RTL_SET_P (var)) |
| ; |
| else if (TREE_TYPE (var) == error_mark_node) |
| { |
| if (really_expand) |
| expand_one_error_var (var); |
| } |
| else if (TREE_CODE (var) == VAR_DECL && DECL_HARD_REGISTER (var)) |
| { |
| if (really_expand) |
| { |
| expand_one_hard_reg_var (var); |
| if (!DECL_HARD_REGISTER (var)) |
| /* Invalid register specification. */ |
| expand_one_error_var (var); |
| } |
| } |
| else if (use_register_for_decl (var)) |
| { |
| if (really_expand) |
| expand_one_register_var (origvar); |
| } |
| else if (! valid_constant_size_p (DECL_SIZE_UNIT (var))) |
| { |
| /* Reject variables which cover more than half of the address-space. */ |
| if (really_expand) |
| { |
| error ("size of variable %q+D is too large", var); |
| expand_one_error_var (var); |
| } |
| } |
| else if (defer_stack_allocation (var, toplevel)) |
| add_stack_var (origvar); |
| else |
| { |
| if (really_expand) |
| expand_one_stack_var (origvar); |
| return tree_to_uhwi (DECL_SIZE_UNIT (var)); |
| } |
| return 0; |
| } |
| |
| /* 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) |
| { |
| tree t; |
| |
| /* Expand all variables at this level. */ |
| for (t = BLOCK_VARS (block); t ; t = DECL_CHAIN (t)) |
| if (TREE_USED (t) |
| && ((TREE_CODE (t) != VAR_DECL && TREE_CODE (t) != RESULT_DECL) |
| || !DECL_NONSHAREABLE (t))) |
| expand_one_var (t, toplevel, true); |
| |
| /* Expand all variables at containing levels. */ |
| for (t = BLOCK_SUBBLOCKS (block); t ; t = BLOCK_CHAIN (t)) |
| expand_used_vars_for_block (t, false); |
| } |
| |
| /* 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 = DECL_CHAIN (t)) |
| /* if (!TREE_STATIC (t) && !DECL_EXTERNAL (t)) */ |
| if ((TREE_CODE (t) != VAR_DECL && TREE_CODE (t) != RESULT_DECL) |
| || !DECL_NONSHAREABLE (t)) |
| TREE_USED (t) = 0; |
| |
| for (t = BLOCK_SUBBLOCKS (block); t ; t = BLOCK_CHAIN (t)) |
| clear_tree_used (t); |
| } |
| |
| enum { |
| SPCT_FLAG_DEFAULT = 1, |
| SPCT_FLAG_ALL = 2, |
| SPCT_FLAG_STRONG = 3, |
| SPCT_FLAG_EXPLICIT = 4 |
| }; |
| |
| /* 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) |
| || !tree_fits_uhwi_p (TYPE_SIZE_UNIT (type))) |
| len = max; |
| else |
| len = tree_to_uhwi (TYPE_SIZE_UNIT (type)); |
| |
| 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 == SPCT_FLAG_ALL |
| || flag_stack_protect == SPCT_FLAG_STRONG |
| || (flag_stack_protect == SPCT_FLAG_EXPLICIT |
| && lookup_attribute ("stack_protect", |
| DECL_ATTRIBUTES (current_function_decl)))) |
| { |
| 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 (size_t i) |
| { |
| return stack_protect_decl_phase (stack_vars[i].decl) == 1; |
| } |
| |
| static bool |
| stack_protect_decl_phase_2 (size_t i) |
| { |
| return stack_protect_decl_phase (stack_vars[i].decl) == 2; |
| } |
| |
| /* And helper function that checks for asan phase (with stack protector |
| it is phase 3). This is used as callback for expand_stack_vars. |
| Returns true if any of the vars in the partition need to be protected. */ |
| |
| static bool |
| asan_decl_phase_3 (size_t i) |
| { |
| while (i != EOC) |
| { |
| if (asan_protect_stack_decl (stack_vars[i].decl)) |
| return true; |
| i = stack_vars[i].next; |
| } |
| return false; |
| } |
| |
| /* 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 = i + 1; j < n; ++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 (DECL_SOURCE_LOCATION (current_function_decl), |
| VAR_DECL, NULL, ptr_type_node); |
| TREE_THIS_VOLATILE (guard) = 1; |
| TREE_USED (guard) = 1; |
| expand_one_stack_var (guard); |
| crtl->stack_protect_guard = guard; |
| } |
| |
| /* Prepare for expanding variables. */ |
| static void |
| init_vars_expansion (void) |
| { |
| /* Conflict bitmaps, and a few related temporary bitmaps, go here. */ |
| bitmap_obstack_initialize (&stack_var_bitmap_obstack); |
| |
| /* A map from decl to stack partition. */ |
| decl_to_stack_part = new hash_map<tree, size_t>; |
| |
| /* Initialize local stack smashing state. */ |
| has_protected_decls = false; |
| has_short_buffer = false; |
| } |
| |
| /* Free up stack variable graph data. */ |
| static void |
| fini_vars_expansion (void) |
| { |
| bitmap_obstack_release (&stack_var_bitmap_obstack); |
| if (stack_vars) |
| XDELETEVEC (stack_vars); |
| if (stack_vars_sorted) |
| XDELETEVEC (stack_vars_sorted); |
| stack_vars = NULL; |
| stack_vars_sorted = NULL; |
| stack_vars_alloc = stack_vars_num = 0; |
| delete decl_to_stack_part; |
| decl_to_stack_part = NULL; |
| } |
| |
| /* Make a fair guess for the size of the stack frame of the function |
| in NODE. This doesn't have to be exact, the result is only used in |
| the inline heuristics. So we don't want to run the full stack var |
| packing algorithm (which is quadratic in the number of stack vars). |
| Instead, we calculate the total size of all stack vars. This turns |
| out to be a pretty fair estimate -- packing of stack vars doesn't |
| happen very often. */ |
| |
| HOST_WIDE_INT |
| estimated_stack_frame_size (struct cgraph_node *node) |
| { |
| HOST_WIDE_INT size = 0; |
| size_t i; |
| tree var; |
| struct function *fn = DECL_STRUCT_FUNCTION (node->decl); |
| |
| push_cfun (fn); |
| |
| init_vars_expansion (); |
| |
| FOR_EACH_LOCAL_DECL (fn, i, var) |
| if (auto_var_in_fn_p (var, fn->decl)) |
| size += expand_one_var (var, true, false); |
| |
| if (stack_vars_num > 0) |
| { |
| /* Fake sorting the stack vars for account_stack_vars (). */ |
| stack_vars_sorted = XNEWVEC (size_t, stack_vars_num); |
| for (i = 0; i < stack_vars_num; ++i) |
| stack_vars_sorted[i] = i; |
| size += account_stack_vars (); |
| } |
| |
| fini_vars_expansion (); |
| pop_cfun (); |
| return size; |
| } |
| |
| /* Helper routine to check if a record or union contains an array field. */ |
| |
| static int |
| record_or_union_type_has_array_p (const_tree tree_type) |
| { |
| tree fields = TYPE_FIELDS (tree_type); |
| tree f; |
| |
| for (f = fields; f; f = DECL_CHAIN (f)) |
| if (TREE_CODE (f) == FIELD_DECL) |
| { |
| tree field_type = TREE_TYPE (f); |
| if (RECORD_OR_UNION_TYPE_P (field_type) |
| && record_or_union_type_has_array_p (field_type)) |
| return 1; |
| if (TREE_CODE (field_type) == ARRAY_TYPE) |
| return 1; |
| } |
| return 0; |
| } |
| |
| /* Check if the current function has local referenced variables that |
| have their addresses taken, contain an array, or are arrays. */ |
| |
| static bool |
| stack_protect_decl_p () |
| { |
| unsigned i; |
| tree var; |
| |
| FOR_EACH_LOCAL_DECL (cfun, i, var) |
| if (!is_global_var (var)) |
| { |
| tree var_type = TREE_TYPE (var); |
| if (TREE_CODE (var) == VAR_DECL |
| && (TREE_CODE (var_type) == ARRAY_TYPE |
| || TREE_ADDRESSABLE (var) |
| || (RECORD_OR_UNION_TYPE_P (var_type) |
| && record_or_union_type_has_array_p (var_type)))) |
| return true; |
| } |
| return false; |
| } |
| |
| /* Check if the current function has calls that use a return slot. */ |
| |
| static bool |
| stack_protect_return_slot_p () |
| { |
| basic_block bb; |
| |
| FOR_ALL_BB_FN (bb, cfun) |
| for (gimple_stmt_iterator gsi = gsi_start_bb (bb); |
| !gsi_end_p (gsi); gsi_next (&gsi)) |
| { |
| gimple stmt = gsi_stmt (gsi); |
| /* This assumes that calls to internal-only functions never |
| use a return slot. */ |
| if (is_gimple_call (stmt) |
| && !gimple_call_internal_p (stmt) |
| && aggregate_value_p (TREE_TYPE (gimple_call_fntype (stmt)), |
| gimple_call_fndecl (stmt))) |
| return true; |
| } |
| return false; |
| } |
| |
| /* Expand all variables used in the function. */ |
| |
| static rtx_insn * |
| expand_used_vars (void) |
| { |
| tree var, outer_block = DECL_INITIAL (current_function_decl); |
| vec<tree> maybe_local_decls = vNULL; |
| rtx_insn *var_end_seq = NULL; |
| unsigned i; |
| unsigned len; |
| bool gen_stack_protect_signal = false; |
| |
| /* 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 local_decls. */ |
| FOR_EACH_LOCAL_DECL (cfun, i, var) |
| TREE_USED (var) = 1; |
| /* Clear TREE_USED on all variables associated with a block scope. */ |
| clear_tree_used (DECL_INITIAL (current_function_decl)); |
| |
| init_vars_expansion (); |
| |
| if (targetm.use_pseudo_pic_reg ()) |
| pic_offset_table_rtx = gen_reg_rtx (Pmode); |
| |
| hash_map<tree, tree> ssa_name_decls; |
| for (i = 0; i < SA.map->num_partitions; i++) |
| { |
| tree var = partition_to_var (SA.map, i); |
| |
| gcc_assert (!virtual_operand_p (var)); |
| |
| /* Assign decls to each SSA name partition, share decls for partitions |
| we could have coalesced (those with the same type). */ |
| if (SSA_NAME_VAR (var) == NULL_TREE) |
| { |
| tree *slot = &ssa_name_decls.get_or_insert (TREE_TYPE (var)); |
| if (!*slot) |
| *slot = create_tmp_reg (TREE_TYPE (var)); |
| replace_ssa_name_symbol (var, *slot); |
| } |
| |
| /* Always allocate space for partitions based on VAR_DECLs. But for |
| those based on PARM_DECLs or RESULT_DECLs and which matter for the |
| debug info, there is no need to do so if optimization is disabled |
| because all the SSA_NAMEs based on these DECLs have been coalesced |
| into a single partition, which is thus assigned the canonical RTL |
| location of the DECLs. If in_lto_p, we can't rely on optimize, |
| a function could be compiled with -O1 -flto first and only the |
| link performed at -O0. */ |
| if (TREE_CODE (SSA_NAME_VAR (var)) == VAR_DECL) |
| expand_one_var (var, true, true); |
| else if (DECL_IGNORED_P (SSA_NAME_VAR (var)) || optimize || in_lto_p) |
| { |
| /* This is a PARM_DECL or RESULT_DECL. For those partitions that |
| contain the default def (representing the parm or result itself) |
| we don't do anything here. But those which don't contain the |
| default def (representing a temporary based on the parm/result) |
| we need to allocate space just like for normal VAR_DECLs. */ |
| if (!bitmap_bit_p (SA.partition_has_default_def, i)) |
| { |
| expand_one_var (var, true, true); |
| gcc_assert (SA.partition_to_pseudo[i]); |
| } |
| } |
| } |
| |
| if (flag_stack_protect == SPCT_FLAG_STRONG) |
| gen_stack_protect_signal |
| = stack_protect_decl_p () || stack_protect_return_slot_p (); |
| |
| /* At this point all variables on the local_decls with TREE_USED |
| set are not associated with any block scope. Lay them out. */ |
| |
| len = vec_safe_length (cfun->local_decls); |
| FOR_EACH_LOCAL_DECL (cfun, i, var) |
| { |
| bool expand_now = false; |
| |
| /* Expanded above already. */ |
| if (is_gimple_reg (var)) |
| { |
| TREE_USED (var) = 0; |
| goto next; |
| } |
| /* 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. */ |
| else if (TREE_STATIC (var) || DECL_EXTERNAL (var)) |
| expand_now = true; |
| |
| /* Expand variables not associated with any block now. Those created by |
| the optimizers could be live anywhere in the function. Those that |
| could possibly have been scoped originally and detached from their |
| block will have their allocation deferred so we coalesce them with |
| others when optimization is enabled. */ |
| 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, true); |
| |
| next: |
| if (DECL_ARTIFICIAL (var) && !DECL_IGNORED_P (var)) |
| { |
| rtx rtl = DECL_RTL_IF_SET (var); |
| |
| /* Keep artificial non-ignored vars in cfun->local_decls |
| chain until instantiate_decls. */ |
| if (rtl && (MEM_P (rtl) || GET_CODE (rtl) == CONCAT)) |
| add_local_decl (cfun, var); |
| else if (rtl == NULL_RTX) |
| /* If rtl isn't set yet, which can happen e.g. with |
| -fstack-protector, retry before returning from this |
| function. */ |
| maybe_local_decls.safe_push (var); |
| } |
| } |
| |
| /* We duplicated some of the decls in CFUN->LOCAL_DECLS. |
| |
| +-----------------+-----------------+ |
| | ...processed... | ...duplicates...| |
| +-----------------+-----------------+ |
| ^ |
| +-- LEN points here. |
| |
| We just want the duplicates, as those are the artificial |
| non-ignored vars that we want to keep until instantiate_decls. |
| Move them down and truncate the array. */ |
| if (!vec_safe_is_empty (cfun->local_decls)) |
| cfun->local_decls->block_remove (0, len); |
| |
| /* 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) |
| { |
| add_scope_conflicts (); |
| |
| /* If stack protection is enabled, we don't share space between |
| vulnerable data and non-vulnerable data. */ |
| if (flag_stack_protect != 0 |
| && (flag_stack_protect != SPCT_FLAG_EXPLICIT |
| || (flag_stack_protect == SPCT_FLAG_EXPLICIT |
| && lookup_attribute ("stack_protect", |
| DECL_ATTRIBUTES (current_function_decl))))) |
| 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 (); |
| } |
| |
| switch (flag_stack_protect) |
| { |
| case SPCT_FLAG_ALL: |
| create_stack_guard (); |
| break; |
| |
| case SPCT_FLAG_STRONG: |
| if (gen_stack_protect_signal |
| || cfun->calls_alloca || has_protected_decls |
| || lookup_attribute ("stack_protect", |
| DECL_ATTRIBUTES (current_function_decl))) |
| create_stack_guard (); |
| break; |
| |
| case SPCT_FLAG_DEFAULT: |
| if (cfun->calls_alloca || has_protected_decls |
| || lookup_attribute ("stack_protect", |
| DECL_ATTRIBUTES (current_function_decl))) |
| create_stack_guard (); |
| break; |
| |
| case SPCT_FLAG_EXPLICIT: |
| if (lookup_attribute ("stack_protect", |
| DECL_ATTRIBUTES (current_function_decl))) |
| create_stack_guard (); |
| break; |
| default: |
| ; |
| } |
| |
| /* Assign rtl to each variable based on these partitions. */ |
| if (stack_vars_num > 0) |
| { |
| struct stack_vars_data data; |
| |
| data.asan_vec = vNULL; |
| data.asan_decl_vec = vNULL; |
| data.asan_base = NULL_RTX; |
| data.asan_alignb = 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, &data); |
| |
| /* Phase 2 contains other kinds of arrays. */ |
| if (flag_stack_protect == SPCT_FLAG_ALL |
| || flag_stack_protect == SPCT_FLAG_STRONG |
| || (flag_stack_protect == SPCT_FLAG_EXPLICIT |
| && lookup_attribute ("stack_protect", |
| DECL_ATTRIBUTES (current_function_decl)))) |
| expand_stack_vars (stack_protect_decl_phase_2, &data); |
| } |
| |
| if ((flag_sanitize & SANITIZE_ADDRESS) && ASAN_STACK) |
| /* Phase 3, any partitions that need asan protection |
| in addition to phase 1 and 2. */ |
| expand_stack_vars (asan_decl_phase_3, &data); |
| |
| if (!data.asan_vec.is_empty ()) |
| { |
| HOST_WIDE_INT prev_offset = frame_offset; |
| HOST_WIDE_INT offset, sz, redzonesz; |
| redzonesz = ASAN_RED_ZONE_SIZE; |
| sz = data.asan_vec[0] - prev_offset; |
| if (data.asan_alignb > ASAN_RED_ZONE_SIZE |
| && data.asan_alignb <= 4096 |
| && sz + ASAN_RED_ZONE_SIZE >= (int) data.asan_alignb) |
| redzonesz = ((sz + ASAN_RED_ZONE_SIZE + data.asan_alignb - 1) |
| & ~(data.asan_alignb - HOST_WIDE_INT_1)) - sz; |
| offset |
| = alloc_stack_frame_space (redzonesz, ASAN_RED_ZONE_SIZE); |
| data.asan_vec.safe_push (prev_offset); |
| data.asan_vec.safe_push (offset); |
| /* Leave space for alignment if STRICT_ALIGNMENT. */ |
| if (STRICT_ALIGNMENT) |
| alloc_stack_frame_space ((GET_MODE_ALIGNMENT (SImode) |
| << ASAN_SHADOW_SHIFT) |
| / BITS_PER_UNIT, 1); |
| |
| var_end_seq |
| = asan_emit_stack_protection (virtual_stack_vars_rtx, |
| data.asan_base, |
| data.asan_alignb, |
| data.asan_vec.address (), |
| data.asan_decl_vec.address (), |
| data.asan_vec.length ()); |
| } |
| |
| expand_stack_vars (NULL, &data); |
| |
| data.asan_vec.release (); |
| data.asan_decl_vec.release (); |
| } |
| |
| fini_vars_expansion (); |
| |
| /* If there were any artificial non-ignored vars without rtl |
| found earlier, see if deferred stack allocation hasn't assigned |
| rtl to them. */ |
| FOR_EACH_VEC_ELT_REVERSE (maybe_local_decls, i, var) |
| { |
| rtx rtl = DECL_RTL_IF_SET (var); |
| |
| /* Keep artificial non-ignored vars in cfun->local_decls |
| chain until instantiate_decls. */ |
| if (rtl && (MEM_P (rtl) || GET_CODE (rtl) == CONCAT)) |
| add_local_decl (cfun, var); |
| } |
| maybe_local_decls.release (); |
| |
| /* 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; |
| } |
| |
| return var_end_seq; |
| } |
| |
| |
| /* 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_gimple_stmt (gimple stmt, rtx_insn *since) |
| { |
| if (dump_file && (dump_flags & TDF_DETAILS)) |
| { |
| fprintf (dump_file, "\n;; "); |
| print_gimple_stmt (dump_file, stmt, 0, |
| TDF_SLIM | (dump_flags & TDF_LINENO)); |
| fprintf (dump_file, "\n"); |
| |
| print_rtl (dump_file, since ? NEXT_INSN (since) : since); |
| } |
| } |
| |
| /* Maps the blocks that do not contain tree labels to rtx labels. */ |
| |
| static hash_map<basic_block, rtx_code_label *> *lab_rtx_for_bb; |
| |
| /* Returns the label_rtx expression for a label starting basic block BB. */ |
| |
| static rtx |
| label_rtx_for_bb (basic_block bb ATTRIBUTE_UNUSED) |
| { |
| gimple_stmt_iterator gsi; |
| tree lab; |
| |
| if (bb->flags & BB_RTL) |
| return block_label (bb); |
| |
| rtx_code_label **elt = lab_rtx_for_bb->get (bb); |
| if (elt) |
| return *elt; |
| |
| /* Find the tree label if it is present. */ |
| |
| for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi)) |
| { |
| glabel *lab_stmt; |
| |
| lab_stmt = dyn_cast <glabel *> (gsi_stmt (gsi)); |
| if (!lab_stmt) |
| break; |
| |
| lab = gimple_label_label (lab_stmt); |
| if (DECL_NONLOCAL (lab)) |
| break; |
| |
| return label_rtx (lab); |
| } |
| |
| rtx_code_label *l = gen_label_rtx (); |
| lab_rtx_for_bb->put (bb, l); |
| return l; |
| } |
| |
| |
| /* A subroutine of expand_gimple_cond. Given E, a fallthrough edge |
| of a basic block where we just expanded the conditional at the end, |
| possibly clean up the CFG and instruction sequence. LAST is the |
| last instruction before the just emitted jump sequence. */ |
| |
| static void |
| maybe_cleanup_end_of_block (edge e, rtx_insn *last) |
| { |
| /* Special case: when jumpif decides that the condition is |
| trivial it emits an unconditional jump (and the necessary |
| barrier). But we still have two edges, the fallthru one is |
| wrong. purge_dead_edges would clean this up later. Unfortunately |
| we have to insert insns (and split edges) before |
| find_many_sub_basic_blocks and hence before purge_dead_edges. |
| But splitting edges might create new blocks which depend on the |
| fact that if there are two edges there's no barrier. So the |
| barrier would get lost and verify_flow_info would ICE. Instead |
| of auditing all edge splitters to care for the barrier (which |
| normally isn't there in a cleaned CFG), fix it here. */ |
| if (BARRIER_P (get_last_insn ())) |
| { |
| rtx_insn *insn; |
| remove_edge (e); |
| /* Now, we have a single successor block, if we have insns to |
| insert on the remaining edge we potentially will insert |
| it at the end of this block (if the dest block isn't feasible) |
| in order to avoid splitting the edge. This insertion will take |
| place in front of the last jump. But we might have emitted |
| multiple jumps (conditional and one unconditional) to the |
| same destination. Inserting in front of the last one then |
| is a problem. See PR 40021. We fix this by deleting all |
| jumps except the last unconditional one. */ |
| insn = PREV_INSN (get_last_insn ()); |
| /* Make sure we have an unconditional jump. Otherwise we're |
| confused. */ |
| gcc_assert (JUMP_P (insn) && !any_condjump_p (insn)); |
| for (insn = PREV_INSN (insn); insn != last;) |
| { |
| insn = PREV_INSN (insn); |
| if (JUMP_P (NEXT_INSN (insn))) |
| { |
| if (!any_condjump_p (NEXT_INSN (insn))) |
| { |
| gcc_assert (BARRIER_P (NEXT_INSN (NEXT_INSN (insn)))); |
| delete_insn (NEXT_INSN (NEXT_INSN (insn))); |
| } |
| delete_insn (NEXT_INSN (insn)); |
| } |
| } |
| } |
| } |
| |
| /* A subroutine of expand_gimple_basic_block. Expand one GIMPLE_COND. |
| Returns a new basic block if we've terminated the current basic |
| block and created a new one. */ |
| |
| static basic_block |
| expand_gimple_cond (basic_block bb, gcond *stmt) |
| { |
| basic_block new_bb, dest; |
| edge new_edge; |
| edge true_edge; |
| edge false_edge; |
| rtx_insn *last2, *last; |
| enum tree_code code; |
| tree op0, op1; |
| |
| code = gimple_cond_code (stmt); |
| op0 = gimple_cond_lhs (stmt); |
| op1 = gimple_cond_rhs (stmt); |
| /* We're sometimes presented with such code: |
| D.123_1 = x < y; |
| if (D.123_1 != 0) |
| ... |
| This would expand to two comparisons which then later might |
| be cleaned up by combine. But some pattern matchers like if-conversion |
| work better when there's only one compare, so make up for this |
| here as special exception if TER would have made the same change. */ |
| if (SA.values |
| && TREE_CODE (op0) == SSA_NAME |
| && TREE_CODE (TREE_TYPE (op0)) == BOOLEAN_TYPE |
| && TREE_CODE (op1) == INTEGER_CST |
| && ((gimple_cond_code (stmt) == NE_EXPR |
| && integer_zerop (op1)) |
| || (gimple_cond_code (stmt) == EQ_EXPR |
| && integer_onep (op1))) |
| && bitmap_bit_p (SA.values, SSA_NAME_VERSION (op0))) |
| { |
| gimple second = SSA_NAME_DEF_STMT (op0); |
| if (gimple_code (second) == GIMPLE_ASSIGN) |
| { |
| enum tree_code code2 = gimple_assign_rhs_code (second); |
| if (TREE_CODE_CLASS (code2) == tcc_comparison) |
| { |
| code = code2; |
| op0 = gimple_assign_rhs1 (second); |
| op1 = gimple_assign_rhs2 (second); |
| } |
| /* If jumps are cheap and the target does not support conditional |
| compare, turn some more codes into jumpy sequences. */ |
| else if (BRANCH_COST (optimize_insn_for_speed_p (), false) < 4 |
| && targetm.gen_ccmp_first == NULL) |
| { |
| if ((code2 == BIT_AND_EXPR |
| && TYPE_PRECISION (TREE_TYPE (op0)) == 1 |
| && TREE_CODE (gimple_assign_rhs2 (second)) != INTEGER_CST) |
| || code2 == TRUTH_AND_EXPR) |
| { |
| code = TRUTH_ANDIF_EXPR; |
| op0 = gimple_assign_rhs1 (second); |
| op1 = gimple_assign_rhs2 (second); |
| } |
| else if (code2 == BIT_IOR_EXPR || code2 == TRUTH_OR_EXPR) |
| { |
| code = TRUTH_ORIF_EXPR; |
| op0 = gimple_assign_rhs1 (second); |
| op1 = gimple_assign_rhs2 (second); |
| } |
| } |
| } |
| } |
| |
| last2 = last = get_last_insn (); |
| |
| extract_true_false_edges_from_block (bb, &true_edge, &false_edge); |
| set_curr_insn_location (gimple_location (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 (false_edge->dest == bb->next_bb) |
| { |
| jumpif_1 (code, op0, op1, label_rtx_for_bb (true_edge->dest), |
| true_edge->probability); |
| maybe_dump_rtl_for_gimple_stmt (stmt, last); |
| if (true_edge->goto_locus != UNKNOWN_LOCATION) |
| set_curr_insn_location (true_edge->goto_locus); |
| false_edge->flags |= EDGE_FALLTHRU; |
| maybe_cleanup_end_of_block (false_edge, last); |
| return NULL; |
| } |
| if (true_edge->dest == bb->next_bb) |
| { |
| jumpifnot_1 (code, op0, op1, label_rtx_for_bb (false_edge->dest), |
| false_edge->probability); |
| maybe_dump_rtl_for_gimple_stmt (stmt, last); |
| if (false_edge->goto_locus != UNKNOWN_LOCATION) |
| set_curr_insn_location (false_edge->goto_locus); |
| true_edge->flags |= EDGE_FALLTHRU; |
| maybe_cleanup_end_of_block (true_edge, last); |
| return NULL; |
| } |
| |
| jumpif_1 (code, op0, op1, label_rtx_for_bb (true_edge->dest), |
| true_edge->probability); |
| last = get_last_insn (); |
| if (false_edge->goto_locus != UNKNOWN_LOCATION) |
| set_curr_insn_location (false_edge->goto_locus); |
| emit_jump (label_rtx_for_bb (false_edge->dest)); |
| |
| 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); |
| add_bb_to_loop (new_bb, bb->loop_father); |
| 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_gimple_stmt (stmt, last2); |
| |
| if (true_edge->goto_locus != UNKNOWN_LOCATION) |
| { |
| set_curr_insn_location (true_edge->goto_locus); |
| true_edge->goto_locus = curr_insn_location (); |
| } |
| |
| return new_bb; |
| } |
| |
| /* Mark all calls that can have a transaction restart. */ |
| |
| static void |
| mark_transaction_restart_calls (gimple stmt) |
| { |
| struct tm_restart_node dummy; |
| tm_restart_node **slot; |
| |
| if (!cfun->gimple_df->tm_restart) |
| return; |
| |
| dummy.stmt = stmt; |
| slot = cfun->gimple_df->tm_restart->find_slot (&dummy, NO_INSERT); |
| if (slot) |
| { |
| struct tm_restart_node *n = *slot; |
| tree list = n->label_or_list; |
| rtx_insn *insn; |
| |
| for (insn = next_real_insn (get_last_insn ()); |
| !CALL_P (insn); |
| insn = next_real_insn (insn)) |
| continue; |
| |
| if (TREE_CODE (list) == LABEL_DECL) |
| add_reg_note (insn, REG_TM, label_rtx (list)); |
| else |
| for (; list ; list = TREE_CHAIN (list)) |
| add_reg_note (insn, REG_TM, label_rtx (TREE_VALUE (list))); |
| } |
| } |
| |
| /* A subroutine of expand_gimple_stmt_1, expanding one GIMPLE_CALL |
| statement STMT. */ |
| |
| static void |
| expand_call_stmt (gcall *stmt) |
| { |
| tree exp, decl, lhs; |
| bool builtin_p; |
| size_t i; |
| |
| if (gimple_call_internal_p (stmt)) |
| { |
| expand_internal_call (stmt); |
| return; |
| } |
| |
| exp = build_vl_exp (CALL_EXPR, gimple_call_num_args (stmt) + 3); |
| |
| CALL_EXPR_FN (exp) = gimple_call_fn (stmt); |
| decl = gimple_call_fndecl (stmt); |
| builtin_p = decl && DECL_BUILT_IN (decl); |
| |
| /* If this is not a builtin function, the function type through which the |
| call is made may be different from the type of the function. */ |
| if (!builtin_p) |
| CALL_EXPR_FN (exp) |
| = fold_convert (build_pointer_type (gimple_call_fntype (stmt)), |
| CALL_EXPR_FN (exp)); |
| |
| TREE_TYPE (exp) = gimple_call_return_type (stmt); |
| CALL_EXPR_STATIC_CHAIN (exp) = gimple_call_chain (stmt); |
| |
| for (i = 0; i < gimple_call_num_args (stmt); i++) |
| { |
| tree arg = gimple_call_arg (stmt, i); |
| gimple def; |
| /* TER addresses into arguments of builtin functions so we have a |
| chance to infer more correct alignment information. See PR39954. */ |
| if (builtin_p |
| && TREE_CODE (arg) == SSA_NAME |
| && (def = get_gimple_for_ssa_name (arg)) |
| && gimple_assign_rhs_code (def) == ADDR_EXPR) |
| arg = gimple_assign_rhs1 (def); |
| CALL_EXPR_ARG (exp, i) = arg; |
| } |
| |
| if (gimple_has_side_effects (stmt)) |
| TREE_SIDE_EFFECTS (exp) = 1; |
| |
| if (gimple_call_nothrow_p (stmt)) |
| TREE_NOTHROW (exp) = 1; |
| |
| CALL_EXPR_TAILCALL (exp) = gimple_call_tail_p (stmt); |
| CALL_EXPR_RETURN_SLOT_OPT (exp) = gimple_call_return_slot_opt_p (stmt); |
| if (decl |
| && DECL_BUILT_IN_CLASS (decl) == BUILT_IN_NORMAL |
| && (DECL_FUNCTION_CODE (decl) == BUILT_IN_ALLOCA |
| || DECL_FUNCTION_CODE (decl) == BUILT_IN_ALLOCA_WITH_ALIGN)) |
| CALL_ALLOCA_FOR_VAR_P (exp) = gimple_call_alloca_for_var_p (stmt); |
| else |
| CALL_FROM_THUNK_P (exp) = gimple_call_from_thunk_p (stmt); |
| CALL_EXPR_VA_ARG_PACK (exp) = gimple_call_va_arg_pack_p (stmt); |
| SET_EXPR_LOCATION (exp, gimple_location (stmt)); |
| CALL_WITH_BOUNDS_P (exp) = gimple_call_with_bounds_p (stmt); |
| |
| /* Ensure RTL is created for debug args. */ |
| if (decl && DECL_HAS_DEBUG_ARGS_P (decl)) |
| { |
| vec<tree, va_gc> **debug_args = decl_debug_args_lookup (decl); |
| unsigned int ix; |
| tree dtemp; |
| |
| if (debug_args) |
| for (ix = 1; (*debug_args)->iterate (ix, &dtemp); ix += 2) |
| { |
| gcc_assert (TREE_CODE (dtemp) == DEBUG_EXPR_DECL); |
| expand_debug_expr (dtemp); |
| } |
| } |
| |
| lhs = gimple_call_lhs (stmt); |
| if (lhs) |
| expand_assignment (lhs, exp, false); |
| else |
| expand_expr (exp, const0_rtx, VOIDmode, EXPAND_NORMAL); |
| |
| mark_transaction_restart_calls (stmt); |
| } |
| |
| |
| /* Generate RTL for an asm statement (explicit assembler code). |
| STRING is a STRING_CST node containing the assembler code text, |
| or an ADDR_EXPR containing a STRING_CST. VOL nonzero means the |
| insn is volatile; don't optimize it. */ |
| |
| static void |
| expand_asm_loc (tree string, int vol, location_t locus) |
| { |
| rtx body; |
| |
| if (TREE_CODE (string) == ADDR_EXPR) |
| string = TREE_OPERAND (string, 0); |
| |
| body = gen_rtx_ASM_INPUT_loc (VOIDmode, |
| ggc_strdup (TREE_STRING_POINTER (string)), |
| locus); |
| |
| MEM_VOLATILE_P (body) = vol; |
| |
| emit_insn (body); |
| } |
| |
| /* Return the number of times character C occurs in string S. */ |
| static int |
| n_occurrences (int c, const char *s) |
| { |
| int n = 0; |
| while (*s) |
| n += (*s++ == c); |
| return n; |
| } |
| |
| /* A subroutine of expand_asm_operands. Check that all operands have |
| the same number of alternatives. Return true if so. */ |
| |
| static bool |
| check_operand_nalternatives (tree outputs, tree inputs) |
| { |
| if (outputs || inputs) |
| { |
| tree tmp = TREE_PURPOSE (outputs ? outputs : inputs); |
| int nalternatives |
| = n_occurrences (',', TREE_STRING_POINTER (TREE_VALUE (tmp))); |
| tree next = inputs; |
| |
| if (nalternatives + 1 > MAX_RECOG_ALTERNATIVES) |
| { |
| error ("too many alternatives in %<asm%>"); |
| return false; |
| } |
| |
| tmp = outputs; |
| while (tmp) |
| { |
| const char *constraint |
| = TREE_STRING_POINTER (TREE_VALUE (TREE_PURPOSE (tmp))); |
| |
| if (n_occurrences (',', constraint) != nalternatives) |
| { |
| error ("operand constraints for %<asm%> differ " |
| "in number of alternatives"); |
| return false; |
| } |
| |
| if (TREE_CHAIN (tmp)) |
| tmp = TREE_CHAIN (tmp); |
| else |
| tmp = next, next = 0; |
| } |
| } |
| |
| return true; |
| } |
| |
| /* Check for overlap between registers marked in CLOBBERED_REGS and |
| anything inappropriate in T. Emit error and return the register |
| variable definition for error, NULL_TREE for ok. */ |
| |
| static bool |
| tree_conflicts_with_clobbers_p (tree t, HARD_REG_SET *clobbered_regs) |
| { |
| /* Conflicts between asm-declared register variables and the clobber |
| list are not allowed. */ |
| tree overlap = tree_overlaps_hard_reg_set (t, clobbered_regs); |
| |
| if (overlap) |
| { |
| error ("asm-specifier for variable %qE conflicts with asm clobber list", |
| DECL_NAME (overlap)); |
| |
| /* Reset registerness to stop multiple errors emitted for a single |
| variable. */ |
| DECL_REGISTER (overlap) = 0; |
| return true; |
| } |
| |
| return false; |
| } |
| |
| /* Generate RTL for an asm statement with arguments. |
| STRING is the instruction template. |
| OUTPUTS is a list of output arguments (lvalues); INPUTS a list of inputs. |
| Each output or input has an expression in the TREE_VALUE and |
| a tree list in TREE_PURPOSE which in turn contains a constraint |
| name in TREE_VALUE (or NULL_TREE) and a constraint string |
| in TREE_PURPOSE. |
| CLOBBERS is a list of STRING_CST nodes each naming a hard register |
| that is clobbered by this insn. |
| |
| LABELS is a list of labels, and if LABELS is non-NULL, FALLTHRU_BB |
| should be the fallthru basic block of the asm goto. |
| |
| Not all kinds of lvalue that may appear in OUTPUTS can be stored directly. |
| Some elements of OUTPUTS may be replaced with trees representing temporary |
| values. The caller should copy those temporary values to the originally |
| specified lvalues. |
| |
| VOL nonzero means the insn is volatile; don't optimize it. */ |
| |
| static void |
| expand_asm_operands (tree string, tree outputs, tree inputs, |
| tree clobbers, tree labels, basic_block fallthru_bb, |
| int vol, location_t locus) |
| { |
| rtvec argvec, constraintvec, labelvec; |
| rtx body; |
| int ninputs = list_length (inputs); |
| int noutputs = list_length (outputs); |
| int nlabels = list_length (labels); |
| int ninout; |
| int nclobbers; |
| HARD_REG_SET clobbered_regs; |
| int clobber_conflict_found = 0; |
| tree tail; |
| tree t; |
| int i; |
| /* Vector of RTX's of evaluated output operands. */ |
| rtx *output_rtx = XALLOCAVEC (rtx, noutputs); |
| int *inout_opnum = XALLOCAVEC (int, noutputs); |
| rtx *real_output_rtx = XALLOCAVEC (rtx, noutputs); |
| machine_mode *inout_mode = XALLOCAVEC (machine_mode, noutputs); |
| const char **constraints = XALLOCAVEC (const char *, noutputs + ninputs); |
| int old_generating_concat_p = generating_concat_p; |
| rtx_code_label *fallthru_label = NULL; |
| |
| /* An ASM with no outputs needs to be treated as volatile, for now. */ |
| if (noutputs == 0) |
| vol = 1; |
| |
| if (! check_operand_nalternatives (outputs, inputs)) |
| return; |
| |
| string = resolve_asm_operand_names (string, outputs, inputs, labels); |
| |
| /* Collect constraints. */ |
| i = 0; |
| for (t = outputs; t ; t = TREE_CHAIN (t), i++) |
| constraints[i] = TREE_STRING_POINTER (TREE_VALUE (TREE_PURPOSE (t))); |
| for (t = inputs; t ; t = TREE_CHAIN (t), i++) |
| constraints[i] = TREE_STRING_POINTER (TREE_VALUE (TREE_PURPOSE (t))); |
| |
| /* Sometimes we wish to automatically clobber registers across an asm. |
| Case in point is when the i386 backend moved from cc0 to a hard reg -- |
| maintaining source-level compatibility means automatically clobbering |
| the flags register. */ |
| clobbers = targetm.md_asm_clobbers (outputs, inputs, clobbers); |
| |
| /* Count the number of meaningful clobbered registers, ignoring what |
| we would ignore later. */ |
| nclobbers = 0; |
| CLEAR_HARD_REG_SET (clobbered_regs); |
| for (tail = clobbers; tail; tail = TREE_CHAIN (tail)) |
| { |
| const char *regname; |
| int nregs; |
| |
| if (TREE_VALUE (tail) == error_mark_node) |
| return; |
| regname = TREE_STRING_POINTER (TREE_VALUE (tail)); |
| |
| i = decode_reg_name_and_count (regname, &nregs); |
| if (i == -4) |
| ++nclobbers; |
| else if (i == -2) |
| error ("unknown register name %qs in %<asm%>", regname); |
| |
| /* Mark clobbered registers. */ |
| if (i >= 0) |
| { |
| int reg; |
| |
| for (reg = i; reg < i + nregs; reg++) |
| { |
| ++nclobbers; |
| |
| /* Clobbering the PIC register is an error. */ |
| if (reg == (int) PIC_OFFSET_TABLE_REGNUM) |
| { |
| error ("PIC register clobbered by %qs in %<asm%>", regname); |
| return; |
| } |
| |
| SET_HARD_REG_BIT (clobbered_regs, reg); |
| } |
| } |
| } |
| |
| /* First pass over inputs and outputs checks validity and sets |
| mark_addressable if needed. */ |
| |
| ninout = 0; |
| for (i = 0, tail = outputs; tail; tail = TREE_CHAIN (tail), i++) |
| { |
| tree val = TREE_VALUE (tail); |
| tree type = TREE_TYPE (val); |
| const char *constraint; |
| bool is_inout; |
| bool allows_reg; |
| bool allows_mem; |
| |
| /* If there's an erroneous arg, emit no insn. */ |
| if (type == error_mark_node) |
| return; |
| |
| /* Try to parse the output constraint. If that fails, there's |
| no point in going further. */ |
| constraint = constraints[i]; |
| if (!parse_output_constraint (&constraint, i, ninputs, noutputs, |
| &allows_mem, &allows_reg, &is_inout)) |
| return; |
| |
| if (! allows_reg |
| && (allows_mem |
| || is_inout |
| || (DECL_P (val) |
| && REG_P (DECL_RTL (val)) |
| && GET_MODE (DECL_RTL (val)) != TYPE_MODE (type)))) |
| mark_addressable (val); |
| |
| if (is_inout) |
| ninout++; |
| } |
| |
| ninputs += ninout; |
| if (ninputs + noutputs + nlabels > MAX_RECOG_OPERANDS) |
| { |
| error ("more than %d operands in %<asm%>", MAX_RECOG_OPERANDS); |
| return; |
| } |
| |
| for (i = 0, tail = inputs; tail; i++, tail = TREE_CHAIN (tail)) |
| { |
| bool allows_reg, allows_mem; |
| const char *constraint; |
| |
| /* If there's an erroneous arg, emit no insn, because the ASM_INPUT |
| would get VOIDmode and that could cause a crash in reload. */ |
| if (TREE_TYPE (TREE_VALUE (tail)) == error_mark_node) |
| return; |
| |
| constraint = constraints[i + noutputs]; |
| if (! parse_input_constraint (&constraint, i, ninputs, noutputs, ninout, |
| constraints, &allows_mem, &allows_reg)) |
| return; |
| |
| if (! allows_reg && allows_mem) |
| mark_addressable (TREE_VALUE (tail)); |
| } |
| |
| /* Second pass evaluates arguments. */ |
| |
| /* Make sure stack is consistent for asm goto. */ |
| if (nlabels > 0) |
| do_pending_stack_adjust (); |
| |
| ninout = 0; |
| for (i = 0, tail = outputs; tail; tail = TREE_CHAIN (tail), i++) |
| { |
| tree val = TREE_VALUE (tail); |
| tree type = TREE_TYPE (val); |
| bool is_inout; |
| bool allows_reg; |
| bool allows_mem; |
| rtx op; |
| bool ok; |
| |
| ok = parse_output_constraint (&constraints[i], i, ninputs, |
| noutputs, &allows_mem, &allows_reg, |
| &is_inout); |
| gcc_assert (ok); |
| |
| /* If an output operand is not a decl or indirect ref and our constraint |
| allows a register, make a temporary to act as an intermediate. |
| Make the asm insn write into that, then our caller will copy it to |
| the real output operand. Likewise for promoted variables. */ |
| |
| generating_concat_p = 0; |
| |
| real_output_rtx[i] = NULL_RTX; |
| if ((TREE_CODE (val) == INDIRECT_REF |
| && allows_mem) |
| || (DECL_P (val) |
| && (allows_mem || REG_P (DECL_RTL (val))) |
| && ! (REG_P (DECL_RTL (val)) |
| && GET_MODE (DECL_RTL (val)) != TYPE_MODE (type))) |
| || ! allows_reg |
| || is_inout) |
| { |
| op = expand_expr (val, NULL_RTX, VOIDmode, |
| !allows_reg ? EXPAND_MEMORY : EXPAND_WRITE); |
| if (MEM_P (op)) |
| op = validize_mem (op); |
| |
| if (! allows_reg && !MEM_P (op)) |
| error ("output number %d not directly addressable", i); |
| if ((! allows_mem && MEM_P (op)) |
| || GET_CODE (op) == CONCAT) |
| { |
| real_output_rtx[i] = op; |
| op = gen_reg_rtx (GET_MODE (op)); |
| if (is_inout) |
| emit_move_insn (op, real_output_rtx[i]); |
| } |
| } |
| else |
| { |
| op = assign_temp (type, 0, 1); |
| op = validize_mem (op); |
| if (!MEM_P (op) && TREE_CODE (TREE_VALUE (tail)) == SSA_NAME) |
| set_reg_attrs_for_decl_rtl (SSA_NAME_VAR (TREE_VALUE (tail)), op); |
| TREE_VALUE (tail) = make_tree (type, op); |
| } |
| output_rtx[i] = op; |
| |
| generating_concat_p = old_generating_concat_p; |
| |
| if (is_inout) |
| { |
| inout_mode[ninout] = TYPE_MODE (type); |
| inout_opnum[ninout++] = i; |
| } |
| |
| if (tree_conflicts_with_clobbers_p (val, &clobbered_regs)) |
| clobber_conflict_found = 1; |
| } |
| |
| /* Make vectors for the expression-rtx, constraint strings, |
| and named operands. */ |
| |
| argvec = rtvec_alloc (ninputs); |
| constraintvec = rtvec_alloc (ninputs); |
| labelvec = rtvec_alloc (nlabels); |
| |
| body = gen_rtx_ASM_OPERANDS ((noutputs == 0 ? VOIDmode |
| : GET_MODE (output_rtx[0])), |
| ggc_strdup (TREE_STRING_POINTER (string)), |
| empty_string, 0, argvec, constraintvec, |
| labelvec, locus); |
| |
| MEM_VOLATILE_P (body) = vol; |
| |
| /* Eval the inputs and put them into ARGVEC. |
| Put their constraints into ASM_INPUTs and store in CONSTRAINTS. */ |
| |
| for (i = 0, tail = inputs; tail; tail = TREE_CHAIN (tail), ++i) |
| { |
| bool allows_reg, allows_mem; |
| const char *constraint; |
| tree val, type; |
| rtx op; |
| bool ok; |
| |
| constraint = constraints[i + noutputs]; |
| ok = parse_input_constraint (&constraint, i, ninputs, noutputs, ninout, |
| constraints, &allows_mem, &allows_reg); |
| gcc_assert (ok); |
| |
| generating_concat_p = 0; |
| |
| val = TREE_VALUE (tail); |
| type = TREE_TYPE (val); |
| /* EXPAND_INITIALIZER will not generate code for valid initializer |
| constants, but will still generate code for other types of operand. |
| This is the behavior we want for constant constraints. */ |
| op = expand_expr (val, NULL_RTX, VOIDmode, |
| allows_reg ? EXPAND_NORMAL |
| : allows_mem ? EXPAND_MEMORY |
| : EXPAND_INITIALIZER); |
| |
| /* Never pass a CONCAT to an ASM. */ |
| if (GET_CODE (op) == CONCAT) |
| op = force_reg (GET_MODE (op), op); |
| else if (MEM_P (op)) |
| op = validize_mem (op); |
| |
| if (asm_operand_ok (op, constraint, NULL) <= 0) |
| { |
| if (allows_reg && TYPE_MODE (type) != BLKmode) |
| op = force_reg (TYPE_MODE (type), op); |
| else if (!allows_mem) |
| warning (0, "asm operand %d probably doesn%'t match constraints", |
| i + noutputs); |
| else if (MEM_P (op)) |
| { |
| /* We won't recognize either volatile memory or memory |
| with a queued address as available a memory_operand |
| at this point. Ignore it: clearly this *is* a memory. */ |
| } |
| else |
| gcc_unreachable (); |
| } |
| |
| generating_concat_p = old_generating_concat_p; |
| ASM_OPERANDS_INPUT (body, i) = op; |
| |
| ASM_OPERANDS_INPUT_CONSTRAINT_EXP (body, i) |
| = gen_rtx_ASM_INPUT_loc (TYPE_MODE (type), |
| ggc_strdup (constraints[i + noutputs]), |
| locus); |
| |
| if (tree_conflicts_with_clobbers_p (val, &clobbered_regs)) |
| clobber_conflict_found = 1; |
| } |
| |
| /* Protect all the operands from the queue now that they have all been |
| evaluated. */ |
| |
| generating_concat_p = 0; |
| |
| /* For in-out operands, copy output rtx to input rtx. */ |
| for (i = 0; i < ninout; i++) |
| { |
| int j = inout_opnum[i]; |
| char buffer[16]; |
| |
| ASM_OPERANDS_INPUT (body, ninputs - ninout + i) |
| = output_rtx[j]; |
| |
| sprintf (buffer, "%d", j); |
| ASM_OPERANDS_INPUT_CONSTRAINT_EXP (body, ninputs - ninout + i) |
| = gen_rtx_ASM_INPUT_loc (inout_mode[i], ggc_strdup (buffer), locus); |
| } |
| |
| /* Copy labels to the vector. */ |
| for (i = 0, tail = labels; i < nlabels; ++i, tail = TREE_CHAIN (tail)) |
| { |
| rtx r; |
| /* If asm goto has any labels in the fallthru basic block, use |
| a label that we emit immediately after the asm goto. Expansion |
| may insert further instructions into the same basic block after |
| asm goto and if we don't do this, insertion of instructions on |
| the fallthru edge might misbehave. See PR58670. */ |
| if (fallthru_bb |
| && label_to_block_fn (cfun, TREE_VALUE (tail)) == fallthru_bb) |
| { |
| if (fallthru_label == NULL_RTX) |
| fallthru_label = gen_label_rtx (); |
| r = fallthru_label; |
| } |
| else |
| r = label_rtx (TREE_VALUE (tail)); |
| ASM_OPERANDS_LABEL (body, i) = gen_rtx_LABEL_REF (Pmode, r); |
| } |
| |
| generating_concat_p = old_generating_concat_p; |
| |
| /* Now, for each output, construct an rtx |
| (set OUTPUT (asm_operands INSN OUTPUTCONSTRAINT OUTPUTNUMBER |
| ARGVEC CONSTRAINTS OPNAMES)) |
| If there is more than one, put them inside a PARALLEL. */ |
| |
| if (nlabels > 0 && nclobbers == 0) |
| { |
| gcc_assert (noutputs == 0); |
| emit_jump_insn (body); |
| } |
| else if (noutputs == 0 && nclobbers == 0) |
| { |
| /* No output operands: put in a raw ASM_OPERANDS rtx. */ |
| emit_insn (body); |
| } |
| else if (noutputs == 1 && nclobbers == 0) |
| { |
| ASM_OPERANDS_OUTPUT_CONSTRAINT (body) = ggc_strdup (constraints[0]); |
| emit_insn (gen_rtx_SET (VOIDmode, output_rtx[0], body)); |
| } |
| else |
| { |
| rtx obody = body; |
| int num = noutputs; |
| |
| if (num == 0) |
| num = 1; |
| |
| body = gen_rtx_PARALLEL (VOIDmode, rtvec_alloc (num + nclobbers)); |
| |
| /* For each output operand, store a SET. */ |
| for (i = 0, tail = outputs; tail; tail = TREE_CHAIN (tail), i++) |
| { |
| XVECEXP (body, 0, i) |
| = gen_rtx_SET (VOIDmode, |
| output_rtx[i], |
| gen_rtx_ASM_OPERANDS |
| (GET_MODE (output_rtx[i]), |
| ggc_strdup (TREE_STRING_POINTER (string)), |
| ggc_strdup (constraints[i]), |
| i, argvec, constraintvec, labelvec, locus)); |
| |
| MEM_VOLATILE_P (SET_SRC (XVECEXP (body, 0, i))) = vol; |
| } |
| |
| /* If there are no outputs (but there are some clobbers) |
| store the bare ASM_OPERANDS into the PARALLEL. */ |
| |
| if (i == 0) |
| XVECEXP (body, 0, i++) = obody; |
| |
| /* Store (clobber REG) for each clobbered register specified. */ |
| |
| for (tail = clobbers; tail; tail = TREE_CHAIN (tail)) |
| { |
| const char *regname = TREE_STRING_POINTER (TREE_VALUE (tail)); |
| int reg, nregs; |
| int j = decode_reg_name_and_count (regname, &nregs); |
| rtx clobbered_reg; |
| |
| if (j < 0) |
| { |
| if (j == -3) /* `cc', which is not a register */ |
| continue; |
| |
| if (j == -4) /* `memory', don't cache memory across asm */ |
| { |
| XVECEXP (body, 0, i++) |
| = gen_rtx_CLOBBER (VOIDmode, |
| gen_rtx_MEM |
| (BLKmode, |
| gen_rtx_SCRATCH (VOIDmode))); |
| continue; |
| } |
| |
| /* Ignore unknown register, error already signaled. */ |
| continue; |
| } |
| |
| for (reg = j; reg < j + nregs; reg++) |
| { |
| /* Use QImode since that's guaranteed to clobber just |
| * one reg. */ |
| clobbered_reg = gen_rtx_REG (QImode, reg); |
| |
| /* Do sanity check for overlap between clobbers and |
| respectively input and outputs that hasn't been |
| handled. Such overlap should have been detected and |
| reported above. */ |
| if (!clobber_conflict_found) |
| { |
| int opno; |
| |
| /* We test the old body (obody) contents to avoid |
| tripping over the under-construction body. */ |
| for (opno = 0; opno < noutputs; opno++) |
| if (reg_overlap_mentioned_p (clobbered_reg, |
| output_rtx[opno])) |
| internal_error |
| ("asm clobber conflict with output operand"); |
| |
| for (opno = 0; opno < ninputs - ninout; opno++) |
| if (reg_overlap_mentioned_p (clobbered_reg, |
| ASM_OPERANDS_INPUT (obody, |
| opno))) |
| internal_error |
| ("asm clobber conflict with input operand"); |
| } |
| |
| XVECEXP (body, 0, i++) |
| = gen_rtx_CLOBBER (VOIDmode, clobbered_reg); |
| } |
| } |
| |
| if (nlabels > 0) |
| emit_jump_insn (body); |
| else |
| emit_insn (body); |
| } |
| |
| if (fallthru_label) |
| emit_label (fallthru_label); |
| |
| /* For any outputs that needed reloading into registers, spill them |
| back to where they belong. */ |
| for (i = 0; i < noutputs; ++i) |
| if (real_output_rtx[i]) |
| emit_move_insn (real_output_rtx[i], output_rtx[i]); |
| |
| crtl->has_asm_statement = 1; |
| free_temp_slots (); |
| } |
| |
| |
| static void |
| expand_asm_stmt (gasm *stmt) |
| { |
| int noutputs; |
| tree outputs, tail, t; |
| tree *o; |
| size_t i, n; |
| const char *s; |
| tree str, out, in, cl, labels; |
| location_t locus = gimple_location (stmt); |
| basic_block fallthru_bb = NULL; |
| |
| /* Meh... convert the gimple asm operands into real tree lists. |
| Eventually we should make all routines work on the vectors instead |
| of relying on TREE_CHAIN. */ |
| out = NULL_TREE; |
| n = gimple_asm_noutputs (stmt); |
| if (n > 0) |
| { |
| t = out = gimple_asm_output_op (stmt, 0); |
| for (i = 1; i < n; i++) |
| t = TREE_CHAIN (t) = gimple_asm_output_op (stmt, i); |
| } |
| |
| in = NULL_TREE; |
| n = gimple_asm_ninputs (stmt); |
| if (n > 0) |
| { |
| t = in = gimple_asm_input_op (stmt, 0); |
| for (i = 1; i < n; i++) |
| t = TREE_CHAIN (t) = gimple_asm_input_op (stmt, i); |
| } |
| |
| cl = NULL_TREE; |
| n = gimple_asm_nclobbers (stmt); |
| if (n > 0) |
| { |
| t = cl = gimple_asm_clobber_op (stmt, 0); |
| for (i = 1; i < n; i++) |
| t = TREE_CHAIN (t) = gimple_asm_clobber_op (stmt, i); |
| } |
| |
| labels = NULL_TREE; |
| n = gimple_asm_nlabels (stmt); |
| if (n > 0) |
| { |
| edge fallthru = find_fallthru_edge (gimple_bb (stmt)->succs); |
| if (fallthru) |
| fallthru_bb = fallthru->dest; |
| t = labels = gimple_asm_label_op (stmt, 0); |
| for (i = 1; i < n; i++) |
| t = TREE_CHAIN (t) = gimple_asm_label_op (stmt, i); |
| } |
| |
| s = gimple_asm_string (stmt); |
| str = build_string (strlen (s), s); |
| |
| if (gimple_asm_input_p (stmt)) |
| { |
| expand_asm_loc (str, gimple_asm_volatile_p (stmt), locus); |
| return; |
| } |
| |
| outputs = out; |
| noutputs = gimple_asm_noutputs (stmt); |
| /* o[I] is the place that output number I should be written. */ |
| o = (tree *) alloca (noutputs * sizeof (tree)); |
| |
| /* Record the contents of OUTPUTS before it is modified. */ |
| for (i = 0, tail = outputs; tail; tail = TREE_CHAIN (tail), i++) |
| o[i] = TREE_VALUE (tail); |
| |
| /* Generate the ASM_OPERANDS insn; store into the TREE_VALUEs of |
| OUTPUTS some trees for where the values were actually stored. */ |
| expand_asm_operands (str, outputs, in, cl, labels, fallthru_bb, |
| gimple_asm_volatile_p (stmt), locus); |
| |
| /* Copy all the intermediate outputs into the specified outputs. */ |
| for (i = 0, tail = outputs; tail; tail = TREE_CHAIN (tail), i++) |
| { |
| if (o[i] != TREE_VALUE (tail)) |
| { |
| expand_assignment (o[i], TREE_VALUE (tail), false); |
| free_temp_slots (); |
| |
| /* Restore the original value so that it's correct the next |
| time we expand this function. */ |
| TREE_VALUE (tail) = o[i]; |
| } |
| } |
| } |
| |
| /* Emit code to jump to the address |
| specified by the pointer expression EXP. */ |
| |
| static void |
| expand_computed_goto (tree exp) |
| { |
| rtx x = expand_normal (exp); |
| |
| do_pending_stack_adjust (); |
| emit_indirect_jump (x); |
| } |
| |
| /* Generate RTL code for a `goto' statement with target label LABEL. |
| LABEL should be a LABEL_DECL tree node that was or will later be |
| defined with `expand_label'. */ |
| |
| static void |
| expand_goto (tree label) |
| { |
| #ifdef ENABLE_CHECKING |
| /* Check for a nonlocal goto to a containing function. Should have |
| gotten translated to __builtin_nonlocal_goto. */ |
| tree context = decl_function_context (label); |
| gcc_assert (!context || context == current_function_decl); |
| #endif |
| |
| emit_jump (label_rtx (label)); |
| } |
| |
| /* Output a return with no value. */ |
| |
| static void |
| expand_null_return_1 (void) |
| { |
| clear_pending_stack_adjust (); |
| do_pending_stack_adjust (); |
| emit_jump (return_label); |
| } |
| |
| /* Generate RTL to return from the current function, with no value. |
| (That is, we do not do anything about returning any value.) */ |
| |
| void |
| expand_null_return (void) |
| { |
| /* If this function was declared to return a value, but we |
| didn't, clobber the return registers so that they are not |
| propagated live to the rest of the function. */ |
| clobber_return_register (); |
| |
| expand_null_return_1 (); |
| } |
| |
| /* Generate RTL to return from the current function, with value VAL. */ |
| |
| static void |
| expand_value_return (rtx val) |
| { |
| /* Copy the value to the return location unless it's already there. */ |
| |
| tree decl = DECL_RESULT (current_function_decl); |
| rtx return_reg = DECL_RTL (decl); |
| if (return_reg != val) |
| { |
| tree funtype = TREE_TYPE (current_function_decl); |
| tree type = TREE_TYPE (decl); |
| int unsignedp = TYPE_UNSIGNED (type); |
| machine_mode old_mode = DECL_MODE (decl); |
| machine_mode mode; |
| if (DECL_BY_REFERENCE (decl)) |
| mode = promote_function_mode (type, old_mode, &unsignedp, funtype, 2); |
| else |
| mode = promote_function_mode (type, old_mode, &unsignedp, funtype, 1); |
| |
| if (mode != old_mode) |
| val = convert_modes (mode, old_mode, val, unsignedp); |
| |
| if (GET_CODE (return_reg) == PARALLEL) |
| emit_group_load (return_reg, val, type, int_size_in_bytes (type)); |
| else |
| emit_move_insn (return_reg, val); |
| } |
| |
| expand_null_return_1 (); |
| } |
| |
| /* Generate RTL to evaluate the expression RETVAL and return it |
| from the current function. */ |
| |
| static void |
| expand_return (tree retval, tree bounds) |
| { |
| rtx result_rtl; |
| rtx val = 0; |
| tree retval_rhs; |
| rtx bounds_rtl; |
| |
| /* If function wants no value, give it none. */ |
| if (TREE_CODE (TREE_TYPE (TREE_TYPE (current_function_decl))) == VOID_TYPE) |
| { |
| expand_normal (retval); |
| expand_null_return (); |
| return; |
| } |
| |
| if (retval == error_mark_node) |
| |