Lower the partial limbs of extended _BitInts like the full limbs for most operations, so that explicit extensions can be inserted only where they are really needed.
gcc/ChangeLog: * gimple-lower-bitint.cc (struct bitint_large_huge): Remove the abi_load_p parameter of limb_access. (bitint_large_huge::limb_access): Same. (bitint_large_huge::limb_access_type): Conditionally express the extended partial limbs with m_limb_type. (MAYBE_EXTEND_PARTIAL_LIMB): Define. (bitint_large_huge::handle_plus_minus): Wrap the actual work that's moved to handle_plus_minus_1 to extend the partial limbs afterwards. (bitint_large_huge::handle_plus_minus_1): Define. (bitint_large_huge::handle_lshift): Extend the partial limbs after the operation, if needed. (bitint_large_huge::handle_cast): Same. (bitint_large_huge::handle_load): Same. (bitint_large_huge::handle_stmt): Same. (bitint_large_huge::lower_mergeable_stmt): Use the type of the its actual precision when masking the widened partial limb. (bitint_large_huge::extend_partial_limb): Define. (bitint_large_huge::lower_muldiv_stmt): Use. (bitint_large_huge::lower_float_conv_stmt): Use. (bitint_large_huge::finish_arith_overflow): Use. (bitint_large_huge::lower_addsub_overflow): Use. (bitint_large_huge::lower_stmt): Initialize m_extending. --- gcc/gimple-lower-bitint.cc | 235 ++++++++++++++++++++++++++++--------- 1 file changed, 181 insertions(+), 54 deletions(-) diff --git a/gcc/gimple-lower-bitint.cc b/gcc/gimple-lower-bitint.cc index 9b4d49395ae..c4d57ef6ab2 100644 --- a/gcc/gimple-lower-bitint.cc +++ b/gcc/gimple-lower-bitint.cc @@ -431,8 +431,8 @@ struct bitint_large_huge ~bitint_large_huge (); void insert_before (gimple *); - tree limb_access_type (tree, tree); - tree limb_access (tree, tree, tree, bool, bool = false); + tree limb_access_type (tree, tree, bool = false); + tree limb_access (tree, tree, tree, bool); tree build_bit_field_ref (tree, tree, unsigned HOST_WIDE_INT, unsigned HOST_WIDE_INT); void if_then (gimple *, profile_probability, edge &, edge &); @@ -444,6 +444,7 @@ struct bitint_large_huge tree prepare_data_in_out (tree, tree, tree *, tree = NULL_TREE); tree add_cast (tree, tree); tree handle_plus_minus (tree_code, tree, tree, tree); + tree handle_plus_minus_1 (tree_code, tree, tree, tree); tree handle_lshift (tree, tree, tree); tree handle_cast (tree, tree, tree); tree handle_bit_field_ref (tree, tree); @@ -455,6 +456,7 @@ struct bitint_large_huge tree lower_comparison_stmt (gimple *, tree_code &, tree, tree); void lower_shift_stmt (tree, gimple *); void lower_muldiv_stmt (tree, gimple *); + void extend_partial_limb (tree, tree); void lower_float_conv_stmt (tree, gimple *); tree arith_overflow_extract_bits (unsigned int, unsigned int, tree, unsigned int, bool); @@ -553,12 +555,21 @@ struct bitint_large_huge constant index after comparing the runtime one for equality with the constant). In these cases, m_cast_conditional is set to true and the bit-field load then communicates its m_data_cnt to handle_cast - using m_bitfld_load. */ + using m_bitfld_load. + + For extended bitints (where bitint_extended is set), the partial limbs + are assumed to be already extended and passed around in m_limb_type. + m_extending should be set when it is desirable to disable this behavior + and express the partial limbs in the type of their actual precision, for + example, when handling statements with operations that require an + explicit extension to re-fill the unused bits (e.g. plus/minus). */ + bool m_first; bool m_var_msb; unsigned m_upwards_2limb; bool m_upwards; bool m_cast_conditional; + bool m_extending; unsigned m_bitfld_load; vec<tree> m_data; unsigned int m_data_cnt; @@ -593,10 +604,20 @@ bitint_large_huge::insert_before (gimple *g) significant limb if any. */ tree -bitint_large_huge::limb_access_type (tree type, tree idx) +bitint_large_huge::limb_access_type (tree type, tree idx, + bool extending_p) { if (type == NULL_TREE) return m_limb_type; + + /* For extended _BitInts, if either of m_extending or extending_p is set, + this function should return the non-extended type of the partial + (high-ordered) limb, so that they get an explicit extension later + if needed (e.g. during an explicit cast or after certain operations). + Otherwise, it should always return m_limb_type, so that the partial + limb is considered already extended to avoid unnecessary extension. */ + extending_p |= m_extending; + unsigned HOST_WIDE_INT i = tree_to_uhwi (idx); unsigned int prec = TYPE_PRECISION (type); gcc_assert (i * limb_prec < prec); @@ -605,29 +626,34 @@ bitint_large_huge::limb_access_type (tree type, tree idx) : (i + 1) * limb_prec <= prec) return m_limb_type; else - return build_nonstandard_integer_type (prec % limb_prec, - TYPE_UNSIGNED (type)); + { + if (bitint_extended && !extending_p) + return m_limb_type; + else + return build_nonstandard_integer_type (prec % limb_prec, + TYPE_UNSIGNED (type)); + } } /* Return a tree how to access limb IDX of VAR corresponding to BITINT_TYPE TYPE. If WRITE_P is true, it will be a store, otherwise a read. */ tree -bitint_large_huge::limb_access (tree type, tree var, tree idx, bool write_p, - bool abi_load_p) +bitint_large_huge::limb_access (tree type, tree var, tree idx, bool write_p) { tree atype = (tree_fits_uhwi_p (idx) ? limb_access_type (type, idx) : m_limb_type); - tree ltype = (bitint_extended && abi_load_p) ? atype : m_limb_type; + tree ltype = m_limb_type; addr_space_t as = TYPE_ADDR_SPACE (TREE_TYPE (var)); + if (as != TYPE_ADDR_SPACE (ltype)) + ltype = build_qualified_type (ltype, TYPE_QUALS (ltype) + | ENCODE_QUAL_ADDR_SPACE (as)); + tree ret; if (DECL_P (var) && tree_fits_uhwi_p (idx)) { - if (as != TYPE_ADDR_SPACE (ltype)) - ltype = build_qualified_type (ltype, TYPE_QUALS (ltype) - | ENCODE_QUAL_ADDR_SPACE (as)); tree ptype = build_pointer_type (strip_array_types (TREE_TYPE (var))); unsigned HOST_WIDE_INT off = tree_to_uhwi (idx) * m_limb_size; ret = build2 (MEM_REF, ltype, @@ -638,9 +664,6 @@ bitint_large_huge::limb_access (tree type, tree var, tree idx, bool write_p, } else if (TREE_CODE (var) == MEM_REF && tree_fits_uhwi_p (idx)) { - if (as != TYPE_ADDR_SPACE (ltype)) - ltype = build_qualified_type (ltype, TYPE_QUALS (ltype) - | ENCODE_QUAL_ADDR_SPACE (as)); ret = build2 (MEM_REF, ltype, unshare_expr (TREE_OPERAND (var, 0)), size_binop (PLUS_EXPR, TREE_OPERAND (var, 1), @@ -653,10 +676,6 @@ bitint_large_huge::limb_access (tree type, tree var, tree idx, bool write_p, } else { - ltype = m_limb_type; - if (as != TYPE_ADDR_SPACE (ltype)) - ltype = build_qualified_type (ltype, TYPE_QUALS (ltype) - | ENCODE_QUAL_ADDR_SPACE (as)); var = unshare_expr (var); if (TREE_CODE (TREE_TYPE (var)) != ARRAY_TYPE || !useless_type_conversion_p (m_limb_type, @@ -1184,11 +1203,46 @@ bitint_large_huge::add_cast (tree type, tree val) return lhs; } -/* Helper of handle_stmt method, handle PLUS_EXPR or MINUS_EXPR. */ +/* For extended _BitInts, it's frequently needed to convert + the top limb back to m_limb_type to work with other operands + out there. */ + +#define MAYBE_EXTEND_PARTIAL_LIMB(op) \ + do { \ + if (bitint_extended \ + && !m_extending \ + && !types_compatible_p (m_limb_type, TREE_TYPE (op))) \ + op = add_cast (m_limb_type, op); \ + } while (0) + +/* Helper of handle_stmt method, handle PLUS_EXPR or MINUS_EXPR with + possible extension. */ tree bitint_large_huge::handle_plus_minus (tree_code code, tree rhs1, tree rhs2, tree idx) +{ + bool m_extending_save = m_extending; + m_extending = true; + + rhs2 = handle_operand (rhs2, idx); + if (rhs1 == NULL_TREE) + rhs1 = build_zero_cst (TREE_TYPE (rhs2)); + else + rhs1 = handle_operand (rhs1, idx); + + m_extending = m_extending_save; + + tree ret = handle_plus_minus_1 (code, rhs1, rhs2, idx); + MAYBE_EXTEND_PARTIAL_LIMB (ret); + return ret; +} + +/* Subroutine of handle_plus_minus that does the actuall work. */ + +tree +bitint_large_huge::handle_plus_minus_1 (tree_code code, tree rhs1, tree rhs2, + tree idx) { tree lhs, data_out, ctype; tree rhs1_type = TREE_TYPE (rhs1); @@ -1300,6 +1354,11 @@ bitint_large_huge::handle_lshift (tree rhs1, tree rhs2, tree idx) if (cnt == 0) return rhs1; + bool m_extending_save = m_extending; + m_extending = true; + rhs1 = handle_operand (rhs1, idx); + m_extending = m_extending_save; + tree lhs, data_out, rhs1_type = TREE_TYPE (rhs1); gimple *g; tree data_in = prepare_data_in_out (build_zero_cst (m_limb_type), idx, @@ -1338,6 +1397,8 @@ bitint_large_huge::handle_lshift (tree rhs1, tree rhs2, tree idx) } else lhs = data_in; + + MAYBE_EXTEND_PARTIAL_LIMB (lhs); m_data[m_data_cnt] = data_out; m_data_cnt += 2; return lhs; @@ -1395,9 +1456,11 @@ bitint_large_huge::handle_cast (tree lhs_type, tree rhs1, tree idx) rhs1 = handle_operand (rhs1, ridx); if (tree_fits_uhwi_p (idx)) { - tree type = limb_access_type (lhs_type, idx); + tree type = limb_access_type (lhs_type, idx, true); if (!types_compatible_p (type, TREE_TYPE (rhs1))) rhs1 = add_cast (type, rhs1); + + MAYBE_EXTEND_PARTIAL_LIMB (rhs1); } return rhs1; } @@ -1713,9 +1776,10 @@ bitint_large_huge::handle_cast (tree lhs_type, tree rhs1, tree idx) else t = m_data[save_data_cnt + 1]; } - tree type = limb_access_type (lhs_type, idx); + tree type = limb_access_type (lhs_type, idx, true); if (!useless_type_conversion_p (type, m_limb_type)) t = add_cast (type, t); + MAYBE_EXTEND_PARTIAL_LIMB (t); m_first = save_first; return t; } @@ -1799,10 +1863,11 @@ bitint_large_huge::handle_cast (tree lhs_type, tree rhs1, tree idx) t = m_data[m_data_cnt + 1]; else { - tree type = limb_access_type (lhs_type, idx); + tree type = limb_access_type (lhs_type, idx, true); t = m_data[m_data_cnt + 2]; if (!useless_type_conversion_p (type, m_limb_type)) t = add_cast (type, t); + MAYBE_EXTEND_PARTIAL_LIMB (t); } m_data_cnt += 3; return t; @@ -1816,7 +1881,7 @@ bitint_large_huge::handle_cast (tree lhs_type, tree rhs1, tree idx) unsigned lcnt = CEIL ((unsigned) TYPE_PRECISION (lhs_type), limb_prec); if (tree_fits_uhwi_p (idx)) { - tree type = limb_access_type (lhs_type, idx); + tree type = limb_access_type (lhs_type, idx, true); if (bitint_big_endian ? tree_to_uhwi (idx) == lcnt - 1 : integer_zerop (idx)) t = m_data[m_data_cnt]; @@ -1829,6 +1894,7 @@ bitint_large_huge::handle_cast (tree lhs_type, tree rhs1, tree idx) t = m_data[m_data_cnt + 2]; if (!useless_type_conversion_p (type, m_limb_type)) t = add_cast (type, t); + MAYBE_EXTEND_PARTIAL_LIMB (t); m_data_cnt += 3; return t; } @@ -2249,9 +2315,11 @@ bitint_large_huge::handle_load (gimple *stmt, tree idx) } if (tree_fits_uhwi_p (idx)) { - tree atype = limb_access_type (rhs_type, idx); + tree atype = limb_access_type (rhs_type, idx, true); if (!useless_type_conversion_p (atype, TREE_TYPE (iv))) iv = add_cast (atype, iv); + + MAYBE_EXTEND_PARTIAL_LIMB (iv); } m_data_cnt += 3; return iv; @@ -2261,10 +2329,12 @@ normal_load: /* Use write_p = true for loads with EH edges to make sure limb_access doesn't add a cast as separate statement after it. */ - rhs1 = limb_access (rhs_type, rhs1, idx, eh, !load_bitfield_p); + rhs1 = limb_access (rhs_type, rhs1, idx, eh); tree ret = make_ssa_name (TREE_TYPE (rhs1)); g = gimple_build_assign (ret, rhs1); insert_before (g); + rhs1 = gimple_assign_lhs (g); + if (eh) { maybe_duplicate_eh_stmt (g, stmt); @@ -2274,14 +2344,29 @@ normal_load: m_gsi = gsi_after_labels (e->dest); add_eh_edge (e->src, eh_edge); } - if (tree_fits_uhwi_p (idx)) - { - tree atype = limb_access_type (rhs_type, idx); - if (!useless_type_conversion_p (atype, TREE_TYPE (rhs1))) - ret = add_cast (atype, ret); - } } - return ret; + + /* Handle type casting. */ + if (tree_fits_uhwi_p (idx)) + { + tree atype = limb_access_type (rhs_type, idx, true); + if (bitint_extended && (((eh && m_extending) || load_bitfield_p))) + { + /* If a bit-field is being loaded, we extend them + to avoid getting adjacent bits from the memory. + + If m_extending is set in the eh case, the return value + should be in limb_access_type (rhs_type, idx, true). */ + + rhs1 = add_cast (atype, rhs1); + + if (load_bitfield_p && !m_extending) + rhs1 = add_cast (m_limb_type, rhs1); + } + else if (eh && !useless_type_conversion_p (atype, TREE_TYPE (rhs1))) + rhs1 = add_cast (atype, rhs1); + } + return rhs1; } /* Return a limb IDX from a mergeable statement STMT. */ @@ -2309,20 +2394,26 @@ bitint_large_huge::handle_stmt (gimple *stmt, tree idx) g = gimple_build_assign (lhs, gimple_assign_rhs_code (stmt), rhs1, rhs2); insert_before (g); + /* Extend the result of BIT_NOT_EXPR. */ + if (rhs2 == NULL_TREE && bitint_extended && tree_fits_uhwi_p (idx)) + { + tree rhs_type = TREE_TYPE (gimple_assign_rhs1 (stmt)); + tree atype = limb_access_type (rhs_type, idx, true); + lhs = add_cast (atype, lhs); + if (!m_extending) + lhs = add_cast (m_limb_type, lhs); + } return lhs; case PLUS_EXPR: case MINUS_EXPR: - rhs1 = handle_operand (gimple_assign_rhs1 (stmt), idx); - rhs2 = handle_operand (gimple_assign_rhs2 (stmt), idx); return handle_plus_minus (gimple_assign_rhs_code (stmt), - rhs1, rhs2, idx); + gimple_assign_rhs1 (stmt), + gimple_assign_rhs2 (stmt), idx); case NEGATE_EXPR: - rhs2 = handle_operand (gimple_assign_rhs1 (stmt), idx); - rhs1 = build_zero_cst (TREE_TYPE (rhs2)); - return handle_plus_minus (MINUS_EXPR, rhs1, rhs2, idx); + return handle_plus_minus (MINUS_EXPR, NULL_TREE, + gimple_assign_rhs1 (stmt), idx); case LSHIFT_EXPR: - return handle_lshift (handle_operand (gimple_assign_rhs1 (stmt), - idx), + return handle_lshift (gimple_assign_rhs1 (stmt), gimple_assign_rhs2 (stmt), idx); case SSA_NAME: case PAREN_EXPR: @@ -3186,7 +3277,8 @@ bitint_large_huge::lower_mergeable_stmt (gimple *stmt, tree_code &cmp_code, && tree_fits_uhwi_p (idx) && !nlhs) { - rhs1 = add_cast (limb_access_type (lhs_type, idx), rhs1); + rhs1 = add_cast (limb_access_type (lhs_type, idx, true), + rhs1); rhs1 = add_cast (TREE_TYPE (l), rhs1); } @@ -3929,20 +4021,39 @@ bitint_large_huge::lower_shift_stmt (tree obj, gimple *stmt) loop or in the if after it. To simplify the code, just read it back from memory and extend. */ m_gsi = gsi_after_labels (edge_false->dest); - idx = bitint_big_endian ? size_zero_node : p; - tree l = limb_access (TREE_TYPE (lhs), obj, idx, true); - tree type = limb_access_type (TREE_TYPE (lhs), idx); - tree v = make_ssa_name (m_limb_type); - g = gimple_build_assign (v, l); - insert_before (g); - v = add_cast (type, v); - l = limb_access (TREE_TYPE (lhs), obj, idx, true); - g = gimple_build_assign (l, add_cast (m_limb_type, v)); - insert_before (g); + extend_partial_limb (obj, TREE_TYPE (lhs)); } } } +/* Extend the partial limb of OBJ of the _BitInt type TYPE. + This is needed after libcalls that output non-extended + _BitInt values on targets that have the bitint_extended + flag set. */ + +void +bitint_large_huge::extend_partial_limb (tree obj, tree type) +{ + int prec = TYPE_PRECISION (type); + if (prec % limb_prec != 0) + { + tree idx = bitint_big_endian ? size_zero_node + : size_int (prec / limb_prec); + + tree hi_limb = limb_access (type, obj, idx, true); + gimple *g = gimple_build_assign (make_ssa_name (m_limb_type), hi_limb); + insert_before (g); + + tree val = gimple_assign_lhs (g); + tree hi_type = limb_access_type (type, idx, true); + val = add_cast (m_limb_type, add_cast (hi_type, val)); + + hi_limb = limb_access (type, obj, idx, true); + g = gimple_build_assign (hi_limb, val); + insert_before (g); + } +} + /* Lower large/huge _BitInt multiplication or division. */ void @@ -4007,6 +4118,11 @@ bitint_large_huge::lower_muldiv_stmt (tree obj, gimple *stmt) default: gcc_unreachable (); } + + /* Extend the result if needed. */ + if (bitint_extended) + extend_partial_limb (obj, type); + if (stmt_ends_bb_p (stmt)) { maybe_duplicate_eh_stmt (g, stmt); @@ -4038,7 +4154,8 @@ bitint_large_huge::lower_float_conv_stmt (tree obj, gimple *stmt) gimple *g; if (rhs_code == FIX_TRUNC_EXPR) { - int prec = TYPE_PRECISION (TREE_TYPE (lhs)); + tree lhs_type = TREE_TYPE (lhs); + int prec = TYPE_PRECISION (lhs_type); if (!TYPE_UNSIGNED (TREE_TYPE (lhs))) prec = -prec; if (obj == NULL_TREE) @@ -4073,6 +4190,10 @@ bitint_large_huge::lower_float_conv_stmt (tree obj, gimple *stmt) lhs, build_int_cst (sitype, prec), rhs1); insert_before (g); + + /* Extend the result if needed (libgcc won't do this now). */ + if (bitint_extended) + extend_partial_limb (obj, lhs_type); } else { @@ -4286,6 +4407,11 @@ bitint_large_huge::finish_arith_overflow (tree var, tree obj, tree type, obj_nelts * m_limb_size)); insert_before (g); } + + /* Extend the result if needed. */ + if (obj && bitint_extended) + extend_partial_limb (obj, type); + if (orig_obj == NULL_TREE && obj) { ovf = add_cast (m_limb_type, ovf); @@ -4678,7 +4804,7 @@ bitint_large_huge::lower_addsub_overflow (tree obj, gimple *stmt) } } } - tree rhs = handle_plus_minus (code, rhs1, rhs2, idx); + tree rhs = handle_plus_minus_1 (code, rhs1, rhs2, idx); if (ovf != boolean_false_node) { if (tree_fits_uhwi_p (idx)) @@ -5953,6 +6079,7 @@ bitint_large_huge::lower_stmt (gimple *stmt) m_upwards = false; m_var_msb = false; m_cast_conditional = false; + m_extending = false; m_bitfld_load = 0; m_loc = gimple_location (stmt); if (is_gimple_call (stmt)) -- 2.46.0