/* Internal functions.
- Copyright (C) 2011-2020 Free Software Foundation, Inc.
+ Copyright (C) 2011-2021 Free Software Foundation, Inc.
This file is part of GCC.
#include "gimple-ssa.h"
#include "tree-phinodes.h"
#include "ssa-iterators.h"
+#include "explow.h"
+#include "rtl-iter.h"
+#include "gimple-range.h"
+
+/* For lang_hooks.types.type_for_mode. */
+#include "langhooks.h"
/* The names of each internal function, indexed by function number. */
const char *const internal_fn_name_array[] = {
{
#define DEF_INTERNAL_FN(CODE, FLAGS, FNSPEC) \
if (FNSPEC) internal_fn_fnspec_array[IFN_##CODE] = \
- build_string ((int) sizeof (FNSPEC), FNSPEC ? FNSPEC : "");
+ build_string ((int) sizeof (FNSPEC) - 1, FNSPEC ? FNSPEC : "");
#include "internal-fn.def"
internal_fn_fnspec_array[IFN_LAST] = 0;
}
#define load_lanes_direct { -1, -1, false }
#define mask_load_lanes_direct { -1, -1, false }
#define gather_load_direct { 3, 1, false }
+#define len_load_direct { -1, -1, false }
#define mask_store_direct { 3, 2, false }
#define store_lanes_direct { 0, 0, false }
#define mask_store_lanes_direct { 0, 0, false }
+#define vec_cond_mask_direct { 1, 0, false }
+#define vec_cond_direct { 2, 0, false }
#define scatter_store_direct { 3, 1, false }
+#define len_store_direct { 3, 3, false }
+#define vec_set_direct { 3, 3, false }
#define unary_direct { 0, 0, true }
#define binary_direct { 0, 0, true }
#define ternary_direct { 0, 0, true }
create_input_operand (&ops[2], align, Pmode);
gcc_assert (targetm.have_omp_simt_enter ());
expand_insn (targetm.code_for_omp_simt_enter, 3, ops);
+ if (!rtx_equal_p (target, ops[0].value))
+ emit_move_insn (target, ops[0].value);
}
/* Deallocate per-lane storage and leave non-uniform execution region. */
create_input_operand (&ops[1], cond, mode);
gcc_assert (targetm.have_omp_simt_last_lane ());
expand_insn (targetm.code_for_omp_simt_last_lane, 2, ops);
+ if (!rtx_equal_p (target, ops[0].value))
+ emit_move_insn (target, ops[0].value);
}
/* Non-transparent predicate used in SIMT lowering of OpenMP "ordered". */
create_input_operand (&ops[1], ctr, mode);
gcc_assert (targetm.have_omp_simt_ordered ());
expand_insn (targetm.code_for_omp_simt_ordered, 2, ops);
+ if (!rtx_equal_p (target, ops[0].value))
+ emit_move_insn (target, ops[0].value);
}
/* "Or" boolean reduction across SIMT lanes: return non-zero in all lanes if
create_input_operand (&ops[1], cond, mode);
gcc_assert (targetm.have_omp_simt_vote_any ());
expand_insn (targetm.code_for_omp_simt_vote_any, 2, ops);
+ if (!rtx_equal_p (target, ops[0].value))
+ emit_move_insn (target, ops[0].value);
}
/* Exchange between SIMT lanes with a "butterfly" pattern: source lane index
create_input_operand (&ops[2], idx, SImode);
gcc_assert (targetm.have_omp_simt_xchg_bfly ());
expand_insn (targetm.code_for_omp_simt_xchg_bfly, 3, ops);
+ if (!rtx_equal_p (target, ops[0].value))
+ emit_move_insn (target, ops[0].value);
}
/* Exchange between SIMT lanes according to given source lane index. */
create_input_operand (&ops[2], idx, SImode);
gcc_assert (targetm.have_omp_simt_xchg_idx ());
expand_insn (targetm.code_for_omp_simt_xchg_idx, 3, ops);
+ if (!rtx_equal_p (target, ops[0].value))
+ emit_move_insn (target, ops[0].value);
}
/* This should get expanded in adjust_simduid_builtins. */
/* This should get expanded in the sanopt pass. */
+static void
+expand_HWASAN_CHECK (internal_fn, gcall *)
+{
+ gcc_unreachable ();
+}
+
+/* For hwasan stack tagging:
+ Clear tags on the dynamically allocated space.
+ For use after an object dynamically allocated on the stack goes out of
+ scope. */
+static void
+expand_HWASAN_ALLOCA_UNPOISON (internal_fn, gcall *gc)
+{
+ gcc_assert (Pmode == ptr_mode);
+ tree restored_position = gimple_call_arg (gc, 0);
+ rtx restored_rtx = expand_expr (restored_position, NULL_RTX, VOIDmode,
+ EXPAND_NORMAL);
+ rtx func = init_one_libfunc ("__hwasan_tag_memory");
+ rtx off = expand_simple_binop (Pmode, MINUS, restored_rtx,
+ stack_pointer_rtx, NULL_RTX, 0,
+ OPTAB_WIDEN);
+ emit_library_call_value (func, NULL_RTX, LCT_NORMAL, VOIDmode,
+ virtual_stack_dynamic_rtx, Pmode,
+ HWASAN_STACK_BACKGROUND, QImode,
+ off, Pmode);
+}
+
+/* For hwasan stack tagging:
+ Return a tag to be used for a dynamic allocation. */
+static void
+expand_HWASAN_CHOOSE_TAG (internal_fn, gcall *gc)
+{
+ tree tag = gimple_call_lhs (gc);
+ rtx target = expand_expr (tag, NULL_RTX, VOIDmode, EXPAND_NORMAL);
+ machine_mode mode = GET_MODE (target);
+ gcc_assert (mode == QImode);
+
+ rtx base_tag = targetm.memtag.extract_tag (hwasan_frame_base (), NULL_RTX);
+ gcc_assert (base_tag);
+ rtx tag_offset = gen_int_mode (hwasan_current_frame_tag (), QImode);
+ rtx chosen_tag = expand_simple_binop (QImode, PLUS, base_tag, tag_offset,
+ target, /* unsignedp = */1,
+ OPTAB_WIDEN);
+ chosen_tag = hwasan_truncate_to_tag_size (chosen_tag, target);
+
+ /* Really need to put the tag into the `target` RTX. */
+ if (chosen_tag != target)
+ {
+ rtx temp = chosen_tag;
+ gcc_assert (GET_MODE (chosen_tag) == mode);
+ emit_move_insn (target, temp);
+ }
+
+ hwasan_increment_frame_tag ();
+}
+
+/* For hwasan stack tagging:
+ Tag a region of space in the shadow stack according to the base pointer of
+ an object on the stack. N.b. the length provided in the internal call is
+ required to be aligned to HWASAN_TAG_GRANULE_SIZE. */
+static void
+expand_HWASAN_MARK (internal_fn, gcall *gc)
+{
+ gcc_assert (ptr_mode == Pmode);
+ HOST_WIDE_INT flag = tree_to_shwi (gimple_call_arg (gc, 0));
+ bool is_poison = ((asan_mark_flags)flag) == ASAN_MARK_POISON;
+
+ tree base = gimple_call_arg (gc, 1);
+ gcc_checking_assert (TREE_CODE (base) == ADDR_EXPR);
+ rtx base_rtx = expand_normal (base);
+
+ rtx tag = is_poison ? HWASAN_STACK_BACKGROUND
+ : targetm.memtag.extract_tag (base_rtx, NULL_RTX);
+ rtx address = targetm.memtag.untagged_pointer (base_rtx, NULL_RTX);
+
+ tree len = gimple_call_arg (gc, 2);
+ rtx r_len = expand_normal (len);
+
+ rtx func = init_one_libfunc ("__hwasan_tag_memory");
+ emit_library_call (func, LCT_NORMAL, VOIDmode, address, Pmode,
+ tag, QImode, r_len, Pmode);
+}
+
+/* For hwasan stack tagging:
+ Store a tag into a pointer. */
+static void
+expand_HWASAN_SET_TAG (internal_fn, gcall *gc)
+{
+ gcc_assert (ptr_mode == Pmode);
+ tree g_target = gimple_call_lhs (gc);
+ tree g_ptr = gimple_call_arg (gc, 0);
+ tree g_tag = gimple_call_arg (gc, 1);
+
+ rtx ptr = expand_normal (g_ptr);
+ rtx tag = expand_expr (g_tag, NULL_RTX, QImode, EXPAND_NORMAL);
+ rtx target = expand_normal (g_target);
+
+ rtx untagged = targetm.memtag.untagged_pointer (ptr, target);
+ rtx tagged_value = targetm.memtag.set_tag (untagged, tag, target);
+ if (tagged_value != target)
+ emit_move_insn (target, tagged_value);
+}
+
+/* This should get expanded in the sanopt pass. */
+
static void
expand_ASAN_CHECK (internal_fn, gcall *)
{
if (++cnt > 30)
return prec + (orig_sign != sign);
}
+ if (CONVERT_EXPR_P (arg)
+ && INTEGRAL_TYPE_P (TREE_TYPE (TREE_OPERAND (arg, 0)))
+ && TYPE_PRECISION (TREE_TYPE (TREE_OPERAND (arg, 0))) > prec)
+ {
+ /* We have e.g. (unsigned short) y_2 where int y_2 = (int) x_1(D);
+ If y_2's min precision is smaller than prec, return that. */
+ int oprec = get_min_precision (TREE_OPERAND (arg, 0), sign);
+ if (oprec < prec)
+ return oprec + (orig_sign != sign);
+ }
if (TREE_CODE (arg) != SSA_NAME)
return prec + (orig_sign != sign);
- wide_int arg_min, arg_max;
- while (get_range_info (arg, &arg_min, &arg_max) != VR_RANGE)
+ value_range r;
+ while (!get_global_range_query ()->range_of_expr (r, arg)
+ || r.kind () != VR_RANGE)
{
gimple *g = SSA_NAME_DEF_STMT (arg);
if (is_gimple_assign (g)
}
if (sign == TYPE_SIGN (TREE_TYPE (arg)))
{
- int p1 = wi::min_precision (arg_min, sign);
- int p2 = wi::min_precision (arg_max, sign);
+ int p1 = wi::min_precision (r.lower_bound (), sign);
+ int p2 = wi::min_precision (r.upper_bound (), sign);
p1 = MAX (p1, p2);
prec = MIN (prec, p1);
}
- else if (sign == UNSIGNED && !wi::neg_p (arg_min, SIGNED))
+ else if (sign == UNSIGNED && !wi::neg_p (r.lower_bound (), SIGNED))
{
- int p = wi::min_precision (arg_max, UNSIGNED);
+ int p = wi::min_precision (r.upper_bound (), UNSIGNED);
prec = MIN (prec, p);
}
return prec + (orig_sign != sign);
/* Add sub/add overflow checking to the statement STMT.
CODE says whether the operation is +, or -. */
-static void
+void
expand_addsub_overflow (location_t loc, tree_code code, tree lhs,
tree arg0, tree arg1, bool unsr_p, bool uns0_p,
bool uns1_p, bool is_ubsan, tree *datap)
NULL, done_label, profile_probability::very_likely ());
goto do_error_label;
case 3:
+ if (get_min_precision (arg1, UNSIGNED)
+ + get_min_precision (arg0, SIGNED) <= GET_MODE_PRECISION (mode))
+ {
+ /* If the first operand is sign extended from narrower type, the
+ second operand is zero extended from narrower type and
+ the sum of the two precisions is smaller or equal to the
+ result precision: if the first argument is at runtime
+ non-negative, maximum result will be 0x7e81 or 0x7f..fe80..01
+ and there will be no overflow, if the first argument is
+ negative and the second argument zero, the result will be
+ 0 and there will be no overflow, if the first argument is
+ negative and the second argument positive, the result when
+ treated as signed will be negative (minimum -0x7f80 or
+ -0x7f..f80..0) there there will be always overflow. So, do
+ res = (U) (s1 * u2)
+ ovf = (S) res < 0 */
+ struct separate_ops ops;
+ ops.code = MULT_EXPR;
+ ops.type
+ = build_nonstandard_integer_type (GET_MODE_PRECISION (mode),
+ 1);
+ ops.op0 = make_tree (ops.type, op0);
+ ops.op1 = make_tree (ops.type, op1);
+ ops.op2 = NULL_TREE;
+ ops.location = loc;
+ res = expand_expr_real_2 (&ops, NULL_RTX, mode, EXPAND_NORMAL);
+ do_compare_rtx_and_jump (res, const0_rtx, GE, false,
+ mode, NULL_RTX, NULL, done_label,
+ profile_probability::very_likely ());
+ goto do_error_label;
+ }
rtx_code_label *do_main_label;
do_main_label = gen_label_rtx ();
do_compare_rtx_and_jump (op0, const0_rtx, GE, false, mode, NULL_RTX,
/* u1 * u2 -> sr */
if (uns0_p && uns1_p && !unsr_p)
{
- uns = true;
+ if ((pos_neg0 | pos_neg1) == 1)
+ {
+ /* If both arguments are zero extended from narrower types,
+ the MSB will be clear on both and so we can pretend it is
+ a normal s1 * s2 -> sr multiplication. */
+ uns0_p = false;
+ uns1_p = false;
+ }
+ else
+ uns = true;
/* Rest of handling of this case after res is computed. */
goto do_main;
}
profile_probability::very_likely ());
goto do_error_label;
}
+ if (get_min_precision (arg0, SIGNED)
+ + get_min_precision (arg1, SIGNED) <= GET_MODE_PRECISION (mode))
+ {
+ /* If both operands are sign extended from narrower types and
+ the sum of the two precisions is smaller or equal to the
+ result precision: if both arguments are at runtime
+ non-negative, maximum result will be 0x3f01 or 0x3f..f0..01
+ and there will be no overflow, if both arguments are negative,
+ maximum result will be 0x40..00 and there will be no overflow
+ either, if one argument is positive and the other argument
+ negative, the result when treated as signed will be negative
+ and there will be always overflow, and if one argument is
+ zero and the other negative the result will be zero and no
+ overflow. So, do
+ res = (U) (s1 * s2)
+ ovf = (S) res < 0 */
+ struct separate_ops ops;
+ ops.code = MULT_EXPR;
+ ops.type
+ = build_nonstandard_integer_type (GET_MODE_PRECISION (mode),
+ 1);
+ ops.op0 = make_tree (ops.type, op0);
+ ops.op1 = make_tree (ops.type, op1);
+ ops.op2 = NULL_TREE;
+ ops.location = loc;
+ res = expand_expr_real_2 (&ops, NULL_RTX, mode, EXPAND_NORMAL);
+ do_compare_rtx_and_jump (res, const0_rtx, GE, false,
+ mode, NULL_RTX, NULL, done_label,
+ profile_probability::very_likely ());
+ goto do_error_label;
+ }
/* The general case, do all the needed comparisons at runtime. */
rtx_code_label *do_main_label, *after_negate_label;
rtx rop0, rop1;
profile_probability::very_likely ());
else
{
+ /* RES is used more than once, place it in a pseudo. */
+ res = force_reg (mode, res);
+
rtx signbit = expand_shift (RSHIFT_EXPR, mode, res, prec - 1,
NULL_RTX, 0);
/* RES is low half of the double width result, HIPART
return fold_build2 (MEM_REF, type, addr, build_int_cst (alias_ptr_type, 0));
}
-/* Expand MASK_LOAD{,_LANES} call STMT using optab OPTAB. */
+/* Expand MASK_LOAD{,_LANES} or LEN_LOAD call STMT using optab OPTAB. */
static void
-expand_mask_load_optab_fn (internal_fn, gcall *stmt, convert_optab optab)
+expand_partial_load_optab_fn (internal_fn, gcall *stmt, convert_optab optab)
{
class expand_operand ops[3];
tree type, lhs, rhs, maskt;
if (optab == vec_mask_load_lanes_optab)
icode = get_multi_vector_move (type, optab);
+ else if (optab == len_load_optab)
+ icode = direct_optab_handler (optab, TYPE_MODE (type));
else
icode = convert_optab_handler (optab, TYPE_MODE (type),
TYPE_MODE (TREE_TYPE (maskt)));
target = expand_expr (lhs, NULL_RTX, VOIDmode, EXPAND_WRITE);
create_output_operand (&ops[0], target, TYPE_MODE (type));
create_fixed_operand (&ops[1], mem);
- create_input_operand (&ops[2], mask, TYPE_MODE (TREE_TYPE (maskt)));
+ if (optab == len_load_optab)
+ create_convert_operand_from (&ops[2], mask, TYPE_MODE (TREE_TYPE (maskt)),
+ TYPE_UNSIGNED (TREE_TYPE (maskt)));
+ else
+ create_input_operand (&ops[2], mask, TYPE_MODE (TREE_TYPE (maskt)));
expand_insn (icode, 3, ops);
if (!rtx_equal_p (target, ops[0].value))
emit_move_insn (target, ops[0].value);
}
+#define expand_mask_load_optab_fn expand_partial_load_optab_fn
#define expand_mask_load_lanes_optab_fn expand_mask_load_optab_fn
+#define expand_len_load_optab_fn expand_partial_load_optab_fn
-/* Expand MASK_STORE{,_LANES} call STMT using optab OPTAB. */
+/* Expand MASK_STORE{,_LANES} or LEN_STORE call STMT using optab OPTAB. */
static void
-expand_mask_store_optab_fn (internal_fn, gcall *stmt, convert_optab optab)
+expand_partial_store_optab_fn (internal_fn, gcall *stmt, convert_optab optab)
{
class expand_operand ops[3];
tree type, lhs, rhs, maskt;
if (optab == vec_mask_store_lanes_optab)
icode = get_multi_vector_move (type, optab);
+ else if (optab == len_store_optab)
+ icode = direct_optab_handler (optab, TYPE_MODE (type));
else
icode = convert_optab_handler (optab, TYPE_MODE (type),
TYPE_MODE (TREE_TYPE (maskt)));
reg = expand_normal (rhs);
create_fixed_operand (&ops[0], mem);
create_input_operand (&ops[1], reg, TYPE_MODE (type));
- create_input_operand (&ops[2], mask, TYPE_MODE (TREE_TYPE (maskt)));
+ if (optab == len_store_optab)
+ create_convert_operand_from (&ops[2], mask, TYPE_MODE (TREE_TYPE (maskt)),
+ TYPE_UNSIGNED (TREE_TYPE (maskt)));
+ else
+ create_input_operand (&ops[2], mask, TYPE_MODE (TREE_TYPE (maskt)));
expand_insn (icode, 3, ops);
}
+#define expand_mask_store_optab_fn expand_partial_store_optab_fn
#define expand_mask_store_lanes_optab_fn expand_mask_store_optab_fn
+#define expand_len_store_optab_fn expand_partial_store_optab_fn
+
+/* Expand VCOND, VCONDU and VCONDEQ optab internal functions.
+ The expansion of STMT happens based on OPTAB table associated. */
+
+static void
+expand_vec_cond_optab_fn (internal_fn, gcall *stmt, convert_optab optab)
+{
+ class expand_operand ops[6];
+ insn_code icode;
+ tree lhs = gimple_call_lhs (stmt);
+ tree op0a = gimple_call_arg (stmt, 0);
+ tree op0b = gimple_call_arg (stmt, 1);
+ tree op1 = gimple_call_arg (stmt, 2);
+ tree op2 = gimple_call_arg (stmt, 3);
+ enum tree_code tcode = (tree_code) int_cst_value (gimple_call_arg (stmt, 4));
+
+ tree vec_cond_type = TREE_TYPE (lhs);
+ tree op_mode = TREE_TYPE (op0a);
+ bool unsignedp = TYPE_UNSIGNED (op_mode);
+
+ machine_mode mode = TYPE_MODE (vec_cond_type);
+ machine_mode cmp_op_mode = TYPE_MODE (op_mode);
+
+ icode = convert_optab_handler (optab, mode, cmp_op_mode);
+ rtx comparison
+ = vector_compare_rtx (VOIDmode, tcode, op0a, op0b, unsignedp, icode, 4);
+ rtx rtx_op1 = expand_normal (op1);
+ rtx rtx_op2 = expand_normal (op2);
+
+ rtx target = expand_expr (lhs, NULL_RTX, VOIDmode, EXPAND_WRITE);
+ create_output_operand (&ops[0], target, mode);
+ create_input_operand (&ops[1], rtx_op1, mode);
+ create_input_operand (&ops[2], rtx_op2, mode);
+ create_fixed_operand (&ops[3], comparison);
+ create_fixed_operand (&ops[4], XEXP (comparison, 0));
+ create_fixed_operand (&ops[5], XEXP (comparison, 1));
+ expand_insn (icode, 6, ops);
+ if (!rtx_equal_p (ops[0].value, target))
+ emit_move_insn (target, ops[0].value);
+}
+
+/* Expand VCOND_MASK optab internal function.
+ The expansion of STMT happens based on OPTAB table associated. */
+
+static void
+expand_vec_cond_mask_optab_fn (internal_fn, gcall *stmt, convert_optab optab)
+{
+ class expand_operand ops[4];
+
+ tree lhs = gimple_call_lhs (stmt);
+ tree op0 = gimple_call_arg (stmt, 0);
+ tree op1 = gimple_call_arg (stmt, 1);
+ tree op2 = gimple_call_arg (stmt, 2);
+ tree vec_cond_type = TREE_TYPE (lhs);
+
+ machine_mode mode = TYPE_MODE (vec_cond_type);
+ machine_mode mask_mode = TYPE_MODE (TREE_TYPE (op0));
+ enum insn_code icode = convert_optab_handler (optab, mode, mask_mode);
+ rtx mask, rtx_op1, rtx_op2;
+
+ gcc_assert (icode != CODE_FOR_nothing);
+
+ mask = expand_normal (op0);
+ rtx_op1 = expand_normal (op1);
+ rtx_op2 = expand_normal (op2);
+
+ mask = force_reg (mask_mode, mask);
+ rtx_op1 = force_reg (mode, rtx_op1);
+
+ rtx target = expand_expr (lhs, NULL_RTX, VOIDmode, EXPAND_WRITE);
+ create_output_operand (&ops[0], target, mode);
+ create_input_operand (&ops[1], rtx_op1, mode);
+ create_input_operand (&ops[2], rtx_op2, mode);
+ create_input_operand (&ops[3], mask, mask_mode);
+ expand_insn (icode, 4, ops);
+ if (!rtx_equal_p (ops[0].value, target))
+ emit_move_insn (target, ops[0].value);
+}
+
+/* Expand VEC_SET internal functions. */
+
+static void
+expand_vec_set_optab_fn (internal_fn, gcall *stmt, convert_optab optab)
+{
+ tree lhs = gimple_call_lhs (stmt);
+ tree op0 = gimple_call_arg (stmt, 0);
+ tree op1 = gimple_call_arg (stmt, 1);
+ tree op2 = gimple_call_arg (stmt, 2);
+ rtx target = expand_expr (lhs, NULL_RTX, VOIDmode, EXPAND_WRITE);
+ rtx src = expand_normal (op0);
+
+ machine_mode outermode = TYPE_MODE (TREE_TYPE (op0));
+ scalar_mode innermode = GET_MODE_INNER (outermode);
+
+ rtx value = expand_normal (op1);
+ rtx pos = expand_normal (op2);
+
+ class expand_operand ops[3];
+ enum insn_code icode = optab_handler (optab, outermode);
+
+ if (icode != CODE_FOR_nothing)
+ {
+ rtx temp = gen_reg_rtx (outermode);
+ emit_move_insn (temp, src);
+
+ create_fixed_operand (&ops[0], temp);
+ create_input_operand (&ops[1], value, innermode);
+ create_convert_operand_from (&ops[2], pos, TYPE_MODE (TREE_TYPE (op2)),
+ true);
+ if (maybe_expand_insn (icode, 3, ops))
+ {
+ emit_move_insn (target, temp);
+ return;
+ }
+ }
+ gcc_unreachable ();
+}
static void
expand_ABNORMAL_DISPATCHER (internal_fn, gcall *)
emit_insn (pattern);
}
+/* Expand the IFN_DEFERRED_INIT function:
+ LHS = DEFERRED_INIT (SIZE of the DECL, INIT_TYPE, IS_VLA);
+
+ if IS_VLA is false, the LHS is the DECL itself,
+ if IS_VLA is true, the LHS is a MEM_REF whose address is the pointer
+ to this DECL.
+
+ Initialize the LHS with zero/pattern according to its second argument
+ INIT_TYPE:
+ if INIT_TYPE is AUTO_INIT_ZERO, use zeroes to initialize;
+ if INIT_TYPE is AUTO_INIT_PATTERN, use 0xFE byte-repeatable pattern
+ to initialize;
+ The LHS variable is initialized including paddings.
+ The reasons to choose 0xFE for pattern initialization are:
+ 1. It is a non-canonical virtual address on x86_64, and at the
+ high end of the i386 kernel address space.
+ 2. It is a very large float value (-1.694739530317379e+38).
+ 3. It is also an unusual number for integers. */
+#define INIT_PATTERN_VALUE 0xFE
+static void
+expand_DEFERRED_INIT (internal_fn, gcall *stmt)
+{
+ tree lhs = gimple_call_lhs (stmt);
+ tree var_size = gimple_call_arg (stmt, 0);
+ enum auto_init_type init_type
+ = (enum auto_init_type) TREE_INT_CST_LOW (gimple_call_arg (stmt, 1));
+ bool reg_lhs = true;
+
+ tree var_type = TREE_TYPE (lhs);
+ gcc_assert (init_type > AUTO_INIT_UNINITIALIZED);
+
+ if (TREE_CODE (lhs) == SSA_NAME)
+ reg_lhs = true;
+ else
+ {
+ rtx tem = expand_expr (lhs, NULL_RTX, VOIDmode, EXPAND_WRITE);
+ reg_lhs = !MEM_P (tem);
+ }
+
+ if (!reg_lhs)
+ {
+ /* If this is a VLA or the variable is not in register,
+ expand to a memset to initialize it. */
+ mark_addressable (lhs);
+ tree var_addr = build_fold_addr_expr (lhs);
+
+ tree value = (init_type == AUTO_INIT_PATTERN) ?
+ build_int_cst (integer_type_node,
+ INIT_PATTERN_VALUE) :
+ integer_zero_node;
+ tree m_call = build_call_expr (builtin_decl_implicit (BUILT_IN_MEMSET),
+ 3, var_addr, value, var_size);
+ /* Expand this memset call. */
+ expand_builtin_memset (m_call, NULL_RTX, TYPE_MODE (var_type));
+ }
+ else
+ {
+ /* If this variable is in a register, use expand_assignment might
+ generate better code. */
+ tree init = build_zero_cst (var_type);
+ unsigned HOST_WIDE_INT total_bytes
+ = tree_to_uhwi (TYPE_SIZE_UNIT (var_type));
+
+ if (init_type == AUTO_INIT_PATTERN)
+ {
+ unsigned char *buf = (unsigned char *) xmalloc (total_bytes);
+ memset (buf, INIT_PATTERN_VALUE, total_bytes);
+ if (can_native_interpret_type_p (var_type))
+ init = native_interpret_expr (var_type, buf, total_bytes);
+ else
+ {
+ tree itype = build_nonstandard_integer_type
+ (total_bytes * BITS_PER_UNIT, 1);
+ wide_int w = wi::from_buffer (buf, total_bytes);
+ init = build1 (VIEW_CONVERT_EXPR, var_type,
+ wide_int_to_tree (itype, w));
+ }
+ }
+
+ expand_assignment (lhs, init, false);
+ }
+}
+
/* The size of an OpenACC compute dimension. */
static void
emit_move_insn (lhs_rtx, ops[0].value);
}
+/* Helper for expand_DIVMOD. Return true if the sequence starting with
+ INSN contains any call insns or insns with {,U}{DIV,MOD} rtxes. */
+
+static bool
+contains_call_div_mod (rtx_insn *insn)
+{
+ subrtx_iterator::array_type array;
+ for (; insn; insn = NEXT_INSN (insn))
+ if (CALL_P (insn))
+ return true;
+ else if (INSN_P (insn))
+ FOR_EACH_SUBRTX (iter, array, PATTERN (insn), NONCONST)
+ switch (GET_CODE (*iter))
+ {
+ case CALL:
+ case DIV:
+ case UDIV:
+ case MOD:
+ case UMOD:
+ return true;
+ default:
+ break;
+ }
+ return false;
+ }
+
/* Expand DIVMOD() using:
a) optab handler for udivmod/sdivmod if it is available.
b) If optab_handler doesn't exist, generate call to
rtx op1 = expand_normal (arg1);
rtx target = expand_expr (lhs, NULL_RTX, VOIDmode, EXPAND_WRITE);
- rtx quotient, remainder, libfunc;
+ rtx quotient = NULL_RTX, remainder = NULL_RTX;
+ rtx_insn *insns = NULL;
+
+ if (TREE_CODE (arg1) == INTEGER_CST)
+ {
+ /* For DIVMOD by integral constants, there could be efficient code
+ expanded inline e.g. using shifts and plus/minus. Try to expand
+ the division and modulo and if it emits any library calls or any
+ {,U}{DIV,MOD} rtxes throw it away and use a divmod optab or
+ divmod libcall. */
+ scalar_int_mode int_mode;
+ if (remainder == NULL_RTX
+ && optimize
+ && CONST_INT_P (op1)
+ && !pow2p_hwi (INTVAL (op1))
+ && is_int_mode (TYPE_MODE (type), &int_mode)
+ && GET_MODE_SIZE (int_mode) == 2 * UNITS_PER_WORD
+ && optab_handler (and_optab, word_mode) != CODE_FOR_nothing
+ && optab_handler (add_optab, word_mode) != CODE_FOR_nothing
+ && optimize_insn_for_speed_p ())
+ {
+ rtx_insn *last = get_last_insn ();
+ remainder = NULL_RTX;
+ quotient = expand_doubleword_divmod (int_mode, op0, op1, &remainder,
+ TYPE_UNSIGNED (type));
+ if (quotient != NULL_RTX)
+ {
+ if (optab_handler (mov_optab, int_mode) != CODE_FOR_nothing)
+ {
+ rtx_insn *move = emit_move_insn (quotient, quotient);
+ set_dst_reg_note (move, REG_EQUAL,
+ gen_rtx_fmt_ee (TYPE_UNSIGNED (type)
+ ? UDIV : DIV, int_mode,
+ copy_rtx (op0), op1),
+ quotient);
+ move = emit_move_insn (remainder, remainder);
+ set_dst_reg_note (move, REG_EQUAL,
+ gen_rtx_fmt_ee (TYPE_UNSIGNED (type)
+ ? UMOD : MOD, int_mode,
+ copy_rtx (op0), op1),
+ quotient);
+ }
+ }
+ else
+ delete_insns_since (last);
+ }
+
+ if (remainder == NULL_RTX)
+ {
+ struct separate_ops ops;
+ ops.code = TRUNC_DIV_EXPR;
+ ops.type = type;
+ ops.op0 = make_tree (ops.type, op0);
+ ops.op1 = arg1;
+ ops.op2 = NULL_TREE;
+ ops.location = gimple_location (call_stmt);
+ start_sequence ();
+ quotient = expand_expr_real_2 (&ops, NULL_RTX, mode, EXPAND_NORMAL);
+ if (contains_call_div_mod (get_insns ()))
+ quotient = NULL_RTX;
+ else
+ {
+ ops.code = TRUNC_MOD_EXPR;
+ remainder = expand_expr_real_2 (&ops, NULL_RTX, mode,
+ EXPAND_NORMAL);
+ if (contains_call_div_mod (get_insns ()))
+ remainder = NULL_RTX;
+ }
+ if (remainder)
+ insns = get_insns ();
+ end_sequence ();
+ }
+ }
+
+ if (remainder)
+ emit_insn (insns);
/* Check if optab_handler exists for divmod_optab for given mode. */
- if (optab_handler (tab, mode) != CODE_FOR_nothing)
+ else if (optab_handler (tab, mode) != CODE_FOR_nothing)
{
quotient = gen_reg_rtx (mode);
remainder = gen_reg_rtx (mode);
}
/* Generate call to divmod libfunc if it exists. */
- else if ((libfunc = optab_libfunc (tab, mode)) != NULL_RTX)
+ else if (rtx libfunc = optab_libfunc (tab, mode))
targetm.expand_divmod_libfunc (libfunc, mode, op0, op1,
"ient, &remainder);
#define direct_cond_unary_optab_supported_p direct_optab_supported_p
#define direct_cond_binary_optab_supported_p direct_optab_supported_p
#define direct_cond_ternary_optab_supported_p direct_optab_supported_p
-#define direct_mask_load_optab_supported_p direct_optab_supported_p
+#define direct_mask_load_optab_supported_p convert_optab_supported_p
#define direct_load_lanes_optab_supported_p multi_vector_optab_supported_p
#define direct_mask_load_lanes_optab_supported_p multi_vector_optab_supported_p
#define direct_gather_load_optab_supported_p convert_optab_supported_p
-#define direct_mask_store_optab_supported_p direct_optab_supported_p
+#define direct_len_load_optab_supported_p direct_optab_supported_p
+#define direct_mask_store_optab_supported_p convert_optab_supported_p
#define direct_store_lanes_optab_supported_p multi_vector_optab_supported_p
#define direct_mask_store_lanes_optab_supported_p multi_vector_optab_supported_p
+#define direct_vec_cond_mask_optab_supported_p convert_optab_supported_p
+#define direct_vec_cond_optab_supported_p convert_optab_supported_p
#define direct_scatter_store_optab_supported_p convert_optab_supported_p
+#define direct_len_store_optab_supported_p direct_optab_supported_p
#define direct_while_optab_supported_p convert_optab_supported_p
#define direct_fold_extract_optab_supported_p direct_optab_supported_p
#define direct_fold_left_optab_supported_p direct_optab_supported_p
#define direct_mask_fold_left_optab_supported_p direct_optab_supported_p
#define direct_check_ptrs_optab_supported_p direct_optab_supported_p
+#define direct_vec_set_optab_supported_p direct_optab_supported_p
/* Return the optab used by internal function FN. */
case IFN_FNMS:
case IFN_AVG_FLOOR:
case IFN_AVG_CEIL:
+ case IFN_MULH:
case IFN_MULHS:
case IFN_MULHRS:
case IFN_FMIN:
case IFN_MASK_LOAD_LANES:
case IFN_GATHER_LOAD:
case IFN_MASK_GATHER_LOAD:
+ case IFN_LEN_LOAD:
return true;
default:
case IFN_MASK_STORE_LANES:
case IFN_SCATTER_STORE:
case IFN_MASK_SCATTER_STORE:
+ case IFN_LEN_STORE:
return true;
default:
switch (fn)
{
case IFN_MASK_STORE:
+ case IFN_MASK_STORE_LANES:
case IFN_SCATTER_STORE:
case IFN_MASK_SCATTER_STORE:
+ case IFN_LEN_STORE:
return 3;
default:
bool
vectorized_internal_fn_supported_p (internal_fn ifn, tree type)
{
+ if (VECTOR_MODE_P (TYPE_MODE (type)))
+ return direct_internal_fn_supported_p (ifn, type, OPTIMIZE_FOR_SPEED);
+
scalar_mode smode;
- if (!VECTOR_TYPE_P (type) && is_a <scalar_mode> (TYPE_MODE (type), &smode))
+ if (!is_a <scalar_mode> (TYPE_MODE (type), &smode))
+ return false;
+
+ machine_mode vmode = targetm.vectorize.preferred_simd_mode (smode);
+ if (VECTOR_MODE_P (vmode))
{
- machine_mode vmode = targetm.vectorize.preferred_simd_mode (smode);
- if (VECTOR_MODE_P (vmode))
- type = build_vector_type_for_mode (type, vmode);
+ tree vectype = build_vector_type_for_mode (type, vmode);
+ if (direct_internal_fn_supported_p (ifn, vectype, OPTIMIZE_FOR_SPEED))
+ return true;
}
- return (VECTOR_MODE_P (TYPE_MODE (type))
- && direct_internal_fn_supported_p (ifn, type, OPTIMIZE_FOR_SPEED));
+ auto_vector_modes vector_modes;
+ targetm.vectorize.autovectorize_vector_modes (&vector_modes, true);
+ for (machine_mode base_mode : vector_modes)
+ if (related_vector_mode (base_mode, smode).exists (&vmode))
+ {
+ tree vectype = build_vector_type_for_mode (type, vmode);
+ if (direct_internal_fn_supported_p (ifn, vectype, OPTIMIZE_FOR_SPEED))
+ return true;
+ }
+
+ return false;
+}
+
+void
+expand_SHUFFLEVECTOR (internal_fn, gcall *)
+{
+ gcc_unreachable ();
}
void