Factor out common internal-fn idiom
internal-fn.c has quite a few functions that simply map the result of the call to an instruction's output operand (if any) and map each argument to an instruction's input operand, in order. This patch adds a single function for doing that. It's really just a generalisation of expand_direct_optab_fn, but with the output operand being optional. Unfortunately, it isn't possible to do this for vcond_mask because the internal function has a different argument order from the optab. gcc/ * internal-fn.cc (expand_fn_using_insn): New function, split out and adapted from... (expand_direct_optab_fn): ...here. (expand_GOMP_SIMT_ENTER_ALLOC): Use it. (expand_GOMP_SIMT_EXIT): Likewise. (expand_GOMP_SIMT_LANE): Likewise. (expand_GOMP_SIMT_LAST_LANE): Likewise. (expand_GOMP_SIMT_ORDERED_PRED): Likewise. (expand_GOMP_SIMT_VOTE_ANY): Likewise. (expand_GOMP_SIMT_XCHG_BFLY): Likewise. (expand_GOMP_SIMT_XCHG_IDX): Likewise.
This commit is contained in:
parent
e55eda2385
commit
1d205dbac1
1 changed files with 89 additions and 154 deletions
|
@ -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. */
|
||||
|
|
Loading…
Add table
Reference in a new issue