This is the mail archive of the
gcc@gcc.gnu.org
mailing list for the GCC project.
ARM gcc generates incorrect code?
- From: Krzysztof Halasa <khc at pm dot waw dot pl>
- To: gcc at gcc dot gnu dot org
- Date: Wed, 27 Feb 2008 12:48:07 +0100
- Subject: ARM gcc generates incorrect code?
Hi,
not sure where the bug is - gcc 4.2.4pre (CVS), binutils 2.17,
cross compiler X86_64 -> ARM BE.
-O2 -fno-strict-aliasing -fno-common -fno-stack-protector -marm
-fno-omit-frame-pointer -mapcs -mno-sched-prolog -mabi=apcs-gnu
-mno-thumb-interwork -march=armv5te -mtune=xscale -mcpu=xscale
-msoft-float -Uarm -fno-omit-frame-pointer -fno-optimize-sibling-calls
Building a test Linux kernel module:
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/device.h>
#define USE_UACCESS 0
#if USE_UACCESS
#include <asm/uaccess.h>
#else
extern int __get_user_1(void *);
/*
.global __get_user_1
__get_user_1:
1: ldrbt r2, [r0]
mov r0, #0
mov pc, lr
*/
#define get_user(x,p) \
({ \
register const u8 __user *__p asm("r0") = (p); \
register unsigned long __r2 asm("r2"); \
register int __e asm("r0"); \
__asm__ __volatile__ ( \
__asmeq("%0", "r0") __asmeq("%1", "r2") \
"bl __get_user_1" \
: "=&r" (__e), "=r" (__r2) \
: "0" (__p) \
: "lr", "cc"); \
x = (u8) __r2; \
__e; \
})
#endif
struct port {
u8 chan_buf[256];
unsigned int tx_count;
u8 modulo;
struct cdev cdev;
struct device *dev;
};
static struct class *test_class;
static dev_t rdev;
static struct port *main_port;
static ssize_t test_chan_write(struct file *file, const char __user *buf,
size_t count, loff_t *f_pos)
{
struct port *port = main_port;
int res = 0;
unsigned int tail, chan, frame;
tail = port->tx_count % 2;
chan = tail % port->modulo;
frame = tail / port->modulo;
if (get_user(port->chan_buf[chan * 2 + frame], buf))
return -EFAULT;
port->tx_count++;
res++;
return res;
}
static const struct file_operations chan_fops = {
.owner = THIS_MODULE,
.llseek = no_llseek,
.write = test_chan_write,
};
static int __init test_init_module(void)
{
int err;
if ((err = alloc_chrdev_region(&rdev, 0, 1, "test")))
return err;
if (IS_ERR(test_class = class_create(THIS_MODULE, "test"))) {
printk(KERN_ERR "Can't register device class 'test'\n");
err = PTR_ERR(test_class);
goto free_chrdev;
}
if (!(main_port = kzalloc(sizeof(*main_port), GFP_KERNEL))) {
err = -ENOBUFS;
goto destroy_class;
}
main_port->dev = device_create(test_class, NULL, rdev, "test");
if (IS_ERR(main_port->dev)) {
err = PTR_ERR(main_port->dev);
goto free;
}
main_port->tx_count = 0;
main_port->modulo = 2;
cdev_init(&main_port->cdev, &chan_fops);
main_port->cdev.owner = THIS_MODULE;
if ((err = cdev_add(&main_port->cdev, rdev, 1)))
goto destroy_device;
dev_set_drvdata(main_port->dev, &main_port);
printk(KERN_CRIT "start\n");
return 0;
destroy_device:
device_unregister(main_port->dev);
free:
kfree(main_port);
destroy_class:
class_destroy(test_class);
free_chrdev:
unregister_chrdev_region(rdev, 1);
return err;
}
static void __exit test_cleanup_module(void)
{
printk(KERN_CRIT "tx_count = %u, modulo = %u\n",
main_port->tx_count, main_port->modulo);
cdev_del(&main_port->cdev);
device_unregister(main_port->dev);
kfree(main_port);
class_destroy(test_class);
unregister_chrdev_region(rdev, 1);
}
MODULE_LICENSE("GPL v2");
module_init(test_init_module);
module_exit(test_cleanup_module);
# Makefile
obj-m := ixp-test.o
default:
make -C /usr/local/build/diskless/xscale_be-router-test-linux SUBDIRS=`pwd` ARCH=arm CROSS_COMPILE=armeb-pc-linux-gnu- modules
gcc produces the following assembly code:
00000000 <test_chan_write>:
0: e1a0c00d mov ip, sp
4: e92dddf0 stmdb sp!, {r4, r5, r6, r7, r8, sl, fp, ip, lr, pc}
8: e24cb004 sub fp, ip, #4 ; 0x4
c: e59f3054 ldr r3, [pc, #84] ; 68 <.text+0x68>
10: e1a00001 mov r0, r1 /* buf */
14: e5938000 ldr r8, [r3]
18: e598a100 ldr sl, [r8, #256] /* port->tx_count */
1c: e5d86104 ldrb r6, [r8, #260] /* port->modulo */
20: e20a4001 and r4, sl, #1 ; 0x1
24: ebfffffe bl 0 <__get_user_1> /* returns value in R0 */
28: e1a01006 mov r1, r6
2c: e1a00004 mov r0, r4 /* R0 overwritten here */
30: e1a07002 mov r7, r2
34: ebfffffe bl 0 <__umodsi3>
38: e1a01006 mov r1, r6
3c: e1a05000 mov r5, r0
40: e1a00004 mov r0, r4
44: ebfffffe bl 0 <__udivsi3>
/* gcc now tests already overwritten __get_user_1() return value here */
48: e3500000 cmp r0, #0 ; 0x0
4c: e0800085 add r0, r0, r5, lsl #1
50: e7c07008 strb r7, [r0, r8]
54: 028a3001 addeq r3, sl, #1 ; 0x1
58: 13e0000d mvnne r0, #13 ; 0xd
5c: 03a00001 moveq r0, #1 ; 0x1
60: 05883100 streq r3, [r8, #256]
64: e89dadf0 ldmia sp, {r4, r5, r6, r7, r8, sl, fp, sp, pc}
68: 00000008 andeq r0, r0, r8
Gcc bug? get_user() bug? Should I file a bug entry?
Gcc-4.1.2 and 4.1-CVS do a similar thing.
--
Krzysztof Halasa