summaryrefslogtreecommitdiff
path: root/gcc/internal-fn.c
diff options
context:
space:
mode:
Diffstat (limited to 'gcc/internal-fn.c')
-rw-r--r--gcc/internal-fn.c771
1 files changed, 611 insertions, 160 deletions
diff --git a/gcc/internal-fn.c b/gcc/internal-fn.c
index c07b538938..75fe027f7b 100644
--- a/gcc/internal-fn.c
+++ b/gcc/internal-fn.c
@@ -1,5 +1,5 @@
/* Internal functions.
- Copyright (C) 2011-2016 Free Software Foundation, Inc.
+ Copyright (C) 2011-2017 Free Software Foundation, Inc.
This file is part of GCC.
@@ -27,8 +27,10 @@ along with GCC; see the file COPYING3. If not see
#include "gimple.h"
#include "predict.h"
#include "stringpool.h"
+#include "tree-vrp.h"
#include "tree-ssanames.h"
#include "expmed.h"
+#include "memmodel.h"
#include "optabs.h"
#include "emit-rtl.h"
#include "diagnostic-core.h"
@@ -39,6 +41,8 @@ along with GCC; see the file COPYING3. If not see
#include "expr.h"
#include "ubsan.h"
#include "recog.h"
+#include "builtins.h"
+#include "optabs-tree.h"
/* The names of each internal function, indexed by function number. */
const char *const internal_fn_name_array[] = {
@@ -154,6 +158,182 @@ expand_ANNOTATE (internal_fn, gcall *)
gcc_unreachable ();
}
+/* This should get expanded in omp_device_lower pass. */
+
+static void
+expand_GOMP_USE_SIMT (internal_fn, gcall *)
+{
+ gcc_unreachable ();
+}
+
+/* This should get expanded in omp_device_lower pass. */
+
+static void
+expand_GOMP_SIMT_ENTER (internal_fn, gcall *)
+{
+ gcc_unreachable ();
+}
+
+/* Allocate per-lane storage and begin non-uniform execution region. */
+
+static void
+expand_GOMP_SIMT_ENTER_ALLOC (internal_fn, gcall *stmt)
+{
+ rtx target;
+ tree lhs = gimple_call_lhs (stmt);
+ if (lhs)
+ target = expand_expr (lhs, NULL_RTX, VOIDmode, EXPAND_WRITE);
+ else
+ target = gen_reg_rtx (Pmode);
+ rtx size = expand_normal (gimple_call_arg (stmt, 0));
+ rtx align = expand_normal (gimple_call_arg (stmt, 1));
+ struct expand_operand ops[3];
+ create_output_operand (&ops[0], target, Pmode);
+ create_input_operand (&ops[1], size, Pmode);
+ create_input_operand (&ops[2], align, Pmode);
+ gcc_assert (targetm.have_omp_simt_enter ());
+ expand_insn (targetm.code_for_omp_simt_enter, 3, ops);
+}
+
+/* Deallocate per-lane storage and leave non-uniform execution region. */
+
+static void
+expand_GOMP_SIMT_EXIT (internal_fn, gcall *stmt)
+{
+ gcc_checking_assert (!gimple_call_lhs (stmt));
+ rtx arg = expand_normal (gimple_call_arg (stmt, 0));
+ struct expand_operand ops[1];
+ create_input_operand (&ops[0], arg, Pmode);
+ gcc_assert (targetm.have_omp_simt_exit ());
+ expand_insn (targetm.code_for_omp_simt_exit, 1, ops);
+}
+
+/* Lane index on SIMT targets: thread index in the warp on NVPTX. On targets
+ without SIMT execution this should be expanded in omp_device_lower pass. */
+
+static void
+expand_GOMP_SIMT_LANE (internal_fn, gcall *stmt)
+{
+ tree lhs = gimple_call_lhs (stmt);
+ if (!lhs)
+ return;
+
+ rtx target = expand_expr (lhs, NULL_RTX, VOIDmode, EXPAND_WRITE);
+ gcc_assert (targetm.have_omp_simt_lane ());
+ emit_insn (targetm.gen_omp_simt_lane (target));
+}
+
+/* This should get expanded in omp_device_lower pass. */
+
+static void
+expand_GOMP_SIMT_VF (internal_fn, gcall *)
+{
+ gcc_unreachable ();
+}
+
+/* Lane index of the first SIMT lane that supplies a non-zero argument.
+ This is a SIMT counterpart to GOMP_SIMD_LAST_LANE, used to represent the
+ lane that executed the last iteration for handling OpenMP lastprivate. */
+
+static void
+expand_GOMP_SIMT_LAST_LANE (internal_fn, gcall *stmt)
+{
+ tree lhs = gimple_call_lhs (stmt);
+ if (!lhs)
+ return;
+
+ rtx target = expand_expr (lhs, NULL_RTX, VOIDmode, EXPAND_WRITE);
+ rtx cond = expand_normal (gimple_call_arg (stmt, 0));
+ machine_mode mode = TYPE_MODE (TREE_TYPE (lhs));
+ struct expand_operand ops[2];
+ create_output_operand (&ops[0], target, mode);
+ 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);
+}
+
+/* Non-transparent predicate used in SIMT lowering of OpenMP "ordered". */
+
+static void
+expand_GOMP_SIMT_ORDERED_PRED (internal_fn, gcall *stmt)
+{
+ tree lhs = gimple_call_lhs (stmt);
+ if (!lhs)
+ return;
+
+ rtx target = expand_expr (lhs, NULL_RTX, VOIDmode, EXPAND_WRITE);
+ rtx ctr = expand_normal (gimple_call_arg (stmt, 0));
+ machine_mode mode = TYPE_MODE (TREE_TYPE (lhs));
+ struct expand_operand ops[2];
+ create_output_operand (&ops[0], target, mode);
+ create_input_operand (&ops[1], ctr, mode);
+ gcc_assert (targetm.have_omp_simt_ordered ());
+ expand_insn (targetm.code_for_omp_simt_ordered, 2, ops);
+}
+
+/* "Or" boolean reduction across SIMT lanes: return non-zero in all lanes if
+ any lane supplies a non-zero argument. */
+
+static void
+expand_GOMP_SIMT_VOTE_ANY (internal_fn, gcall *stmt)
+{
+ tree lhs = gimple_call_lhs (stmt);
+ if (!lhs)
+ return;
+
+ rtx target = expand_expr (lhs, NULL_RTX, VOIDmode, EXPAND_WRITE);
+ rtx cond = expand_normal (gimple_call_arg (stmt, 0));
+ machine_mode mode = TYPE_MODE (TREE_TYPE (lhs));
+ struct expand_operand ops[2];
+ create_output_operand (&ops[0], target, mode);
+ 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);
+}
+
+/* Exchange between SIMT lanes with a "butterfly" pattern: source lane index
+ is destination lane index XOR given offset. */
+
+static void
+expand_GOMP_SIMT_XCHG_BFLY (internal_fn, gcall *stmt)
+{
+ tree lhs = gimple_call_lhs (stmt);
+ if (!lhs)
+ return;
+
+ rtx target = expand_expr (lhs, NULL_RTX, VOIDmode, EXPAND_WRITE);
+ rtx src = expand_normal (gimple_call_arg (stmt, 0));
+ rtx idx = expand_normal (gimple_call_arg (stmt, 1));
+ machine_mode mode = TYPE_MODE (TREE_TYPE (lhs));
+ struct expand_operand ops[3];
+ create_output_operand (&ops[0], target, mode);
+ create_input_operand (&ops[1], src, mode);
+ 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);
+}
+
+/* Exchange between SIMT lanes according to given source lane index. */
+
+static void
+expand_GOMP_SIMT_XCHG_IDX (internal_fn, gcall *stmt)
+{
+ tree lhs = gimple_call_lhs (stmt);
+ if (!lhs)
+ return;
+
+ rtx target = expand_expr (lhs, NULL_RTX, VOIDmode, EXPAND_WRITE);
+ rtx src = expand_normal (gimple_call_arg (stmt, 0));
+ rtx idx = expand_normal (gimple_call_arg (stmt, 1));
+ machine_mode mode = TYPE_MODE (TREE_TYPE (lhs));
+ struct expand_operand ops[3];
+ create_output_operand (&ops[0], target, mode);
+ create_input_operand (&ops[1], src, mode);
+ 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);
+}
+
/* This should get expanded in adjust_simduid_builtins. */
static void
@@ -234,92 +414,45 @@ expand_ASAN_CHECK (internal_fn, gcall *)
gcc_unreachable ();
}
-/* This should get expanded in the tsan pass. */
+/* This should get expanded in the sanopt pass. */
static void
-expand_TSAN_FUNC_EXIT (internal_fn, gcall *)
+expand_ASAN_MARK (internal_fn, gcall *)
{
gcc_unreachable ();
}
-/* Helper function for expand_addsub_overflow. Return 1
- if ARG interpreted as signed in its precision is known to be always
- positive or 2 if ARG is known to be always negative, or 3 if ARG may
- be positive or negative. */
+/* This should get expanded in the sanopt pass. */
-static int
-get_range_pos_neg (tree arg)
+static void
+expand_ASAN_POISON (internal_fn, gcall *)
{
- if (arg == error_mark_node)
- return 3;
+ gcc_unreachable ();
+}
- int prec = TYPE_PRECISION (TREE_TYPE (arg));
- int cnt = 0;
- if (TREE_CODE (arg) == INTEGER_CST)
- {
- wide_int w = wi::sext (arg, prec);
- if (wi::neg_p (w))
- return 2;
- else
- return 1;
- }
- while (CONVERT_EXPR_P (arg)
- && INTEGRAL_TYPE_P (TREE_TYPE (TREE_OPERAND (arg, 0)))
- && TYPE_PRECISION (TREE_TYPE (TREE_OPERAND (arg, 0))) <= prec)
- {
- arg = TREE_OPERAND (arg, 0);
- /* Narrower value zero extended into wider type
- will always result in positive values. */
- if (TYPE_UNSIGNED (TREE_TYPE (arg))
- && TYPE_PRECISION (TREE_TYPE (arg)) < prec)
- return 1;
- prec = TYPE_PRECISION (TREE_TYPE (arg));
- if (++cnt > 30)
- return 3;
- }
+/* This should get expanded in the sanopt pass. */
- if (TREE_CODE (arg) != SSA_NAME)
- return 3;
- wide_int arg_min, arg_max;
- while (get_range_info (arg, &arg_min, &arg_max) != VR_RANGE)
- {
- gimple *g = SSA_NAME_DEF_STMT (arg);
- if (is_gimple_assign (g)
- && CONVERT_EXPR_CODE_P (gimple_assign_rhs_code (g)))
- {
- tree t = gimple_assign_rhs1 (g);
- if (INTEGRAL_TYPE_P (TREE_TYPE (t))
- && TYPE_PRECISION (TREE_TYPE (t)) <= prec)
- {
- if (TYPE_UNSIGNED (TREE_TYPE (t))
- && TYPE_PRECISION (TREE_TYPE (t)) < prec)
- return 1;
- prec = TYPE_PRECISION (TREE_TYPE (t));
- arg = t;
- if (++cnt > 30)
- return 3;
- continue;
- }
- }
- return 3;
- }
- if (TYPE_UNSIGNED (TREE_TYPE (arg)))
- {
- /* For unsigned values, the "positive" range comes
- below the "negative" range. */
- if (!wi::neg_p (wi::sext (arg_max, prec), SIGNED))
- return 1;
- if (wi::neg_p (wi::sext (arg_min, prec), SIGNED))
- return 2;
- }
- else
- {
- if (!wi::neg_p (wi::sext (arg_min, prec), SIGNED))
- return 1;
- if (wi::neg_p (wi::sext (arg_max, prec), SIGNED))
- return 2;
- }
- return 3;
+static void
+expand_ASAN_POISON_USE (internal_fn, gcall *)
+{
+ gcc_unreachable ();
+}
+
+/* This should get expanded in the tsan pass. */
+
+static void
+expand_TSAN_FUNC_EXIT (internal_fn, gcall *)
+{
+ gcc_unreachable ();
+}
+
+/* This should get expanded in the lower pass. */
+
+static void
+expand_FALLTHROUGH (internal_fn, gcall *call)
+{
+ error_at (gimple_location (call),
+ "invalid use of attribute %<fallthrough%>");
}
/* Return minimum precision needed to represent all values
@@ -404,9 +537,23 @@ get_min_precision (tree arg, signop sign)
return prec + (orig_sign != sign);
}
+/* Helper for expand_*_overflow. Set the __imag__ part to true
+ (1 except for signed:1 type, in which case store -1). */
+
+static void
+expand_arith_set_overflow (tree lhs, rtx target)
+{
+ if (TYPE_PRECISION (TREE_TYPE (TREE_TYPE (lhs))) == 1
+ && !TYPE_UNSIGNED (TREE_TYPE (TREE_TYPE (lhs))))
+ write_complex_part (target, constm1_rtx, true);
+ else
+ write_complex_part (target, const1_rtx, true);
+}
+
/* Helper for expand_*_overflow. Store RES into the __real__ part
of TARGET. If RES has larger MODE than __real__ part of TARGET,
- set the __imag__ part to 1 if RES doesn't fit into it. */
+ set the __imag__ part to 1 if RES doesn't fit into it. Similarly
+ if LHS has smaller precision than its mode. */
static void
expand_arith_overflow_result_store (tree lhs, rtx target,
@@ -423,7 +570,35 @@ expand_arith_overflow_result_store (tree lhs, rtx target,
do_compare_rtx_and_jump (res, convert_modes (mode, tgtmode, lres, uns),
EQ, true, mode, NULL_RTX, NULL, done_label,
PROB_VERY_LIKELY);
- write_complex_part (target, const1_rtx, true);
+ expand_arith_set_overflow (lhs, target);
+ emit_label (done_label);
+ }
+ int prec = TYPE_PRECISION (TREE_TYPE (TREE_TYPE (lhs)));
+ int tgtprec = GET_MODE_PRECISION (tgtmode);
+ if (prec < tgtprec)
+ {
+ rtx_code_label *done_label = gen_label_rtx ();
+ int uns = TYPE_UNSIGNED (TREE_TYPE (TREE_TYPE (lhs)));
+ res = lres;
+ if (uns)
+ {
+ rtx mask
+ = immed_wide_int_const (wi::shifted_mask (0, prec, false, tgtprec),
+ tgtmode);
+ lres = expand_simple_binop (tgtmode, AND, res, mask, NULL_RTX,
+ true, OPTAB_LIB_WIDEN);
+ }
+ else
+ {
+ lres = expand_shift (LSHIFT_EXPR, tgtmode, res, tgtprec - prec,
+ NULL_RTX, 1);
+ lres = expand_shift (RSHIFT_EXPR, tgtmode, lres, tgtprec - prec,
+ NULL_RTX, 0);
+ }
+ do_compare_rtx_and_jump (res, lres,
+ EQ, true, tgtmode, NULL_RTX, NULL, done_label,
+ PROB_VERY_LIKELY);
+ expand_arith_set_overflow (lhs, target);
emit_label (done_label);
}
write_complex_part (target, lres, false);
@@ -450,7 +625,7 @@ expand_ubsan_result_store (rtx target, rtx res)
static 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)
+ bool uns1_p, bool is_ubsan, tree *datap)
{
rtx res, target = NULL_RTX;
tree fn;
@@ -793,56 +968,68 @@ expand_addsub_overflow (location_t loc, tree_code code, tree lhs,
delete_insns_since (last);
}
- rtx_code_label *sub_check = gen_label_rtx ();
- int pos_neg = 3;
-
/* Compute the operation. On RTL level, the addition is always
unsigned. */
res = expand_binop (mode, code == PLUS_EXPR ? add_optab : sub_optab,
op0, op1, NULL_RTX, false, OPTAB_LIB_WIDEN);
- /* If we can prove one of the arguments (for MINUS_EXPR only
+ /* If we can prove that one of the arguments (for MINUS_EXPR only
the second operand, as subtraction is not commutative) is always
non-negative or always negative, we can do just one comparison
- and conditional jump instead of 2 at runtime, 3 present in the
- emitted code. If one of the arguments is CONST_INT, all we
- need is to make sure it is op1, then the first
- do_compare_rtx_and_jump will be just folded. Otherwise try
- to use range info if available. */
- if (code == PLUS_EXPR && CONST_INT_P (op0))
- std::swap (op0, op1);
- else if (CONST_INT_P (op1))
- ;
- else if (code == PLUS_EXPR && TREE_CODE (arg0) == SSA_NAME)
+ and conditional jump. */
+ int pos_neg = get_range_pos_neg (arg1);
+ if (code == PLUS_EXPR)
{
- pos_neg = get_range_pos_neg (arg0);
- if (pos_neg != 3)
- std::swap (op0, op1);
+ int pos_neg0 = get_range_pos_neg (arg0);
+ if (pos_neg0 != 3 && pos_neg == 3)
+ {
+ std::swap (op0, op1);
+ pos_neg = pos_neg0;
+ }
}
- if (pos_neg == 3 && !CONST_INT_P (op1) && TREE_CODE (arg1) == SSA_NAME)
- pos_neg = get_range_pos_neg (arg1);
- /* If the op1 is negative, we have to use a different check. */
+ /* Addition overflows if and only if the two operands have the same sign,
+ and the result has the opposite sign. Subtraction overflows if and
+ only if the two operands have opposite sign, and the subtrahend has
+ the same sign as the result. Here 0 is counted as positive. */
if (pos_neg == 3)
- do_compare_rtx_and_jump (op1, const0_rtx, LT, false, mode, NULL_RTX,
- NULL, sub_check, PROB_EVEN);
+ {
+ /* Compute op0 ^ op1 (operands have opposite sign). */
+ rtx op_xor = expand_binop (mode, xor_optab, op0, op1, NULL_RTX, false,
+ OPTAB_LIB_WIDEN);
- /* Compare the result of the operation with one of the operands. */
- if (pos_neg & 1)
- do_compare_rtx_and_jump (res, op0, code == PLUS_EXPR ? GE : LE,
- false, mode, NULL_RTX, NULL, done_label,
- PROB_VERY_LIKELY);
+ /* Compute res ^ op1 (result and 2nd operand have opposite sign). */
+ rtx res_xor = expand_binop (mode, xor_optab, res, op1, NULL_RTX, false,
+ OPTAB_LIB_WIDEN);
- /* If we get here, we have to print the error. */
- if (pos_neg == 3)
- {
- emit_jump (do_error);
- emit_label (sub_check);
+ rtx tem;
+ if (code == PLUS_EXPR)
+ {
+ /* Compute (res ^ op1) & ~(op0 ^ op1). */
+ tem = expand_unop (mode, one_cmpl_optab, op_xor, NULL_RTX, false);
+ tem = expand_binop (mode, and_optab, res_xor, tem, NULL_RTX, false,
+ OPTAB_LIB_WIDEN);
+ }
+ else
+ {
+ /* Compute (op0 ^ op1) & ~(res ^ op1). */
+ tem = expand_unop (mode, one_cmpl_optab, res_xor, NULL_RTX, false);
+ tem = expand_binop (mode, and_optab, op_xor, tem, NULL_RTX, false,
+ OPTAB_LIB_WIDEN);
+ }
+
+ /* No overflow if the result has bit sign cleared. */
+ do_compare_rtx_and_jump (tem, const0_rtx, GE, false, mode, NULL_RTX,
+ NULL, done_label, PROB_VERY_LIKELY);
}
- /* We have k = a + b for b < 0 here. k <= a must hold. */
- if (pos_neg & 2)
- do_compare_rtx_and_jump (res, op0, code == PLUS_EXPR ? LE : GE,
+ /* Compare the result of the operation with the first operand.
+ No overflow for addition if second operand is positive and result
+ is larger or second operand is negative and result is smaller.
+ Likewise for subtraction with sign of second operand flipped. */
+ else
+ do_compare_rtx_and_jump (res, op0,
+ (pos_neg == 1) ^ (code == MINUS_EXPR) ? GE : LE,
false, mode, NULL_RTX, NULL, done_label,
PROB_VERY_LIKELY);
}
@@ -854,13 +1041,13 @@ expand_addsub_overflow (location_t loc, tree_code code, tree lhs,
/* Expand the ubsan builtin call. */
push_temp_slots ();
fn = ubsan_build_overflow_builtin (code, loc, TREE_TYPE (arg0),
- arg0, arg1);
+ arg0, arg1, datap);
expand_normal (fn);
pop_temp_slots ();
do_pending_stack_adjust ();
}
else if (lhs)
- write_complex_part (target, const1_rtx, true);
+ expand_arith_set_overflow (lhs, target);
/* We're done. */
emit_label (done_label);
@@ -883,7 +1070,8 @@ expand_addsub_overflow (location_t loc, tree_code code, tree lhs,
/* Add negate overflow checking to the statement STMT. */
static void
-expand_neg_overflow (location_t loc, tree lhs, tree arg1, bool is_ubsan)
+expand_neg_overflow (location_t loc, tree lhs, tree arg1, bool is_ubsan,
+ tree *datap)
{
rtx res, op1;
tree fn;
@@ -949,13 +1137,13 @@ expand_neg_overflow (location_t loc, tree lhs, tree arg1, bool is_ubsan)
/* Expand the ubsan builtin call. */
push_temp_slots ();
fn = ubsan_build_overflow_builtin (NEGATE_EXPR, loc, TREE_TYPE (arg1),
- arg1, NULL_TREE);
+ arg1, NULL_TREE, datap);
expand_normal (fn);
pop_temp_slots ();
do_pending_stack_adjust ();
}
else if (lhs)
- write_complex_part (target, const1_rtx, true);
+ expand_arith_set_overflow (lhs, target);
/* We're done. */
emit_label (done_label);
@@ -973,7 +1161,8 @@ expand_neg_overflow (location_t loc, tree lhs, tree arg1, bool is_ubsan)
static void
expand_mul_overflow (location_t loc, tree lhs, tree arg0, tree arg1,
- bool unsr_p, bool uns0_p, bool uns1_p, bool is_ubsan)
+ bool unsr_p, bool uns0_p, bool uns1_p, bool is_ubsan,
+ tree *datap)
{
rtx res, op0, op1;
tree fn, type;
@@ -1081,7 +1270,7 @@ expand_mul_overflow (location_t loc, tree lhs, tree arg0, tree arg1,
NULL, do_main_label, PROB_VERY_LIKELY);
do_compare_rtx_and_jump (op1, const0_rtx, EQ, true, mode, NULL_RTX,
NULL, do_main_label, PROB_VERY_LIKELY);
- write_complex_part (target, const1_rtx, true);
+ expand_arith_set_overflow (lhs, target);
emit_label (do_main_label);
goto do_main;
default:
@@ -1212,7 +1401,7 @@ expand_mul_overflow (location_t loc, tree lhs, tree arg0, tree arg1,
is, thus we can keep do_main code oring in overflow as is. */
do_compare_rtx_and_jump (tem, const0_rtx, EQ, true, mode, NULL_RTX,
NULL, do_main_label, PROB_VERY_LIKELY);
- write_complex_part (target, const1_rtx, true);
+ expand_arith_set_overflow (lhs, target);
emit_label (do_main_label);
goto do_main;
default:
@@ -1271,8 +1460,8 @@ expand_mul_overflow (location_t loc, tree lhs, tree arg0, tree arg1,
res = expand_expr_real_2 (&ops, NULL_RTX, wmode, EXPAND_NORMAL);
rtx hipart = expand_shift (RSHIFT_EXPR, wmode, res, prec,
NULL_RTX, uns);
- hipart = gen_lowpart (mode, hipart);
- res = gen_lowpart (mode, res);
+ hipart = convert_modes (mode, wmode, hipart, uns);
+ res = convert_modes (mode, wmode, res, uns);
if (uns)
/* For the unsigned multiplication, there was overflow if
HIPART is non-zero. */
@@ -1305,16 +1494,16 @@ expand_mul_overflow (location_t loc, tree lhs, tree arg0, tree arg1,
unsigned int hprec = GET_MODE_PRECISION (hmode);
rtx hipart0 = expand_shift (RSHIFT_EXPR, mode, op0, hprec,
NULL_RTX, uns);
- hipart0 = gen_lowpart (hmode, hipart0);
- rtx lopart0 = gen_lowpart (hmode, op0);
+ hipart0 = convert_modes (hmode, mode, hipart0, uns);
+ rtx lopart0 = convert_modes (hmode, mode, op0, uns);
rtx signbit0 = const0_rtx;
if (!uns)
signbit0 = expand_shift (RSHIFT_EXPR, hmode, lopart0, hprec - 1,
NULL_RTX, 0);
rtx hipart1 = expand_shift (RSHIFT_EXPR, mode, op1, hprec,
NULL_RTX, uns);
- hipart1 = gen_lowpart (hmode, hipart1);
- rtx lopart1 = gen_lowpart (hmode, op1);
+ hipart1 = convert_modes (hmode, mode, hipart1, uns);
+ rtx lopart1 = convert_modes (hmode, mode, op1, uns);
rtx signbit1 = const0_rtx;
if (!uns)
signbit1 = expand_shift (RSHIFT_EXPR, hmode, lopart1, hprec - 1,
@@ -1505,11 +1694,12 @@ expand_mul_overflow (location_t loc, tree lhs, tree arg0, tree arg1,
if (loxhi >> (bitsize / 2) == 0 (if uns). */
rtx hipartloxhi = expand_shift (RSHIFT_EXPR, mode, loxhi, hprec,
NULL_RTX, 0);
- hipartloxhi = gen_lowpart (hmode, hipartloxhi);
+ hipartloxhi = convert_modes (hmode, mode, hipartloxhi, 0);
rtx signbitloxhi = const0_rtx;
if (!uns)
signbitloxhi = expand_shift (RSHIFT_EXPR, hmode,
- gen_lowpart (hmode, loxhi),
+ convert_modes (hmode, mode,
+ loxhi, 0),
hprec - 1, NULL_RTX, 0);
do_compare_rtx_and_jump (signbitloxhi, hipartloxhi, NE, true, hmode,
@@ -1519,7 +1709,8 @@ expand_mul_overflow (location_t loc, tree lhs, tree arg0, tree arg1,
/* res = (loxhi << (bitsize / 2)) | (hmode) lo0xlo1; */
rtx loxhishifted = expand_shift (LSHIFT_EXPR, mode, loxhi, hprec,
NULL_RTX, 1);
- tem = convert_modes (mode, hmode, gen_lowpart (hmode, lo0xlo1), 1);
+ tem = convert_modes (mode, hmode,
+ convert_modes (hmode, mode, lo0xlo1, 1), 1);
tem = expand_simple_binop (mode, IOR, loxhishifted, tem, res,
1, OPTAB_DIRECT);
@@ -1610,13 +1801,13 @@ expand_mul_overflow (location_t loc, tree lhs, tree arg0, tree arg1,
/* Expand the ubsan builtin call. */
push_temp_slots ();
fn = ubsan_build_overflow_builtin (MULT_EXPR, loc, TREE_TYPE (arg0),
- arg0, arg1);
+ arg0, arg1, datap);
expand_normal (fn);
pop_temp_slots ();
do_pending_stack_adjust ();
}
else if (lhs)
- write_complex_part (target, const1_rtx, true);
+ expand_arith_set_overflow (lhs, target);
/* We're done. */
emit_label (done_label);
@@ -1627,7 +1818,7 @@ expand_mul_overflow (location_t loc, tree lhs, tree arg0, tree arg1,
rtx_code_label *all_done_label = gen_label_rtx ();
do_compare_rtx_and_jump (res, const0_rtx, GE, false, mode, NULL_RTX,
NULL, all_done_label, PROB_VERY_LIKELY);
- write_complex_part (target, const1_rtx, true);
+ expand_arith_set_overflow (lhs, target);
emit_label (all_done_label);
}
@@ -1638,7 +1829,7 @@ expand_mul_overflow (location_t loc, tree lhs, tree arg0, tree arg1,
rtx_code_label *set_noovf = gen_label_rtx ();
do_compare_rtx_and_jump (op1, const0_rtx, GE, false, mode, NULL_RTX,
NULL, all_done_label, PROB_VERY_LIKELY);
- write_complex_part (target, const1_rtx, true);
+ expand_arith_set_overflow (lhs, target);
do_compare_rtx_and_jump (op0, const0_rtx, EQ, true, mode, NULL_RTX,
NULL, set_noovf, PROB_VERY_LIKELY);
do_compare_rtx_and_jump (op0, constm1_rtx, NE, true, mode, NULL_RTX,
@@ -1659,6 +1850,153 @@ expand_mul_overflow (location_t loc, tree lhs, tree arg0, tree arg1,
}
}
+/* Expand UBSAN_CHECK_* internal function if it has vector operands. */
+
+static void
+expand_vector_ubsan_overflow (location_t loc, enum tree_code code, tree lhs,
+ tree arg0, tree arg1)
+{
+ int cnt = TYPE_VECTOR_SUBPARTS (TREE_TYPE (arg0));
+ rtx_code_label *loop_lab = NULL;
+ rtx cntvar = NULL_RTX;
+ tree cntv = NULL_TREE;
+ tree eltype = TREE_TYPE (TREE_TYPE (arg0));
+ tree sz = TYPE_SIZE (eltype);
+ tree data = NULL_TREE;
+ tree resv = NULL_TREE;
+ rtx lhsr = NULL_RTX;
+ rtx resvr = NULL_RTX;
+
+ if (lhs)
+ {
+ optab op;
+ lhsr = expand_expr (lhs, NULL_RTX, VOIDmode, EXPAND_WRITE);
+ if (!VECTOR_MODE_P (GET_MODE (lhsr))
+ || (op = optab_for_tree_code (code, TREE_TYPE (arg0),
+ optab_default)) == unknown_optab
+ || (optab_handler (op, TYPE_MODE (TREE_TYPE (arg0)))
+ == CODE_FOR_nothing))
+ {
+ if (MEM_P (lhsr))
+ resv = make_tree (TREE_TYPE (lhs), lhsr);
+ else
+ {
+ resvr = assign_temp (TREE_TYPE (lhs), 1, 1);
+ resv = make_tree (TREE_TYPE (lhs), resvr);
+ }
+ }
+ }
+ if (cnt > 4)
+ {
+ do_pending_stack_adjust ();
+ loop_lab = gen_label_rtx ();
+ cntvar = gen_reg_rtx (TYPE_MODE (sizetype));
+ cntv = make_tree (sizetype, cntvar);
+ emit_move_insn (cntvar, const0_rtx);
+ emit_label (loop_lab);
+ }
+ if (TREE_CODE (arg0) != VECTOR_CST)
+ {
+ rtx arg0r = expand_normal (arg0);
+ arg0 = make_tree (TREE_TYPE (arg0), arg0r);
+ }
+ if (TREE_CODE (arg1) != VECTOR_CST)
+ {
+ rtx arg1r = expand_normal (arg1);
+ arg1 = make_tree (TREE_TYPE (arg1), arg1r);
+ }
+ for (int i = 0; i < (cnt > 4 ? 1 : cnt); i++)
+ {
+ tree op0, op1, res = NULL_TREE;
+ if (cnt > 4)
+ {
+ tree atype = build_array_type_nelts (eltype, cnt);
+ op0 = uniform_vector_p (arg0);
+ if (op0 == NULL_TREE)
+ {
+ op0 = fold_build1_loc (loc, VIEW_CONVERT_EXPR, atype, arg0);
+ op0 = build4_loc (loc, ARRAY_REF, eltype, op0, cntv,
+ NULL_TREE, NULL_TREE);
+ }
+ op1 = uniform_vector_p (arg1);
+ if (op1 == NULL_TREE)
+ {
+ op1 = fold_build1_loc (loc, VIEW_CONVERT_EXPR, atype, arg1);
+ op1 = build4_loc (loc, ARRAY_REF, eltype, op1, cntv,
+ NULL_TREE, NULL_TREE);
+ }
+ if (resv)
+ {
+ res = fold_build1_loc (loc, VIEW_CONVERT_EXPR, atype, resv);
+ res = build4_loc (loc, ARRAY_REF, eltype, res, cntv,
+ NULL_TREE, NULL_TREE);
+ }
+ }
+ else
+ {
+ tree bitpos = bitsize_int (tree_to_uhwi (sz) * i);
+ op0 = fold_build3_loc (loc, BIT_FIELD_REF, eltype, arg0, sz, bitpos);
+ op1 = fold_build3_loc (loc, BIT_FIELD_REF, eltype, arg1, sz, bitpos);
+ if (resv)
+ res = fold_build3_loc (loc, BIT_FIELD_REF, eltype, resv, sz,
+ bitpos);
+ }
+ switch (code)
+ {
+ case PLUS_EXPR:
+ expand_addsub_overflow (loc, PLUS_EXPR, res, op0, op1,
+ false, false, false, true, &data);
+ break;
+ case MINUS_EXPR:
+ if (cnt > 4 ? integer_zerop (arg0) : integer_zerop (op0))
+ expand_neg_overflow (loc, res, op1, true, &data);
+ else
+ expand_addsub_overflow (loc, MINUS_EXPR, res, op0, op1,
+ false, false, false, true, &data);
+ break;
+ case MULT_EXPR:
+ expand_mul_overflow (loc, res, op0, op1, false, false, false,
+ true, &data);
+ break;
+ default:
+ gcc_unreachable ();
+ }
+ }
+ if (cnt > 4)
+ {
+ struct separate_ops ops;
+ ops.code = PLUS_EXPR;
+ ops.type = TREE_TYPE (cntv);
+ ops.op0 = cntv;
+ ops.op1 = build_int_cst (TREE_TYPE (cntv), 1);
+ ops.op2 = NULL_TREE;
+ ops.location = loc;
+ rtx ret = expand_expr_real_2 (&ops, cntvar, TYPE_MODE (sizetype),
+ EXPAND_NORMAL);
+ if (ret != cntvar)
+ emit_move_insn (cntvar, ret);
+ do_compare_rtx_and_jump (cntvar, GEN_INT (cnt), NE, false,
+ TYPE_MODE (sizetype), NULL_RTX, NULL, loop_lab,
+ PROB_VERY_LIKELY);
+ }
+ if (lhs && resv == NULL_TREE)
+ {
+ struct separate_ops ops;
+ ops.code = code;
+ ops.type = TREE_TYPE (arg0);
+ ops.op0 = arg0;
+ ops.op1 = arg1;
+ ops.op2 = NULL_TREE;
+ ops.location = loc;
+ rtx ret = expand_expr_real_2 (&ops, lhsr, TYPE_MODE (TREE_TYPE (arg0)),
+ EXPAND_NORMAL);
+ if (ret != lhsr)
+ emit_move_insn (lhsr, ret);
+ }
+ else if (resvr)
+ emit_move_insn (lhsr, resvr);
+}
+
/* Expand UBSAN_CHECK_ADD call STMT. */
static void
@@ -1668,8 +2006,11 @@ expand_UBSAN_CHECK_ADD (internal_fn, gcall *stmt)
tree lhs = gimple_call_lhs (stmt);
tree arg0 = gimple_call_arg (stmt, 0);
tree arg1 = gimple_call_arg (stmt, 1);
- expand_addsub_overflow (loc, PLUS_EXPR, lhs, arg0, arg1,
- false, false, false, true);
+ if (VECTOR_TYPE_P (TREE_TYPE (arg0)))
+ expand_vector_ubsan_overflow (loc, PLUS_EXPR, lhs, arg0, arg1);
+ else
+ expand_addsub_overflow (loc, PLUS_EXPR, lhs, arg0, arg1,
+ false, false, false, true, NULL);
}
/* Expand UBSAN_CHECK_SUB call STMT. */
@@ -1681,11 +2022,13 @@ expand_UBSAN_CHECK_SUB (internal_fn, gcall *stmt)
tree lhs = gimple_call_lhs (stmt);
tree arg0 = gimple_call_arg (stmt, 0);
tree arg1 = gimple_call_arg (stmt, 1);
- if (integer_zerop (arg0))
- expand_neg_overflow (loc, lhs, arg1, true);
+ if (VECTOR_TYPE_P (TREE_TYPE (arg0)))
+ expand_vector_ubsan_overflow (loc, MINUS_EXPR, lhs, arg0, arg1);
+ else if (integer_zerop (arg0))
+ expand_neg_overflow (loc, lhs, arg1, true, NULL);
else
expand_addsub_overflow (loc, MINUS_EXPR, lhs, arg0, arg1,
- false, false, false, true);
+ false, false, false, true, NULL);
}
/* Expand UBSAN_CHECK_MUL call STMT. */
@@ -1697,7 +2040,11 @@ expand_UBSAN_CHECK_MUL (internal_fn, gcall *stmt)
tree lhs = gimple_call_lhs (stmt);
tree arg0 = gimple_call_arg (stmt, 0);
tree arg1 = gimple_call_arg (stmt, 1);
- expand_mul_overflow (loc, lhs, arg0, arg1, false, false, false, true);
+ if (VECTOR_TYPE_P (TREE_TYPE (arg0)))
+ expand_vector_ubsan_overflow (loc, MULT_EXPR, lhs, arg0, arg1);
+ else
+ expand_mul_overflow (loc, lhs, arg0, arg1, false, false, false, true,
+ NULL);
}
/* Helper function for {ADD,SUB,MUL}_OVERFLOW call stmt expansion. */
@@ -1770,12 +2117,11 @@ expand_arith_overflow (enum tree_code code, gimple *stmt)
return;
}
- /* For sub-word operations, if target doesn't have them, start
- with precres widening right away, otherwise do it only
- if the most simple cases can't be used. */
- if (WORD_REGISTER_OPERATIONS
- && orig_precres == precres
- && precres < BITS_PER_WORD)
+ /* For operations with low precision, if target doesn't have them, start
+ with precres widening right away, otherwise do it only if the most
+ simple cases can't be used. */
+ const int min_precision = targetm.min_arithmetic_precision ();
+ if (orig_precres == precres && precres < min_precision)
;
else if ((uns0_p && uns1_p && unsr_p && prec0 <= precres
&& prec1 <= precres)
@@ -1789,15 +2135,18 @@ expand_arith_overflow (enum tree_code code, gimple *stmt)
{
case MINUS_EXPR:
if (integer_zerop (arg0) && !unsr_p)
- expand_neg_overflow (loc, lhs, arg1, false);
+ {
+ expand_neg_overflow (loc, lhs, arg1, false, NULL);
+ return;
+ }
/* FALLTHRU */
case PLUS_EXPR:
- expand_addsub_overflow (loc, code, lhs, arg0, arg1,
- unsr_p, unsr_p, unsr_p, false);
+ expand_addsub_overflow (loc, code, lhs, arg0, arg1, unsr_p,
+ unsr_p, unsr_p, false, NULL);
return;
case MULT_EXPR:
- expand_mul_overflow (loc, lhs, arg0, arg1,
- unsr_p, unsr_p, unsr_p, false);
+ expand_mul_overflow (loc, lhs, arg0, arg1, unsr_p,
+ unsr_p, unsr_p, false, NULL);
return;
default:
gcc_unreachable ();
@@ -1807,11 +2156,7 @@ expand_arith_overflow (enum tree_code code, gimple *stmt)
/* For sub-word operations, retry with a wider type first. */
if (orig_precres == precres && precop <= BITS_PER_WORD)
{
-#if WORD_REGISTER_OPERATIONS
- int p = BITS_PER_WORD;
-#else
- int p = precop;
-#endif
+ int p = MAX (min_precision, precop);
enum machine_mode m = smallest_mode_for_size (p, MODE_INT);
tree optype = build_nonstandard_integer_type (GET_MODE_PRECISION (m),
uns0_p && uns1_p
@@ -1843,10 +2188,10 @@ expand_arith_overflow (enum tree_code code, gimple *stmt)
arg1 = fold_convert_loc (loc, types[uns1_p], arg1);
if (code != MULT_EXPR)
expand_addsub_overflow (loc, code, lhs, arg0, arg1, unsr_p,
- uns0_p, uns1_p, false);
+ uns0_p, uns1_p, false, NULL);
else
expand_mul_overflow (loc, lhs, arg0, arg1, unsr_p,
- uns0_p, uns1_p, false);
+ uns0_p, uns1_p, false, NULL);
return;
}
@@ -2104,6 +2449,14 @@ expand_GOACC_REDUCTION (internal_fn, gcall *)
gcc_unreachable ();
}
+/* This is expanded by oacc_device_lower pass. */
+
+static void
+expand_GOACC_TILE (internal_fn, gcall *)
+{
+ gcc_unreachable ();
+}
+
/* Set errno to EDOM. */
static void
@@ -2122,6 +2475,98 @@ expand_SET_EDOM (internal_fn, gcall *)
#endif
}
+/* Expand atomic bit test and set. */
+
+static void
+expand_ATOMIC_BIT_TEST_AND_SET (internal_fn, gcall *call)
+{
+ expand_ifn_atomic_bit_test_and (call);
+}
+
+/* Expand atomic bit test and complement. */
+
+static void
+expand_ATOMIC_BIT_TEST_AND_COMPLEMENT (internal_fn, gcall *call)
+{
+ expand_ifn_atomic_bit_test_and (call);
+}
+
+/* Expand atomic bit test and reset. */
+
+static void
+expand_ATOMIC_BIT_TEST_AND_RESET (internal_fn, gcall *call)
+{
+ expand_ifn_atomic_bit_test_and (call);
+}
+
+/* Expand atomic bit test and set. */
+
+static void
+expand_ATOMIC_COMPARE_EXCHANGE (internal_fn, gcall *call)
+{
+ expand_ifn_atomic_compare_exchange (call);
+}
+
+/* Expand LAUNDER to assignment, lhs = arg0. */
+
+static void
+expand_LAUNDER (internal_fn, gcall *call)
+{
+ tree lhs = gimple_call_lhs (call);
+
+ if (!lhs)
+ return;
+
+ expand_assignment (lhs, gimple_call_arg (call, 0), false);
+}
+
+/* Expand DIVMOD() using:
+ a) optab handler for udivmod/sdivmod if it is available.
+ b) If optab_handler doesn't exist, generate call to
+ target-specific divmod libfunc. */
+
+static void
+expand_DIVMOD (internal_fn, gcall *call_stmt)
+{
+ tree lhs = gimple_call_lhs (call_stmt);
+ tree arg0 = gimple_call_arg (call_stmt, 0);
+ tree arg1 = gimple_call_arg (call_stmt, 1);
+
+ gcc_assert (TREE_CODE (TREE_TYPE (lhs)) == COMPLEX_TYPE);
+ tree type = TREE_TYPE (TREE_TYPE (lhs));
+ machine_mode mode = TYPE_MODE (type);
+ bool unsignedp = TYPE_UNSIGNED (type);
+ optab tab = (unsignedp) ? udivmod_optab : sdivmod_optab;
+
+ rtx op0 = expand_normal (arg0);
+ rtx op1 = expand_normal (arg1);
+ rtx target = expand_expr (lhs, NULL_RTX, VOIDmode, EXPAND_WRITE);
+
+ rtx quotient, remainder, libfunc;
+
+ /* Check if optab_handler exists for divmod_optab for given mode. */
+ if (optab_handler (tab, mode) != CODE_FOR_nothing)
+ {
+ quotient = gen_reg_rtx (mode);
+ remainder = gen_reg_rtx (mode);
+ expand_twoval_binop (tab, op0, op1, quotient, remainder, unsignedp);
+ }
+
+ /* Generate call to divmod libfunc if it exists. */
+ else if ((libfunc = optab_libfunc (tab, mode)) != NULL_RTX)
+ targetm.expand_divmod_libfunc (libfunc, mode, op0, op1,
+ &quotient, &remainder);
+
+ else
+ gcc_unreachable ();
+
+ /* Wrap the return value (quotient, remainder) within COMPLEX_EXPR. */
+ expand_expr (build2 (COMPLEX_EXPR, TREE_TYPE (lhs),
+ make_tree (TREE_TYPE (arg0), quotient),
+ make_tree (TREE_TYPE (arg1), remainder)),
+ target, VOIDmode, EXPAND_NORMAL);
+}
+
/* Expand a call to FN using the operands in STMT. FN has a single
output operand and NARGS input operands. */
@@ -2340,3 +2785,9 @@ expand_internal_call (gcall *stmt)
{
expand_internal_call (gimple_call_internal_fn (stmt), stmt);
}
+
+void
+expand_PHI (internal_fn, gcall *)
+{
+ gcc_unreachable ();
+}