https://gcc.gnu.org/bugzilla/show_bug.cgi?id=78355
Bug ID: 78355
Summary: LRA generates unaligned accesses when
SLOW_UNALIGNED_ACCESS is 1
Product: gcc
Version: 7.0
Status: UNCONFIRMED
Severity: normal
Priority: P3
Component: middle-end
Assignee: unassigned at gcc dot gnu.org
Reporter: pipcet at gmail dot com
Target Milestone: ---
Created attachment 40041
--> https://gcc.gnu.org/bugzilla/attachment.cgi?id=40041&action=edit
patch to illustrate proposed fix
(I hope I've selected the right component).
I'm working on a gcc (+binutils,glibc) backend for asm.js at
https://github.com/pipcet/asmjs. Things are working fairly well, but I
think I found a bug in the LRA handling of (subreg:SI (mem:HI ...))
expressions when STRICT_ALIGNMENT is true.
Summary: applying the attached patch will prevent the creation of
unaligned memory accesses on targets that don't support them, but it's
probably not the best fix.
I'm using LRA, as is required for new ports.
asm.js truncates unaligned accesses. I read the documentation as
suggesting I #define STRICT_ALIGNMENT 1 in this case, and #define
SLOW_UNALIGNED_ACCESS(x,y) 1 as well, which is what defaults.h would
have done had I not provided a definition.
In compiling the following code:
short int x[] = {
1, 2, 3, 4, 5, -1, -2,
};
int f(int index)
{
return x[index];
}
I see that the reload (LRA) pass turns
(insn 10 9 11 2 (set (reg:SI 45)
(ashift:SI (subreg:SI (mem:HI (plus:SI (reg:SI 47)
(symbol_ref:SI ("y") [flags 0x2] <var_decl
0x7ffff7fec510 y>)) [1 y S2 A16]) 0)
(const_int 16 [0x10]))) "sign-extend.c":16 9 {*assignsi_binop}
(expr_list:REG_DEAD (reg:SI 47)
(nil)))
into
(insn 10 9 11 2 (set (reg:SI 8 r0 [45])
(ashift:SI (mem:SI (plus:SI (reg:SI 8 r0 [47])
(symbol_ref:SI ("y") [flags 0x2] <var_decl 0x7ffff7fec510
y>)) [1 y S4 A16])
(const_int 16 [0x10]))) "sign-extend.c":16 9 {*assignsi_binop}
(nil))
The former is correct; the latter is not, because the access might be
unaligned and silently access the wrong data.
The problem is this code in simplify_operand_subreg (lra-constraints.c):
if (!SLOW_UNALIGNED_ACCESS (mode, MEM_ALIGN (reg))
|| SLOW_UNALIGNED_ACCESS (innermode, MEM_ALIGN (reg))
|| MEM_ALIGN (reg) >= GET_MODE_ALIGNMENT (mode))
return true;
... reload the mem in innermode
the second SLOW_UNALIGNED_ACCESS() expression is true (as it's defined
to be so in all cases), so we never reload the mem expression in the
right mode.
Commenting out the second line fixes the problem.
I think the problem here is that SLOW_UNALIGNED_ACCESS is sloppily
defined by my port, and defaults.h, to be a constant, but it is then
used with naturally-aligned data, access to which is never slow, and
expected to return false. So it would probably be best to replace the
condition by
if (!SLOW_UNALIGNED_ACCESS (mode, MEM_ALIGN (reg))
|| (MEM_ALIGN (reg) < GET_MODE_ALIGNMENT (innermode)
&& SLOW_UNALIGNED_ACCESS (innermode, MEM_ALIGN (reg)))
|| MEM_ALIGN (reg) >= GET_MODE_ALIGNMENT (mode))
return true;
Or is there a better way? I hope I'm not totally wrong in my analysis.