#include "tree-cfg.h"
#include "bitmap.h"
#include "tree-ssa-dce.h"
+#include "memmodel.h"
+#include "optabs.h"
+
+/* Expand all ARRAY_REF(VIEW_CONVERT_EXPR) gimple assignments into calls to
+ internal function based on vector type of selected expansion.
+ i.e.:
+ VIEW_CONVERT_EXPR<int[4]>(u)[_1] = = i_4(D);
+ =>
+ _7 = u;
+ _8 = .VEC_SET (_7, i_4(D), _1);
+ u = _8; */
+
+static gimple *
+gimple_expand_vec_set_expr (gimple_stmt_iterator *gsi)
+{
+ enum tree_code code;
+ gcall *new_stmt = NULL;
+ gassign *ass_stmt = NULL;
+
+ /* Only consider code == GIMPLE_ASSIGN. */
+ gassign *stmt = dyn_cast<gassign *> (gsi_stmt (*gsi));
+ if (!stmt)
+ return NULL;
+
+ tree lhs = gimple_assign_lhs (stmt);
+ code = TREE_CODE (lhs);
+ if (code != ARRAY_REF)
+ return NULL;
+
+ tree val = gimple_assign_rhs1 (stmt);
+ tree op0 = TREE_OPERAND (lhs, 0);
+ if (TREE_CODE (op0) == VIEW_CONVERT_EXPR && DECL_P (TREE_OPERAND (op0, 0))
+ && VECTOR_TYPE_P (TREE_TYPE (TREE_OPERAND (op0, 0)))
+ && TYPE_MODE (TREE_TYPE (lhs))
+ == TYPE_MODE (TREE_TYPE (TREE_TYPE (TREE_OPERAND (op0, 0)))))
+ {
+ tree pos = TREE_OPERAND (lhs, 1);
+ tree view_op0 = TREE_OPERAND (op0, 0);
+ machine_mode outermode = TYPE_MODE (TREE_TYPE (view_op0));
+ if (auto_var_in_fn_p (view_op0, cfun->decl)
+ && !TREE_ADDRESSABLE (view_op0) && can_vec_set_var_idx_p (outermode))
+ {
+ location_t loc = gimple_location (stmt);
+ tree var_src = make_ssa_name (TREE_TYPE (view_op0));
+ tree var_dst = make_ssa_name (TREE_TYPE (view_op0));
+
+ ass_stmt = gimple_build_assign (var_src, view_op0);
+ gimple_set_vuse (ass_stmt, gimple_vuse (stmt));
+ gimple_set_location (ass_stmt, loc);
+ gsi_insert_before (gsi, ass_stmt, GSI_SAME_STMT);
+
+ new_stmt
+ = gimple_build_call_internal (IFN_VEC_SET, 3, var_src, val, pos);
+ gimple_call_set_lhs (new_stmt, var_dst);
+ gimple_set_location (new_stmt, loc);
+ gsi_insert_before (gsi, new_stmt, GSI_SAME_STMT);
+
+ ass_stmt = gimple_build_assign (view_op0, var_dst);
+ gimple_set_location (ass_stmt, loc);
+ gsi_insert_before (gsi, ass_stmt, GSI_SAME_STMT);
+
+ gimple_move_vops (ass_stmt, stmt);
+ gsi_remove (gsi, true);
+ }
+ }
+
+ return ass_stmt;
+}
/* Expand all VEC_COND_EXPR gimple assignments into calls to internal
function based on type of selected expansion. */
VEC_COND_EXPR assignments. */
static unsigned int
-gimple_expand_vec_cond_exprs (void)
+gimple_expand_vec_exprs (void)
{
gimple_stmt_iterator gsi;
basic_block bb;
{
gimple *g = gimple_expand_vec_cond_expr (&gsi,
&vec_cond_ssa_name_uses);
+
if (g != NULL)
{
tree lhs = gimple_assign_lhs (gsi_stmt (gsi));
gimple_set_lhs (g, lhs);
gsi_replace (&gsi, g, false);
}
+
+ gimple_expand_vec_set_expr (&gsi);
}
}
virtual unsigned int execute (function *)
{
- return gimple_expand_vec_cond_exprs ();
+ return gimple_expand_vec_exprs ();
}
}; // class pass_gimple_isel
#define vec_condeq_direct { 0, 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 }
#define expand_vec_cond_mask_optab_fn expand_vect_cond_mask_optab_fn
+/* 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 *)
{
#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. */
DEF_INTERNAL_OPTAB_FN (VCONDEQ, 0, vcondeq, vec_condeq)
DEF_INTERNAL_OPTAB_FN (VCOND_MASK, 0, vcond_mask, vec_cond_mask)
+DEF_INTERNAL_OPTAB_FN (VEC_SET, 0, vec_set, vec_set)
+
DEF_INTERNAL_OPTAB_FN (LEN_STORE, 0, len_store, len_store)
DEF_INTERNAL_OPTAB_FN (WHILE_ULT, ECF_CONST | ECF_NOTHROW, while_ult, while)
&& insn_operand_matches (icode, 3, test);
}
+/* Return whether the backend can emit vector set instructions for inserting
+ element into vector at variable index position. */
+
+bool
+can_vec_set_var_idx_p (machine_mode vec_mode)
+{
+ if (!VECTOR_MODE_P (vec_mode))
+ return false;
+
+ machine_mode inner_mode = GET_MODE_INNER (vec_mode);
+ rtx reg1 = alloca_raw_REG (vec_mode, LAST_VIRTUAL_REGISTER + 1);
+ rtx reg2 = alloca_raw_REG (inner_mode, LAST_VIRTUAL_REGISTER + 2);
+ rtx reg3 = alloca_raw_REG (VOIDmode, LAST_VIRTUAL_REGISTER + 3);
+
+ enum insn_code icode = optab_handler (vec_set_optab, vec_mode);
+
+ return icode != CODE_FOR_nothing && insn_operand_matches (icode, 0, reg1)
+ && insn_operand_matches (icode, 1, reg2)
+ && insn_operand_matches (icode, 2, reg3);
+}
+
/* This function is called when we are going to emit a compare instruction that
compares the values found in X and Y, using the rtl operator COMPARISON.
VALUE_MODE. */
extern bool can_vcond_compare_p (enum rtx_code, machine_mode, machine_mode);
+/* Return whether the backend can emit vector set instructions for inserting
+ element into vector at variable index position. */
+extern bool can_vec_set_var_idx_p (machine_mode);
+
extern rtx prepare_operand (enum insn_code, rtx, int, machine_mode,
machine_mode, int);
/* Emit a pair of rtl insns to compare two rtx's and to jump