Hi, when we expand a misaligned MEM_REF on the LHS, we must not call the code in expand_expr_real_1 if the subsequent patch is applied, because the code generates code extracting the contents of the memory to a register, which is of course bad if the intent is to write into that memory. Therefore expand_assignment should expand MEM_REFs itself, just as it do when it encounters naked misaligned ones.
In order not to have nearly identical code twice in expand_assignment and once more in expand_expr_real_1, I put it into a separate function expand_mem_ref_to_mem_rtx (I'll be happy to change the name to anything more correct or fitting). Nevertheless, the existing code pieces in expand_assignment and expand_expr_real_1 sre not exactly identical, the differences are: - expand_expr_real_1 handles a special case when the defining statement of the MEM_REF base is a BIT_AND_EXPR, expand_assignment does not. The changelog introducing the change says "TER BIT_AND_EXPRs into MEM_REFs" which I suspect is a good thing for LHSs as well, so I kept the code. - When expanding the base, the two functions differ in the expand_modifier they pass down to expand_expr. expand_assignment uses EXPAND_NORMAL while expand_expr_real_1 passes EXPAND_SUM. According to the comment in expr.h the latter seemed more permissive and so I used that, even though I admit I do not really know what the implications of this modifier are. Is it OK to use EXPAND_SUM also on a LHS? - expand_expr_real_1 calls memory_address_addr_space twice, whereas expand_assignment replaces the first call with convert_memory_address_addr_space. Looking at the two functions I thought it might be OK to call memory_address_addr_space (which itself calls convert_memory_address_addr_space) only once. But again, my expertise in this area is limited, I'll be happy to be shown I'm wrong here. So far I have bootstrapped and tested this patch separately on x86_64-linx and i686-linux. Additionally, it has also passed bootstrap and testing on usparc64-linux and ia64-linux. Thanks in advance for any comments, Martin 2012-03-09 Martin Jambor <mjam...@suse.cz> * expr.c (expand_mem_ref_to_mem_rtx): New function. (expand_assignment): Call it when expanding a MEM_REF on the LHS. (expand_expr_real_1): Likewise. Index: src/gcc/expr.c =================================================================== --- src.orig/gcc/expr.c +++ src/gcc/expr.c @@ -4565,6 +4565,47 @@ mem_ref_refers_to_non_mem_p (tree ref) && !MEM_P (DECL_RTL (base))); } +/* Expand a MEM_REF referring an object in memory to a MEM RTX. Any spacial + treatment of misalignment must be handled on top of the returned result. */ + +static rtx +expand_mem_ref_to_mem_rtx (tree ref) +{ + enum machine_mode address_mode, mode = TYPE_MODE (TREE_TYPE (ref)); + tree base = TREE_OPERAND (ref, 0); + addr_space_t as + = TYPE_ADDR_SPACE (TREE_TYPE (TREE_TYPE (TREE_OPERAND (ref, 0)))); + gimple def_stmt; + rtx mem, op0; + + gcc_checking_assert (!mem_ref_refers_to_non_mem_p (ref)); + + address_mode = targetm.addr_space.address_mode (as); + + if ((def_stmt = get_def_for_expr (base, BIT_AND_EXPR))) + { + tree mask = gimple_assign_rhs2 (def_stmt); + base = build2 (BIT_AND_EXPR, TREE_TYPE (base), + gimple_assign_rhs1 (def_stmt), mask); + TREE_OPERAND (ref, 0) = base; + } + op0 = expand_expr (base, NULL_RTX, VOIDmode, EXPAND_SUM); + op0 = convert_memory_address_addr_space (address_mode, op0, as); + if (!integer_zerop (TREE_OPERAND (ref, 1))) + { + rtx off + = immed_double_int_const (mem_ref_offset (ref), address_mode); + op0 = simplify_gen_binary (PLUS, address_mode, op0, off); + } + op0 = memory_address_addr_space (mode, op0, as); + mem = gen_rtx_MEM (mode, op0); + set_mem_attributes (mem, ref, 0); + set_mem_addr_space (mem, as); + if (TREE_THIS_VOLATILE (ref)) + MEM_VOLATILE_P (mem) = 1; + return mem; +} + /* Expand an assignment that stores the value of FROM into TO. If NONTEMPORAL is true, try generating a nontemporal store. */ @@ -4600,46 +4641,31 @@ expand_assignment (tree to, tree from, b != CODE_FOR_nothing) || SLOW_UNALIGNED_ACCESS (mode, align))) { - addr_space_t as - = TYPE_ADDR_SPACE (TREE_TYPE (TREE_TYPE (TREE_OPERAND (to, 0)))); struct expand_operand ops[2]; - enum machine_mode address_mode; - rtx reg, op0, mem; + rtx reg, mem; reg = expand_expr (from, NULL_RTX, VOIDmode, EXPAND_NORMAL); reg = force_not_mem (reg); if (TREE_CODE (to) == MEM_REF) - { - tree base = TREE_OPERAND (to, 0); - address_mode = targetm.addr_space.address_mode (as); - op0 = expand_expr (base, NULL_RTX, VOIDmode, EXPAND_NORMAL); - op0 = convert_memory_address_addr_space (address_mode, op0, as); - if (!integer_zerop (TREE_OPERAND (to, 1))) - { - rtx off - = immed_double_int_const (mem_ref_offset (to), address_mode); - op0 = simplify_gen_binary (PLUS, address_mode, op0, off); - } - op0 = memory_address_addr_space (mode, op0, as); - mem = gen_rtx_MEM (mode, op0); - set_mem_attributes (mem, to, 0); - set_mem_addr_space (mem, as); - } + mem = expand_mem_ref_to_mem_rtx (to); else if (TREE_CODE (to) == TARGET_MEM_REF) { + addr_space_t as + = TYPE_ADDR_SPACE (TREE_TYPE (TREE_TYPE (TREE_OPERAND (to, 0)))); struct mem_address addr; + rtx op0; get_address_description (to, &addr); op0 = addr_for_mem_ref (&addr, as, true); op0 = memory_address_addr_space (mode, op0, as); mem = gen_rtx_MEM (mode, op0); set_mem_attributes (mem, to, 0); set_mem_addr_space (mem, as); + if (TREE_THIS_VOLATILE (to)) + MEM_VOLATILE_P (mem) = 1; } else gcc_unreachable (); - if (TREE_THIS_VOLATILE (to)) - MEM_VOLATILE_P (mem) = 1; if (icode != CODE_FOR_nothing) { @@ -4737,7 +4763,11 @@ expand_assignment (tree to, tree from, b else { misalignp = false; - to_rtx = expand_normal (tem); + if (TREE_CODE (tem) == MEM_REF + && !mem_ref_refers_to_non_mem_p (tem)) + to_rtx = expand_mem_ref_to_mem_rtx (tem); + else + to_rtx = expand_normal (tem); } /* If the bitfield is volatile, we want to access it in the @@ -9395,17 +9425,12 @@ expand_expr_real_1 (tree exp, rtx target case MEM_REF: { - addr_space_t as - = TYPE_ADDR_SPACE (TREE_TYPE (TREE_TYPE (TREE_OPERAND (exp, 0)))); - enum machine_mode address_mode; - tree base = TREE_OPERAND (exp, 0); - gimple def_stmt; enum insn_code icode; - unsigned align; /* Handle expansion of non-aliased memory with non-BLKmode. That might end up in a register. */ if (mem_ref_refers_to_non_mem_p (exp)) { + tree base = TREE_OPERAND (exp, 0); HOST_WIDE_INT offset = mem_ref_offset (exp).low; tree bit_offset; tree bftype; @@ -9437,32 +9462,9 @@ expand_expr_real_1 (tree exp, rtx target bit_offset), target, tmode, modifier); } - address_mode = targetm.addr_space.address_mode (as); - base = TREE_OPERAND (exp, 0); - if ((def_stmt = get_def_for_expr (base, BIT_AND_EXPR))) - { - tree mask = gimple_assign_rhs2 (def_stmt); - base = build2 (BIT_AND_EXPR, TREE_TYPE (base), - gimple_assign_rhs1 (def_stmt), mask); - TREE_OPERAND (exp, 0) = base; - } - align = get_object_or_type_alignment (exp); - op0 = expand_expr (base, NULL_RTX, VOIDmode, EXPAND_SUM); - op0 = memory_address_addr_space (address_mode, op0, as); - if (!integer_zerop (TREE_OPERAND (exp, 1))) - { - rtx off - = immed_double_int_const (mem_ref_offset (exp), address_mode); - op0 = simplify_gen_binary (PLUS, address_mode, op0, off); - } - op0 = memory_address_addr_space (mode, op0, as); - temp = gen_rtx_MEM (mode, op0); - set_mem_attributes (temp, exp, 0); - set_mem_addr_space (temp, as); - if (TREE_THIS_VOLATILE (exp)) - MEM_VOLATILE_P (temp) = 1; + temp = expand_mem_ref_to_mem_rtx (exp); if (mode != BLKmode - && align < GET_MODE_ALIGNMENT (mode) + && get_object_or_type_alignment (exp) < GET_MODE_ALIGNMENT (mode) /* If the target does not have special handling for unaligned loads of mode then it can use regular moves for them. */ && ((icode = optab_handler (movmisalign_optab, mode))