Bug 48863 - A Bug When Assembler Instructions with C Expression Operands in arm-elf-gcc 4.5
Summary: A Bug When Assembler Instructions with C Expression Operands in arm-elf-gcc 4.5
Status: RESOLVED FIXED
Alias: None
Product: gcc
Classification: Unclassified
Component: target (show other bugs)
Version: 4.5.1
: P3 normal
Target Milestone: 6.3
Assignee: Not yet assigned to anyone
URL:
Keywords: wrong-code
: 48861 48862 (view as bug list)
Depends on:
Blocks:
 
Reported: 2011-05-04 06:38 UTC by Dillon
Modified: 2017-08-16 11:41 UTC (History)
4 users (show)

See Also:
Host:
Target: arm
Build:
Known to work: 7.0
Known to fail:
Last reconfirmed: 2016-04-01 00:00:00


Attachments
test case for this bug (642 bytes, application/gzip)
2011-05-04 06:38 UTC, Dillon
Details
runtime test case (283 bytes, text/plain)
2011-06-19 16:26 UTC, Mikael Pettersson
Details

Note You need to log in before you can comment on or make changes to this bug.
Description Dillon 2011-05-04 06:38:19 UTC
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 ===============================
Comment 1 Mikael Pettersson 2011-05-04 08:48:23 UTC
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.
Comment 2 Dillon 2011-05-10 00:40:16 UTC
*** Bug 48861 has been marked as a duplicate of this bug. ***
Comment 3 Dillon 2011-05-10 00:41:40 UTC
*** Bug 48862 has been marked as a duplicate of this bug. ***
Comment 4 Mikael Pettersson 2011-06-19 16:26:25 UTC
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.
Comment 5 Ramana Radhakrishnan 2016-04-01 13:44:38 UTC
Well confirmed.
Comment 6 ktkachov 2016-11-24 15:23:05 UTC
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
Comment 7 ktkachov 2016-11-24 15:25:17 UTC
Fixed on trunk for GCC 7.
I'll wait for a bit before proposing a backport (if this is needed on the branches)
Comment 8 ktkachov 2016-12-01 13:47:44 UTC
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
Comment 9 Jakub Jelinek 2017-05-02 15:58:08 UTC
GCC 7.1 has been released.
Comment 10 Ramana Radhakrishnan 2017-08-16 11:41:02 UTC
This was really fixed for 6.3 ...