diff options
Diffstat (limited to 'gcc/function.c')
-rw-r--r-- | gcc/function.c | 231 |
1 files changed, 158 insertions, 73 deletions
diff --git a/gcc/function.c b/gcc/function.c index 26334f124da..12f2b21dc0d 100644 --- a/gcc/function.c +++ b/gcc/function.c @@ -122,6 +122,9 @@ int current_function_uses_only_leaf_regs; post-instantiation libcalls. */ int virtuals_instantiated; +/* Nonzero if at least one trampoline has been created. */ +int trampolines_created; + /* Assign unique numbers to labels generated for profiling, debugging, etc. */ static int funcdef_no; @@ -223,9 +226,8 @@ static rtx assign_stack_local_1 PARAMS ((enum machine_mode, HOST_WIDE_INT, int, struct function *)); static struct temp_slot *find_temp_slot_from_address PARAMS ((rtx)); static void put_reg_into_stack PARAMS ((struct function *, rtx, tree, - enum machine_mode, enum machine_mode, - int, unsigned int, int, - htab_t)); + enum machine_mode, unsigned int, + int, int, int, htab_t)); static void schedule_fixup_var_refs PARAMS ((struct function *, rtx, tree, enum machine_mode, htab_t)); @@ -289,6 +291,7 @@ static void compute_insns_for_mem PARAMS ((rtx, rtx, htab_t)); static void prepare_function_start PARAMS ((void)); static void do_clobber_return_reg PARAMS ((rtx, void *)); static void do_use_return_reg PARAMS ((rtx, void *)); +static void instantiate_virtual_regs_lossage PARAMS ((rtx)); /* Pointer to chain of `struct function' for containing functions. */ static GTY(()) struct function *outer_function_chain; @@ -450,6 +453,8 @@ free_after_compilation (f) f->x_nonlocal_goto_stack_level = NULL; f->x_cleanup_label = NULL; f->x_return_label = NULL; + f->computed_goto_common_label = NULL; + f->computed_goto_common_reg = NULL; f->x_save_expr_regs = NULL; f->x_stack_slot_list = NULL; f->x_rtl_expr_chain = NULL; @@ -502,6 +507,7 @@ get_frame_size () ALIGN controls the amount of alignment for the address of the slot: 0 means according to MODE, -1 means use BIGGEST_ALIGNMENT and round size to multiple of that, + -2 means use BITS_PER_UNIT, positive specifies alignment boundary in bits. We do not round to stack_boundary here. @@ -542,6 +548,8 @@ assign_stack_local_1 (mode, size, align, function) alignment = BIGGEST_ALIGNMENT / BITS_PER_UNIT; size = CEIL_ROUND (size, alignment); } + else if (align == -2) + alignment = 1; /* BITS_PER_UNIT / BITS_PER_UNIT */ else alignment = align / BITS_PER_UNIT; @@ -771,7 +779,7 @@ assign_stack_temp_for_type (mode, size, keep, type) if (keep == 2) { p->level = target_temp_slot_level; - p->keep = 0; + p->keep = 1; } else if (keep == 3) { @@ -1321,20 +1329,24 @@ init_temp_slots () target_temp_slot_level = 0; } -/* Retroactively move an auto variable from a register to a stack slot. - This is done when an address-reference to the variable is seen. */ +/* Retroactively move an auto variable from a register to a stack + slot. This is done when an address-reference to the variable is + seen. If RESCAN is true, all previously emitted instructions are + examined and modified to handle the fact that DECL is now + addressable. */ void -put_var_into_stack (decl) +put_var_into_stack (decl, rescan) tree decl; + int rescan; { rtx reg; enum machine_mode promoted_mode, decl_mode; struct function *function = 0; tree context; - int can_use_addressof; - int volatilep = TREE_CODE (decl) != SAVE_EXPR && TREE_THIS_VOLATILE (decl); - int usedp = (TREE_USED (decl) + int can_use_addressof_p; + int volatile_p = TREE_CODE (decl) != SAVE_EXPR && TREE_THIS_VOLATILE (decl); + int used_p = (TREE_USED (decl) || (TREE_CODE (decl) != SAVE_EXPR && DECL_INITIAL (decl) != 0)); context = decl_function_context (decl); @@ -1366,8 +1378,9 @@ put_var_into_stack (decl) if (function->decl == context) break; - /* If this is a variable-size object with a pseudo to address it, - put that pseudo into the stack, if the var is nonlocal. */ + /* If this is a variable-sized object or a structure passed by invisible + reference, with a pseudo to address it, put that pseudo into the stack + if the var is non-local. */ if (TREE_CODE (decl) != SAVE_EXPR && DECL_NONLOCAL (decl) && GET_CODE (reg) == MEM && GET_CODE (XEXP (reg, 0)) == REG @@ -1377,8 +1390,12 @@ put_var_into_stack (decl) decl_mode = promoted_mode = GET_MODE (reg); } - can_use_addressof + /* If this variable lives in the current function and we don't need to put it + in the stack for the sake of setjmp or the non-locality, try to keep it in + a register until we know we actually need the address. */ + can_use_addressof_p = (function == 0 + && ! (TREE_CODE (decl) != SAVE_EXPR && DECL_NONLOCAL (decl)) && optimize > 0 /* FIXME make it work for promoted modes too */ && decl_mode == promoted_mode @@ -1389,7 +1406,8 @@ put_var_into_stack (decl) /* If we can't use ADDRESSOF, make sure we see through one we already generated. */ - if (! can_use_addressof && GET_CODE (reg) == MEM + if (! can_use_addressof_p + && GET_CODE (reg) == MEM && GET_CODE (XEXP (reg, 0)) == ADDRESSOF) reg = XEXP (XEXP (reg, 0), 0); @@ -1397,14 +1415,11 @@ put_var_into_stack (decl) if (GET_CODE (reg) == REG) { - /* If this variable lives in the current function and we don't need - to put things in the stack for the sake of setjmp, try to keep it - in a register until we know we actually need the address. */ - if (can_use_addressof) - gen_mem_addressof (reg, decl); + if (can_use_addressof_p) + gen_mem_addressof (reg, decl, rescan); else - put_reg_into_stack (function, reg, TREE_TYPE (decl), promoted_mode, - decl_mode, volatilep, 0, usedp, 0); + put_reg_into_stack (function, reg, TREE_TYPE (decl), decl_mode, + 0, volatile_p, used_p, 0, 0); } else if (GET_CODE (reg) == CONCAT) { @@ -1420,14 +1435,14 @@ put_var_into_stack (decl) #ifdef FRAME_GROWS_DOWNWARD /* Since part 0 should have a lower address, do it second. */ put_reg_into_stack (function, hipart, part_type, part_mode, - part_mode, volatilep, 0, 0, 0); + 0, volatile_p, 0, 0, 0); put_reg_into_stack (function, lopart, part_type, part_mode, - part_mode, volatilep, 0, 0, 0); + 0, volatile_p, 0, 1, 0); #else put_reg_into_stack (function, lopart, part_type, part_mode, - part_mode, volatilep, 0, 0, 0); + 0, volatile_p, 0, 0, 0); put_reg_into_stack (function, hipart, part_type, part_mode, - part_mode, volatilep, 0, 0, 0); + 0, volatile_p, 0, 1, 0); #endif /* Change the CONCAT into a combined MEM for both parts. */ @@ -1448,7 +1463,7 @@ put_var_into_stack (decl) /* Prevent sharing of rtl that might lose. */ if (GET_CODE (XEXP (reg, 0)) == PLUS) XEXP (reg, 0) = copy_rtx (XEXP (reg, 0)); - if (usedp) + if (used_p && rescan) { schedule_fixup_var_refs (function, reg, TREE_TYPE (decl), promoted_mode, 0); @@ -1462,35 +1477,43 @@ put_var_into_stack (decl) /* Subroutine of put_var_into_stack. This puts a single pseudo reg REG into the stack frame of FUNCTION (0 means the current function). + TYPE is the user-level data type of the value hold in the register. DECL_MODE is the machine mode of the user-level data type. - PROMOTED_MODE is the machine mode of the register. - VOLATILE_P is nonzero if this is for a "volatile" decl. - USED_P is nonzero if this reg might have already been used in an insn. */ + ORIGINAL_REGNO must be set if the real regno is not visible in REG. + VOLATILE_P is true if this is for a "volatile" decl. + USED_P is true if this reg might have already been used in an insn. + CONSECUTIVE_P is true if the stack slot assigned to reg must be + consecutive with the previous stack slot. */ static void -put_reg_into_stack (function, reg, type, promoted_mode, decl_mode, volatile_p, - original_regno, used_p, ht) +put_reg_into_stack (function, reg, type, decl_mode, original_regno, + volatile_p, used_p, consecutive_p, ht) struct function *function; rtx reg; tree type; - enum machine_mode promoted_mode, decl_mode; - int volatile_p; + enum machine_mode decl_mode; unsigned int original_regno; - int used_p; + int volatile_p, used_p, consecutive_p; htab_t ht; { struct function *func = function ? function : cfun; - rtx new = 0; + enum machine_mode mode = GET_MODE (reg); unsigned int regno = original_regno; + rtx new = 0; if (regno == 0) regno = REGNO (reg); if (regno < func->x_max_parm_reg) - new = func->x_parm_reg_stack_loc[regno]; + { + if (!func->x_parm_reg_stack_loc) + abort (); + new = func->x_parm_reg_stack_loc[regno]; + } if (new == 0) - new = assign_stack_local_1 (decl_mode, GET_MODE_SIZE (decl_mode), 0, func); + new = assign_stack_local_1 (decl_mode, GET_MODE_SIZE (decl_mode), + consecutive_p ? -2 : 0, func); PUT_CODE (reg, MEM); PUT_MODE (reg, decl_mode); @@ -1512,7 +1535,7 @@ put_reg_into_stack (function, reg, type, promoted_mode, decl_mode, volatile_p, } if (used_p) - schedule_fixup_var_refs (function, reg, type, promoted_mode, ht); + schedule_fixup_var_refs (function, reg, type, mode, ht); } /* Make sure that all refs to the variable, previously made @@ -1700,7 +1723,7 @@ fixup_var_refs_insns_with_hash (ht, var, promoted_mode, unsignedp, may_share) tmp.key = var; ime = (struct insns_for_mem_entry *) htab_find (ht, &tmp); for (insn_list = ime->insns; insn_list != 0; insn_list = XEXP (insn_list, 1)) - if (INSN_P (XEXP (insn_list, 0))) + if (INSN_P (XEXP (insn_list, 0)) && !INSN_DELETED_P (XEXP (insn_list, 0))) fixup_var_refs_insn (XEXP (insn_list, 0), var, promoted_mode, unsignedp, 1, may_share); } @@ -2168,7 +2191,23 @@ fixup_var_refs_1 (var, promoted_mode, loc, insn, replacements, no_share) replacement = find_fixup_replacement (replacements, x); if (replacement->new) { + enum machine_mode mode = GET_MODE (x); *loc = replacement->new; + + /* Careful! We may have just replaced a SUBREG by a MEM, which + means that the insn may have become invalid again. We can't + in this case make a new replacement since we already have one + and we must deal with MATCH_DUPs. */ + if (GET_CODE (replacement->new) == MEM) + { + INSN_CODE (insn) = -1; + if (recog_memoized (insn) >= 0) + return; + + fixup_var_refs_1 (replacement->new, mode, &PATTERN (insn), + insn, replacements, no_share); + } + return; } @@ -2888,15 +2927,19 @@ static int cfa_offset; #define ARG_POINTER_CFA_OFFSET(FNDECL) FIRST_PARM_OFFSET (FNDECL) #endif -/* Build up a (MEM (ADDRESSOF (REG))) rtx for a register REG that just had its - address taken. DECL is the decl or SAVE_EXPR for the object stored in the - register, for later use if we do need to force REG into the stack. REG is - overwritten by the MEM like in put_reg_into_stack. */ +/* Build up a (MEM (ADDRESSOF (REG))) rtx for a register REG that just + had its address taken. DECL is the decl or SAVE_EXPR for the + object stored in the register, for later use if we do need to force + REG into the stack. REG is overwritten by the MEM like in + put_reg_into_stack. RESCAN is true if previously emitted + instructions must be rescanned and modified now that the REG has + been transformed. */ rtx -gen_mem_addressof (reg, decl) +gen_mem_addressof (reg, decl, rescan) rtx reg; tree decl; + int rescan; { rtx r = gen_rtx_ADDRESSOF (Pmode, gen_reg_rtx (GET_MODE (reg)), REGNO (reg), decl); @@ -2934,10 +2977,11 @@ gen_mem_addressof (reg, decl) if (DECL_P (decl) && decl_rtl == reg) SET_DECL_RTL (decl, reg); - if (TREE_USED (decl) || (DECL_P (decl) && DECL_INITIAL (decl) != 0)) + if (rescan + && (TREE_USED (decl) || (DECL_P (decl) && DECL_INITIAL (decl) != 0))) fixup_var_refs (reg, GET_MODE (reg), TREE_UNSIGNED (type), reg, 0); } - else + else if (rescan) fixup_var_refs (reg, GET_MODE (reg), 0, reg, 0); return reg; @@ -2988,8 +3032,8 @@ put_addressof_into_stack (r, ht) used_p = 1; } - put_reg_into_stack (0, reg, type, GET_MODE (reg), GET_MODE (reg), - volatile_p, ADDRESSOF_REGNO (r), used_p, ht); + put_reg_into_stack (0, reg, type, GET_MODE (reg), ADDRESSOF_REGNO (r), + volatile_p, used_p, 0, ht); } /* List of replacements made below in purge_addressof_1 when creating @@ -3045,10 +3089,7 @@ purge_addressof_1 (loc, insn, force, store, ht) rtx sub, insns; if (GET_CODE (XEXP (x, 0)) != MEM) - { - put_addressof_into_stack (x, ht); - return true; - } + put_addressof_into_stack (x, ht); /* We must create a copy of the rtx because it was created by overwriting a REG rtx which is always shared. */ @@ -3151,10 +3192,16 @@ purge_addressof_1 (loc, insn, force, store, ht) size_x = GET_MODE_BITSIZE (GET_MODE (x)); size_sub = GET_MODE_BITSIZE (GET_MODE (sub)); + /* Do not frob unchanging MEMs. If a later reference forces the + pseudo to the stack, we can wind up with multiple writes to + an unchanging memory, which is invalid. */ + if (RTX_UNCHANGING_P (x) && size_x != size_sub) + ; + /* Don't even consider working with paradoxical subregs, or the moral equivalent seen here. */ - if (size_x <= size_sub - && int_mode_for_mode (GET_MODE (sub)) != BLKmode) + else if (size_x <= size_sub + && int_mode_for_mode (GET_MODE (sub)) != BLKmode) { /* Do a bitfield insertion to mirror what would happen in memory. */ @@ -3562,11 +3609,19 @@ instantiate_virtual_regs (fndecl, insns) || GET_CODE (insn) == CALL_INSN) { instantiate_virtual_regs_1 (&PATTERN (insn), insn, 1); + if (INSN_DELETED_P (insn)) + continue; instantiate_virtual_regs_1 (®_NOTES (insn), NULL_RTX, 0); /* Instantiate any virtual registers in CALL_INSN_FUNCTION_USAGE. */ if (GET_CODE (insn) == CALL_INSN) instantiate_virtual_regs_1 (&CALL_INSN_FUNCTION_USAGE (insn), NULL_RTX, 0); + + /* Past this point all ASM statements should match. Verify that + to avoid failures later in the compilation process. */ + if (asm_noperands (PATTERN (insn)) >= 0 + && ! check_asm_operands (PATTERN (insn))) + instantiate_virtual_regs_lossage (insn); } /* Instantiate the stack slots for the parm registers, for later use in @@ -3734,6 +3789,22 @@ instantiate_new_reg (x, poffset) return new; } + +/* Called when instantiate_virtual_regs has failed to update the instruction. + Usually this means that non-matching instruction has been emit, however for + asm statements it may be the problem in the constraints. */ +static void +instantiate_virtual_regs_lossage (insn) + rtx insn; +{ + if (asm_noperands (PATTERN (insn)) >= 0) + { + error_for_asm (insn, "impossible constraint in `asm'"); + delete_insn (insn); + } + else + abort (); +} /* Given a pointer to a piece of rtx and an optional pointer to the containing object, instantiate any virtual registers present in it. @@ -3770,6 +3841,10 @@ instantiate_virtual_regs_1 (loc, object, extra_insns) if (x == 0) return 1; + /* We may have detected and deleted invalid asm statements. */ + if (object && INSN_P (object) && INSN_DELETED_P (object)) + return 1; + code = GET_CODE (x); /* Check for some special cases. */ @@ -3807,7 +3882,10 @@ instantiate_virtual_regs_1 (loc, object, extra_insns) /* The only valid sources here are PLUS or REG. Just do the simplest possible thing to handle them. */ if (GET_CODE (src) != REG && GET_CODE (src) != PLUS) - abort (); + { + instantiate_virtual_regs_lossage (object); + return 1; + } start_sequence (); if (GET_CODE (src) != REG) @@ -3823,7 +3901,7 @@ instantiate_virtual_regs_1 (loc, object, extra_insns) if (! validate_change (object, &SET_SRC (x), temp, 0) || ! extra_insns) - abort (); + instantiate_virtual_regs_lossage (object); return 1; } @@ -3933,7 +4011,10 @@ instantiate_virtual_regs_1 (loc, object, extra_insns) emit_insn_before (seq, object); if (! validate_change (object, loc, temp, 0) && ! validate_replace_rtx (x, temp, object)) - abort (); + { + instantiate_virtual_regs_lossage (object); + return 1; + } } } @@ -4087,7 +4168,7 @@ instantiate_virtual_regs_1 (loc, object, extra_insns) emit_insn_before (seq, object); if (! validate_change (object, loc, temp, 0) && ! validate_replace_rtx (x, temp, object)) - abort (); + instantiate_virtual_regs_lossage (object); } } @@ -4539,6 +4620,12 @@ assign_parms (fndecl) if (nregs > 0) { +#if defined (REG_PARM_STACK_SPACE) && !defined (MAYBE_REG_PARM_STACK_SPACE) + /* When REG_PARM_STACK_SPACE is nonzero, stack space for + split parameters was allocated by our caller, so we + won't be pushing it in the prolog. */ + if (REG_PARM_STACK_SPACE (fndecl) == 0) +#endif current_function_pretend_args_size = (((nregs * UNITS_PER_WORD) + (PARM_BOUNDARY / BITS_PER_UNIT) - 1) / (PARM_BOUNDARY / BITS_PER_UNIT) @@ -4989,7 +5076,7 @@ assign_parms (fndecl) stack. So, we go back to that sequence, just so that the fixups will happen. */ push_to_sequence (conversion_insns); - put_var_into_stack (parm); + put_var_into_stack (parm, /*rescan=*/true); conversion_insns = get_insns (); end_sequence (); } @@ -5525,7 +5612,7 @@ setjmp_protect (block) || #endif ! DECL_REGISTER (decl))) - put_var_into_stack (decl); + put_var_into_stack (decl, /*rescan=*/true); for (sub = BLOCK_SUBBLOCKS (block); sub; sub = TREE_CHAIN (sub)) setjmp_protect (sub); } @@ -5552,7 +5639,7 @@ setjmp_protect_args () || #endif ! DECL_REGISTER (decl))) - put_var_into_stack (decl); + put_var_into_stack (decl, /*rescan=*/true); } /* Return the context-pointer register corresponding to DECL, @@ -6824,6 +6911,7 @@ expand_function_end (filename, line, end_bindings) emit_block_move (blktramp, initial_trampoline, GEN_INT (TRAMPOLINE_SIZE), BLOCK_OP_NORMAL); #endif + trampolines_created = 1; INITIALIZE_TRAMPOLINE (tramp, XEXP (DECL_RTL (function), 0), context); seq = get_insns (); end_sequence (); @@ -6884,6 +6972,14 @@ expand_function_end (filename, line, end_bindings) clear_pending_stack_adjust (); do_pending_stack_adjust (); + /* ??? This is a kludge. We want to ensure that instructions that + may trap are not moved into the epilogue by scheduling, because + we don't always emit unwind information for the epilogue. + However, not all machine descriptions define a blockage insn, so + emit an ASM_INPUT to act as one. */ + if (flag_non_call_exceptions) + emit_insn (gen_rtx_ASM_INPUT (VOIDmode, "")); + /* Mark the end of the function body. If control reaches this insn, the function can drop through without returning a value. */ @@ -7646,19 +7742,8 @@ thread_prologue_and_epilogue_insns (f) that with a conditional return instruction. */ else if (condjump_p (jump)) { - rtx ret, *loc; - - ret = SET_SRC (PATTERN (jump)); - if (GET_CODE (XEXP (ret, 1)) == LABEL_REF) - loc = &XEXP (ret, 1); - else - loc = &XEXP (ret, 2); - ret = gen_rtx_RETURN (VOIDmode); - - if (! validate_change (jump, loc, ret, 0)) + if (! redirect_jump (jump, 0, 0)) continue; - if (JUMP_LABEL (jump)) - LABEL_NUSES (JUMP_LABEL (jump))--; /* If this block has only one successor, it both jumps and falls through to the fallthru block, so we can't |