aboutsummaryrefslogtreecommitdiff
path: root/gcc/gimple-isel.cc
diff options
context:
space:
mode:
Diffstat (limited to 'gcc/gimple-isel.cc')
-rw-r--r--gcc/gimple-isel.cc244
1 files changed, 244 insertions, 0 deletions
diff --git a/gcc/gimple-isel.cc b/gcc/gimple-isel.cc
new file mode 100644
index 0000000..97f9208
--- /dev/null
+++ b/gcc/gimple-isel.cc
@@ -0,0 +1,244 @@
+/* Schedule GIMPLE vector statements.
+ Copyright (C) 2020 Free Software Foundation, Inc.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by the
+Free Software Foundation; either version 3, or (at your option) any
+later version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3. If not see
+<http://www.gnu.org/licenses/>. */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "backend.h"
+#include "rtl.h"
+#include "tree.h"
+#include "gimple.h"
+#include "tree-pass.h"
+#include "ssa.h"
+#include "expmed.h"
+#include "optabs-tree.h"
+#include "tree-eh.h"
+#include "gimple-iterator.h"
+#include "gimplify-me.h"
+#include "gimplify.h"
+#include "tree-cfg.h"
+
+/* Expand all VEC_COND_EXPR gimple assignments into calls to internal
+ function based on type of selected expansion. */
+
+static gimple *
+gimple_expand_vec_cond_expr (gimple_stmt_iterator *gsi,
+ hash_map<tree, unsigned int> *vec_cond_ssa_name_uses)
+{
+ tree lhs, op0a = NULL_TREE, op0b = NULL_TREE;
+ enum tree_code code;
+ enum tree_code tcode;
+ machine_mode cmp_op_mode;
+ bool unsignedp;
+ enum insn_code icode;
+ imm_use_iterator imm_iter;
+
+ /* Only consider code == GIMPLE_ASSIGN. */
+ gassign *stmt = dyn_cast<gassign *> (gsi_stmt (*gsi));
+ if (!stmt)
+ return NULL;
+
+ code = gimple_assign_rhs_code (stmt);
+ if (code != VEC_COND_EXPR)
+ return NULL;
+
+ tree op0 = gimple_assign_rhs1 (stmt);
+ tree op1 = gimple_assign_rhs2 (stmt);
+ tree op2 = gimple_assign_rhs3 (stmt);
+ lhs = gimple_assign_lhs (stmt);
+ machine_mode mode = TYPE_MODE (TREE_TYPE (lhs));
+
+ gcc_assert (!COMPARISON_CLASS_P (op0));
+ if (TREE_CODE (op0) == SSA_NAME)
+ {
+ unsigned int used_vec_cond_exprs = 0;
+ unsigned int *slot = vec_cond_ssa_name_uses->get (op0);
+ if (slot)
+ used_vec_cond_exprs = *slot;
+ else
+ {
+ gimple *use_stmt;
+ FOR_EACH_IMM_USE_STMT (use_stmt, imm_iter, op0)
+ {
+ gassign *assign = dyn_cast<gassign *> (use_stmt);
+ if (assign != NULL
+ && gimple_assign_rhs_code (assign) == VEC_COND_EXPR
+ && gimple_assign_rhs1 (assign) == op0)
+ used_vec_cond_exprs++;
+ }
+ vec_cond_ssa_name_uses->put (op0, used_vec_cond_exprs);
+ }
+
+ gassign *def_stmt = dyn_cast<gassign *> (SSA_NAME_DEF_STMT (op0));
+ if (def_stmt)
+ {
+ tcode = gimple_assign_rhs_code (def_stmt);
+ op0a = gimple_assign_rhs1 (def_stmt);
+ op0b = gimple_assign_rhs2 (def_stmt);
+
+ tree op0a_type = TREE_TYPE (op0a);
+ if (used_vec_cond_exprs >= 2
+ && (get_vcond_mask_icode (mode, TYPE_MODE (op0a_type))
+ != CODE_FOR_nothing)
+ && expand_vec_cmp_expr_p (op0a_type, TREE_TYPE (lhs), tcode))
+ {
+ /* Keep the SSA name and use vcond_mask. */
+ tcode = TREE_CODE (op0);
+ }
+ }
+ else
+ tcode = TREE_CODE (op0);
+ }
+ else
+ tcode = TREE_CODE (op0);
+
+ if (TREE_CODE_CLASS (tcode) != tcc_comparison)
+ {
+ gcc_assert (VECTOR_BOOLEAN_TYPE_P (TREE_TYPE (op0)));
+ if (get_vcond_mask_icode (mode, TYPE_MODE (TREE_TYPE (op0)))
+ != CODE_FOR_nothing)
+ return gimple_build_call_internal (IFN_VCOND_MASK, 3, op0, op1, op2);
+ /* Fake op0 < 0. */
+ else
+ {
+ gcc_assert (GET_MODE_CLASS (TYPE_MODE (TREE_TYPE (op0)))
+ == MODE_VECTOR_INT);
+ op0a = op0;
+ op0b = build_zero_cst (TREE_TYPE (op0));
+ tcode = LT_EXPR;
+ }
+ }
+ cmp_op_mode = TYPE_MODE (TREE_TYPE (op0a));
+ unsignedp = TYPE_UNSIGNED (TREE_TYPE (op0a));
+
+
+ gcc_assert (known_eq (GET_MODE_SIZE (mode), GET_MODE_SIZE (cmp_op_mode))
+ && known_eq (GET_MODE_NUNITS (mode),
+ GET_MODE_NUNITS (cmp_op_mode)));
+
+ icode = get_vcond_icode (mode, cmp_op_mode, unsignedp);
+ if (icode == CODE_FOR_nothing)
+ {
+ if (tcode == LT_EXPR
+ && op0a == op0
+ && TREE_CODE (op0) == VECTOR_CST)
+ {
+ /* A VEC_COND_EXPR condition could be folded from EQ_EXPR/NE_EXPR
+ into a constant when only get_vcond_eq_icode is supported.
+ Verify < 0 and != 0 behave the same and change it to NE_EXPR. */
+ unsigned HOST_WIDE_INT nelts;
+ if (!VECTOR_CST_NELTS (op0).is_constant (&nelts))
+ {
+ if (VECTOR_CST_STEPPED_P (op0))
+ gcc_unreachable ();
+ nelts = vector_cst_encoded_nelts (op0);
+ }
+ for (unsigned int i = 0; i < nelts; ++i)
+ if (tree_int_cst_sgn (vector_cst_elt (op0, i)) == 1)
+ gcc_unreachable ();
+ tcode = NE_EXPR;
+ }
+ if (tcode == EQ_EXPR || tcode == NE_EXPR)
+ {
+ tree tcode_tree = build_int_cst (integer_type_node, tcode);
+ return gimple_build_call_internal (IFN_VCONDEQ, 5, op0a, op0b, op1,
+ op2, tcode_tree);
+ }
+ }
+
+ gcc_assert (icode != CODE_FOR_nothing);
+ tree tcode_tree = build_int_cst (integer_type_node, tcode);
+ return gimple_build_call_internal (unsignedp ? IFN_VCONDU : IFN_VCOND,
+ 5, op0a, op0b, op1, op2, tcode_tree);
+}
+
+
+
+/* Iterate all gimple statements and try to expand
+ VEC_COND_EXPR assignments. */
+
+static unsigned int
+gimple_expand_vec_cond_exprs (void)
+{
+ gimple_stmt_iterator gsi;
+ basic_block bb;
+ bool cfg_changed = false;
+ hash_map<tree, unsigned int> vec_cond_ssa_name_uses;
+
+ FOR_EACH_BB_FN (bb, cfun)
+ {
+ for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi))
+ {
+ 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);
+ }
+ }
+ }
+
+ return cfg_changed ? TODO_cleanup_cfg : 0;
+}
+
+namespace {
+
+const pass_data pass_data_gimple_isel =
+{
+ GIMPLE_PASS, /* type */
+ "isel", /* name */
+ OPTGROUP_VEC, /* optinfo_flags */
+ TV_NONE, /* tv_id */
+ PROP_cfg, /* properties_required */
+ 0, /* properties_provided */
+ 0, /* properties_destroyed */
+ 0, /* todo_flags_start */
+ TODO_update_ssa, /* todo_flags_finish */
+};
+
+class pass_gimple_isel : public gimple_opt_pass
+{
+public:
+ pass_gimple_isel (gcc::context *ctxt)
+ : gimple_opt_pass (pass_data_gimple_isel, ctxt)
+ {}
+
+ /* opt_pass methods: */
+ virtual bool gate (function *)
+ {
+ return true;
+ }
+
+ virtual unsigned int execute (function *)
+ {
+ return gimple_expand_vec_cond_exprs ();
+ }
+
+}; // class pass_gimple_isel
+
+} // anon namespace
+
+gimple_opt_pass *
+make_pass_gimple_isel (gcc::context *ctxt)
+{
+ return new pass_gimple_isel (ctxt);
+}
+