aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gcc/internal-fn.cc243
1 files changed, 89 insertions, 154 deletions
diff --git a/gcc/internal-fn.cc b/gcc/internal-fn.cc
index 8b1733e..ab2b1ba 100644
--- a/gcc/internal-fn.cc
+++ b/gcc/internal-fn.cc
@@ -140,6 +140,86 @@ const direct_internal_fn_info direct_internal_fn_array[IFN_LAST + 1] = {
not_direct
};
+/* Expand STMT using instruction ICODE. The instruction has NOUTPUTS
+ output operands and NINPUTS input operands, where NOUTPUTS is either
+ 0 or 1. The output operand (if any) comes first, followed by the
+ NINPUTS input operands. */
+
+static void
+expand_fn_using_insn (gcall *stmt, insn_code icode, unsigned int noutputs,
+ unsigned int ninputs)
+{
+ gcc_assert (icode != CODE_FOR_nothing);
+
+ expand_operand *ops = XALLOCAVEC (expand_operand, noutputs + ninputs);
+ unsigned int opno = 0;
+ rtx lhs_rtx = NULL_RTX;
+ tree lhs = gimple_call_lhs (stmt);
+
+ if (noutputs)
+ {
+ gcc_assert (noutputs == 1);
+ if (lhs)
+ lhs_rtx = expand_expr (lhs, NULL_RTX, VOIDmode, EXPAND_WRITE);
+
+ /* Do not assign directly to a promoted subreg, since there is no
+ guarantee that the instruction will leave the upper bits of the
+ register in the state required by SUBREG_PROMOTED_SIGN. */
+ rtx dest = lhs_rtx;
+ if (dest && GET_CODE (dest) == SUBREG && SUBREG_PROMOTED_VAR_P (dest))
+ dest = NULL_RTX;
+ create_output_operand (&ops[opno], dest,
+ insn_data[icode].operand[opno].mode);
+ opno += 1;
+ }
+ else
+ gcc_assert (!lhs);
+
+ for (unsigned int i = 0; i < ninputs; ++i)
+ {
+ tree rhs = gimple_call_arg (stmt, i);
+ tree rhs_type = TREE_TYPE (rhs);
+ rtx rhs_rtx = expand_normal (rhs);
+ if (INTEGRAL_TYPE_P (rhs_type))
+ create_convert_operand_from (&ops[opno], rhs_rtx,
+ TYPE_MODE (rhs_type),
+ TYPE_UNSIGNED (rhs_type));
+ else
+ create_input_operand (&ops[opno], rhs_rtx, TYPE_MODE (rhs_type));
+ opno += 1;
+ }
+
+ gcc_assert (opno == noutputs + ninputs);
+ expand_insn (icode, opno, ops);
+ if (lhs_rtx && !rtx_equal_p (lhs_rtx, ops[0].value))
+ {
+ /* If the return value has an integral type, convert the instruction
+ result to that type. This is useful for things that return an
+ int regardless of the size of the input. If the instruction result
+ is smaller than required, assume that it is signed.
+
+ If the return value has a nonintegral type, its mode must match
+ the instruction result. */
+ if (GET_CODE (lhs_rtx) == SUBREG && SUBREG_PROMOTED_VAR_P (lhs_rtx))
+ {
+ /* If this is a scalar in a register that is stored in a wider
+ mode than the declared mode, compute the result into its
+ declared mode and then convert to the wider mode. */
+ gcc_checking_assert (INTEGRAL_TYPE_P (TREE_TYPE (lhs)));
+ rtx tmp = convert_to_mode (GET_MODE (lhs_rtx), ops[0].value, 0);
+ convert_move (SUBREG_REG (lhs_rtx), tmp,
+ SUBREG_PROMOTED_SIGN (lhs_rtx));
+ }
+ else if (GET_MODE (lhs_rtx) == GET_MODE (ops[0].value))
+ emit_move_insn (lhs_rtx, ops[0].value);
+ else
+ {
+ gcc_checking_assert (INTEGRAL_TYPE_P (TREE_TYPE (lhs)));
+ convert_move (lhs_rtx, ops[0].value, 0);
+ }
+ }
+}
+
/* ARRAY_TYPE is an array of vector modes. Return the associated insn
for load-lanes-style optab OPTAB, or CODE_FOR_nothing if none. */
@@ -233,22 +313,8 @@ expand_GOMP_SIMT_ENTER (internal_fn, gcall *)
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));
- class 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);
- if (!rtx_equal_p (target, ops[0].value))
- emit_move_insn (target, ops[0].value);
+ expand_fn_using_insn (stmt, targetm.code_for_omp_simt_enter, 1, 2);
}
/* Deallocate per-lane storage and leave non-uniform execution region. */
@@ -256,12 +322,8 @@ expand_GOMP_SIMT_ENTER_ALLOC (internal_fn, gcall *stmt)
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));
- class 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);
+ expand_fn_using_insn (stmt, targetm.code_for_omp_simt_exit, 0, 1);
}
/* Lane index on SIMT targets: thread index in the warp on NVPTX. On targets
@@ -270,13 +332,8 @@ expand_GOMP_SIMT_EXIT (internal_fn, gcall *stmt)
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));
+ expand_fn_using_insn (stmt, targetm.code_for_omp_simt_lane, 1, 0);
}
/* This should get expanded in omp_device_lower pass. */
@@ -294,20 +351,8 @@ expand_GOMP_SIMT_VF (internal_fn, gcall *)
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));
- class 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);
- if (!rtx_equal_p (target, ops[0].value))
- emit_move_insn (target, ops[0].value);
+ expand_fn_using_insn (stmt, targetm.code_for_omp_simt_last_lane, 1, 1);
}
/* Non-transparent predicate used in SIMT lowering of OpenMP "ordered". */
@@ -315,20 +360,8 @@ expand_GOMP_SIMT_LAST_LANE (internal_fn, gcall *stmt)
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));
- class 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);
- if (!rtx_equal_p (target, ops[0].value))
- emit_move_insn (target, ops[0].value);
+ expand_fn_using_insn (stmt, targetm.code_for_omp_simt_ordered, 1, 1);
}
/* "Or" boolean reduction across SIMT lanes: return non-zero in all lanes if
@@ -337,20 +370,8 @@ expand_GOMP_SIMT_ORDERED_PRED (internal_fn, gcall *stmt)
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));
- class 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);
- if (!rtx_equal_p (target, ops[0].value))
- emit_move_insn (target, ops[0].value);
+ expand_fn_using_insn (stmt, targetm.code_for_omp_simt_vote_any, 1, 1);
}
/* Exchange between SIMT lanes with a "butterfly" pattern: source lane index
@@ -359,22 +380,8 @@ expand_GOMP_SIMT_VOTE_ANY (internal_fn, gcall *stmt)
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));
- class 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);
- if (!rtx_equal_p (target, ops[0].value))
- emit_move_insn (target, ops[0].value);
+ expand_fn_using_insn (stmt, targetm.code_for_omp_simt_xchg_bfly, 1, 2);
}
/* Exchange between SIMT lanes according to given source lane index. */
@@ -382,22 +389,8 @@ expand_GOMP_SIMT_XCHG_BFLY (internal_fn, gcall *stmt)
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));
- class 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);
- if (!rtx_equal_p (target, ops[0].value))
- emit_move_insn (target, ops[0].value);
+ expand_fn_using_insn (stmt, targetm.code_for_omp_simt_xchg_idx, 1, 2);
}
/* This should get expanded in adjust_simduid_builtins. */
@@ -3565,67 +3558,9 @@ static void
expand_direct_optab_fn (internal_fn fn, gcall *stmt, direct_optab optab,
unsigned int nargs)
{
- expand_operand *ops = XALLOCAVEC (expand_operand, nargs + 1);
-
tree_pair types = direct_internal_fn_types (fn, stmt);
insn_code icode = direct_optab_handler (optab, TYPE_MODE (types.first));
- gcc_assert (icode != CODE_FOR_nothing);
-
- tree lhs = gimple_call_lhs (stmt);
- rtx lhs_rtx = NULL_RTX;
- if (lhs)
- lhs_rtx = expand_expr (lhs, NULL_RTX, VOIDmode, EXPAND_WRITE);
-
- /* Do not assign directly to a promoted subreg, since there is no
- guarantee that the instruction will leave the upper bits of the
- register in the state required by SUBREG_PROMOTED_SIGN. */
- rtx dest = lhs_rtx;
- if (dest && GET_CODE (dest) == SUBREG && SUBREG_PROMOTED_VAR_P (dest))
- dest = NULL_RTX;
-
- create_output_operand (&ops[0], dest, insn_data[icode].operand[0].mode);
-
- for (unsigned int i = 0; i < nargs; ++i)
- {
- tree rhs = gimple_call_arg (stmt, i);
- tree rhs_type = TREE_TYPE (rhs);
- rtx rhs_rtx = expand_normal (rhs);
- if (INTEGRAL_TYPE_P (rhs_type))
- create_convert_operand_from (&ops[i + 1], rhs_rtx,
- TYPE_MODE (rhs_type),
- TYPE_UNSIGNED (rhs_type));
- else
- create_input_operand (&ops[i + 1], rhs_rtx, TYPE_MODE (rhs_type));
- }
-
- expand_insn (icode, nargs + 1, ops);
- if (lhs_rtx && !rtx_equal_p (lhs_rtx, ops[0].value))
- {
- /* If the return value has an integral type, convert the instruction
- result to that type. This is useful for things that return an
- int regardless of the size of the input. If the instruction result
- is smaller than required, assume that it is signed.
-
- If the return value has a nonintegral type, its mode must match
- the instruction result. */
- if (GET_CODE (lhs_rtx) == SUBREG && SUBREG_PROMOTED_VAR_P (lhs_rtx))
- {
- /* If this is a scalar in a register that is stored in a wider
- mode than the declared mode, compute the result into its
- declared mode and then convert to the wider mode. */
- gcc_checking_assert (INTEGRAL_TYPE_P (TREE_TYPE (lhs)));
- rtx tmp = convert_to_mode (GET_MODE (lhs_rtx), ops[0].value, 0);
- convert_move (SUBREG_REG (lhs_rtx), tmp,
- SUBREG_PROMOTED_SIGN (lhs_rtx));
- }
- else if (GET_MODE (lhs_rtx) == GET_MODE (ops[0].value))
- emit_move_insn (lhs_rtx, ops[0].value);
- else
- {
- gcc_checking_assert (INTEGRAL_TYPE_P (TREE_TYPE (lhs)));
- convert_move (lhs_rtx, ops[0].value, 0);
- }
- }
+ expand_fn_using_insn (stmt, icode, 1, nargs);
}
/* Expand WHILE_ULT call STMT using optab OPTAB. */