X-Git-Url: http://git.sourceforge.jp/view?a=blobdiff_plain;f=gcc%2Fexplow.c;h=1f66f3e770538fae8f716223adaec3548f1474b5;hb=41ad616dd2b78a0c53a488448d00ad7a20c35153;hp=8d5114397c979706054bcdcd50e9d067451a1f57;hpb=6ef828f9e997f2a593360aa6ce158d9f5ae739a0;p=pf3gnuchains%2Fgcc-fork.git diff --git a/gcc/explow.c b/gcc/explow.c index 8d5114397c9..1f66f3e7705 100644 --- a/gcc/explow.c +++ b/gcc/explow.c @@ -1,12 +1,13 @@ /* Subroutines for manipulating rtx's in semantically interesting ways. Copyright (C) 1987, 1991, 1994, 1995, 1996, 1997, 1998, - 1999, 2000, 2001, 2002 Free Software Foundation, Inc. + 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 + Free Software Foundation, Inc. This file is part of GCC. GCC is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free -Software Foundation; either version 2, or (at your option) any later +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 @@ -15,18 +16,20 @@ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License -along with GCC; see the file COPYING. If not, write to the Free -Software Foundation, 59 Temple Place - Suite 330, Boston, MA -02111-1307, USA. */ +along with GCC; see the file COPYING3. If not see +. */ #include "config.h" #include "system.h" +#include "coretypes.h" +#include "tm.h" #include "toplev.h" #include "rtl.h" #include "tree.h" #include "tm_p.h" #include "flags.h" +#include "except.h" #include "function.h" #include "expr.h" #include "optabs.h" @@ -35,23 +38,22 @@ Software Foundation, 59 Temple Place - Suite 330, Boston, MA #include "ggc.h" #include "recog.h" #include "langhooks.h" +#include "target.h" +#include "output.h" -static rtx break_out_memory_refs PARAMS ((rtx)); -static void emit_stack_probe PARAMS ((rtx)); +static rtx break_out_memory_refs (rtx); +static void emit_stack_probe (rtx); /* Truncate and perhaps sign-extend C as appropriate for MODE. */ HOST_WIDE_INT -trunc_int_for_mode (c, mode) - HOST_WIDE_INT c; - enum machine_mode mode; +trunc_int_for_mode (HOST_WIDE_INT c, enum machine_mode mode) { int width = GET_MODE_BITSIZE (mode); /* You want to truncate to a _what_? */ - if (! SCALAR_INT_MODE_P (mode)) - abort (); + gcc_assert (SCALAR_INT_MODE_P (mode)); /* Canonicalize BImode to 0 and STORE_FLAG_VALUE. */ if (mode == BImode) @@ -71,14 +73,10 @@ trunc_int_for_mode (c, mode) return c; } -/* Return an rtx for the sum of X and the integer C. - - This function should be used via the `plus_constant' macro. */ +/* Return an rtx for the sum of X and the integer C. */ rtx -plus_constant_wide (x, c) - rtx x; - HOST_WIDE_INT c; +plus_constant (rtx x, HOST_WIDE_INT c) { RTX_CODE code; rtx y; @@ -155,7 +153,7 @@ plus_constant_wide (x, c) We may not immediately return from the recursive call here, lest all_constant gets lost. */ - if (GET_CODE (XEXP (x, 1)) == CONST_INT) + if (CONST_INT_P (XEXP (x, 1))) { c += INTVAL (XEXP (x, 1)); @@ -204,9 +202,7 @@ plus_constant_wide (x, c) it is not isomorphic to X. */ rtx -eliminate_constant_term (x, constptr) - rtx x; - rtx *constptr; +eliminate_constant_term (rtx x, rtx *constptr) { rtx x0, x1; rtx tem; @@ -215,10 +211,10 @@ eliminate_constant_term (x, constptr) return x; /* First handle constants appearing at this level explicitly. */ - if (GET_CODE (XEXP (x, 1)) == CONST_INT + if (CONST_INT_P (XEXP (x, 1)) && 0 != (tem = simplify_binary_operation (PLUS, GET_MODE (x), *constptr, XEXP (x, 1))) - && GET_CODE (tem) == CONST_INT) + && CONST_INT_P (tem)) { *constptr = tem; return eliminate_constant_term (XEXP (x, 0), constptr); @@ -230,7 +226,7 @@ eliminate_constant_term (x, constptr) if ((x1 != XEXP (x, 1) || x0 != XEXP (x, 0)) && 0 != (tem = simplify_binary_operation (PLUS, GET_MODE (x), *constptr, tem)) - && GET_CODE (tem) == CONST_INT) + && CONST_INT_P (tem)) { *constptr = tem; return gen_rtx_PLUS (GET_MODE (x), x0, x1); @@ -239,84 +235,45 @@ eliminate_constant_term (x, constptr) return x; } -/* Returns the insn that next references REG after INSN, or 0 - if REG is clobbered before next referenced or we cannot find - an insn that references REG in a straight-line piece of code. */ +/* Return an rtx for the size in bytes of the value of EXP. */ rtx -find_next_ref (reg, insn) - rtx reg; - rtx insn; +expr_size (tree exp) { - rtx next; + tree size; - for (insn = NEXT_INSN (insn); insn; insn = next) + if (TREE_CODE (exp) == WITH_SIZE_EXPR) + size = TREE_OPERAND (exp, 1); + else { - next = NEXT_INSN (insn); - if (GET_CODE (insn) == NOTE) - continue; - if (GET_CODE (insn) == CODE_LABEL - || GET_CODE (insn) == BARRIER) - return 0; - if (GET_CODE (insn) == INSN - || GET_CODE (insn) == JUMP_INSN - || GET_CODE (insn) == CALL_INSN) - { - if (reg_set_p (reg, insn)) - return 0; - if (reg_mentioned_p (reg, PATTERN (insn))) - return insn; - if (GET_CODE (insn) == JUMP_INSN) - { - if (any_uncondjump_p (insn)) - next = JUMP_LABEL (insn); - else - return 0; - } - if (GET_CODE (insn) == CALL_INSN - && REGNO (reg) < FIRST_PSEUDO_REGISTER - && call_used_regs[REGNO (reg)]) - return 0; - } - else - abort (); + size = tree_expr_size (exp); + gcc_assert (size); + gcc_assert (size == SUBSTITUTE_PLACEHOLDER_IN_EXPR (size, exp)); } - return 0; -} - -/* Return an rtx for the size in bytes of the value of EXP. */ -rtx -expr_size (exp) - tree exp; -{ - tree size = (*lang_hooks.expr_size) (exp); - - if (TREE_CODE (size) != INTEGER_CST - && contains_placeholder_p (size)) - size = build (WITH_RECORD_EXPR, sizetype, size, exp); - - return expand_expr (size, NULL_RTX, TYPE_MODE (sizetype), 0); + return expand_expr (size, NULL_RTX, TYPE_MODE (sizetype), EXPAND_NORMAL); } /* Return a wide integer for the size in bytes of the value of EXP, or -1 if the size can vary or is larger than an integer. */ HOST_WIDE_INT -int_expr_size (exp) - tree exp; +int_expr_size (tree exp) { - tree t = (*lang_hooks.expr_size) (exp); - - if (t == 0 - || TREE_CODE (t) != INTEGER_CST - || TREE_OVERFLOW (t) - || TREE_INT_CST_HIGH (t) != 0 - /* If the result would appear negative, it's too big to represent. */ - || (HOST_WIDE_INT) TREE_INT_CST_LOW (t) < 0) + tree size; + + if (TREE_CODE (exp) == WITH_SIZE_EXPR) + size = TREE_OPERAND (exp, 1); + else + { + size = tree_expr_size (exp); + gcc_assert (size); + } + + if (size == 0 || !host_integerp (size, 0)) return -1; - return TREE_INT_CST_LOW (t); + return tree_low_cst (size, 0); } /* Return a copy of X in which all memory references @@ -336,10 +293,9 @@ int_expr_size (exp) Values returned by expand_expr with 1 for sum_ok fit this constraint. */ static rtx -break_out_memory_refs (x) - rtx x; +break_out_memory_refs (rtx x) { - if (GET_CODE (x) == MEM + if (MEM_P (x) || (CONSTANT_P (x) && CONSTANT_ADDRESS_P (x) && GET_MODE (x) != VOIDmode)) x = force_reg (GET_MODE (x), x); @@ -350,29 +306,38 @@ break_out_memory_refs (x) rtx op1 = break_out_memory_refs (XEXP (x, 1)); if (op0 != XEXP (x, 0) || op1 != XEXP (x, 1)) - x = gen_rtx_fmt_ee (GET_CODE (x), Pmode, op0, op1); + x = simplify_gen_binary (GET_CODE (x), GET_MODE (x), op0, op1); } return x; } -#ifdef POINTERS_EXTEND_UNSIGNED - -/* Given X, a memory address in ptr_mode, convert it to an address - in Pmode, or vice versa (TO_MODE says which way). We take advantage of - the fact that pointers are not allowed to overflow by commuting arithmetic - operations over conversions so that address arithmetic insns can be - used. */ +/* Given X, a memory address in address space AS' pointer mode, convert it to + an address in the address space's address mode, or vice versa (TO_MODE says + which way). We take advantage of the fact that pointers are not allowed to + overflow by commuting arithmetic operations over conversions so that address + arithmetic insns can be used. */ rtx -convert_memory_address (to_mode, x) - enum machine_mode to_mode; - rtx x; +convert_memory_address_addr_space (enum machine_mode to_mode ATTRIBUTE_UNUSED, + rtx x, addr_space_t as ATTRIBUTE_UNUSED) { - enum machine_mode from_mode = to_mode == ptr_mode ? Pmode : ptr_mode; +#ifndef POINTERS_EXTEND_UNSIGNED + gcc_assert (GET_MODE (x) == to_mode || GET_MODE (x) == VOIDmode); + return x; +#else /* defined(POINTERS_EXTEND_UNSIGNED) */ + enum machine_mode pointer_mode, address_mode, from_mode; rtx temp; enum rtx_code code; + /* If X already has the right mode, just return it. */ + if (GET_MODE (x) == to_mode) + return x; + + pointer_mode = targetm.addr_space.pointer_mode (as); + address_mode = targetm.addr_space.address_mode (as); + from_mode = to_mode == pointer_mode ? address_mode : pointer_mode; + /* Here we handle some special cases. If none of them apply, fall through to the default case. */ switch (GET_CODE (x)) @@ -405,30 +370,34 @@ convert_memory_address (to_mode, x) break; case SYMBOL_REF: - temp = gen_rtx_SYMBOL_REF (to_mode, XSTR (x, 0)); - SYMBOL_REF_FLAG (temp) = SYMBOL_REF_FLAG (x); - CONSTANT_POOL_ADDRESS_P (temp) = CONSTANT_POOL_ADDRESS_P (x); - STRING_POOL_ADDRESS_P (temp) = STRING_POOL_ADDRESS_P (x); + temp = shallow_copy_rtx (x); + PUT_MODE (temp, to_mode); return temp; break; case CONST: return gen_rtx_CONST (to_mode, - convert_memory_address (to_mode, XEXP (x, 0))); + convert_memory_address_addr_space + (to_mode, XEXP (x, 0), as)); break; case PLUS: case MULT: /* For addition we can safely permute the conversion and addition operation if one operand is a constant and converting the constant - does not change it. We can always safely permute them if we are - making the address narrower. */ + does not change it or if one operand is a constant and we are + using a ptr_extend instruction (POINTERS_EXTEND_UNSIGNED < 0). + We can always safely permute them if we are making the address + narrower. */ if (GET_MODE_SIZE (to_mode) < GET_MODE_SIZE (from_mode) || (GET_CODE (x) == PLUS - && GET_CODE (XEXP (x, 1)) == CONST_INT - && XEXP (x, 1) == convert_memory_address (to_mode, XEXP (x, 1)))) + && CONST_INT_P (XEXP (x, 1)) + && (XEXP (x, 1) == convert_memory_address_addr_space + (to_mode, XEXP (x, 1), as) + || POINTERS_EXTEND_UNSIGNED < 0))) return gen_rtx_fmt_ee (GET_CODE (x), to_mode, - convert_memory_address (to_mode, XEXP (x, 0)), + convert_memory_address_addr_space + (to_mode, XEXP (x, 0), as), XEXP (x, 1)); break; @@ -438,79 +407,25 @@ convert_memory_address (to_mode, x) return convert_modes (to_mode, from_mode, x, POINTERS_EXTEND_UNSIGNED); -} -#endif - -/* Given a memory address or facsimile X, construct a new address, - currently equivalent, that is stable: future stores won't change it. - - X must be composed of constants, register and memory references - combined with addition, subtraction and multiplication: - in other words, just what you can get from expand_expr if sum_ok is 1. - - Works by making copies of all regs and memory locations used - by X and combining them the same way X does. - You could also stabilize the reference to this address - by copying the address to a register with copy_to_reg; - but then you wouldn't get indexed addressing in the reference. */ - -rtx -copy_all_regs (x) - rtx x; -{ - if (GET_CODE (x) == REG) - { - if (REGNO (x) != FRAME_POINTER_REGNUM -#if HARD_FRAME_POINTER_REGNUM != FRAME_POINTER_REGNUM - && REGNO (x) != HARD_FRAME_POINTER_REGNUM -#endif - ) - x = copy_to_reg (x); - } - else if (GET_CODE (x) == MEM) - x = copy_to_reg (x); - else if (GET_CODE (x) == PLUS || GET_CODE (x) == MINUS - || GET_CODE (x) == MULT) - { - rtx op0 = copy_all_regs (XEXP (x, 0)); - rtx op1 = copy_all_regs (XEXP (x, 1)); - if (op0 != XEXP (x, 0) || op1 != XEXP (x, 1)) - x = gen_rtx_fmt_ee (GET_CODE (x), Pmode, op0, op1); - } - return x; +#endif /* defined(POINTERS_EXTEND_UNSIGNED) */ } -/* Return something equivalent to X but valid as a memory address - for something of mode MODE. When X is not itself valid, this - works by copying X or subexpressions of it into registers. */ +/* Return something equivalent to X but valid as a memory address for something + of mode MODE in the named address space AS. When X is not itself valid, + this works by copying X or subexpressions of it into registers. */ rtx -memory_address (mode, x) - enum machine_mode mode; - rtx x; +memory_address_addr_space (enum machine_mode mode, rtx x, addr_space_t as) { rtx oldx = x; + enum machine_mode address_mode = targetm.addr_space.address_mode (as); - if (GET_CODE (x) == ADDRESSOF) - return x; + x = convert_memory_address_addr_space (address_mode, x, as); -#ifdef POINTERS_EXTEND_UNSIGNED - if (GET_MODE (x) != Pmode) - x = convert_memory_address (Pmode, x); -#endif - - /* By passing constant addresses thru registers + /* By passing constant addresses through registers we get a chance to cse them. */ if (! cse_not_expected && CONSTANT_P (x) && CONSTANT_ADDRESS_P (x)) - x = force_reg (Pmode, x); - - /* Accept a QUEUED that refers to a REG - even though that isn't a valid address. - On attempting to put this in an insn we will call protect_from_queue - which will turn it into a REG, which is valid. */ - else if (GET_CODE (x) == QUEUED - && GET_CODE (QUEUED_VAR (x)) == REG) - ; + x = force_reg (address_mode, x); /* We get better cse by rejecting indirect addressing at this stage. Let the combiner create indirect addresses where appropriate. @@ -518,22 +433,31 @@ memory_address (mode, x) are visible. But not if cse won't be done! */ else { - if (! cse_not_expected && GET_CODE (x) != REG) + if (! cse_not_expected && !REG_P (x)) x = break_out_memory_refs (x); /* At this point, any valid address is accepted. */ - GO_IF_LEGITIMATE_ADDRESS (mode, x, win); + if (memory_address_addr_space_p (mode, x, as)) + goto done; /* If it was valid before but breaking out memory refs invalidated it, use it the old way. */ - if (memory_address_p (mode, oldx)) - goto win2; + if (memory_address_addr_space_p (mode, oldx, as)) + { + x = oldx; + goto done; + } /* Perform machine-dependent transformations on X in certain cases. This is not necessary since the code below can handle all possible cases, but machine-dependent transformations can make better code. */ - LEGITIMIZE_ADDRESS (x, oldx, mode, win); + { + rtx orig_x = x; + x = targetm.addr_space.legitimize_address (x, oldx, mode, as); + if (orig_x != x && memory_address_addr_space_p (mode, x, as)) + goto done; + } /* PLUS and MULT can appear in special ways as the result of attempts to make an address usable for indexing. @@ -549,12 +473,12 @@ memory_address (mode, x) rtx constant_term = const0_rtx; rtx y = eliminate_constant_term (x, &constant_term); if (constant_term == const0_rtx - || ! memory_address_p (mode, y)) + || ! memory_address_addr_space_p (mode, y, as)) x = force_operand (x, NULL_RTX); else { y = gen_rtx_PLUS (GET_MODE (x), copy_to_reg (y), constant_term); - if (! memory_address_p (mode, y)) + if (! memory_address_addr_space_p (mode, y, as)) x = force_operand (x, NULL_RTX); else x = y; @@ -566,43 +490,27 @@ memory_address (mode, x) /* If we have a register that's an invalid address, it must be a hard reg of the wrong class. Copy it to a pseudo. */ - else if (GET_CODE (x) == REG) + else if (REG_P (x)) x = copy_to_reg (x); /* Last resort: copy the value to a register, since the register is a valid address. */ else - x = force_reg (Pmode, x); - - goto done; - - win2: - x = oldx; - win: - if (flag_force_addr && ! cse_not_expected && GET_CODE (x) != REG - /* Don't copy an addr via a reg if it is one of our stack slots. */ - && ! (GET_CODE (x) == PLUS - && (XEXP (x, 0) == virtual_stack_vars_rtx - || XEXP (x, 0) == virtual_incoming_args_rtx))) - { - if (general_operand (x, Pmode)) - x = force_reg (Pmode, x); - else - x = force_operand (x, NULL_RTX); - } + x = force_reg (address_mode, x); } done: + gcc_assert (memory_address_addr_space_p (mode, x, as)); /* If we didn't change the address, we are done. Otherwise, mark a reg as a pointer if we have REG or REG + CONST_INT. */ if (oldx == x) return x; - else if (GET_CODE (x) == REG) + else if (REG_P (x)) mark_reg_pointer (x, BITS_PER_UNIT); else if (GET_CODE (x) == PLUS - && GET_CODE (XEXP (x, 0)) == REG - && GET_CODE (XEXP (x, 1)) == CONST_INT) + && REG_P (XEXP (x, 0)) + && CONST_INT_P (XEXP (x, 1))) mark_reg_pointer (XEXP (x, 0), BITS_PER_UNIT); /* OLDX may have been the address on a temporary. Update the address @@ -612,83 +520,82 @@ memory_address (mode, x) return x; } -/* Like `memory_address' but pretend `flag_force_addr' is 0. */ - -rtx -memory_address_noforce (mode, x) - enum machine_mode mode; - rtx x; -{ - int ambient_force_addr = flag_force_addr; - rtx val; - - flag_force_addr = 0; - val = memory_address (mode, x); - flag_force_addr = ambient_force_addr; - return val; -} - /* Convert a mem ref into one with a valid memory address. Pass through anything else unchanged. */ rtx -validize_mem (ref) - rtx ref; +validize_mem (rtx ref) { - if (GET_CODE (ref) != MEM) + if (!MEM_P (ref)) return ref; - if (! (flag_force_addr && CONSTANT_ADDRESS_P (XEXP (ref, 0))) - && memory_address_p (GET_MODE (ref), XEXP (ref, 0))) + ref = use_anchored_address (ref); + if (memory_address_addr_space_p (GET_MODE (ref), XEXP (ref, 0), + MEM_ADDR_SPACE (ref))) return ref; /* Don't alter REF itself, since that is probably a stack slot. */ return replace_equiv_address (ref, XEXP (ref, 0)); } - -/* Given REF, either a MEM or a REG, and T, either the type of X or - the expression corresponding to REF, set RTX_UNCHANGING_P if - appropriate. */ -void -maybe_set_unchanging (ref, t) - rtx ref; - tree t; -{ - /* We can set RTX_UNCHANGING_P from TREE_READONLY for decls whose - initialization is only executed once, or whose initializer always - has the same value. Currently we simplify this to PARM_DECLs in the - first case, and decls with TREE_CONSTANT initializers in the second. */ - if ((TREE_READONLY (t) && DECL_P (t) - && (TREE_CODE (t) == PARM_DECL - || DECL_INITIAL (t) == NULL_TREE - || TREE_CONSTANT (DECL_INITIAL (t)))) - || TREE_CODE_CLASS (TREE_CODE (t)) == 'c') - RTX_UNCHANGING_P (ref) = 1; -} - -/* Return a modified copy of X with its memory address copied - into a temporary register to protect it from side effects. - If X is not a MEM, it is returned unchanged (and not copied). - Perhaps even if it is a MEM, if there is no need to change it. */ +/* If X is a memory reference to a member of an object block, try rewriting + it to use an anchor instead. Return the new memory reference on success + and the old one on failure. */ rtx -stabilize (x) - rtx x; +use_anchored_address (rtx x) { + rtx base; + HOST_WIDE_INT offset; + + if (!flag_section_anchors) + return x; - if (GET_CODE (x) != MEM - || ! rtx_unstable_p (XEXP (x, 0))) + if (!MEM_P (x)) return x; - return - replace_equiv_address (x, force_reg (Pmode, copy_all_regs (XEXP (x, 0)))); + /* Split the address into a base and offset. */ + base = XEXP (x, 0); + offset = 0; + if (GET_CODE (base) == CONST + && GET_CODE (XEXP (base, 0)) == PLUS + && CONST_INT_P (XEXP (XEXP (base, 0), 1))) + { + offset += INTVAL (XEXP (XEXP (base, 0), 1)); + base = XEXP (XEXP (base, 0), 0); + } + + /* Check whether BASE is suitable for anchors. */ + if (GET_CODE (base) != SYMBOL_REF + || !SYMBOL_REF_HAS_BLOCK_INFO_P (base) + || SYMBOL_REF_ANCHOR_P (base) + || SYMBOL_REF_BLOCK (base) == NULL + || !targetm.use_anchors_for_symbol_p (base)) + return x; + + /* Decide where BASE is going to be. */ + place_block_symbol (base); + + /* Get the anchor we need to use. */ + offset += SYMBOL_REF_BLOCK_OFFSET (base); + base = get_section_anchor (SYMBOL_REF_BLOCK (base), offset, + SYMBOL_REF_TLS_MODEL (base)); + + /* Work out the offset from the anchor. */ + offset -= SYMBOL_REF_BLOCK_OFFSET (base); + + /* If we're going to run a CSE pass, force the anchor into a register. + We will then be able to reuse registers for several accesses, if the + target costs say that that's worthwhile. */ + if (!cse_not_expected) + base = force_reg (GET_MODE (base), base); + + return replace_equiv_address (x, plus_constant (base, offset)); } /* Copy the value or contents of X to a new temp reg and return that reg. */ rtx -copy_to_reg (x) - rtx x; +copy_to_reg (rtx x) { rtx temp = gen_reg_rtx (GET_MODE (x)); @@ -707,8 +614,7 @@ copy_to_reg (x) in case X is a constant. */ rtx -copy_addr_to_reg (x) - rtx x; +copy_addr_to_reg (rtx x) { return copy_to_mode_reg (Pmode, x); } @@ -717,9 +623,7 @@ copy_addr_to_reg (x) in case X is a constant. */ rtx -copy_to_mode_reg (mode, x) - enum machine_mode mode; - rtx x; +copy_to_mode_reg (enum machine_mode mode, rtx x) { rtx temp = gen_reg_rtx (mode); @@ -728,8 +632,7 @@ copy_to_mode_reg (mode, x) if (! general_operand (x, VOIDmode)) x = force_operand (x, temp); - if (GET_MODE (x) != mode && GET_MODE (x) != VOIDmode) - abort (); + gcc_assert (GET_MODE (x) == mode || GET_MODE (x) == VOIDmode); if (x != temp) emit_move_insn (temp, x); return temp; @@ -744,13 +647,11 @@ copy_to_mode_reg (mode, x) since we mark it as a "constant" register. */ rtx -force_reg (mode, x) - enum machine_mode mode; - rtx x; +force_reg (enum machine_mode mode, rtx x) { rtx temp, insn, set; - if (GET_CODE (x) == REG) + if (REG_P (x)) return x; if (general_operand (x, mode)) @@ -761,7 +662,7 @@ force_reg (mode, x) else { temp = force_operand (x, NULL_RTX); - if (GET_CODE (temp) == REG) + if (REG_P (temp)) insn = get_last_insn (); else { @@ -776,9 +677,44 @@ force_reg (mode, x) if INSN set something else (such as a SUBREG of TEMP). */ if (CONSTANT_P (x) && (set = single_set (insn)) != 0 - && SET_DEST (set) == temp) + && SET_DEST (set) == temp + && ! rtx_equal_p (x, SET_SRC (set))) set_unique_reg_note (insn, REG_EQUAL, x); + /* Let optimizers know that TEMP is a pointer, and if so, the + known alignment of that pointer. */ + { + unsigned align = 0; + if (GET_CODE (x) == SYMBOL_REF) + { + align = BITS_PER_UNIT; + if (SYMBOL_REF_DECL (x) && DECL_P (SYMBOL_REF_DECL (x))) + align = DECL_ALIGN (SYMBOL_REF_DECL (x)); + } + else if (GET_CODE (x) == LABEL_REF) + align = BITS_PER_UNIT; + else if (GET_CODE (x) == CONST + && GET_CODE (XEXP (x, 0)) == PLUS + && GET_CODE (XEXP (XEXP (x, 0), 0)) == SYMBOL_REF + && CONST_INT_P (XEXP (XEXP (x, 0), 1))) + { + rtx s = XEXP (XEXP (x, 0), 0); + rtx c = XEXP (XEXP (x, 0), 1); + unsigned sa, ca; + + sa = BITS_PER_UNIT; + if (SYMBOL_REF_DECL (s) && DECL_P (SYMBOL_REF_DECL (s))) + sa = DECL_ALIGN (SYMBOL_REF_DECL (s)); + + ca = exact_log2 (INTVAL (c) & -INTVAL (c)) * BITS_PER_UNIT; + + align = MIN (sa, ca); + } + + if (align || (MEM_P (x) && MEM_POINTER (x))) + mark_reg_pointer (temp, align); + } + return temp; } @@ -786,15 +722,18 @@ force_reg (mode, x) that reg. Otherwise, return X. */ rtx -force_not_mem (x) - rtx x; +force_not_mem (rtx x) { rtx temp; - if (GET_CODE (x) != MEM || GET_MODE (x) == BLKmode) + if (!MEM_P (x) || GET_MODE (x) == BLKmode) return x; temp = gen_reg_rtx (GET_MODE (x)); + + if (MEM_POINTER (x)) + REG_POINTER (temp) = 1; + emit_move_insn (temp, x); return temp; } @@ -804,13 +743,11 @@ force_not_mem (x) MODE is the mode to use for X in case it is a constant. */ rtx -copy_to_suggested_reg (x, target, mode) - rtx x, target; - enum machine_mode mode; +copy_to_suggested_reg (rtx x, rtx target, enum machine_mode mode) { rtx temp; - if (target && GET_CODE (target) == REG) + if (target && REG_P (target)) temp = target; else temp = gen_reg_rtx (mode); @@ -819,68 +756,110 @@ copy_to_suggested_reg (x, target, mode) return temp; } -/* Return the mode to use to store a scalar of TYPE and MODE. +/* Return the mode to use to pass or return a scalar of TYPE and MODE. PUNSIGNEDP points to the signedness of the type and may be adjusted to show what signedness to use on extension operations. - FOR_CALL is nonzero if this call is promoting args for a call. */ + FOR_RETURN is nonzero if the caller is promoting the return value + of FNDECL, else it is for promoting args. */ enum machine_mode -promote_mode (type, mode, punsignedp, for_call) - tree type; - enum machine_mode mode; - int *punsignedp; - int for_call ATTRIBUTE_UNUSED; +promote_function_mode (const_tree type, enum machine_mode mode, int *punsignedp, + const_tree funtype, int for_return) { - enum tree_code code = TREE_CODE (type); - int unsignedp = *punsignedp; + switch (TREE_CODE (type)) + { + case INTEGER_TYPE: case ENUMERAL_TYPE: case BOOLEAN_TYPE: + case REAL_TYPE: case OFFSET_TYPE: case FIXED_POINT_TYPE: + case POINTER_TYPE: case REFERENCE_TYPE: + return targetm.calls.promote_function_mode (type, mode, punsignedp, funtype, + for_return); -#ifdef PROMOTE_FOR_CALL_ONLY - if (! for_call) - return mode; -#endif + default: + return mode; + } +} +/* Return the mode to use to store a scalar of TYPE and MODE. + PUNSIGNEDP points to the signedness of the type and may be adjusted + to show what signedness to use on extension operations. */ + +enum machine_mode +promote_mode (const_tree type ATTRIBUTE_UNUSED, enum machine_mode mode, + int *punsignedp ATTRIBUTE_UNUSED) +{ + /* FIXME: this is the same logic that was there until GCC 4.4, but we + probably want to test POINTERS_EXTEND_UNSIGNED even if PROMOTE_MODE + is not defined. The affected targets are M32C, S390, SPARC. */ +#ifdef PROMOTE_MODE + const enum tree_code code = TREE_CODE (type); + int unsignedp = *punsignedp; switch (code) { -#ifdef PROMOTE_MODE case INTEGER_TYPE: case ENUMERAL_TYPE: case BOOLEAN_TYPE: - case CHAR_TYPE: case REAL_TYPE: case OFFSET_TYPE: + case REAL_TYPE: case OFFSET_TYPE: case FIXED_POINT_TYPE: PROMOTE_MODE (mode, unsignedp, type); + *punsignedp = unsignedp; + return mode; break; -#endif #ifdef POINTERS_EXTEND_UNSIGNED case REFERENCE_TYPE: case POINTER_TYPE: - mode = Pmode; - unsignedp = POINTERS_EXTEND_UNSIGNED; + *punsignedp = POINTERS_EXTEND_UNSIGNED; + return targetm.addr_space.address_mode + (TYPE_ADDR_SPACE (TREE_TYPE (type))); break; #endif default: - break; + return mode; } - - *punsignedp = unsignedp; +#else return mode; +#endif +} + + +/* Use one of promote_mode or promote_function_mode to find the promoted + mode of DECL. If PUNSIGNEDP is not NULL, store there the unsignedness + of DECL after promotion. */ + +enum machine_mode +promote_decl_mode (const_tree decl, int *punsignedp) +{ + tree type = TREE_TYPE (decl); + int unsignedp = TYPE_UNSIGNED (type); + enum machine_mode mode = DECL_MODE (decl); + enum machine_mode pmode; + + if (TREE_CODE (decl) == RESULT_DECL + || TREE_CODE (decl) == PARM_DECL) + pmode = promote_function_mode (type, mode, &unsignedp, + TREE_TYPE (current_function_decl), 2); + else + pmode = promote_mode (type, mode, &unsignedp); + + if (punsignedp) + *punsignedp = unsignedp; + return pmode; } + /* Adjust the stack pointer by ADJUST (an rtx for a number of bytes). This pops when ADJUST is positive. ADJUST need not be constant. */ void -adjust_stack (adjust) - rtx adjust; +adjust_stack (rtx adjust) { rtx temp; - adjust = protect_from_queue (adjust, 0); if (adjust == const0_rtx) return; /* We expect all variable sized adjustments to be multiple of PREFERRED_STACK_BOUNDARY. */ - if (GET_CODE (adjust) == CONST_INT) + if (CONST_INT_P (adjust)) stack_pointer_delta -= INTVAL (adjust); temp = expand_binop (Pmode, @@ -900,18 +879,16 @@ adjust_stack (adjust) This pushes when ADJUST is positive. ADJUST need not be constant. */ void -anti_adjust_stack (adjust) - rtx adjust; +anti_adjust_stack (rtx adjust) { rtx temp; - adjust = protect_from_queue (adjust, 0); if (adjust == const0_rtx) return; /* We expect all variable sized adjustments to be multiple of PREFERRED_STACK_BOUNDARY. */ - if (GET_CODE (adjust) == CONST_INT) + if (CONST_INT_P (adjust)) stack_pointer_delta += INTVAL (adjust); temp = expand_binop (Pmode, @@ -930,18 +907,20 @@ anti_adjust_stack (adjust) /* Round the size of a block to be pushed up to the boundary required by this machine. SIZE is the desired size, which need not be constant. */ -rtx -round_push (size) - rtx size; +static rtx +round_push (rtx size) { int align = PREFERRED_STACK_BOUNDARY / BITS_PER_UNIT; + if (align == 1) return size; - if (GET_CODE (size) == CONST_INT) + + if (CONST_INT_P (size)) { - int new = (INTVAL (size) + align - 1) / align * align; - if (INTVAL (size) != new) - size = GEN_INT (new); + HOST_WIDE_INT new_size = (INTVAL (size) + align - 1) / align * align; + + if (INTVAL (size) != new_size) + size = GEN_INT (new_size); } else { @@ -954,6 +933,7 @@ round_push (size) NULL_RTX, 1); size = expand_mult (Pmode, size, GEN_INT (align), NULL_RTX, 1); } + return size; } @@ -966,14 +946,11 @@ round_push (size) are emitted at the current position. */ void -emit_stack_save (save_level, psave, after) - enum save_level save_level; - rtx *psave; - rtx after; +emit_stack_save (enum save_level save_level, rtx *psave, rtx after) { rtx sa = *psave; /* The default is that we use a move insn and save in a Pmode object. */ - rtx (*fcn) PARAMS ((rtx, rtx)) = gen_move_insn; + rtx (*fcn) (rtx, rtx) = gen_move_insn; enum machine_mode mode = STACK_SAVEAREA_MODE (save_level); /* See if this machine has anything special to do for this kind of save. */ @@ -1014,17 +991,13 @@ emit_stack_save (save_level, psave, after) *psave = sa = gen_reg_rtx (mode); } } - else - { - if (mode == VOIDmode || GET_MODE (sa) != mode) - abort (); - } if (after) { rtx seq; start_sequence (); + do_pending_stack_adjust (); /* We must validize inside the sequence, to ensure that any instructions created by the validize call also get moved to the right place. */ if (sa != 0) @@ -1036,6 +1009,7 @@ emit_stack_save (save_level, psave, after) } else { + do_pending_stack_adjust (); if (sa != 0) sa = validize_mem (sa); emit_insn (fcn (sa, stack_pointer_rtx)); @@ -1049,13 +1023,10 @@ emit_stack_save (save_level, psave, after) current position. */ void -emit_stack_restore (save_level, sa, after) - enum save_level save_level; - rtx after; - rtx sa; +emit_stack_restore (enum save_level save_level, rtx sa, rtx after) { /* The default is that we use a move insn. */ - rtx (*fcn) PARAMS ((rtx, rtx)) = gen_move_insn; + rtx (*fcn) (rtx, rtx) = gen_move_insn; /* See if this machine has anything special to do for this kind of save. */ switch (save_level) @@ -1088,13 +1059,12 @@ emit_stack_restore (save_level, sa, after) /* These clobbers prevent the scheduler from moving references to variable arrays below the code that deletes (pops) the arrays. */ - emit_insn (gen_rtx_CLOBBER (VOIDmode, - gen_rtx_MEM (BLKmode, - gen_rtx_SCRATCH (VOIDmode)))); - emit_insn (gen_rtx_CLOBBER (VOIDmode, - gen_rtx_MEM (BLKmode, stack_pointer_rtx))); + emit_clobber (gen_rtx_MEM (BLKmode, gen_rtx_SCRATCH (VOIDmode))); + emit_clobber (gen_rtx_MEM (BLKmode, stack_pointer_rtx)); } + discard_pending_stack_adjust (); + if (after) { rtx seq; @@ -1108,87 +1078,28 @@ emit_stack_restore (save_level, sa, after) else emit_insn (fcn (stack_pointer_rtx, sa)); } - -#ifdef SETJMP_VIA_SAVE_AREA -/* Optimize RTL generated by allocate_dynamic_stack_space for targets - where SETJMP_VIA_SAVE_AREA is true. The problem is that on these - platforms, the dynamic stack space used can corrupt the original - frame, thus causing a crash if a longjmp unwinds to it. */ + +/* Invoke emit_stack_save on the nonlocal_goto_save_area for the current + function. This function should be called whenever we allocate or + deallocate dynamic stack space. */ void -optimize_save_area_alloca (insns) - rtx insns; +update_nonlocal_goto_save_area (void) { - rtx insn; - - for (insn = insns; insn; insn = NEXT_INSN(insn)) - { - rtx note; - - if (GET_CODE (insn) != INSN) - continue; - - for (note = REG_NOTES (insn); note; note = XEXP (note, 1)) - { - if (REG_NOTE_KIND (note) != REG_SAVE_AREA) - continue; - - if (!current_function_calls_setjmp) - { - rtx pat = PATTERN (insn); - - /* If we do not see the note in a pattern matching - these precise characteristics, we did something - entirely wrong in allocate_dynamic_stack_space. - - Note, one way this could happen is if SETJMP_VIA_SAVE_AREA - was defined on a machine where stacks grow towards higher - addresses. - - Right now only supported port with stack that grow upward - is the HPPA and it does not define SETJMP_VIA_SAVE_AREA. */ - if (GET_CODE (pat) != SET - || SET_DEST (pat) != stack_pointer_rtx - || GET_CODE (SET_SRC (pat)) != MINUS - || XEXP (SET_SRC (pat), 0) != stack_pointer_rtx) - abort (); - - /* This will now be transformed into a (set REG REG) - so we can just blow away all the other notes. */ - XEXP (SET_SRC (pat), 1) = XEXP (note, 0); - REG_NOTES (insn) = NULL_RTX; - } - else - { - /* setjmp was called, we must remove the REG_SAVE_AREA - note so that later passes do not get confused by its - presence. */ - if (note == REG_NOTES (insn)) - { - REG_NOTES (insn) = XEXP (note, 1); - } - else - { - rtx srch; - - for (srch = REG_NOTES (insn); srch; srch = XEXP (srch, 1)) - if (XEXP (srch, 1) == note) - break; - - if (srch == NULL_RTX) - abort (); - - XEXP (srch, 1) = XEXP (note, 1); - } - } - /* Once we've seen the note of interest, we need not look at - the rest of them. */ - break; - } - } + tree t_save; + rtx r_save; + + /* The nonlocal_goto_save_area object is an array of N pointers. The + first one is used for the frame pointer save; the rest are sized by + STACK_SAVEAREA_MODE. Create a reference to array index 1, the first + of the stack save area slots. */ + t_save = build4 (ARRAY_REF, ptr_type_node, cfun->nonlocal_goto_save_area, + integer_one_node, NULL_TREE, NULL_TREE); + r_save = expand_expr (t_save, NULL_RTX, VOIDmode, EXPAND_WRITE); + + emit_stack_save (SAVE_NONLOCAL, &r_save, NULL_RTX); } -#endif /* SETJMP_VIA_SAVE_AREA */ - + /* Return an rtx representing the address of an area of memory dynamically pushed on the stack. This region of memory is always aligned to a multiple of BIGGEST_ALIGNMENT. @@ -1201,15 +1112,8 @@ optimize_save_area_alloca (insns) KNOWN_ALIGN is the alignment (in bits) that we know SIZE has. */ rtx -allocate_dynamic_stack_space (size, target, known_align) - rtx size; - rtx target; - int known_align; +allocate_dynamic_stack_space (rtx size, rtx target, int known_align) { -#ifdef SETJMP_VIA_SAVE_AREA - rtx setjmpless_size = NULL_RTX; -#endif - /* If we're asking for zero bytes, it doesn't matter what we point to since we can't dereference it. But return a reasonable address anyway. */ @@ -1217,7 +1121,7 @@ allocate_dynamic_stack_space (size, target, known_align) return virtual_stack_dynamic_rtx; /* Otherwise, show we're calling alloca or equivalent. */ - current_function_calls_alloca = 1; + cfun->calls_alloca = 1; /* Ensure the size is in the proper mode. */ if (GET_MODE (size) != VOIDmode && GET_MODE (size) != Pmode) @@ -1226,7 +1130,7 @@ allocate_dynamic_stack_space (size, target, known_align) /* We can't attempt to minimize alignment necessary, because we don't know the final value of preferred_stack_boundary yet while executing this code. */ - cfun->preferred_stack_boundary = PREFERRED_STACK_BOUNDARY; + crtl->preferred_stack_boundary = PREFERRED_STACK_BOUNDARY; /* We will need to ensure that the address we return is aligned to BIGGEST_ALIGNMENT. If STACK_DYNAMIC_OFFSET is defined, we don't @@ -1258,52 +1162,47 @@ allocate_dynamic_stack_space (size, target, known_align) avoid clobbering the reg save area. Note that the offset of virtual_incoming_args_rtx includes the preallocated stack args space. It would be no problem to clobber that, but it's on the wrong side - of the old save area. */ - { - rtx dynamic_offset - = expand_binop (Pmode, sub_optab, virtual_stack_dynamic_rtx, - stack_pointer_rtx, NULL_RTX, 1, OPTAB_LIB_WIDEN); + of the old save area. + + What used to happen is that, since we did not know for sure + whether setjmp() was invoked until after RTL generation, we + would use reg notes to store the "optimized" size and fix things + up later. These days we know this information before we ever + start building RTL so the reg notes are unnecessary. */ + if (!cfun->calls_setjmp) + { + int align = PREFERRED_STACK_BOUNDARY / BITS_PER_UNIT; - if (!current_function_calls_setjmp) - { - int align = PREFERRED_STACK_BOUNDARY / BITS_PER_UNIT; - - /* See optimize_save_area_alloca to understand what is being - set up here. */ - - /* ??? Code below assumes that the save area needs maximal - alignment. This constraint may be too strong. */ - if (PREFERRED_STACK_BOUNDARY != BIGGEST_ALIGNMENT) - abort (); - - if (GET_CODE (size) == CONST_INT) - { - HOST_WIDE_INT new = INTVAL (size) / align * align; - - if (INTVAL (size) != new) - setjmpless_size = GEN_INT (new); - else - setjmpless_size = size; - } - else - { - /* Since we know overflow is not possible, we avoid using - CEIL_DIV_EXPR and use TRUNC_DIV_EXPR instead. */ - setjmpless_size = expand_divmod (0, TRUNC_DIV_EXPR, Pmode, size, - GEN_INT (align), NULL_RTX, 1); - setjmpless_size = expand_mult (Pmode, setjmpless_size, - GEN_INT (align), NULL_RTX, 1); - } - /* Our optimization works based upon being able to perform a simple - transformation of this RTL into a (set REG REG) so make sure things - did in fact end up in a REG. */ - if (!register_operand (setjmpless_size, Pmode)) - setjmpless_size = force_reg (Pmode, setjmpless_size); - } + /* ??? Code below assumes that the save area needs maximal + alignment. This constraint may be too strong. */ + gcc_assert (PREFERRED_STACK_BOUNDARY == BIGGEST_ALIGNMENT); - size = expand_binop (Pmode, add_optab, size, dynamic_offset, - NULL_RTX, 1, OPTAB_LIB_WIDEN); - } + if (CONST_INT_P (size)) + { + HOST_WIDE_INT new_size = INTVAL (size) / align * align; + + if (INTVAL (size) != new_size) + size = GEN_INT (new_size); + } + else + { + /* Since we know overflow is not possible, we avoid using + CEIL_DIV_EXPR and use TRUNC_DIV_EXPR instead. */ + size = expand_divmod (0, TRUNC_DIV_EXPR, Pmode, size, + GEN_INT (align), NULL_RTX, 1); + size = expand_mult (Pmode, size, + GEN_INT (align), NULL_RTX, 1); + } + } + else + { + rtx dynamic_offset + = expand_binop (Pmode, sub_optab, virtual_stack_dynamic_rtx, + stack_pointer_rtx, NULL_RTX, 1, OPTAB_LIB_WIDEN); + + size = expand_binop (Pmode, add_optab, size, dynamic_offset, + NULL_RTX, 1, OPTAB_LIB_WIDEN); + } #endif /* SETJMP_VIA_SAVE_AREA */ /* Round the size to a multiple of the required stack alignment. @@ -1331,16 +1230,21 @@ allocate_dynamic_stack_space (size, target, known_align) /* We ought to be called always on the toplevel and stack ought to be aligned properly. */ - if (stack_pointer_delta % (PREFERRED_STACK_BOUNDARY / BITS_PER_UNIT)) - abort (); + gcc_assert (!(stack_pointer_delta + % (PREFERRED_STACK_BOUNDARY / BITS_PER_UNIT))); /* If needed, check that we have the required amount of stack. Take into account what has already been checked. */ - if (flag_stack_check && ! STACK_CHECK_BUILTIN) - probe_stack_range (STACK_CHECK_MAX_FRAME_SIZE + STACK_CHECK_PROTECT, size); + if (STACK_CHECK_MOVING_SP) + ; + else if (flag_stack_check == GENERIC_STACK_CHECK) + probe_stack_range (STACK_OLD_CHECK_PROTECT + STACK_CHECK_MAX_FRAME_SIZE, + size); + else if (flag_stack_check == STATIC_BUILTIN_STACK_CHECK) + probe_stack_range (STACK_CHECK_PROTECT, size); /* Don't use a TARGET that isn't a pseudo or is the wrong mode. */ - if (target == 0 || GET_CODE (target) != REG + if (target == 0 || !REG_P (target) || REGNO (target) < FIRST_PSEUDO_REGISTER || GET_MODE (target) != Pmode) target = gen_reg_rtx (Pmode); @@ -1365,7 +1269,7 @@ allocate_dynamic_stack_space (size, target, known_align) pred = insn_data[(int) CODE_FOR_allocate_stack].operand[1].predicate; if (pred && ! ((*pred) (size, mode))) - size = copy_to_mode_reg (mode, size); + size = copy_to_mode_reg (mode, convert_to_mode (mode, size, 1)); emit_insn (gen_allocate_stack (target, size)); } @@ -1377,7 +1281,7 @@ allocate_dynamic_stack_space (size, target, known_align) #endif /* Check stack bounds if necessary. */ - if (current_function_limit_stack) + if (crtl->limit_stack) { rtx available; rtx space_available = gen_label_rtx (); @@ -1402,17 +1306,10 @@ allocate_dynamic_stack_space (size, target, known_align) emit_label (space_available); } - anti_adjust_stack (size); -#ifdef SETJMP_VIA_SAVE_AREA - if (setjmpless_size != NULL_RTX) - { - rtx note_target = get_last_insn (); - - REG_NOTES (note_target) - = gen_rtx_EXPR_LIST (REG_SAVE_AREA, setjmpless_size, - REG_NOTES (note_target)); - } -#endif /* SETJMP_VIA_SAVE_AREA */ + if (flag_stack_check && STACK_CHECK_MOVING_SP) + anti_adjust_stack_and_probe (size, false); + else + anti_adjust_stack (size); #ifdef STACK_GROWS_DOWNWARD emit_move_insn (target, virtual_stack_dynamic_rtx); @@ -1435,16 +1332,9 @@ allocate_dynamic_stack_space (size, target, known_align) NULL_RTX, 1); } - /* Some systems require a particular insn to refer to the stack - to make the pages exist. */ -#ifdef HAVE_probe - if (HAVE_probe) - emit_insn (gen_probe ()); -#endif - /* Record the new stack level for nonlocal gotos. */ - if (nonlocal_goto_handler_slots != 0) - emit_stack_save (SAVE_NONLOCAL, &nonlocal_goto_stack_level, NULL_RTX); + if (cfun->nonlocal_goto_save_area != 0) + update_nonlocal_goto_save_area (); return target; } @@ -1456,8 +1346,7 @@ allocate_dynamic_stack_space (size, target, known_align) static GTY(()) rtx stack_check_libfunc; void -set_stack_check_libfunc (libfunc) - rtx libfunc; +set_stack_check_libfunc (rtx libfunc) { stack_check_libfunc = libfunc; } @@ -1465,189 +1354,345 @@ set_stack_check_libfunc (libfunc) /* Emit one stack probe at ADDRESS, an address within the stack. */ static void -emit_stack_probe (address) - rtx address; +emit_stack_probe (rtx address) { rtx memref = gen_rtx_MEM (word_mode, address); MEM_VOLATILE_P (memref) = 1; - if (STACK_CHECK_PROBE_LOAD) - emit_move_insn (gen_reg_rtx (word_mode), memref); + /* See if we have an insn to probe the stack. */ +#ifdef HAVE_probe_stack + if (HAVE_probe_stack) + emit_insn (gen_probe_stack (memref)); else +#endif emit_move_insn (memref, const0_rtx); } /* Probe a range of stack addresses from FIRST to FIRST+SIZE, inclusive. - FIRST is a constant and size is a Pmode RTX. These are offsets from the - current stack pointer. STACK_GROWS_DOWNWARD says whether to add or - subtract from the stack. If SIZE is constant, this is done - with a fixed number of probes. Otherwise, we must make a loop. */ + FIRST is a constant and size is a Pmode RTX. These are offsets from + the current stack pointer. STACK_GROWS_DOWNWARD says whether to add + or subtract them from the stack pointer. */ + +#define PROBE_INTERVAL (1 << STACK_CHECK_PROBE_INTERVAL_EXP) #ifdef STACK_GROWS_DOWNWARD #define STACK_GROW_OP MINUS +#define STACK_GROW_OPTAB sub_optab +#define STACK_GROW_OFF(off) -(off) #else #define STACK_GROW_OP PLUS +#define STACK_GROW_OPTAB add_optab +#define STACK_GROW_OFF(off) (off) #endif void -probe_stack_range (first, size) - HOST_WIDE_INT first; - rtx size; +probe_stack_range (HOST_WIDE_INT first, rtx size) { /* First ensure SIZE is Pmode. */ if (GET_MODE (size) != VOIDmode && GET_MODE (size) != Pmode) size = convert_to_mode (Pmode, size, 1); - /* Next see if the front end has set up a function for us to call to - check the stack. */ - if (stack_check_libfunc != 0) + /* Next see if we have a function to check the stack. */ + if (stack_check_libfunc) { - rtx addr = memory_address (QImode, + rtx addr = memory_address (Pmode, gen_rtx_fmt_ee (STACK_GROW_OP, Pmode, stack_pointer_rtx, plus_constant (size, first))); - -#ifdef POINTERS_EXTEND_UNSIGNED - if (GET_MODE (addr) != ptr_mode) - addr = convert_memory_address (ptr_mode, addr); -#endif - emit_library_call (stack_check_libfunc, LCT_NORMAL, VOIDmode, 1, addr, - ptr_mode); + Pmode); } - /* Next see if we have an insn to check the stack. Use it if so. */ + /* Next see if we have an insn to check the stack. */ #ifdef HAVE_check_stack else if (HAVE_check_stack) { - insn_operand_predicate_fn pred; - rtx last_addr - = force_operand (gen_rtx_fmt_ee (STACK_GROW_OP, Pmode, - stack_pointer_rtx, - plus_constant (size, first)), - NULL_RTX); - - pred = insn_data[(int) CODE_FOR_check_stack].operand[0].predicate; - if (pred && ! ((*pred) (last_addr, Pmode))) - last_addr = copy_to_mode_reg (Pmode, last_addr); + rtx addr = memory_address (Pmode, + gen_rtx_fmt_ee (STACK_GROW_OP, Pmode, + stack_pointer_rtx, + plus_constant (size, first))); + insn_operand_predicate_fn pred + = insn_data[(int) CODE_FOR_check_stack].operand[0].predicate; + if (pred && !((*pred) (addr, Pmode))) + addr = copy_to_mode_reg (Pmode, addr); - emit_insn (gen_check_stack (last_addr)); + emit_insn (gen_check_stack (addr)); } #endif - /* If we have to generate explicit probes, see if we have a constant - small number of them to generate. If so, that's the easy case. */ - else if (GET_CODE (size) == CONST_INT - && INTVAL (size) < 10 * STACK_CHECK_PROBE_INTERVAL) + /* Otherwise we have to generate explicit probes. If we have a constant + small number of them to generate, that's the easy case. */ + else if (CONST_INT_P (size) && INTVAL (size) < 7 * PROBE_INTERVAL) { - HOST_WIDE_INT offset; - - /* Start probing at FIRST + N * STACK_CHECK_PROBE_INTERVAL - for values of N from 1 until it exceeds LAST. If only one - probe is needed, this will not generate any code. Then probe - at LAST. */ - for (offset = first + STACK_CHECK_PROBE_INTERVAL; - offset < INTVAL (size); - offset = offset + STACK_CHECK_PROBE_INTERVAL) - emit_stack_probe (gen_rtx_fmt_ee (STACK_GROW_OP, Pmode, - stack_pointer_rtx, - GEN_INT (offset))); - - emit_stack_probe (gen_rtx_fmt_ee (STACK_GROW_OP, Pmode, - stack_pointer_rtx, - plus_constant (size, first))); + HOST_WIDE_INT isize = INTVAL (size), i; + rtx addr; + + /* Probe at FIRST + N * PROBE_INTERVAL for values of N from 1 until + it exceeds SIZE. If only one probe is needed, this will not + generate any code. Then probe at FIRST + SIZE. */ + for (i = PROBE_INTERVAL; i < isize; i += PROBE_INTERVAL) + { + addr = memory_address (Pmode, + plus_constant (stack_pointer_rtx, + STACK_GROW_OFF (first + i))); + emit_stack_probe (addr); + } + + addr = memory_address (Pmode, + plus_constant (stack_pointer_rtx, + STACK_GROW_OFF (first + isize))); + emit_stack_probe (addr); } - /* In the variable case, do the same as above, but in a loop. We emit loop - notes so that loop optimization can be done. */ + /* In the variable case, do the same as above, but in a loop. Note that we + must be extra careful with variables wrapping around because we might be + at the very top (or the very bottom) of the address space and we have to + be able to handle this case properly; in particular, we use an equality + test for the loop condition. */ else { - rtx test_addr - = force_operand (gen_rtx_fmt_ee (STACK_GROW_OP, Pmode, - stack_pointer_rtx, - GEN_INT (first + STACK_CHECK_PROBE_INTERVAL)), - NULL_RTX); - rtx last_addr - = force_operand (gen_rtx_fmt_ee (STACK_GROW_OP, Pmode, - stack_pointer_rtx, - plus_constant (size, first)), - NULL_RTX); - rtx incr = GEN_INT (STACK_CHECK_PROBE_INTERVAL); + rtx rounded_size, rounded_size_op, test_addr, last_addr, temp; rtx loop_lab = gen_label_rtx (); - rtx test_lab = gen_label_rtx (); rtx end_lab = gen_label_rtx (); - rtx temp; - if (GET_CODE (test_addr) != REG - || REGNO (test_addr) < FIRST_PSEUDO_REGISTER) - test_addr = force_reg (Pmode, test_addr); - emit_note (NULL, NOTE_INSN_LOOP_BEG); - emit_jump (test_lab); + /* Step 1: round SIZE to the previous multiple of the interval. */ + + /* ROUNDED_SIZE = SIZE & -PROBE_INTERVAL */ + rounded_size + = simplify_gen_binary (AND, Pmode, size, GEN_INT (-PROBE_INTERVAL)); + rounded_size_op = force_operand (rounded_size, NULL_RTX); + + + /* Step 2: compute initial and final value of the loop counter. */ + + /* TEST_ADDR = SP + FIRST. */ + test_addr = force_operand (gen_rtx_fmt_ee (STACK_GROW_OP, Pmode, + stack_pointer_rtx, + GEN_INT (first)), NULL_RTX); + + /* LAST_ADDR = SP + FIRST + ROUNDED_SIZE. */ + last_addr = force_operand (gen_rtx_fmt_ee (STACK_GROW_OP, Pmode, + test_addr, + rounded_size_op), NULL_RTX); + + + /* Step 3: the loop + + while (TEST_ADDR != LAST_ADDR) + { + TEST_ADDR = TEST_ADDR + PROBE_INTERVAL + probe at TEST_ADDR + } + + probes at FIRST + N * PROBE_INTERVAL for values of N from 1 + until it is equal to ROUNDED_SIZE. */ emit_label (loop_lab); - emit_stack_probe (test_addr); - emit_note (NULL, NOTE_INSN_LOOP_CONT); + /* Jump to END_LAB if TEST_ADDR == LAST_ADDR. */ + emit_cmp_and_jump_insns (test_addr, last_addr, EQ, NULL_RTX, Pmode, 1, + end_lab); -#ifdef STACK_GROWS_DOWNWARD -#define CMP_OPCODE GTU - temp = expand_binop (Pmode, sub_optab, test_addr, incr, test_addr, - 1, OPTAB_WIDEN); -#else -#define CMP_OPCODE LTU - temp = expand_binop (Pmode, add_optab, test_addr, incr, test_addr, + /* TEST_ADDR = TEST_ADDR + PROBE_INTERVAL. */ + temp = expand_binop (Pmode, STACK_GROW_OPTAB, test_addr, + GEN_INT (PROBE_INTERVAL), test_addr, 1, OPTAB_WIDEN); -#endif - if (temp != test_addr) - abort (); + gcc_assert (temp == test_addr); + + /* Probe at TEST_ADDR. */ + emit_stack_probe (test_addr); + + emit_jump (loop_lab); - emit_label (test_lab); - emit_cmp_and_jump_insns (test_addr, last_addr, CMP_OPCODE, - NULL_RTX, Pmode, 1, loop_lab); - emit_jump (end_lab); - emit_note (NULL, NOTE_INSN_LOOP_END); emit_label (end_lab); - emit_stack_probe (last_addr); + + /* Step 4: probe at FIRST + SIZE if we cannot assert at compile-time + that SIZE is equal to ROUNDED_SIZE. */ + + /* TEMP = SIZE - ROUNDED_SIZE. */ + temp = simplify_gen_binary (MINUS, Pmode, size, rounded_size); + if (temp != const0_rtx) + { + rtx addr; + + if (GET_CODE (temp) == CONST_INT) + { + /* Use [base + disp} addressing mode if supported. */ + HOST_WIDE_INT offset = INTVAL (temp); + addr = memory_address (Pmode, + plus_constant (last_addr, + STACK_GROW_OFF (offset))); + } + else + { + /* Manual CSE if the difference is not known at compile-time. */ + temp = gen_rtx_MINUS (Pmode, size, rounded_size_op); + addr = memory_address (Pmode, + gen_rtx_fmt_ee (STACK_GROW_OP, Pmode, + last_addr, temp)); + } + + emit_stack_probe (addr); + } } } - + +/* Adjust the stack pointer by minus SIZE (an rtx for a number of bytes) + while probing it. This pushes when SIZE is positive. SIZE need not + be constant. If ADJUST_BACK is true, adjust back the stack pointer + by plus SIZE at the end. */ + +void +anti_adjust_stack_and_probe (rtx size, bool adjust_back) +{ + /* We skip the probe for the first interval + a small dope of 4 words and + probe that many bytes past the specified size to maintain a protection + area at the botton of the stack. */ + const int dope = 4 * UNITS_PER_WORD; + + /* First ensure SIZE is Pmode. */ + if (GET_MODE (size) != VOIDmode && GET_MODE (size) != Pmode) + size = convert_to_mode (Pmode, size, 1); + + /* If we have a constant small number of probes to generate, that's the + easy case. */ + if (GET_CODE (size) == CONST_INT && INTVAL (size) < 7 * PROBE_INTERVAL) + { + HOST_WIDE_INT isize = INTVAL (size), i; + bool first_probe = true; + + /* Adjust SP and probe to PROBE_INTERVAL + N * PROBE_INTERVAL for + values of N from 1 until it exceeds SIZE. If only one probe is + needed, this will not generate any code. Then adjust and probe + to PROBE_INTERVAL + SIZE. */ + for (i = PROBE_INTERVAL; i < isize; i += PROBE_INTERVAL) + { + if (first_probe) + { + anti_adjust_stack (GEN_INT (2 * PROBE_INTERVAL + dope)); + first_probe = false; + } + else + anti_adjust_stack (GEN_INT (PROBE_INTERVAL)); + emit_stack_probe (stack_pointer_rtx); + } + + if (first_probe) + anti_adjust_stack (plus_constant (size, PROBE_INTERVAL + dope)); + else + anti_adjust_stack (plus_constant (size, PROBE_INTERVAL - i)); + emit_stack_probe (stack_pointer_rtx); + } + + /* In the variable case, do the same as above, but in a loop. Note that we + must be extra careful with variables wrapping around because we might be + at the very top (or the very bottom) of the address space and we have to + be able to handle this case properly; in particular, we use an equality + test for the loop condition. */ + else + { + rtx rounded_size, rounded_size_op, last_addr, temp; + rtx loop_lab = gen_label_rtx (); + rtx end_lab = gen_label_rtx (); + + + /* Step 1: round SIZE to the previous multiple of the interval. */ + + /* ROUNDED_SIZE = SIZE & -PROBE_INTERVAL */ + rounded_size + = simplify_gen_binary (AND, Pmode, size, GEN_INT (-PROBE_INTERVAL)); + rounded_size_op = force_operand (rounded_size, NULL_RTX); + + + /* Step 2: compute initial and final value of the loop counter. */ + + /* SP = SP_0 + PROBE_INTERVAL. */ + anti_adjust_stack (GEN_INT (PROBE_INTERVAL + dope)); + + /* LAST_ADDR = SP_0 + PROBE_INTERVAL + ROUNDED_SIZE. */ + last_addr = force_operand (gen_rtx_fmt_ee (STACK_GROW_OP, Pmode, + stack_pointer_rtx, + rounded_size_op), NULL_RTX); + + + /* Step 3: the loop + + while (SP != LAST_ADDR) + { + SP = SP + PROBE_INTERVAL + probe at SP + } + + adjusts SP and probes to PROBE_INTERVAL + N * PROBE_INTERVAL for + values of N from 1 until it is equal to ROUNDED_SIZE. */ + + emit_label (loop_lab); + + /* Jump to END_LAB if SP == LAST_ADDR. */ + emit_cmp_and_jump_insns (stack_pointer_rtx, last_addr, EQ, NULL_RTX, + Pmode, 1, end_lab); + + /* SP = SP + PROBE_INTERVAL and probe at SP. */ + anti_adjust_stack (GEN_INT (PROBE_INTERVAL)); + emit_stack_probe (stack_pointer_rtx); + + emit_jump (loop_lab); + + emit_label (end_lab); + + + /* Step 4: adjust SP and probe to PROBE_INTERVAL + SIZE if we cannot + assert at compile-time that SIZE is equal to ROUNDED_SIZE. */ + + /* TEMP = SIZE - ROUNDED_SIZE. */ + temp = simplify_gen_binary (MINUS, Pmode, size, rounded_size); + if (temp != const0_rtx) + { + /* Manual CSE if the difference is not known at compile-time. */ + if (GET_CODE (temp) != CONST_INT) + temp = gen_rtx_MINUS (Pmode, size, rounded_size_op); + anti_adjust_stack (temp); + emit_stack_probe (stack_pointer_rtx); + } + } + + /* Adjust back and account for the additional first interval. */ + if (adjust_back) + adjust_stack (plus_constant (size, PROBE_INTERVAL + dope)); + else + adjust_stack (GEN_INT (PROBE_INTERVAL + dope)); +} + /* Return an rtx representing the register or memory location in which a scalar value of data type VALTYPE was returned by a function call to function FUNC. - FUNC is a FUNCTION_DECL node if the precise function is known, - otherwise 0. + FUNC is a FUNCTION_DECL, FNTYPE a FUNCTION_TYPE node if the precise + function is known, otherwise 0. OUTGOING is 1 if on a machine with register windows this function should return the register in which the function will put its result and 0 otherwise. */ rtx -hard_function_value (valtype, func, outgoing) - tree valtype; - tree func ATTRIBUTE_UNUSED; - int outgoing ATTRIBUTE_UNUSED; +hard_function_value (const_tree valtype, const_tree func, const_tree fntype, + int outgoing ATTRIBUTE_UNUSED) { rtx val; -#ifdef FUNCTION_OUTGOING_VALUE - if (outgoing) - val = FUNCTION_OUTGOING_VALUE (valtype, func); - else -#endif - val = FUNCTION_VALUE (valtype, func); + val = targetm.calls.function_value (valtype, func ? func : fntype, outgoing); - if (GET_CODE (val) == REG + if (REG_P (val) && GET_MODE (val) == BLKmode) { unsigned HOST_WIDE_INT bytes = int_size_in_bytes (valtype); enum machine_mode tmpmode; /* int_size_in_bytes can return -1. We don't need a check here - since the value of bytes will be large enough that no mode - will match and we will abort later in this function. */ + since the value of bytes will then be large enough that no + mode will match anyway. */ for (tmpmode = GET_CLASS_NARROWEST_MODE (MODE_INT); tmpmode != VOIDmode; @@ -1659,8 +1704,7 @@ hard_function_value (valtype, func, outgoing) } /* No suitable mode found. */ - if (tmpmode == VOIDmode) - abort (); + gcc_assert (tmpmode != VOIDmode); PUT_MODE (val, tmpmode); } @@ -1671,10 +1715,9 @@ hard_function_value (valtype, func, outgoing) in which a scalar value of mode MODE was returned by a library call. */ rtx -hard_libcall_value (mode) - enum machine_mode mode; +hard_libcall_value (enum machine_mode mode, rtx fun) { - return LIBCALL_VALUE (mode); + return targetm.calls.libcall_value (mode, fun); } /* Look up the tree code for a given rtx code @@ -1683,8 +1726,7 @@ hard_libcall_value (mode) what `enum tree_code' means. */ int -rtx_to_tree_code (code) - enum rtx_code code; +rtx_to_tree_code (enum rtx_code code) { enum tree_code tcode; @@ -1716,4 +1758,3 @@ rtx_to_tree_code (code) } #include "gt-explow.h" -