/* Subroutines for manipulating rtx's in semantically interesting ways.
- Copyright (C) 1987-2016 Free Software Foundation, Inc.
+ Copyright (C) 1987-2021 Free Software Foundation, Inc.
This file is part of GCC.
#include "tree.h"
#include "memmodel.h"
#include "tm_p.h"
-#include "expmed.h"
#include "optabs.h"
+#include "expmed.h"
+#include "profile-count.h"
#include "emit-rtl.h"
#include "recog.h"
#include "diagnostic-core.h"
#include "dojump.h"
#include "explow.h"
#include "expr.h"
+#include "stringpool.h"
#include "common/common-target.h"
#include "output.h"
HOST_WIDE_INT
trunc_int_for_mode (HOST_WIDE_INT c, machine_mode mode)
{
- int width = GET_MODE_PRECISION (mode);
+ /* Not scalar_int_mode because we also allow pointer bound modes. */
+ scalar_mode smode = as_a <scalar_mode> (mode);
+ int width = GET_MODE_PRECISION (smode);
/* You want to truncate to a _what_? */
- gcc_assert (SCALAR_INT_MODE_P (mode)
- || POINTER_BOUNDS_MODE_P (mode));
+ gcc_assert (SCALAR_INT_MODE_P (mode));
/* Canonicalize BImode to 0 and STORE_FLAG_VALUE. */
- if (mode == BImode)
+ if (smode == BImode)
return c & 1 ? STORE_FLAG_VALUE : 0;
/* Sign-extend for the requested mode. */
return c;
}
+/* Likewise for polynomial values, using the sign-extended representation
+ for each individual coefficient. */
+
+poly_int64
+trunc_int_for_mode (poly_int64 x, machine_mode mode)
+{
+ for (unsigned int i = 0; i < NUM_POLY_INT_COEFFS; ++i)
+ x.coeffs[i] = trunc_int_for_mode (x.coeffs[i], mode);
+ return x;
+}
+
/* Return an rtx for the sum of X and the integer C, given that X has
mode MODE. INPLACE is true if X can be modified inplace or false
if it must be treated as immutable. */
rtx
-plus_constant (machine_mode mode, rtx x, HOST_WIDE_INT c,
- bool inplace)
+plus_constant (machine_mode mode, rtx x, poly_int64 c, bool inplace)
{
RTX_CODE code;
rtx y;
gcc_assert (GET_MODE (x) == VOIDmode || GET_MODE (x) == mode);
- if (c == 0)
+ if (known_eq (c, 0))
return x;
restart:
switch (code)
{
CASE_CONST_SCALAR_INT:
- return immed_wide_int_const (wi::add (std::make_pair (x, mode), c),
- mode);
+ return immed_wide_int_const (wi::add (rtx_mode_t (x, mode), c), mode);
case MEM:
/* If this is a reference to the constant pool, try replacing it with
a reference to a new constant. If the resulting address isn't
cst = gen_lowpart (mode, cst);
gcc_assert (cst);
}
+ else if (GET_MODE (cst) == VOIDmode
+ && get_pool_mode (XEXP (x, 0)) != mode)
+ break;
if (GET_MODE (cst) == VOIDmode || GET_MODE (cst) == mode)
{
tem = plus_constant (mode, cst, c);
break;
default:
+ if (CONST_POLY_INT_P (x))
+ return immed_wide_int_const (const_poly_int_value (x) + c, mode);
break;
}
- if (c != 0)
+ if (maybe_ne (c, 0))
x = gen_rtx_PLUS (mode, x, gen_int_mode (c, mode));
if (GET_CODE (x) == SYMBOL_REF || GET_CODE (x) == LABEL_REF)
/* First handle constants appearing at this level explicitly. */
if (CONST_INT_P (XEXP (x, 1))
- && 0 != (tem = simplify_binary_operation (PLUS, GET_MODE (x), *constptr,
- XEXP (x, 1)))
+ && (tem = simplify_binary_operation (PLUS, GET_MODE (x), *constptr,
+ XEXP (x, 1))) != 0
&& CONST_INT_P (tem))
{
*constptr = tem;
x0 = eliminate_constant_term (XEXP (x, 0), &tem);
x1 = eliminate_constant_term (XEXP (x, 1), &tem);
if ((x1 != XEXP (x, 1) || x0 != XEXP (x, 0))
- && 0 != (tem = simplify_binary_operation (PLUS, GET_MODE (x),
- *constptr, tem))
+ && (tem = simplify_binary_operation (PLUS, GET_MODE (x),
+ *constptr, tem)) != 0
&& CONST_INT_P (tem))
{
*constptr = tem;
it should return NULL if it can't be simplified without emitting insns. */
rtx
-convert_memory_address_addr_space_1 (machine_mode to_mode ATTRIBUTE_UNUSED,
+convert_memory_address_addr_space_1 (scalar_int_mode to_mode ATTRIBUTE_UNUSED,
rtx x, addr_space_t as ATTRIBUTE_UNUSED,
bool in_const ATTRIBUTE_UNUSED,
bool no_emit ATTRIBUTE_UNUSED)
gcc_assert (GET_MODE (x) == to_mode || GET_MODE (x) == VOIDmode);
return x;
#else /* defined(POINTERS_EXTEND_UNSIGNED) */
- machine_mode pointer_mode, address_mode, from_mode;
+ scalar_int_mode pointer_mode, address_mode, from_mode;
rtx temp;
enum rtx_code code;
}
break;
+ case UNSPEC:
+ /* Assume that all UNSPECs in a constant address can be converted
+ operand-by-operand. We could add a target hook if some targets
+ require different behavior. */
+ if (in_const && GET_MODE (x) == from_mode)
+ {
+ unsigned int n = XVECLEN (x, 0);
+ rtvec v = gen_rtvec (n);
+ for (unsigned int i = 0; i < n; ++i)
+ {
+ rtx op = XVECEXP (x, 0, i);
+ if (GET_MODE (op) == from_mode)
+ op = convert_memory_address_addr_space_1 (to_mode, op, as,
+ in_const, no_emit);
+ RTVEC_ELT (v, i) = op;
+ }
+ return gen_rtx_UNSPEC (to_mode, v, XINT (x, 1));
+ }
+ break;
+
default:
break;
}
arithmetic insns can be used. */
rtx
-convert_memory_address_addr_space (machine_mode to_mode, rtx x, addr_space_t as)
+convert_memory_address_addr_space (scalar_int_mode to_mode, rtx x,
+ addr_space_t as)
{
return convert_memory_address_addr_space_1 (to_mode, x, as, false, false);
}
memory_address_addr_space (machine_mode mode, rtx x, addr_space_t as)
{
rtx oldx = x;
- machine_mode address_mode = targetm.addr_space.address_mode (as);
+ scalar_int_mode address_mode = targetm.addr_space.address_mode (as);
x = convert_memory_address_addr_space (address_mode, x, as);
#ifdef PROMOTE_MODE
enum tree_code code;
int unsignedp;
+ scalar_mode smode;
#endif
/* For libcalls this is invoked without TYPE from the backends
{
case INTEGER_TYPE: case ENUMERAL_TYPE: case BOOLEAN_TYPE:
case REAL_TYPE: case OFFSET_TYPE: case FIXED_POINT_TYPE:
- PROMOTE_MODE (mode, unsignedp, type);
+ /* Values of these types always have scalar mode. */
+ smode = as_a <scalar_mode> (mode);
+ PROMOTE_MODE (smode, unsignedp, type);
*punsignedp = unsignedp;
- return mode;
+ return smode;
#ifdef POINTERS_EXTEND_UNSIGNED
case REFERENCE_TYPE:
tree type = TREE_TYPE (name);
int unsignedp = TYPE_UNSIGNED (type);
- machine_mode mode = TYPE_MODE (type);
-
- /* Bypass TYPE_MODE when it maps vector modes to BLKmode. */
- if (mode == BLKmode)
- {
- gcc_assert (VECTOR_TYPE_P (type));
- mode = type->type_common.mode;
- }
-
- machine_mode pmode = promote_mode (type, mode, &unsignedp);
+ machine_mode pmode = promote_mode (type, TYPE_MODE (type), &unsignedp);
if (punsignedp)
*punsignedp = unsignedp;
}
if (!suppress_reg_args_size)
- add_reg_note (insn, REG_ARGS_SIZE, GEN_INT (stack_pointer_delta));
+ add_args_size_note (insn, stack_pointer_delta);
}
/* Adjust the stack pointer by ADJUST (an rtx for a number of bytes).
/* We expect all variable sized adjustments to be multiple of
PREFERRED_STACK_BOUNDARY. */
- if (CONST_INT_P (adjust))
- stack_pointer_delta -= INTVAL (adjust);
+ poly_int64 const_adjust;
+ if (poly_int_rtx_p (adjust, &const_adjust))
+ stack_pointer_delta -= const_adjust;
adjust_stack_1 (adjust, false);
}
/* We expect all variable sized adjustments to be multiple of
PREFERRED_STACK_BOUNDARY. */
- if (CONST_INT_P (adjust))
- stack_pointer_delta += INTVAL (adjust);
+ poly_int64 const_adjust;
+ if (poly_int_rtx_p (adjust, &const_adjust))
+ stack_pointer_delta += const_adjust;
adjust_stack_1 (adjust, true);
}
if (targetm_common.except_unwind_info (&global_options) == UI_SJLJ)
update_sjlj_context ();
}
-\f
+
/* Return an rtx doing runtime alignment to REQUIRED_ALIGN on TARGET. */
-static rtx
+
+rtx
align_dynamic_address (rtx target, unsigned required_align)
{
/* CEIL_DIV_EXPR needs to worry about the addition overflowing,
unsigned required_align,
HOST_WIDE_INT *pstack_usage_size)
{
- unsigned extra = 0;
rtx size = *psize;
/* Ensure the size is in the proper mode. */
example), so we must preventively align the value. We leave space
in SIZE for the hole that might result from the alignment operation. */
- extra = (required_align - BITS_PER_UNIT) / BITS_PER_UNIT;
- size = plus_constant (Pmode, size, extra);
- size = force_operand (size, NULL_RTX);
-
- if (flag_stack_usage_info && pstack_usage_size)
- *pstack_usage_size += extra;
+ unsigned known_align = REGNO_POINTER_ALIGN (VIRTUAL_STACK_DYNAMIC_REGNUM);
+ if (known_align == 0)
+ known_align = BITS_PER_UNIT;
+ if (required_align > known_align)
+ {
+ unsigned extra = (required_align - known_align) / BITS_PER_UNIT;
+ size = plus_constant (Pmode, size, extra);
+ size = force_operand (size, NULL_RTX);
+ if (size_align > known_align)
+ size_align = known_align;
- if (extra && size_align > BITS_PER_UNIT)
- size_align = BITS_PER_UNIT;
+ if (flag_stack_usage_info && pstack_usage_size)
+ *pstack_usage_size += extra;
+ }
/* Round the size to a multiple of the required stack alignment.
Since the stack is presumed to be rounded before this allocation,
*psize = size;
}
+/* Return the number of bytes to "protect" on the stack for -fstack-check.
+
+ "protect" in the context of -fstack-check means how many bytes we need
+ to always ensure are available on the stack; as a consequence, this is
+ also how many bytes are first skipped when probing the stack.
+
+ On some targets we want to reuse the -fstack-check prologue support
+ to give a degree of protection against stack clashing style attacks.
+
+ In that scenario we do not want to skip bytes before probing as that
+ would render the stack clash protections useless.
+
+ So we never use STACK_CHECK_PROTECT directly. Instead we indirectly
+ use it through this helper, which allows to provide different values
+ for -fstack-check and -fstack-clash-protection. */
+
+HOST_WIDE_INT
+get_stack_check_protect (void)
+{
+ if (flag_stack_clash_protection)
+ return 0;
+
+ return STACK_CHECK_PROTECT;
+}
+
/* Return an rtx representing the address of an area of memory dynamically
pushed on the stack.
REQUIRED_ALIGN is the alignment (in bits) required for the region
of memory.
+ MAX_SIZE is an upper bound for SIZE, if SIZE is not constant, or -1 if
+ no such upper bound is known.
+
If CANNOT_ACCUMULATE is set to TRUE, the caller guarantees that the
stack space allocated by the generated code cannot be added with itself
in the course of the execution of the function. It is always safe to
rtx
allocate_dynamic_stack_space (rtx size, unsigned size_align,
- unsigned required_align, bool cannot_accumulate)
+ unsigned required_align,
+ HOST_WIDE_INT max_size,
+ bool cannot_accumulate)
{
HOST_WIDE_INT stack_usage_size = -1;
rtx_code_label *final_label;
}
}
- /* If the size is not constant, we can't say anything. */
- if (stack_usage_size == -1)
+ /* If the size is not constant, try the maximum size. */
+ if (stack_usage_size < 0)
+ stack_usage_size = max_size;
+
+ /* If the size is still not constant, we can't say anything. */
+ if (stack_usage_size < 0)
{
current_function_has_unbounded_dynamic_stack_size = 1;
stack_usage_size = 0;
func = init_one_libfunc ("__morestack_allocate_stack_space");
space = emit_library_call_value (func, target, LCT_NORMAL, Pmode,
- 1, ask, Pmode);
+ ask, Pmode);
if (available_label == NULL_RTX)
return space;
/* We ought to be called always on the toplevel and stack ought to be aligned
properly. */
- gcc_assert (!(stack_pointer_delta
- % (PREFERRED_STACK_BOUNDARY / BITS_PER_UNIT)));
+ gcc_assert (multiple_p (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. */
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);
+ probe_stack_range (get_stack_check_protect (), size);
/* Don't let anti_adjust_stack emit notes. */
suppress_reg_args_size = true;
stack pointer, such as acquiring the space by calling malloc(). */
if (targetm.have_allocate_stack ())
{
- struct expand_operand ops[2];
+ class expand_operand ops[2];
/* We don't have to check against the predicate for operand 0 since
TARGET is known to be a pseudo of the proper mode, which must
be valid for the operand. */
}
else
{
- int saved_stack_pointer_delta;
+ poly_int64 saved_stack_pointer_delta;
if (!STACK_GROWS_DOWNWARD)
emit_move_insn (target, virtual_stack_dynamic_rtx);
saved_stack_pointer_delta = stack_pointer_delta;
+ /* If stack checking or stack clash protection is requested,
+ then probe the stack while allocating space from it. */
if (flag_stack_check && STACK_CHECK_MOVING_SP)
anti_adjust_stack_and_probe (size, false);
+ else if (flag_stack_clash_protection)
+ anti_adjust_stack_and_probe_stack_clash (size);
else
anti_adjust_stack (size);
OFFSET is the offset of the area into the virtual stack vars area.
REQUIRED_ALIGN is the alignment (in bits) required for the region
- of memory. */
+ of memory.
+
+ BASE is the rtx of the base of this virtual stack vars area.
+ The only time this is not `virtual_stack_vars_rtx` is when tagging pointers
+ on the stack. */
rtx
-get_dynamic_stack_base (HOST_WIDE_INT offset, unsigned required_align)
+get_dynamic_stack_base (poly_int64 offset, unsigned required_align, rtx base)
{
rtx target;
crtl->preferred_stack_boundary = PREFERRED_STACK_BOUNDARY;
target = gen_reg_rtx (Pmode);
- emit_move_insn (target, virtual_stack_vars_rtx);
+ emit_move_insn (target, base);
target = expand_binop (Pmode, add_optab, target,
gen_int_mode (offset, Pmode),
NULL_RTX, 1, OPTAB_LIB_WIDEN);
{
gcc_assert (stack_check_libfunc == NULL_RTX);
stack_check_libfunc = gen_rtx_SYMBOL_REF (Pmode, libfunc_name);
+ tree decl = build_decl (UNKNOWN_LOCATION, FUNCTION_DECL,
+ get_identifier (libfunc_name), void_type_node);
+ DECL_EXTERNAL (decl) = 1;
+ SET_SYMBOL_REF_DECL (stack_check_libfunc, decl);
}
\f
/* Emit one stack probe at ADDRESS, an address within the stack. */
emit_stack_probe (rtx address)
{
if (targetm.have_probe_stack_address ())
- emit_insn (targetm.gen_probe_stack_address (address));
+ {
+ class expand_operand ops[1];
+ insn_code icode = targetm.code_for_probe_stack_address;
+ create_address_operand (ops, address);
+ maybe_legitimize_operands (icode, 0, 1, ops);
+ expand_insn (icode, 1, ops);
+ }
else
{
rtx memref = gen_rtx_MEM (word_mode, address);
MEM_VOLATILE_P (memref) = 1;
+ memref = validize_mem (memref);
/* See if we have an insn to probe the stack. */
if (targetm.have_probe_stack ())
- emit_insn (targetm.gen_probe_stack (memref));
+ emit_insn (targetm.gen_probe_stack (memref));
else
- emit_move_insn (memref, const0_rtx);
+ emit_move_insn (memref, const0_rtx);
}
}
stack_pointer_rtx,
plus_constant (Pmode,
size, first)));
- emit_library_call (stack_check_libfunc, LCT_THROW, VOIDmode, 1, addr,
- Pmode);
+ emit_library_call (stack_check_libfunc, LCT_THROW, VOIDmode,
+ addr, Pmode);
}
/* Next see if we have an insn to check the stack. */
else if (targetm.have_check_stack ())
{
- struct expand_operand ops[1];
+ class expand_operand ops[1];
rtx addr = memory_address (Pmode,
gen_rtx_fmt_ee (STACK_GROW_OP, Pmode,
stack_pointer_rtx,
emit_insn (gen_blockage ());
}
+/* Compute parameters for stack clash probing a dynamic stack
+ allocation of SIZE bytes.
+
+ We compute ROUNDED_SIZE, LAST_ADDR, RESIDUAL and PROBE_INTERVAL.
+
+ Additionally we conditionally dump the type of probing that will
+ be needed given the values computed. */
+
+void
+compute_stack_clash_protection_loop_data (rtx *rounded_size, rtx *last_addr,
+ rtx *residual,
+ HOST_WIDE_INT *probe_interval,
+ rtx size)
+{
+ /* Round SIZE down to STACK_CLASH_PROTECTION_PROBE_INTERVAL */
+ *probe_interval
+ = 1 << param_stack_clash_protection_probe_interval;
+ *rounded_size = simplify_gen_binary (AND, Pmode, size,
+ GEN_INT (-*probe_interval));
+
+ /* Compute the value of the stack pointer for the last iteration.
+ It's just SP + ROUNDED_SIZE. */
+ rtx rounded_size_op = force_operand (*rounded_size, NULL_RTX);
+ *last_addr = force_operand (gen_rtx_fmt_ee (STACK_GROW_OP, Pmode,
+ stack_pointer_rtx,
+ rounded_size_op),
+ NULL_RTX);
+
+ /* Compute any residuals not allocated by the loop above. Residuals
+ are just the ROUNDED_SIZE - SIZE. */
+ *residual = simplify_gen_binary (MINUS, Pmode, size, *rounded_size);
+
+ /* Dump key information to make writing tests easy. */
+ if (dump_file)
+ {
+ if (*rounded_size == CONST0_RTX (Pmode))
+ fprintf (dump_file,
+ "Stack clash skipped dynamic allocation and probing loop.\n");
+ else if (CONST_INT_P (*rounded_size)
+ && INTVAL (*rounded_size) <= 4 * *probe_interval)
+ fprintf (dump_file,
+ "Stack clash dynamic allocation and probing inline.\n");
+ else if (CONST_INT_P (*rounded_size))
+ fprintf (dump_file,
+ "Stack clash dynamic allocation and probing in "
+ "rotated loop.\n");
+ else
+ fprintf (dump_file,
+ "Stack clash dynamic allocation and probing in loop.\n");
+
+ if (*residual != CONST0_RTX (Pmode))
+ fprintf (dump_file,
+ "Stack clash dynamic allocation and probing residuals.\n");
+ else
+ fprintf (dump_file,
+ "Stack clash skipped dynamic allocation and "
+ "probing residuals.\n");
+ }
+}
+
+/* Emit the start of an allocate/probe loop for stack
+ clash protection.
+
+ LOOP_LAB and END_LAB are returned for use when we emit the
+ end of the loop.
+
+ LAST addr is the value for SP which stops the loop. */
+void
+emit_stack_clash_protection_probe_loop_start (rtx *loop_lab,
+ rtx *end_lab,
+ rtx last_addr,
+ bool rotated)
+{
+ /* Essentially we want to emit any setup code, the top of loop
+ label and the comparison at the top of the loop. */
+ *loop_lab = gen_label_rtx ();
+ *end_lab = gen_label_rtx ();
+
+ emit_label (*loop_lab);
+ if (!rotated)
+ emit_cmp_and_jump_insns (stack_pointer_rtx, last_addr, EQ, NULL_RTX,
+ Pmode, 1, *end_lab);
+}
+
+/* Emit the end of a stack clash probing loop.
+
+ This consists of just the jump back to LOOP_LAB and
+ emitting END_LOOP after the loop. */
+
+void
+emit_stack_clash_protection_probe_loop_end (rtx loop_lab, rtx end_loop,
+ rtx last_addr, bool rotated)
+{
+ if (rotated)
+ emit_cmp_and_jump_insns (stack_pointer_rtx, last_addr, NE, NULL_RTX,
+ Pmode, 1, loop_lab);
+ else
+ emit_jump (loop_lab);
+
+ emit_label (end_loop);
+
+}
+
+/* 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.
+
+ This is subtly different than anti_adjust_stack_and_probe to try and
+ prevent stack-clash attacks
+
+ 1. It must assume no knowledge of the probing state, any allocation
+ must probe.
+
+ Consider the case of a 1 byte alloca in a loop. If the sum of the
+ allocations is large, then this could be used to jump the guard if
+ probes were not emitted.
+
+ 2. It never skips probes, whereas anti_adjust_stack_and_probe will
+ skip the probe on the first PROBE_INTERVAL on the assumption it
+ was already done in the prologue and in previous allocations.
+
+ 3. It only allocates and probes SIZE bytes, it does not need to
+ allocate/probe beyond that because this probing style does not
+ guarantee signal handling capability if the guard is hit. */
+
+void
+anti_adjust_stack_and_probe_stack_clash (rtx size)
+{
+ /* First ensure SIZE is Pmode. */
+ if (GET_MODE (size) != VOIDmode && GET_MODE (size) != Pmode)
+ size = convert_to_mode (Pmode, size, 1);
+
+ /* We can get here with a constant size on some targets. */
+ rtx rounded_size, last_addr, residual;
+ HOST_WIDE_INT probe_interval, probe_range;
+ bool target_probe_range_p = false;
+ compute_stack_clash_protection_loop_data (&rounded_size, &last_addr,
+ &residual, &probe_interval, size);
+
+ /* Get the back-end specific probe ranges. */
+ probe_range = targetm.stack_clash_protection_alloca_probe_range ();
+ target_probe_range_p = probe_range != 0;
+ gcc_assert (probe_range >= 0);
+
+ /* If no back-end specific range defined, default to the top of the newly
+ allocated range. */
+ if (probe_range == 0)
+ probe_range = probe_interval - GET_MODE_SIZE (word_mode);
+
+ if (rounded_size != CONST0_RTX (Pmode))
+ {
+ if (CONST_INT_P (rounded_size)
+ && INTVAL (rounded_size) <= 4 * probe_interval)
+ {
+ for (HOST_WIDE_INT i = 0;
+ i < INTVAL (rounded_size);
+ i += probe_interval)
+ {
+ anti_adjust_stack (GEN_INT (probe_interval));
+ /* The prologue does not probe residuals. Thus the offset
+ here to probe just beyond what the prologue had already
+ allocated. */
+ emit_stack_probe (plus_constant (Pmode, stack_pointer_rtx,
+ probe_range));
+
+ emit_insn (gen_blockage ());
+ }
+ }
+ else
+ {
+ rtx loop_lab, end_loop;
+ bool rotate_loop = CONST_INT_P (rounded_size);
+ emit_stack_clash_protection_probe_loop_start (&loop_lab, &end_loop,
+ last_addr, rotate_loop);
+
+ anti_adjust_stack (GEN_INT (probe_interval));
+
+ /* The prologue does not probe residuals. Thus the offset here
+ to probe just beyond what the prologue had already
+ allocated. */
+ emit_stack_probe (plus_constant (Pmode, stack_pointer_rtx,
+ probe_range));
+
+ emit_stack_clash_protection_probe_loop_end (loop_lab, end_loop,
+ last_addr, rotate_loop);
+ emit_insn (gen_blockage ());
+ }
+ }
+
+ if (residual != CONST0_RTX (Pmode))
+ {
+ rtx label = NULL_RTX;
+ /* RESIDUAL could be zero at runtime and in that case *sp could
+ hold live data. Furthermore, we do not want to probe into the
+ red zone.
+
+ If TARGET_PROBE_RANGE_P then the target has promised it's safe to
+ probe at offset 0. In which case we no longer have to check for
+ RESIDUAL == 0. However we still need to probe at the right offset
+ when RESIDUAL > PROBE_RANGE, in which case we probe at PROBE_RANGE.
+
+ If !TARGET_PROBE_RANGE_P then go ahead and just guard the probe at *sp
+ on RESIDUAL != 0 at runtime if RESIDUAL is not a compile time constant.
+ */
+ anti_adjust_stack (residual);
+
+ if (!CONST_INT_P (residual))
+ {
+ label = gen_label_rtx ();
+ rtx_code op = target_probe_range_p ? LT : EQ;
+ rtx probe_cmp_value = target_probe_range_p
+ ? gen_rtx_CONST_INT (GET_MODE (residual), probe_range)
+ : CONST0_RTX (GET_MODE (residual));
+
+ if (target_probe_range_p)
+ emit_stack_probe (stack_pointer_rtx);
+
+ emit_cmp_and_jump_insns (residual, probe_cmp_value,
+ op, NULL_RTX, Pmode, 1, label);
+ }
+
+ rtx x = NULL_RTX;
+
+ /* If RESIDUAL isn't a constant and TARGET_PROBE_RANGE_P then we probe up
+ by the ABI defined safe value. */
+ if (!CONST_INT_P (residual) && target_probe_range_p)
+ x = GEN_INT (probe_range);
+ /* If RESIDUAL is a constant but smaller than the ABI defined safe value,
+ we still want to probe up, but the safest amount if a word. */
+ else if (target_probe_range_p)
+ {
+ if (INTVAL (residual) <= probe_range)
+ x = GEN_INT (GET_MODE_SIZE (word_mode));
+ else
+ x = GEN_INT (probe_range);
+ }
+ else
+ /* If nothing else, probe at the top of the new allocation. */
+ x = plus_constant (Pmode, residual, -GET_MODE_SIZE (word_mode));
+
+ emit_stack_probe (gen_rtx_PLUS (Pmode, stack_pointer_rtx, x));
+
+ emit_insn (gen_blockage ());
+ if (!CONST_INT_P (residual))
+ emit_label (label);
+ }
+}
+
+
/* 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
if (REG_P (val)
&& GET_MODE (val) == BLKmode)
{
- unsigned HOST_WIDE_INT bytes = int_size_in_bytes (valtype);
- machine_mode tmpmode;
+ unsigned HOST_WIDE_INT bytes = arg_int_size_in_bytes (valtype);
+ opt_scalar_int_mode tmpmode;
/* int_size_in_bytes can return -1. We don't need a check here
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;
- tmpmode = GET_MODE_WIDER_MODE (tmpmode))
+ FOR_EACH_MODE_IN_CLASS (tmpmode, MODE_INT)
{
/* Have we found a large enough mode? */
- if (GET_MODE_SIZE (tmpmode) >= bytes)
+ if (GET_MODE_SIZE (tmpmode.require ()) >= bytes)
break;
}
- /* No suitable mode found. */
- gcc_assert (tmpmode != VOIDmode);
-
- PUT_MODE (val, tmpmode);
+ PUT_MODE (val, tmpmode.require ());
}
return val;
}