The compiler seems to use not or wrong initialized registers for memory references. 1. # arm-linux-g++ -v Reading specs from /usr/local/lib/gcc-lib/arm-linux/3.3/specs Configured with: ./configure --prefix=/usr/local --target=arm-linux --with-newlib --enable-languages=c,c++ --disable-libgcj --program-suffix=-3.3 -v : (reconfigured) ./configure --prefix=/usr/local --target=arm-linux --exec-prefix=/usr/local/arm-linux --with-newlib --enable-languages=c,c++ --disable-libgcj --program-suffix=-3.3 -v : (reconfigured) ./configure --prefix=/usr/local --target=arm-linux --exec-prefix=/usr/local --with-newlib --enable-languages=c,c++ --disable-libgcj -v : (reconfigured) ./configure --prefix=/usr/local --target=arm-linux --exec-prefix=/usr/local --with-newlib --enable-languages=c,c++ --disable-libgcj -v : (reconfigured) ./configure --prefix=/usr/local --target=arm-linux --exec-prefix=/usr/local --with-newlib --enable-languages=c,c++ --disable-libgcj -v : (reconfigured) ./configure --prefix=/usr/local --target=arm-linux --exec-prefix=/usr/local --with-newlib --enable-languages=c,c++ --disable-libgcj --disable-threads -v Thread model: single gcc version 3.3 Command line: arm-linux-g++ -c -MD -nostdinc -I../src/types/arm -I../src/types \ -I../src/drivers/arm -I../src/drivers \ -I../src/lib/minilibc/arm/include -I../src/lib/minilibc/include \ -I../src/lib/lmm -I../src/lib/amm -I../src/boot \ -I../src/kern/arm -I../src/kern -I../src/lib/libk/arm \ -I../src/lib/libk -I. -Iauto \ -I/usr/local/lib/gcc-lib/arm-linux/3.3//include -W -Wall -fno-rtti \ -fno-exceptions -fno-defer-pop -freg-struct-return -g -Wall -W \ -frename-registers -finline-limit=10000 -O1 ../src/sigma0-arm/test.cc \ -o test.o Compiler output: ../src/sigma0-arm/test.cc: In function `int main()': ../src/sigma0-arm/test.cc:21: warning: unused variable `l4_threadid_t my_pager' ../src/sigma0-arm/test.cc:21: warning: unused variable `l4_threadid_t my_preempter' ../src/sigma0-arm/test.cc: In function `void thread_action(unsigned int, unsigned int, unsigned int)': ../src/sigma0-arm/test.cc:85: warning: unused parameter `unsigned int b' ../src/sigma0-arm/test.cc:85: warning: unused parameter `unsigned int c' Preprocessed file: (test.ii) # 1 "../src/sigma0-arm/test.cc" # 1 "<built-in>" # 1 "<command line>" # 1 "../src/sigma0-arm/test.cc" # 1 "../src/lib/minilibc/include/cstdio" 1 # 1 "../src/lib/minilibc/include/stdio.h" 1 # 1 "../src/lib/minilibc/include/cdefs.h" 1 # 5 "../src/lib/minilibc/include/stdio.h" 2 # 1 "/usr/local/lib/gcc-lib/arm-linux/3.3/include/stddef.h" 1 # 151 "/usr/local/lib/gcc-lib/arm-linux/3.3/include/stddef.h" typedef int ptrdiff_t; # 213 "/usr/local/lib/gcc-lib/arm-linux/3.3/include/stddef.h" typedef unsigned int size_t; # 6 "../src/lib/minilibc/include/stdio.h" 2 # 1 "../src/lib/minilibc/include/mini_defs.h" 1 # 7 "../src/lib/minilibc/include/stdio.h" 2 extern "C" { int putchar(int c); int puts(const char *s); int putstr(const char *const s); int putnstr( const char *const c, int len ); int printf(const char *format, ...) __attribute__((format(printf,1,2))); int sprintf(char *str, const char *format, ...) __attribute__((format(printf,2,3))); int snprintf(char *str, size_t size, const char *format, ...) __attribute__((format(printf,3,4))); int asprintf(char **ptr, const char* format, ...) __attribute__((format(printf,2,3))); # 1 "../src/lib/minilibc/include/stdarg.h" 1 # 9 "../src/lib/minilibc/include/stdarg.h" pedef __builtin_va_list va_list; # 25 "../src/lib/minilibc/include/stdio.h" 2 int vprintf(const char *format, va_list ap) __attribute__((format(printf,1,0))); int vsprintf(char *str, const char *format, va_list ap) __attribute__((format(printf,2,0))); int vsnprintf(char *str, size_t size, const char *format, va_list ap) __attribute__((format(printf,3,0))); typedef int FILE; int getchar(void); char *gets(char *s) __attribute__((deprecated)); char *fgets(char *s, int size, FILE *stream); int vscanf(const char *format, va_list ap) __attribute__((format(scanf,1,0))); int vsscanf(const char *str, const char *format, va_list ap) __attribute__((format(scanf,2,0))); int sscanf(const char *str, const char *format, ...); } # 7 "../src/lib/minilibc/include/cstdio" 2 namespace std { using ::putchar; using ::puts; using ::putstr; using ::putnstr; using ::printf; using ::sprintf; using ::snprintf; using ::asprintf; using ::sscanf; using ::vprintf; using ::vsprintf; using ::vsnprintf; using ::getchar; using ::gets; using ::fgets; using ::vscanf; using ::vsscanf; }; # 2 "../src/sigma0-arm/test.cc" 2 # 1 "../src/types/types.h" 1 # 11 "../src/types/types.h" # 1 "/usr/local/lib/gcc-lib/arm-linux/3.3/include/stddef.h" 1 # 12 "../src/types/types.h" 2 # 1 "../src/types/arm/types-arch.h" 1 # 15 "../src/types/arm/types-arch.h" typedef unsigned char Unsigned8; typedef signed char Signed8; typedef unsigned short Unsigned16; typedef signed short Signed16; typedef unsigned int Unsigned32; typedef signed int Signed32; typedef unsigned long long int Unsigned64; typedef signed long long int Signed64; typedef Signed32 Smword; typedef Unsigned32 Mword; typedef Unsigned32 Address; typedef Unsigned64 Cpu_time; # 13 "../src/types/types.h" 2 template< typename a, typename b > inline a nonull_static_cast( b p ) { int d = reinterpret_cast<int>(static_cast<a>(reinterpret_cast<b>(10))) - 10; return reinterpret_cast<a>( reinterpret_cast<Mword>(p) + d ); } typedef struct { Unsigned32 low, high; } l4_low_high_t; typedef struct { Address low, high; } l4_addr_range_t; typedef Smword ssize_t; # 3 "../src/sigma0-arm/test.cc" 2 # 1 "../src/sigma0-arm/syscalls.h" 1 # 33 "../src/sigma0-arm/syscalls.h" extern "C" { # 44 "../src/sigma0-arm/syscalls.h" typedef Unsigned64 cpu_time_t; typedef struct { unsigned prio:8; unsigned small:8; unsigned zero:4; unsigned time_exp:4; unsigned time_man:8; } l4_sched_param_struct_t; typedef union { Mword sched_param; l4_sched_param_struct_t sp; } l4_sched_param_t; typedef struct { unsigned grant:1; unsigned write:1; unsigned size:6; unsigned zero:4; unsigned page:20; } l4_fpage_struct_t; typedef union { Mword raw; l4_fpage_struct_t fp; } l4_fpage_t; typedef struct { Mword snd_size; Mword snd_str; Mword rcv_size; Mword rcv_str; } l4_strdope_t; typedef union { struct { unsigned version : 10; unsigned thread : 6; unsigned task : 8; unsigned chief : 8; } id; Mword raw; } l4_threadid_t; typedef struct { unsigned rcv_exp:4; unsigned snd_exp:4; unsigned rcv_pfault:4; unsigned snd_pfault:4; unsigned snd_man:8; unsigned rcv_man:8; } l4_timeout_struct_t; typedef union { Mword raw; l4_timeout_struct_t timeout; } l4_timeout_t; # 121 "../src/sigma0-arm/syscalls.h" typedef union { struct { Mword msg_deceited :1; Mword fpage_received :1; Mword msg_redirected :1; Mword src_inside :1; Mword error_code :4; Mword strings :5; Mword dwords :19; } md; Mword raw; } l4_msgdope_t; # 173 "../src/sigma0-arm/syscalls.h" extern __inline__ l4_threadid_t l4_myself (void); extern __inline__ int l4_nchief (l4_threadid_t destination, l4_threadid_t *next_chief); extern __inline__ void l4_fpage_unmap (l4_fpage_t fpage, Mword mask); extern __inline__ l4_fpage_t l4_fpage (Mword address, Mword size, Mword write, Mword grant); extern __inline__ void l4_thread_switch (l4_threadid_t destintaion); extern __inline__ void l4_yield (void); extern __inline__ void l4_thread_ex_regs (l4_threadid_t destination, Mword ip, Mword sp, l4_threadid_t *preempter, l4_threadid_t *pager, Mword *old_ip, Mword *old_sp, Mword *old_cpsr); extern __inline__ void l4_task_new (l4_threadid_t dest, Mword mcp, Mword usp, Mword uip, l4_threadid_t pager); extern __inline__ cpu_time_t l4_thread_schedule(l4_threadid_t dest, l4_sched_param_t param, l4_threadid_t *ext_preempter, l4_threadid_t *partner, l4_sched_param_t *old_param); # 253 "../src/sigma0-arm/syscalls.h" extern __inline__ void outstring(const char* x) { __asm__ __volatile__ ( " mov r0, %1 \n" " mov lr, pc \n" " mov pc, %0 \n" " cmp lr, #2 \n" : : "i" ((-0x00000020-8)), "r"(x) : "r0", "lr"); } extern __inline__ void outnstring(const char* x, unsigned len) { __asm__ __volatile__ ( " mov r0, %1 \n" " mov r1, %2 \n" " mov lr, pc \n" " mov pc, %0 \n" " cmp lr, #3 \n" : : "i" ((-0x00000020-8)), "r"(x), "r"(len) : "r0", "r1", "lr"); } extern __inline__ void outdec(const Mword x ) { (void)x; } extern __inline__ void kd_display(const char* x) { (void)x; } extern __inline__ char kd_inchar() { char c; __asm__ __volatile__ ( " mov lr, pc \n" " mov pc, %1 \n" " cmp lr, #13 \n" " mov %0, r0 \n" : "=r" (c) : "i" ((-0x00000020-8)) : "r0", "lr"); return c; } # 310 "../src/sigma0-arm/syscalls.h" extern __inline__ l4_threadid_t l4_myself(void) { l4_threadid_t id; asm volatile ( " stmdb sp!, {fp} \n" " mov r0, %2 \n" " mov lr, pc \n" " mov pc, %1 \n" " mov %0, r1 \n" " ldmia sp!, {fp} \n" :"=r" (id) :"i" ((-0x00000008-8)), "i" (((l4_threadid_t) { raw : 0 }).raw) :"r1", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12", "r14", "memory"); return id; } extern __inline__ void l4_fpage_unmap(l4_fpage_t fpage, Mword mask) { __asm__ __volatile__ ( " stmdb sp!, {fp} \n" " mov r0, %1 @ l4_fpage_unmap \n" " mov r1, %2 \n" " mov pc, %0 \n" " ldmia sp!, {fp} \n" : :"i" ((-0x0000000C-8)), "ri" (fpage.raw), "ri" (mask) :"r0", "r1", "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12", "r14", "memory"); } extern __inline__ l4_fpage_t l4_fpage(Mword address, Mword size, Mword write, Mword grant) { return ((l4_fpage_t){fp:{grant, write, size, 0, (address & 0xfffff000U) >> 12 }}); } extern __inline__ void l4_thread_switch(l4_threadid_t dest) { __asm__ __volatile__ ( " stmdb sp!, {fp} \n" " mov r0, %1 @ l4_thread_switch \n" " mov lr, pc \n" " mov pc, %0 \n" " ldmia sp!, {fp} \n" : :"i" ((-0x00000010-8)), "r" (dest) :"r1", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12", "r14", "memory"); } extern __inline__ void l4_yield(void) { l4_thread_switch(((l4_threadid_t) { raw : 0 })); } extern __inline__ void l4_thread_ex_regs(l4_threadid_t destination, Mword ip, Mword sp, l4_threadid_t *preempter, l4_threadid_t *pager, Mword *old_ip, Mword *old_sp, Mword *old_cpsr){ register Mword _dst asm("r0") = destination.id.thread; register Mword _ip asm("r1") = ip; register Mword _sp asm("r2") = sp; register Mword _pager asm("r3") = pager->raw; register Mword _flags asm("r4"); (void)preempter; __asm__ __volatile__ ( " ldr r10, [sp,#24] \n" " stmdb sp!, {fp} \n" " mov lr, pc \n" " mov pc, %5 \n" " ldmia sp!, {fp} \n" : "=r" (_dst), "=r" (_ip), "=r" (_sp), "=r" (_pager), "=r" (_flags) : "i" ((-0x00000018-8)), "0" (_dst), "1" (_ip), "2" (_sp), "3" (_pager) : "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12", "r14", "memory"); if(pager) pager->raw = _pager; if(old_ip) *old_ip = _ip; if(old_sp) *old_sp = _sp; if(old_cpsr) *old_cpsr = _flags; } extern __inline__ void l4_task_new(l4_threadid_t dest, Mword mcp, Mword usp, Mword uip, l4_threadid_t pager) { Mword x[] = {dest.raw, mcp, pager.raw, uip, usp}; __asm__ __volatile__ ( " /* l4_task_new() */ \n" " ldr r0, %1 \n" " ldr r1, %2 \n" " ldr r2, %3 \n" " ldr r3, %4 \n" " ldr r4, %5 \n" " stmdb sp!, {fp} \n" " mov lr, pc \n" " mov pc, %0 \n" " ldmia sp!, {fp} \n" : :"i" ((-0x0000001C-8)), "m" (x[0]), "m" (x[1]), "m" (x[2]), "m" (x[3]), "m" (x[4]) :"r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12", "r14", "memory"); } extern __inline__ cpu_time_t l4_thread_schedule(l4_threadid_t dest, l4_sched_param_t param, l4_threadid_t *ext_preempter, l4_threadid_t *partner, l4_sched_param_t *old_param) { (void)ext_preempter; __asm__ __volatile__ ( "/* l4_thread_schedule */ \n" " stmdb sp!, {fp} \n" " mov r0, %4 \n" " mov r1, %3 \n" " mov lr, pc \n" " mov pc, %2 \n" " mov r0, %1 \n" " ldmia sp!, {fp} \n" :"=r" (partner), "=r" (*old_param) :"i" ((-0x00000014-8)), "r" (dest), "r" (param) :"r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", "r10", "r11", "r12", "r14"); return 0; } extern __inline__ int l4_ipc_wait(l4_threadid_t *src, void *rcv_msg, Mword *rcv_dword0, Mword *rcv_dword1, Mword *rcv_dword2, l4_timeout_t timeout, l4_msgdope_t *result) { union { struct { Mword rcv; l4_timeout_t timeout; } in; struct { Mword dw0; Mword dw1; Mword dw2; l4_threadid_t src; l4_msgdope_t result; } out; } x = { in: {((Mword) rcv_msg | 1), timeout}}; asm volatile( "/* l4_ipc_wait(start) */ \n" " ldr r2, %1 \n" " ldr r3, %7 \n" " mov r1, #0xFFFFFFFF \n" " stmdb sp!, {fp} \n" " mov lr, pc \n" " mov pc, %0 \n" " ldmia sp!, {fp} \n" " str r0, %6 \n" " str r1, %2 \n" " str r4, %3 \n" " str r5, %4 \n" " str r6, %5 \n" "\t/* l4_ipc_wait(end) */ \n" : : "i" ((-0x00000004-8)), "m" (x.in.rcv), "m" (x.out.src), "m" (x.out.dw0), "m" (x.out.dw1), "m" (x.out.dw2), "m" (x.out.result), "m" (x.in.timeout) : "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12", "r14", "memory"); *rcv_dword0 = x.out.dw0; *rcv_dword1 = x.out.dw1; *rcv_dword2 = x.out.dw2; *result = x.out.result; *src = x.out.src; return result->md.error_code; } extern __inline__ int l4_ipc_receive(l4_threadid_t src, void *rcv_msg, Mword *rcv_dword0, Mword *rcv_dword1, Mword *rcv_dword2, l4_timeout_t timeout, l4_msgdope_t *result) { union { struct { l4_threadid_t src; Mword rcv; l4_timeout_t timeout; } in; struct { Mword dw0; Mword dw1; Mword dw2; l4_msgdope_t result; } out; } x = { in: {src, (Mword) rcv_msg, timeout}}; asm volatile( "/* l4_ipc_receive(start) */ \n" " ldr r1, %2 \n" " ldr r2, %1 \n" " ldr r3, %7 \n" " mov r1, #0xFFFFFFFF \n" " stmdb sp!, {fp} \n" " mov lr, pc \n" " mov pc, %0 \n" " ldmia sp!, {fp} \n" " str r0, %6 \n" " str r1, %2 \n" " str r4, %3 \n" " str r5, %4 \n" " str r6, %5 \n" "\t/* l4_ipc_receive(end) */ \n" : : "i" ((-0x00000004-8)), "m" (x.in.rcv), "m" (x.in.src), "m" (x.out.dw0), "m" (x.out.dw1), "m" (x.out.dw2), "m" (x.out.result), "m" (x.in.timeout) : "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12", "r14", "memory" ); *rcv_dword0 = x.out.dw0; *rcv_dword1 = x.out.dw1; *rcv_dword2 = x.out.dw2; *result = x.out.result; return result->md.error_code; } extern __inline__ int l4_ipc_send(l4_threadid_t dest, const void *snd_msg, Mword w0, Mword w1, Mword w2, l4_timeout_t timeout, l4_msgdope_t *result) { register Mword _dest asm("r0") = dest.raw; register Mword _snd_msg asm("r1") = (Mword)snd_msg; register Mword _rcv_desc asm("r2") = ~0U; register Mword _timeout asm("r3") = timeout.raw; register Mword _w0 asm("r4") = w0; register Mword _w1 asm("r5") = w1; register Mword _w2 asm("r6") = w2; __asm__ __volatile__ ("/* l4_ipc_send(start) */\n\t" " stmdb sp!, {fp} \n\t" " mov lr, pc \n\t" " mov pc, %7 \n\t" " ldmia sp!, {fp} \n\t" " str r0, %8 \n\t" "/*l4_ipc_send(end) */ \n\t" : "=r" (_dest), "=r" (_snd_msg), "=r" (_rcv_desc), "=r" (_timeout), "=r" (_w0), "=r" (_w1), "=r" (_w2) : "i" ((-0x00000004-8)), "m" (*result), "0" (_dest), "1" (_snd_msg), "2" (_rcv_desc), "3" (_timeout), "4" (_w0), "5" (_w1), "6" (_w2) : "r7", "r8", "r9", "r10", "r11", "r12", "r14", "memory" ); return result->md.error_code; } extern __inline__ int l4_ipc_call(l4_threadid_t dest, const void *snd_msg, Mword snd_dword0, Mword snd_dword1, Mword snd_dword2, void *rcv_msg, Mword *rcv_dword0, Mword *rcv_dword1, Mword *rcv_dword2, l4_timeout_t timeout, l4_msgdope_t *result) { struct { Mword tid; Mword snd_dsc; Mword rcv_dsc; Mword timeout; Mword dw0; Mword dw1; Mword dw2; l4_msgdope_t result; } x = {dest.raw, (Mword) snd_msg, (Mword) rcv_msg, timeout.raw, snd_dword0, snd_dword1, snd_dword2 , {raw: 0}}; __asm__ __volatile__ ( "/* l4_ipc_call(start) */ \n" " ldr r0, %1 \n" " ldr r1, %2 \n" " ldr r2, %3 \n" " ldr r3, %8 \n" " ldr r4, %4 \n" " ldr r5, %5 \n" " ldr r6, %6 \n" " stmdb sp!, {fp} \n" " mov lr, pc \n" " mov pc, %0 \n" " ldmia sp!, {fp} \n" " str r0, %7 \n" " str r4, %4 \n" " str r5, %5 \n" " str r6, %6 \n" "\t/*l4_ipc_call(end) */ \n" : : "i" ((-0x00000004-8)), "m" (x.tid), "m" (x.snd_dsc), "m" (x.rcv_dsc), "m" (x.dw0), "m" (x.dw1), "m" (x.dw2), "m" (x.result), "m" (x.timeout) :"r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12", "r14", "memory"); *rcv_dword0 = x.dw0; *rcv_dword1 = x.dw1; *rcv_dword2 = x.dw2; *result = x.result; return result->md.error_code; } extern __inline__ int l4_ipc_reply_and_wait(l4_threadid_t dest, const void *snd_msg, Mword snd_dword0, Mword snd_dword1, Mword snd_dword2, l4_threadid_t *src, void *rcv_msg, Mword *rcv_dword0, Mword *rcv_dword1, Mword *rcv_dword2, l4_timeout_t timeout, l4_msgdope_t *result) { struct { Mword tid; Mword snd_dsc; Mword rcv_dsc; l4_timeout_t timeout; Mword dw0; Mword dw1; Mword dw2; l4_msgdope_t result; } x = {dest.raw, (Mword) snd_msg, (Mword) rcv_msg | 1, timeout, snd_dword0, snd_dword1, snd_dword2, {raw: 0}}; __asm__ __volatile__ ( "/* l4_ipc_reply_and_wait(start) */ \n" " ldr r0, %1 \n" " ldr r1, %2 \n" " ldr r2, %3 \n" " ldr r3, %8 \n" " ldr r4, %4 \n" " ldr r5, %5 \n" " ldr r6, %6 \n" " stmdb sp!, {fp} \n" " mov lr, pc \n" " mov pc, %0 \n" " ldmia sp!, {fp} \n" " str r0, %7 \n" " str r1, %1 \n" " str r4, %4 \n" " str r5, %5 \n" " str r6, %6 \n" "\t/*l4_ipc_reply_and_wait(end) */ \n" : : "i" ((-0x00000004-8)), "m" (x.tid), "m" (x.snd_dsc), "m" (x.rcv_dsc), "m" (x.dw0), "m" (x.dw1), "m" (x.dw2), "m" (x.result), "m" (x.timeout) :"r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12", "r14", "memory"); *rcv_dword0 = x.dw0; *rcv_dword1 = x.dw1; *rcv_dword2 = x.dw2; src->raw = x.tid; *result = x.result; return result->md.error_code; return 0; } } # 4 "../src/sigma0-arm/test.cc" 2 Mword stack[1024]; extern "C" { void thread_action(unsigned sta, unsigned, unsigned); void thread_action_asm(); } int main() { l4_threadid_t my_pager, my_preempter, myself, dst; l4_threadid_t sigma0; sigma0.raw = 0; sigma0.id.task=2; dst.raw = 0; dst.id.task=4; dst.id.thread=1; myself = l4_myself(); int x=0; Mword d1,d2,d3; l4_msgdope_t result; printf("TEST[""%d.%d""]: Start thread ""%d.%d"": sp= %p\n", (myself).id.task, (myself).id.thread,(dst).id.task, (dst).id.thread,stack); printf("TEST: pager= %08x, ""%d.%d""\n",sigma0.raw,(sigma0).id.task, (sigma0).id.thread); l4_thread_ex_regs(dst,(Mword)&thread_action_asm,(Mword)(stack+1020), &((l4_threadid_t) { raw : ~0 }),&sigma0, 0,0,0 ); printf("TEST[""%d.%d""]: Start should be up\n", (myself).id.task, (myself).id.thread); if(l4_ipc_send(dst, 0, d1, d2, d3, ((l4_timeout_t) { raw: 0}), &result)==0) { puts("SUCCESS"); } else { puts("ERROR"); } __asm__ __volatile__ ( " mov lr, pc \n" " mov pc, %0 \n" " b 1f \n" " .ascii \"" "\" \n" " .byte 0 \n" " .align 2 \n" "1: \n" : : "i" ((-0x00000020-8)) : "lr"); for(x=0;x<10;x++) { printf("TEST[""%d.%d""]: Hello World!\n", (myself).id.task, (myself).id.thread ); l4_ipc_receive(((l4_threadid_t) { raw : 0 }), 0, &d1, &d2, &d3, ( (l4_timeout_t) {timeout: { 9, 0, 0, 0, 0, 122 } } ), &result); } thread_action(10,20,30); return 1; } asm (".text \n" ".globl thread_action_asm \n" "thread_action_asm: \n" " mov r0, sp \n" " adr sp, 1f \n" " ldr sp, [sp] \n" " b thread_action \n" "1: .word stack+4096 \n"); extern "C" __attribute__((noinline)) void thread_action(unsigned sta,unsigned b, unsigned c) { Mword d1,d2,d3; l4_msgdope_t result; l4_threadid_t myself, other; myself = l4_myself(); other=myself; other.id.thread=0; printf("TEST[""%d.%d""]: thread_action (ostack=%08x)!!!\n", (myself).id.task, (myself).id.thread,sta); printf("TEST[""%d.%d""]: waiting for ipc from ""%d.%d""\n", (myself).id.task, (myself).id.thread, (other).id.task, (other).id.thread); l4_ipc_receive(other,0,&d1,&d2,&d3, ((l4_timeout_t) { raw: 0}), &result); __asm__ __volatile__ ( " mov lr, pc \n" " mov pc, %0 \n" " b 1f \n" " .ascii \"" "\" \n" " .byte 0 \n" " .align 2 \n" "1: \n" : : "i" ((-0x00000020-8)) : "lr"); l4_ipc_receive(((l4_threadid_t) { raw : 0 }), 0, &d1, &d2, &d3, ((l4_timeout_t) { raw: 0}), &result); return; }
From <http://gcc.gnu.org/ml/gcc-bugs/2003-08/msg00669.html>: fp (r11) is the Compiler's frame-pointer register. You can't use it for your own purposes, the compiler cannot handle that. So I am closing as invalid.
I'm not using fp explicitely in my code, as you can see in test.ii. I just preserve it across system calls. The compiler uses fp to store non-pointer values in fp and later uses it as pointer! So it is seemingly a BUG.
Subject: Re: Wrong usage of registers on ARM > I'm not using fp explicitely in my code, as you can see in test.ii. I just > preserve it across system calls. The compiler uses fp to store non-pointer > values in fp and later uses it as pointer! > > So it is seemingly a BUG. No, this asm statement clobbers fp (or appears to): extern __inline__ l4_threadid_t l4_myself(void) { l4_threadid_t id; asm volatile ( " stmdb sp!, {fp} \n" " mov r0, %2 \n" " mov lr, pc \n" " mov pc, %1 \n" " mov %0, r1 \n" " ldmia sp!, {fp} \n" :"=r" (id) :"i" ((-0x00000008-8)), "i" (((l4_threadid_t) { raw : 0 }).raw) :"r1", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12", "r14", "memory"); return id; } This *will* confuse the compiler. R.
If gcc can't cope with the FP being clobbered, shouldn't it abort at compile time rather than generating bogus code?
gcc does error when the PIC register is clobered so it should error out when the frame pointer or stack pointer is clobered. An example for PPC: int f() { asm("addi r30, r30, 1":::"r30"); asm("addi r1, r1, 1":::"r1"); } I can confirm this on the mainline (20030805).
Not a regression.
*** Bug 13079 has been marked as a duplicate of this bug. ***
Here's what I think: 1. No way we can support clobbering both SP and FP. 2. We could support clobbering the FP by pushing it onto the stack and restoring it. Doesn't seem worth the trouble, though, since people can do this themselves in the asm. 3. We could support clobbering the SP by introducing a FP and being very careful. I cannot see any useful application though, so it doesn't seem worth the trouble. So I suggest a patch like this: --- stmt.c~ 2003-11-16 04:25:28.000000000 +0100 +++ stmt.c 2003-11-17 12:21:24.000000000 +0100 @@ -1524,13 +1524,17 @@ /* Mark clobbered registers. */ if (i >= 0) { - /* Clobbering the PIC register is an error */ - if (i == (int) PIC_OFFSET_TABLE_REGNUM) - { - error ("PIC register `%s' clobbered in `asm'", regname); - return; - } - + switch (i) { + case PIC_OFFSET_TABLE_REGNUM: + error ("PIC register `%s' clobbered in `asm'", regname); + return; + case STACK_POINTER_REGNUM: + error ("Stack pointer register `%s' clobbered in `asm'", regname); + return; + case HARD_FRAME_POINTER_REGNUM: + error ("Frame pointer register `%s' clobbered in `asm'", regname); + return; + } SET_HARD_REG_BIT (clobbered_regs, i); } } I have no time to test it currently, though.
Presumably if the programmer is intentionally, explicitly clobbering the frame pointer, the base pointer, or both using inline assembly, they are doing so knowing full well what they are doing -- and are attempting to do something unusual and specific with the stack. We should certainly take this into consideration when making decisions about what is the correct action for gcc to take. The assertion that there is no useful application for such clobbering is false, as careful adjusting of the frame and base pointers in assembly interleaved with struct/array accesses in C can produce fairly elegant context-switching code for multiplexing between threads/processes. Other applications may exist. Given that this is the case, it seems that gcc should do one of the following to provide "correct" behaviour: 1. (the obvious solution) Fail. If this is the case it should be noted in the inline assembly documentation that clobbering the frame or stack pointer will result in a compile-time error. 2. (refinement of Falk's #2) Insert code to save the stack and/or frame pointers in other registers before the clobber and restore them prior to usage (which would entail some very sophisticated code and would probably frustrate programmers who are intentionally abusing the stack discipline, but it would not be unreasonable). This doesn't seem like a viable solution, because it would entail tracking changes through large sections of code and adding significant amounts of extremely complex register copying, but would not add much in the way of usefulness other than letting programmers use the stack and/or frame pointers for arithmetic (not very useful at all). 3. (not considered in this thread so far, because it relies on the assumption that the programmer knows they're munging the stack) Emit code for function calls which takes the clobbering of the stack pointer into account when generating function calls and returns -- don't over-write anything above the stack pointer when making a function call, but allow arbitrary changes to the frame pointer, being careful not to use it for any other arithmetic (setting the frame pointer manually being tantamount to context-switching the execution of the function). This is arguably the most intuitive (and useful) behaviour (and the one that has the most obvious application to context switching), although it would also require quite a bit of fairly sophisticated code to track the changes to the stack pointer enough to emit correct function calls. This is possible to implement, although it may be quite difficult given the GCC codebase. I would suggest testing and implementing Falk's patch (solution #1) for now to preserve correct compilation in the immediate future, with an eye toward possibly implementing #3 at a later date.
As a side note, someone should change the summary of this bug to reflect the correct spelling of "clobbering", in case anyone (like me) searches for "clobber" or "clobbering" when checking for previously reported bugs. =P
(In reply to comment #8) > + switch (i) { > + case PIC_OFFSET_TABLE_REGNUM: Can't use a switch; PIC_OFFSET_TABLE_REGNUM is not constant. > + case HARD_FRAME_POINTER_REGNUM: > + error ("Frame pointer register `%s' clobbered in `asm'", regname); This one's more interesting, since if CAN_DEBUG_WITHOUT_FP we often won't need or use a frame pointer. I think this error should be delayed until reload has decided that a frame pointer is necessary.
(In reply to comment #9) > Presumably if the programmer is intentionally, explicitly clobbering > the frame pointer, the base pointer, or both using inline assembly, > they are doing so knowing full well what they are doing I would say this is demonstrably false. Indeed, I would say exactly the opposite. If they knew what they were doing, they would realize that clobbering the frame pointer will throw the compiler into a tizzy and WILL NEVER WORK. The nature of GCC's inline assembly is unfortunate. It's extremely powerful, but it exposes so much of compiler internals that you have to know how the compiler works so as to avoid invalid corner cases. > The assertion that there is no useful application for such clobbering > is false, as careful adjusting of the frame and base pointers in > assembly interleaved with struct/array accesses in C can produce ... ... a mess. Use out-of-line assembly if you're going to be playing with such stack frame fundamentals.
(In reply to comment #12) > > Presumably if the programmer is intentionally, explicitly clobbering > > the frame pointer, the base pointer, or both using inline assembly, > > they are doing so knowing full well what they are doing >.... > If they knew what they were doing, they would realize > that clobbering the frame pointer will throw the compiler into a > tizzy and WILL NEVER WORK. Just because you know not to trust the compiler, that doesn't mean that trusting the compiler to generate correct code is unreasonable. There are two assumptions that a programmer could make regarding the compiler: that the compiler writer knows how their code works and has it behave in reasonable ways; or that the compiler writer has forgotton or ignored some cases, which are broken. If the latter is the better assumption, no one should ever compile code with GCC because they *may* be using broken features -- there's no way to reliably tell which ones are and which ones aren't. This is clearly not an assumption that you want users to be making if you want your compiler to be taken seriously. Anything that can be fed successfully to the compiler should produce correct output; it should never be possible to "throw the compiler into a tizzy", because the compiler should have some well-defined behaviour for every allowable piece of code that it processes. It *does* have control over every line of code it generates. Your point here (and one that seems to be repeated far too often) seems to be that no one should use a language in a subtle, unusual way -- which has clearly not been the case, and probably never will be. Programmers will always push the system as far as it can go, and the system should (and historically has, for the most part) be able to handle it. In a compiler with a robust register allocator, it *is* possible for the compiler to deal with these very sorts of edge cases in a well-defined and intuitive manner, and whether they "work" in the sense that the programmer intended or not, they should at the bare minimum "work" in the sense that they do something anticipatable and rational (see comment #9 for suggestions of what that well-defined, rational behaviour could be). Some possibilities for dealing with the frame pointer include saving before the clobber and restoring after, and using a safer (and probably slower) stack discipline in the presence of volatile stack frames. If the code "WILL NEVER WORK" in the compiler, that's because the compiler contains errors -- and errors should be corrected, not avoided. > The nature of GCC's inline assembly is unfortunate. It's extremely > powerful, but it exposes so much of compiler internals that you have > to know how the compiler works so as to avoid invalid corner cases. Saying "it's ok for the compiler to generate invalid code because it's a corner case" is equivalent to saying "Apache may immediately crash upon receiving a malformed HTTP request" or "bash can accept strings into a buffer without performing any buffer length checks", which are clearly false -- corner cases, which have been thoroughly considered and handled in a robust manner (or at least that's what we expect). When it comes to programming, corner cases are a fact of life -- and they have to be dealt with responsibly rather than swept under the rug. It is the compiler-writer's duty to resolve corner cases in the compiler, not the programmer's -- they have their own set to worry about. > > The assertion that there is no useful application for such clobbering > > is false, as careful adjusting of the frame and base pointers in > > assembly interleaved with struct/array accesses in C can produce ... > > ... a mess. Use out-of-line assembly if you're going to be playing > with such stack frame fundamentals. Once again, if the language accepted by the compiler contains unusual constructs, they *must* be compiled correctly or else the compiler is *broken*. I have successfully written a context switch even in the presence of the current stack frame bugs -- by doing so in a function with no local variables. If I were to re-write the code in pure assembly, I would either have to completely commit myself to a particular, unrefinable data structure for storing process information, or check all of my assembly offsets every time I made a change. If I didn't want to use the features of GCC to my advantage, I would have written it in assembly to begin with -- but higher-level languages are much more elegant, especially when large, complex data structures are concerned.
(In reply to comment #11) > > + case HARD_FRAME_POINTER_REGNUM: > > + error ("Frame pointer register `%s' clobbered in `asm'", regname); > > This one's more interesting, since if CAN_DEBUG_WITHOUT_FP we often won't > need or use a frame pointer. I think this error should be delayed until > reload has decided that a frame pointer is necessary. Maybe. It's currently broken even for frameless, though, at least on Alpha: int f(int x) { asm ("nop" : : : "$15"); return x; } mov a0,v0 nop ret even though $15/fp/s6 is a callee-saved register in frameless functions.
The Frame pointer part is fixed by: * basic-block.h (PROP_ASM_SCAN): Define. * final.c (regs_asm_clobbered): New array. * regs.h (regs_asm_clobbered): Declare. * flow.c (life_analysis): Init it. (mark_set_regs): Set PROP_ASM_SCAN for asms. (mark_set_1): Set regs_asm_clobbered. * global.c (global_alloc): Don't set eliminable_regset when regs_asm_clobbered.
Frame pointer is still not fixed at -O0.
*** Bug 23313 has been marked as a duplicate of this bug. ***
Small testcase from PR 23313, showing ICE on invalid: ----------------------------------------- int main(){ int i; asm ( "xorl %%ebp, %%ebp\n\t" "movl %0, %%ebp\n\t" :: "m" (i) : "%ebp" ); return 0; } ----------------------------------------- This makes this PR a bug, not simply an enhancement.
(In reply to comment #18) > Small testcase from PR 23313, showing ICE on invalid: the code does not ICE but creates "wrong code" at runtime.
gcc shouldn't always error out in this situation. For suspend to disk, we clobber all registers when restoring the original cpu context after copying back the original kernel context. We could lie to gcc and say we don't clobber the register, but I'd prefer to be honest :> Nigel
(In reply to comment #20) > gcc shouldn't always error out in this situation. For suspend to disk, we > clobber all registers when restoring the original cpu context after copying back > the original kernel context. We could lie to gcc and say we don't clobber the > register, but I'd prefer to be honest :> You know you can use a function to do that and a .s file for that right? You don't need to use an inline- asm, right?
Subject: Re: GCC should error out when clobbering the stack pointer and frame pointer The function needs to be inlined - the return value especially is pivotal in the transition from the booted kernel context to the suspended one. That said, I'm no x86 assembly guru, so there might be a way around it. Regards, Nigel On Tue, 2005-08-23 at 21:34, pinskia at gcc dot gnu dot org wrote: > ------- Additional Comments From pinskia at gcc dot gnu dot org 2005-08-23 11:34 ------- > (In reply to comment #20) > > gcc shouldn't always error out in this situation. For suspend to disk, we > > clobber all registers when restoring the original cpu context after copying back > > the original kernel context. We could lie to gcc and say we don't clobber the > > register, but I'd prefer to be honest :> > > You know you can use a function to do that and a .s file for that right? You don't need to use an inline- > asm, right?
GCC must error out when clobbering the frame pointer register because it is likely to generate faulty code when the frame pointer is clobbered. You have to save and restore the frame pointer on your own with (x86 push ebp; pop ebp). The you need not to clobber it. The only exception could possibly be the compiler option -fno-frame-pointer. However at the moment GCC for arm generates faulty code even with this option, if the frame pointer is clobbered.
PIC register: while the user could save and restore this, the whole point of this fancy assembly notation is so that gcc can do nice scheduling and register allocation. The save and restore should thus be done by gcc. Perhaps the PIC register won't soon be needed, so gcc could use it for something else until it gets reloaded with the normal content. At the very least, gcc could pick a good location for the save/restore operations. Frame pointer register: this is the same as the PIC register. It's good to schedule the save/restore nicely. Stack pointer register: this one is special. For app code, signals may arrive. For kernel code, interrupts might be delivered without a stack change. Not supporting a clobbered stack pointer is definitely tolerable. Ideally this case could be handled (making the code non-reentrant), but the resulting mess is probably of very limited use. I could imagine abusing push/pop to iterate over arrays, but that's insane.
(In reply to comment #8) > 1. No way we can support clobbering both SP and FP. You could in theory by storing one of them to some fixed memory location. But here things get really ugly, so this is more of a philosophical answer. > 2. We could support clobbering the FP by pushing it onto the stack and > restoring it. Doesn't seem worth the trouble, though, since people can > do this themselves in the asm. Not necessarily. For x86 with -fomit-frame-pointer, local variables are usually adressed relative to SP. If you push FP onto the stack, you modify SP, and operands are prehaps no longer accessible. If gcc would store FP on the stack, it could take the offset into account and make things work. By the way, the same is imo true for the x86 PIC register ebx, which I would like to see clobberable and stored by gcc as well. Saving and restoring ebx in asm can be a real pain for the above reason. It would be great if the compiler additionaly would ensure that no other operand is an address relative to ebx. > 3. We could support clobbering the SP by introducing a FP and being very > careful. I cannot see any useful application though, so it doesn't seem > worth the trouble. One useful application would be in the sense of "no clobber register may overlap with any of the other operands". You could place the SP on the clobber list to indicate that you are going to modify it, and gcc could then try to avoid emitting SP-relative operands, e.g. by making them FP-relative or shouting if this is not possible. You might even think of this in the sense of "a clobbered register may contain any data at the end of the asm". That would be asm code that uses the stack, but does not clean up afterwards. You could in many cases restore the SP as a fixed offset from the FP, to allow such asm code. BTW: The test case I just wrote for bug 28686 exhibits this problem here as well, along with two other issues.
*** Bug 30579 has been marked as a duplicate of this bug. ***
Bug 30579 was marked as a duplicate of this bug. If I compile the test case from that bug with -fomit-frame-pointer, gcc still generates invalid code. (We don't use frame pointers in our system at all). Is this already known? It seems like gcc should support clobbering of fp if you've disabled frame pointers.