Created attachment 24175 [details] test case for this bug Assembler instructions with C expression operands, gcc(arm-elf-gcc) compiler may produce the wrong instrctions sequence with option -O2.There is a case only for test below. In the case, the second instruction ("mov r0, r1") destroyed r0 without saving, but r0 kept the value of variable fd and the variable should be passed to "swi 0". I think it's a serious bug, gcc compiler does not consider that "unsigned high = length / 23" may produce a function call. ================================case start ================================ static __inline__ int __syscall_test(int fd, unsigned pad, unsigned long high, unsigned low) { unsigned int __sys_result; { register int _a1 __asm__ ("r0") = fd; register int _a2 __asm__ ("r1") = pad; register int _a3 __asm__ ("r2") = high; register int _a4 __asm__ ("r3") = low; __asm__ __volatile__ ("swi 0" : "=r"(_a1) : "0"(_a1),"r"(_a3), "r"(_a4)); __sys_result = _a1; } return __sys_result; } int f_test(int fd, long long length) { unsigned low = length & 0xffffffff; unsigned high = length / 23; return __syscall_test(fd, 0, high, low); } ---------------------- compile result -------------- .file "case.c" .global __divdi3 .text .align 2 .global f_test .type f_test, %function f_test: @ args = 0, pretend = 0, frame = 0 @ frame_needed = 0, uses_anonymous_args = 0 stmfd sp!, {r4, lr} mov r0, r1 mov r4, r1 mov r3, #0 mov r1, r2 mov r2, #23 bl __divdi3 mov r3, r4 mov r2, r0 @ 10 "case.c" 1 swi 0 @ 0 "" 2 ldmfd sp!, {r4, pc} .size f_test, .-f_test .ident "GCC: (GNU) 4.5.2" ==================== end ===============================
I see this on armv5tel-linux-gnueabi too, with the glibc-ports-2.10.1 definition of the syscall wrapper macros. It seems to be caused by / being expanded to a libcall by the backend. If I move the "/ 23" expression to a helper function and make that noinline, then the division does occur well before the swi registers are set up. Without the noinline a call to __aeabi_uldivmod occurs just before the swi, clobbering some of its parameters.
*** Bug 48861 has been marked as a duplicate of this bug. ***
*** Bug 48862 has been marked as a duplicate of this bug. ***
Created attachment 24562 [details] runtime test case Here's a small runtime test case. > cat pr48863.c /* pr48863.c */ static inline int dosvc(int fd, unsigned long high, unsigned low) { register int r0 asm("r0") = fd; register int r2 asm("r2") = high; register int r3 asm("r3") = low; asm volatile("" : "=r"(r0) : "0"(r0), "r"(r2), "r"(r3)); return r0; } struct s { int fd; long long length; } s = { 2, 0 }, *p = &s; int main(void) { unsigned low = p->length & 0xffffffff; unsigned high = p->length / 23; if (dosvc(p->fd, high, low) != 2) __builtin_abort(); return 0; } > /mnt/scratch/objdir47/gcc/xgcc -B/mnt/scratch/objdir47/gcc/ -O2 pr48863.c ; ./a.out Abort > /mnt/scratch/objdir47/gcc/xgcc -B/mnt/scratch/objdir47/gcc/ -O2 -S pr48863.c ; cat pr48863.s ... main: @ args = 0, pretend = 0, frame = 0 @ frame_needed = 0, uses_anonymous_args = 0 # loads &p to r1 ldr r1, .L5 stmfd sp!, {r4, lr} # loads *&p to r1 ldr r1, [r1, #0] mov r2, #23 mov r3, #0 ldr r4, [r1, #8] ldr r1, [r1, #12] mov r0, r4 bl __aeabi_ldivmod mov r3, r4 mov r2, r0 # here's where the SWI would have been, note how: # 1. p->fd was never loaded into r0 # 2. r0 was clobbered by the libcall to __aeabi_ldivmod cmp r0, #2 # so this comparison will fail and we'll abort ... Works when compiled with -O0.
Well confirmed.
Author: ktkachov Date: Thu Nov 24 15:22:34 2016 New Revision: 242840 URL: https://gcc.gnu.org/viewcvs?rev=242840&root=gcc&view=rev Log: [TER] PR target/48863 : Don't replace expressions across local register variable definitions PR target/48863 PR inline-asm/70184 * tree-ssa-ter.c (temp_expr_table): Add reg_vars_cnt field. (new_temp_expr_table): Initialise reg_vars_cnt. (free_temp_expr_table): Release reg_vars_cnt. (process_replaceable): Add reg_vars_cnt argument, set reg_vars_cnt field of TAB. (find_replaceable_in_bb): Use the above to record register variable write occurrences and cancel replacement across them. * gcc.target/arm/pr48863.c: New test. Added: trunk/gcc/testsuite/gcc.target/arm/pr48863.c Modified: trunk/gcc/ChangeLog trunk/gcc/testsuite/ChangeLog trunk/gcc/tree-ssa-ter.c
Fixed on trunk for GCC 7. I'll wait for a bit before proposing a backport (if this is needed on the branches)
Author: ktkachov Date: Thu Dec 1 13:47:13 2016 New Revision: 243109 URL: https://gcc.gnu.org/viewcvs?rev=243109&root=gcc&view=rev Log: [TER] PR target/48863: Don't replace expressions across local register variable definitions Backport from mainline 2016-11-24 Kyrylo Tkachov <kyrylo.tkachov@arm.com> PR target/48863 PR inline-asm/70184 * tree-ssa-ter.c (temp_expr_table): Add reg_vars_cnt field. (new_temp_expr_table): Initialise reg_vars_cnt. (free_temp_expr_table): Release reg_vars_cnt. (process_replaceable): Add reg_vars_cnt argument, set reg_vars_cnt field of TAB. (find_replaceable_in_bb): Use the above to record register variable write occurrences and cancel replacement across them. * gcc.target/arm/pr48863.c: New test. Added: branches/gcc-6-branch/gcc/testsuite/gcc.target/arm/pr48863.c Modified: branches/gcc-6-branch/gcc/ChangeLog branches/gcc-6-branch/gcc/testsuite/ChangeLog branches/gcc-6-branch/gcc/tree-ssa-ter.c
GCC 7.1 has been released.
This was really fixed for 6.3 ...