esirisc has a sub-target that has DImode with 4 byte alignment, and DFmode with 8 byte alignment. For code like g++.dg/torture/pr39713.C , the optimizes changes the mode of a MEM from DImode to DFmode even if the required alignment is not available.
Appended is is the target-independent part of the fix. Required target changes were to make sure that (subreg:DF: (mem:DI ...)) is handled or rejected as an operandad as appropriate, and a secondary reload to a suitable register and in a suitable mode are done where needed (GENERAL_REGS in DImode in our case). Bootstrapped and regression tested in gcc version d6f1cf644c45b76a27b6a6869dedaa030e3c7570 on x86_64 GNU/Linux.
2021-01-14 Joern Rennecke <joern.renne...@riscy-ip.com> * combine.c (gen_lowpart_for_combine): Reject change to MEM with mode with higher alignment than given. * expr.c (expand_expr_real_1 <VIEW_CONVERT_EXPR>): When don't discard mode of a MEM before checking alignment. For STRICT_ALIGNMENT, try to load a reg in the inner mode. * simplify-rtx.c (simplify_subreg): Reject change to MEM with mode with higher alignment than given. diff --git a/gcc/combine.c b/gcc/combine.c index 3294575..966fa4a 100644 --- a/gcc/combine.c +++ b/gcc/combine.c @@ -11793,6 +11793,10 @@ gen_lowpart_for_combine (machine_mode omode, rtx x) || mode_dependent_address_p (XEXP (x, 0), MEM_ADDR_SPACE (x))) goto fail; + if (GET_MODE_ALIGNMENT (omode) > GET_MODE_ALIGNMENT (imode) + && GET_MODE_ALIGNMENT (omode) > MEM_ALIGN (x)) + goto fail; + /* If we want to refer to something bigger than the original memref, generate a paradoxical subreg instead. That will force a reload of the original memref X. */ diff --git a/gcc/expr.c b/gcc/expr.c index 04ef5ad..1f0117c 100644 --- a/gcc/expr.c +++ b/gcc/expr.c @@ -11362,7 +11362,9 @@ expand_expr_real_1 (tree exp, rtx target, machine_mode tmode, { if (!REG_P (op0) && !MEM_P (op0)) op0 = force_reg (GET_MODE (op0), op0); - op0 = gen_lowpart (mode, op0); + rtx tmp = gen_lowpart (mode, op0); + if (!MEM_P (tmp)) + op0 = tmp; } } /* If both types are integral, convert from one mode to the other. */ @@ -11383,6 +11385,8 @@ expand_expr_real_1 (tree exp, rtx target, machine_mode tmode, force_const_mem for constants because we don't allow pool constants to change mode. */ tree inner_type = TREE_TYPE (treeop0); + /* ??? Should probably modify the INNER_TYPE to bigger alignemnt if + TYPE requires it. */ gcc_assert (!TREE_ADDRESSABLE (exp)); @@ -11430,6 +11434,14 @@ expand_expr_real_1 (tree exp, rtx target, machine_mode tmode, } else if (STRICT_ALIGNMENT) { + machine_mode inner_mode = TYPE_MODE (TREE_TYPE (treeop0)); + if (GET_MODE (op0) == inner_mode + && targetm.can_change_mode_class + (inner_mode, mode, GENERAL_REGS)) + { + op0 = force_reg (inner_mode, op0); + return gen_lowpart (mode, op0); + } poly_uint64 mode_size = GET_MODE_SIZE (mode); poly_uint64 temp_size = mode_size; if (GET_MODE (op0) != BLKmode) diff --git a/gcc/simplify-rtx.c b/gcc/simplify-rtx.c index 571e233..766e0d0 100644 --- a/gcc/simplify-rtx.c +++ b/gcc/simplify-rtx.c @@ -7206,6 +7206,9 @@ simplify_context::simplify_subreg (machine_mode outermode, rtx op, if (MEM_P (op) && ! mode_dependent_address_p (XEXP (op, 0), MEM_ADDR_SPACE (op)) + /* Don't imply a larger alignment than is available. */ + && (GET_MODE_ALIGNMENT (outermode) <= GET_MODE_ALIGNMENT (innermode) + || GET_MODE_ALIGNMENT (outermode) <= MEM_ALIGN (op)) /* Allow splitting of volatile memory references in case we don't have instruction to move the whole thing. */ && (! MEM_VOLATILE_P (op)