From 9e5c8fe425ea2e7a1ad88a14bcbc599e59f9f185 Mon Sep 17 00:00:00 2001 From: Mariam Arutunian Date: Fri, 17 Feb 2023 18:00:48 +0400 Subject: [PATCH] CRC code generation v2: - Complete expand_crc_optab_fn function. - Added RTL generation for CRC (crcqihi4). But yet it doesn't generate the correct sequence. - Changed the code for removing the body of CRC function. - Added direct_internal_fn_supported_p check before adding the IFN_CRC. --- gcc/config/riscv/bitmanip.md | 28 +++++ gcc/config/riscv/riscv.md | 2 + gcc/gimple-crc-optimization.cc | 199 +++++---------------------------- gcc/internal-fn.cc | 24 +++- 4 files changed, 81 insertions(+), 172 deletions(-) diff --git a/gcc/config/riscv/bitmanip.md b/gcc/config/riscv/bitmanip.md index ff9ecb433e4d..2419b5a35cf8 100644 --- a/gcc/config/riscv/bitmanip.md +++ b/gcc/config/riscv/bitmanip.md @@ -655,3 +655,31 @@ operands[8] = GEN_INT (setbit); operands[9] = GEN_INT (clearbit); }) + +(define_expand "crcqihi4" +[(set (match_operand:HI 0 "register_operand" "=r") +(unspec:HI +[(match_operand:HI 1) +(match_operand:QI 2) +(match_operand:HI 3)] +UNSPEC_CRC16))] ;; +"" +{ +// FIXME: Note correct instruction sequence. +rtx data = force_reg (SImode, gen_rtx_ASHIFT (SImode, operands[1], + GEN_INT (32))); + +rtx op3 = simplify_gen_subreg (SImode, operands[3], HImode, 0); +rtx t2 = force_reg (SImode, gen_rtx_PLUS (SImode, data, op3)); // Must be CLMULH + +t2 = force_reg (SImode, gen_rtx_ASHIFT (SImode, t2, GEN_INT (16+1))); + +t2 = force_reg (SImode, gen_rtx_LSHIFTRT (SImode, t2, GEN_INT (48-1))); + +t2 = force_reg (SImode, gen_rtx_PLUS (SImode, data, t2)); // Must be CLMULH + +rtx tgt = simplify_gen_subreg (SImode, operands[0], HImode, 0); +rtx crc = simplify_gen_subreg (SImode, operands[2], QImode, 0); +emit_move_insn (tgt, gen_rtx_XOR (SImode, crc, t2)); +DONE; +}) \ No newline at end of file diff --git a/gcc/config/riscv/riscv.md b/gcc/config/riscv/riscv.md index 6c3176042fbd..f9a62b5f88c8 100644 --- a/gcc/config/riscv/riscv.md +++ b/gcc/config/riscv/riscv.md @@ -65,6 +65,8 @@ ;; OR-COMBINE UNSPEC_ORC_B + + UNSPEC_CRC16 ]) (define_c_enum "unspecv" [ diff --git a/gcc/gimple-crc-optimization.cc b/gcc/gimple-crc-optimization.cc index bfd9562ff846..48bd0efee160 100644 --- a/gcc/gimple-crc-optimization.cc +++ b/gcc/gimple-crc-optimization.cc @@ -205,7 +205,7 @@ class crc_optimization { /* Replaces function body with CRC_IFN call. Returns true if replacement is succeeded, otherwise false. */ - bool faster_crc_code_generation (function *); + bool faster_crc_code_generation (function *fun); public: unsigned int execute (function *fun); @@ -1011,209 +1011,70 @@ crc_optimization::function_may_calculate_crc (function *fun) /* Remove all statements and basic blocks of the function except for the return statement. */ gimple * -remove_stmts_besides_return (function *fun) +remove_stmts_besides_return (function *fun, class loop *crc_loop) { gimple_stmt_iterator gsi; basic_block bb, next_bb; gimple *return_stmt = NULL; + cancel_loop_tree (crc_loop); for (bb = fun->cfg->x_entry_block_ptr->next_bb; bb != fun->cfg->x_exit_block_ptr; bb = next_bb) { - - for (gsi = gsi_start_phis (bb); !gsi_end_p (gsi);) - gsi_remove (&gsi, true); - - for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi);) + for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi)) { gimple *gs = gsi_stmt (gsi); + // TODO: Check whether it's better to get return + // statement during symbolic execution step and change. if (gimple_code (gs) == GIMPLE_RETURN) - { - return_stmt = gs; - gsi_next (&gsi); - } - else - { - gimple *stmt = gsi_stmt (gsi); - gsi_remove (&gsi, true); - release_defs (stmt); - } - } -/* - edge e; - edge_iterator ei; - for (ei = ei_start (bb->preds); (e = ei_safe_edge (ei));) - { - e->dest = bb->next_bb; - remove_edge (e); + return_stmt = gs; } - for (ei = ei_start (bb->succs); (e = ei_safe_edge (ei));) - { - e->src = bb->prev_bb; - remove_edge (e); - }*/ - + next_bb = bb->next_bb; + // TODO: what if return statement's block isn't the last one?! if (!return_stmt) + delete_basic_block (bb); + else { - next_bb = bb->next_bb; - delete_basic_block (bb); + // TODO: Check whether there can be other statements + // besides return in the block. Delete them if any. + /* Delete phi statements of the return block. */ + for (gsi = gsi_start_phis (bb); !gsi_end_p (gsi);) + gsi_remove (&gsi, true); } - else - next_bb = bb->next_bb; } - set_immediate_dominator (CDI_DOMINATORS, fun->cfg->x_entry_block_ptr, - return_stmt->bb); make_edge (fun->cfg->x_entry_block_ptr, return_stmt->bb, EDGE_FALLTHRU); return return_stmt; } -void -remove_statements_and_update_ssa (function *fn) -{ - basic_block bb, next_bb; - gimple_stmt_iterator gsi; - - for (bb = fn->cfg->x_entry_block_ptr->next_bb; - bb != fn->cfg->x_exit_block_ptr; bb = next_bb) - { - gsi = gsi_last_bb (bb); - - // Remove all statements from the basic block. - while (!gsi_end_p (gsi)) - { - gimple *stmt = gsi_stmt (gsi); - gsi_remove (&gsi, true); - - // Update the SSA form to remove the dead statement. - release_defs (stmt); - } - - // Remove the basic block from the CFG. - next_bb = bb->next_bb; - delete_basic_block (bb); - } - remove_unused_locals (); - /*set_immediate_dominator (CDI_DOMINATORS, fn->cfg->x_entry_block_ptr, - fn->cfg->x_exit_block_ptr); -make_edge (fn->cfg->x_entry_block_ptr, EXIT_BLOCK_PTR_FOR_FN (fn), - EDGE_FALLTHRU);*/ - update_ssa (TODO_update_ssa); - free_dominance_info (fn, CDI_DOMINATORS); - calculate_dominance_info (CDI_DOMINATORS); -} - -void -add_return_statement (function *fn) -{ - basic_block bb = create_basic_block (NULL, 0, ENTRY_BLOCK_PTR_FOR_FN (fn)); - gimple_stmt_iterator gsi = gsi_last_bb (bb); - - tree return_value = build_int_cst (integer_type_node, 10); - gimple *stmt = gimple_build_return (return_value); - - gsi_insert_after (&gsi, stmt, GSI_NEW_STMT); - calculate_dominance_info (CDI_DOMINATORS); - // Update the SSA form to reflect the new return statement. - update_ssa (TODO_update_ssa); - -} - -/* Will be removed from here, after writing a correct code. */ -void -destroy_loop (class loop *loop) -{ - unsigned nbbs = loop->num_nodes; - edge exit = single_exit (loop); - basic_block src = loop_preheader_edge (loop)->src, dest = exit->dest; - basic_block *bbs; - unsigned i; - - bbs = get_loop_body_in_dom_order (loop); - - gimple_stmt_iterator dst_gsi = gsi_after_labels (exit->dest); - bool safe_p = single_pred_p (exit->dest); - for (unsigned i = 0; i < nbbs; ++i) - { - /* We have made sure to not leave any dangling uses of SSA - names defined in the loop. With the exception of virtuals. - Make sure we replace all uses of virtual defs that will remain - outside of the loop with the bare symbol as delete_basic_block - will release them. */ - for (gphi_iterator gsi = gsi_start_phis (bbs[i]); !gsi_end_p (gsi); - gsi_next (&gsi)) - { - gphi *phi = gsi.phi (); - if (virtual_operand_p (gimple_phi_result (phi))) - mark_virtual_phi_result_for_renaming (phi); - } - for (gimple_stmt_iterator gsi = gsi_start_bb (bbs[i]); !gsi_end_p (gsi);) - { - gimple *stmt = gsi_stmt (gsi); - tree vdef = gimple_vdef (stmt); - if (vdef && TREE_CODE (vdef) == SSA_NAME) - mark_virtual_operand_for_renaming (vdef); - /* Also move and eventually reset debug stmts. We can leave - constant values in place in case the stmt dominates the exit. - ??? Non-constant values from the last iteration can be - replaced with final values if we can compute them. */ - if (gimple_debug_bind_p (stmt)) - { - tree val = gimple_debug_bind_get_value (stmt); - gsi_move_before (&gsi, &dst_gsi); - if (val - && (!safe_p - || !is_gimple_min_invariant (val) - || !dominated_by_p (CDI_DOMINATORS, exit->src, bbs[i]))) - { - gimple_debug_bind_reset_value (stmt); - update_stmt (stmt); - } - } - else - gsi_next (&gsi); - } - } - - redirect_edge_pred (exit, src); - exit->flags &= ~(EDGE_TRUE_VALUE | EDGE_FALSE_VALUE); - exit->flags |= EDGE_FALLTHRU; - cancel_loop_tree (loop); - rescan_loop_exit (exit, false, true); - - i = nbbs; - do - { - --i; - delete_basic_block (bbs[i]); - } - while (i != 0); - - free (bbs); - - set_immediate_dominator (CDI_DOMINATORS, dest, - recompute_dominator (CDI_DOMINATORS, dest)); -} - /* Replaces function body with CRC_IFN call. Returns true if replacement is succeeded, otherwise false. */ bool crc_optimization::faster_crc_code_generation (function *fun) { if (!(data && first_phi_for_crc)) - return false; + return false; + // TODO: Use another way of figuring out which argument is data, as this won't + // work correctly in case when data is xor-ed with crc before the loop. tree crc_arg = PHI_ARG_DEF (first_phi_for_crc, 1); tree data_arg = PHI_ARG_DEF (data, 1); - destroy_loop (crc_loop); - //repair_loop_structures (); - gimple *return_stmt = remove_stmts_besides_return (fun); + if (!direct_internal_fn_supported_p + (IFN_CRC, tree_pair (TREE_TYPE (data_arg), TREE_TYPE (crc_arg)), + OPTIMIZE_FOR_BOTH)) + { + if (dump_file) + fprintf (dump_file, "No matching optab entry\n"); + return false; + } + + gimple *return_stmt = remove_stmts_besides_return (fun, crc_loop); /* Add IFN call and return the value. */ gcall *call = gimple_build_call_internal (IFN_CRC, 3, crc_arg, data_arg, - data_arg); + crc_arg); // must be changed to polynomial tree result = make_ssa_name (TREE_TYPE (DECL_RESULT (fun->decl))); gimple_call_set_lhs (call, result); diff --git a/gcc/internal-fn.cc b/gcc/internal-fn.cc index 140ac255289f..90db7aedaf23 100644 --- a/gcc/internal-fn.cc +++ b/gcc/internal-fn.cc @@ -3720,9 +3720,27 @@ static void expand_crc_optab_fn (internal_fn, gcall *stmt, convert_optab optab) { tree lhs = gimple_call_lhs (stmt); - tree rhs1 = gimple_call_arg (stmt, 0); - tree rhs2 = gimple_call_arg (stmt, 1); - tree rhs3 = gimple_call_arg (stmt, 2); + tree rhs1 = gimple_call_arg (stmt, 0); // crc + tree rhs2 = gimple_call_arg (stmt, 1); // data + tree rhs3 = gimple_call_arg (stmt, 2); // polynomial + tree result_type = TREE_TYPE (lhs); + tree data_type = TREE_TYPE (rhs2); + + rtx dest = expand_expr (lhs, NULL_RTX, SImode, EXPAND_WRITE); + rtx op1 = expand_normal (rhs1); + rtx op2 = expand_normal (rhs2); + rtx op3 = expand_normal (rhs3); + + class expand_operand ops[4]; + create_output_operand (&ops[0], dest, TYPE_MODE (result_type)); + create_input_operand (&ops[1], op1, TYPE_MODE (result_type)); // crc + create_input_operand (&ops[2], op2, TYPE_MODE (data_type)); // data + create_input_operand (&ops[3], op3, TYPE_MODE (result_type)); //polynom + insn_code icode = convert_optab_handler (optab, TYPE_MODE (data_type), + TYPE_MODE (result_type)); + expand_insn (icode, 4, ops); + if (!rtx_equal_p (dest, ops[0].value)) + emit_move_insn (dest, ops[0].value); } /* Expanders for optabs that can use expand_direct_optab_fn. */ -- 2.47.2