+ So we just assume type differences at this point are ok.
+ The only exception we make here are pointer types, which can be different
+ in e.g. structurally equal, but non-identical RECORD_TYPEs. */
+ if (POINTER_TYPE_P (TREE_TYPE (dst))
+ && !useless_type_conversion_p (TREE_TYPE (dst), TREE_TYPE (src)))
+ src = fold_convert (TREE_TYPE (dst), src);
+
+ return build_gimple_modify_stmt (dst, src);
+}
+
+/* BIT_FIELD_REFs must not be shared. sra_build_elt_assignment()
+ takes care of assignments, but we must create copies for uses. */
+#define REPLDUP(t) (TREE_CODE (t) != BIT_FIELD_REF ? (t) : unshare_expr (t))
+
+/* Emit an assignment from SRC to DST, but if DST is a scalarizable
+ BIT_FIELD_REF, turn it into bit operations. */
+
+static tree
+sra_build_bf_assignment (tree dst, tree src)
+{
+ tree var, type, utype, tmp, tmp2, tmp3;
+ tree list, stmt;
+ tree cst, cst2, mask;
+ tree minshift, maxshift;
+
+ if (TREE_CODE (dst) != BIT_FIELD_REF)
+ return sra_build_assignment (dst, src);
+
+ var = TREE_OPERAND (dst, 0);
+
+ if (!scalar_bitfield_p (dst))
+ return sra_build_assignment (REPLDUP (dst), src);
+
+ list = NULL;
+
+ cst = fold_convert (bitsizetype, TREE_OPERAND (dst, 2));
+ cst2 = size_binop (PLUS_EXPR,
+ fold_convert (bitsizetype, TREE_OPERAND (dst, 1)),
+ cst);
+
+ if (BYTES_BIG_ENDIAN)
+ {
+ maxshift = size_binop (MINUS_EXPR, TYPE_SIZE (TREE_TYPE (var)), cst);
+ minshift = size_binop (MINUS_EXPR, TYPE_SIZE (TREE_TYPE (var)), cst2);
+ }
+ else
+ {
+ maxshift = cst2;
+ minshift = cst;
+ }
+
+ type = TREE_TYPE (var);
+ if (!INTEGRAL_TYPE_P (type))
+ type = lang_hooks.types.type_for_size
+ (TREE_INT_CST_LOW (TYPE_SIZE (TREE_TYPE (var))), 1);
+ if (TYPE_UNSIGNED (type))
+ utype = type;
+ else
+ utype = unsigned_type_for (type);
+
+ mask = build_int_cst_wide (utype, 1, 0);
+ if (TREE_INT_CST_LOW (maxshift) == TYPE_PRECISION (utype))
+ cst = build_int_cst_wide (utype, 0, 0);
+ else
+ cst = int_const_binop (LSHIFT_EXPR, mask, maxshift, true);
+ if (integer_zerop (minshift))
+ cst2 = mask;
+ else
+ cst2 = int_const_binop (LSHIFT_EXPR, mask, minshift, true);
+ mask = int_const_binop (MINUS_EXPR, cst, cst2, true);
+ mask = fold_build1 (BIT_NOT_EXPR, utype, mask);
+
+ if (TYPE_MAIN_VARIANT (utype) != TYPE_MAIN_VARIANT (TREE_TYPE (var))
+ && !integer_zerop (mask))
+ {
+ tmp = var;
+ if (!is_gimple_variable (tmp))
+ tmp = unshare_expr (var);
+
+ tmp2 = make_rename_temp (utype, "SR");
+
+ if (INTEGRAL_TYPE_P (TREE_TYPE (var)))
+ stmt = build_gimple_modify_stmt (tmp2, fold_convert (utype, tmp));
+ else
+ stmt = build_gimple_modify_stmt (tmp2, fold_build1 (VIEW_CONVERT_EXPR,
+ utype, tmp));
+ append_to_statement_list (stmt, &list);
+ }
+ else
+ tmp2 = var;
+
+ if (!integer_zerop (mask))
+ {
+ tmp = make_rename_temp (utype, "SR");
+ stmt = build_gimple_modify_stmt (tmp,
+ fold_build2 (BIT_AND_EXPR, utype,
+ tmp2, mask));
+ append_to_statement_list (stmt, &list);
+ }
+ else
+ tmp = mask;
+
+ if (is_gimple_reg (src) && INTEGRAL_TYPE_P (TREE_TYPE (src)))
+ tmp2 = src;
+ else if (INTEGRAL_TYPE_P (TREE_TYPE (src)))
+ {
+ tmp2 = make_rename_temp (TREE_TYPE (src), "SR");
+ stmt = sra_build_assignment (tmp2, src);
+ append_to_statement_list (stmt, &list);
+ }
+ else
+ {
+ tmp2 = make_rename_temp
+ (lang_hooks.types.type_for_size
+ (TREE_INT_CST_LOW (TYPE_SIZE (TREE_TYPE (src))),
+ 1), "SR");
+ stmt = sra_build_assignment (tmp2, fold_build1 (VIEW_CONVERT_EXPR,
+ TREE_TYPE (tmp2), src));
+ append_to_statement_list (stmt, &list);
+ }
+
+ if (!TYPE_UNSIGNED (TREE_TYPE (tmp2)))
+ {
+ tree ut = unsigned_type_for (TREE_TYPE (tmp2));
+ tmp3 = make_rename_temp (ut, "SR");
+ tmp2 = fold_convert (ut, tmp2);
+ stmt = sra_build_assignment (tmp3, tmp2);
+ append_to_statement_list (stmt, &list);
+
+ tmp2 = fold_build1 (BIT_NOT_EXPR, utype, mask);
+ tmp2 = int_const_binop (RSHIFT_EXPR, tmp2, minshift, true);
+ tmp2 = fold_convert (ut, tmp2);
+ tmp2 = fold_build2 (BIT_AND_EXPR, ut, tmp3, tmp2);
+
+ if (tmp3 != tmp2)
+ {
+ tmp3 = make_rename_temp (ut, "SR");
+ stmt = sra_build_assignment (tmp3, tmp2);
+ append_to_statement_list (stmt, &list);
+ }
+
+ tmp2 = tmp3;
+ }
+
+ if (TYPE_MAIN_VARIANT (TREE_TYPE (tmp2)) != TYPE_MAIN_VARIANT (utype))
+ {
+ tmp3 = make_rename_temp (utype, "SR");
+ tmp2 = fold_convert (utype, tmp2);
+ stmt = sra_build_assignment (tmp3, tmp2);
+ append_to_statement_list (stmt, &list);
+ tmp2 = tmp3;
+ }
+
+ if (!integer_zerop (minshift))
+ {
+ tmp3 = make_rename_temp (utype, "SR");
+ stmt = build_gimple_modify_stmt (tmp3,
+ fold_build2 (LSHIFT_EXPR, utype,
+ tmp2, minshift));
+ append_to_statement_list (stmt, &list);
+ tmp2 = tmp3;
+ }
+
+ if (utype != TREE_TYPE (var))
+ tmp3 = make_rename_temp (utype, "SR");
+ else
+ tmp3 = var;
+ stmt = build_gimple_modify_stmt (tmp3,
+ fold_build2 (BIT_IOR_EXPR, utype,
+ tmp, tmp2));
+ append_to_statement_list (stmt, &list);
+
+ if (tmp3 != var)
+ {
+ if (TREE_TYPE (var) == type)
+ stmt = build_gimple_modify_stmt (var,
+ fold_convert (type, tmp3));
+ else
+ stmt = build_gimple_modify_stmt (var,
+ fold_build1 (VIEW_CONVERT_EXPR,
+ TREE_TYPE (var), tmp3));
+ append_to_statement_list (stmt, &list);
+ }
+
+ return list;
+}
+
+/* Expand an assignment of SRC to the scalarized representation of
+ ELT. If it is a field group, try to widen the assignment to cover
+ the full variable. */
+
+static tree
+sra_build_elt_assignment (struct sra_elt *elt, tree src)
+{
+ tree dst = elt->replacement;
+ tree var, tmp, cst, cst2, list, stmt;
+
+ if (TREE_CODE (dst) != BIT_FIELD_REF
+ || !elt->in_bitfld_block)
+ return sra_build_assignment (REPLDUP (dst), src);
+
+ var = TREE_OPERAND (dst, 0);
+
+ /* Try to widen the assignment to the entire variable.
+ We need the source to be a BIT_FIELD_REF as well, such that, for
+ BIT_FIELD_REF<d,sz,dp> = BIT_FIELD_REF<s,sz,sp>,
+ by design, conditions are met such that we can turn it into
+ d = BIT_FIELD_REF<s,dw,sp-dp>. */
+ if (elt->in_bitfld_block == 2
+ && TREE_CODE (src) == BIT_FIELD_REF)
+ {
+ tmp = src;
+ cst = TYPE_SIZE (TREE_TYPE (var));
+ cst2 = size_binop (MINUS_EXPR, TREE_OPERAND (src, 2),
+ TREE_OPERAND (dst, 2));
+
+ src = TREE_OPERAND (src, 0);
+
+ /* Avoid full-width bit-fields. */
+ if (integer_zerop (cst2)
+ && tree_int_cst_equal (cst, TYPE_SIZE (TREE_TYPE (src))))
+ {
+ if (INTEGRAL_TYPE_P (TREE_TYPE (src))
+ && !TYPE_UNSIGNED (TREE_TYPE (src)))
+ src = fold_convert (unsigned_type_for (TREE_TYPE (src)), src);
+
+ /* If a single conversion won't do, we'll need a statement
+ list. */
+ if (TYPE_MAIN_VARIANT (TREE_TYPE (var))
+ != TYPE_MAIN_VARIANT (TREE_TYPE (src)))
+ {
+ list = NULL;
+
+ if (!INTEGRAL_TYPE_P (TREE_TYPE (src)))
+ src = fold_build1 (VIEW_CONVERT_EXPR,
+ lang_hooks.types.type_for_size
+ (TREE_INT_CST_LOW
+ (TYPE_SIZE (TREE_TYPE (src))),
+ 1), src);
+ gcc_assert (TYPE_UNSIGNED (TREE_TYPE (src)));
+
+ tmp = make_rename_temp (TREE_TYPE (src), "SR");
+ stmt = build_gimple_modify_stmt (tmp, src);
+ append_to_statement_list (stmt, &list);
+
+ stmt = sra_build_assignment (var,
+ fold_convert (TREE_TYPE (var),
+ tmp));
+ append_to_statement_list (stmt, &list);
+
+ return list;
+ }
+
+ src = fold_convert (TREE_TYPE (var), src);
+ }
+ else
+ {
+ src = fold_convert (TREE_TYPE (var), tmp);
+ }
+
+ return sra_build_assignment (var, src);
+ }
+
+ return sra_build_bf_assignment (dst, src);