EmbDev.net

Forum: ARM programming with GCC/GNU tools Getting invalid code when trying to inline a SWI


von Geroge M. (geomil42)


Rate this post
useful
not useful
Hi group!

I try to inline a very simple SWI call that returns a value in r0.

Somehow I must do it wrong, as gcc messes up the registers.

Here's the code:

/*** armtest.c ***/

void test(void);
void my_printf(const char *fmt, ...);

static inline int getXXX(void) {
  register long res_r0 asm("r0");
  long res;

  asm volatile ("swi 42"
                : "=r" (res_r0)
                :
                : "r12", "cc");

  res = res_r0;

  return (int)res;
}

void test(void) {
  my_printf("XXX=%d\n", getXXX());
}

/****/

Compile with: arm-elf-gcc -Os -c armtest.c -o armtest.o

Disassembly or armtest.o gives:

0:  ef00002a  swi     0x0000002a
4:  e59f0004  ldr     r0, [pc, #4] ; 10 <.text+0x10>
8:  e1a01000  mov     r1, r0
c:  eafffffe  b       c <test+0xc>
10: 00000000  andeq   r0, r0, r0

Note how gcc clobbers r0 at offset 4. my_printf would have expected
the string pointer in r0 and the result of the swi call in r1.


This is the GCC version:

Using built-in specs.
Target: arm-elf
Configured with: ../gcc-4.1.1/configure --target=arm-elf
--prefix=/local/gnuarm-4.1.1 --enable-interwork --enable-multilib
--enable-languages=c,c++
Thread model: single
gcc version 4.1.1

What am I doing wrong? Thanks in advance!

von Martin Thomas (Guest)


Rate this post
useful
not useful
It looks like r0 and r1 are both set to 0. I tried to reproduce this
here (with WinARM 20060606) but the result is different (of cause the
"other code" might be completly different from yours).

[...]

static inline int getXXX(void) {
  register long res_r0 asm("r0");
  long res;

  asm volatile ("swi 42"
                : "=r" (res_r0)
                :
                : "r12", "cc");

  res = res_r0;

  return (int)res;
}

static inline int getXXX2(void) {
  unsigned long res;
  asm ("swi 42; mov %0, r0"
    : "=r" (res)
    :
    : "r0");
  return (int)res;
}

#define rprintf efsl_debug_printf_arm

int main(void)
{
  int ch;
  int8_t res;

  systemInit();
  gpioInit();

  uart1Init(UART_BAUD(BAUD), UART_8N1, UART_FIFO_8); // setup the UART

  /* init efsl debug-output */
  efsl_debug_devopen_arm(uart1Putch);

  ledToggle();

  ch = 20;
  rprintf("T1: %i", ch);
  rprintf("T2: %i", getXXX() );
  rprintf("T3: %i", getXXX2() );
[...]

[...]
  ledToggle();
 1d8:  f7ff ffb0   bl  13c <ledToggle>

  ch = 20;
  rprintf("T1: %i", ch);
 1dc:  4818        ldr  r0, [pc, #96]  (240 <.text+0x240>)
 1de:  2114        movs  r1, #20
 1e0:  f000 f93c   bl  45c <efsl_debug_printf_arm>
 1e4:  df2a        svc  42
  rprintf("T2: %i", getXXX() );
 1e6:  4917        ldr  r1, [pc, #92]  (244 <.text+0x244>)
 1e8:  1c08        adds  r0, r1, #0
 1ea:  f000 f937   bl  45c <efsl_debug_printf_arm>
 1ee:  df2a        svc  42
 1f0:  1c01        adds  r1, r0, #0
  rprintf("T3: %i", getXXX2() );
 1f2:  4815        ldr  r0, [pc, #84]  (248 <.text+0x248>)
 1f4:  f000 f932   bl  45c <efsl_debug_printf_arm>
[...]

Please create minimal example (source incl. startup-up, makefile,
linker-scripts) place it somewhere on an internet-server and send the
link here.

Martin Thomas

von Geroge M. (geomil42)


Rate this post
useful
not useful
Martin Thomas wrote:
> It looks like r0 and r1 are both set to 0.

This is because the code is not linked yet. I provided the disassembly
of
the armtest.o file.

However, the test you did comes out wrong too here:

 1e4:  df2a        svc  42
  rprintf("T2: %i", getXXX() );
 1e6:  4917        ldr  r1, [pc, #92]  (244 <.text+0x244>)
 1e8:  1c08        adds  r0, r1, #0
 1ea:  f000 f937   bl  45c <efsl_debug_printf_arm>

At offset 1e8 the r0 register is clobbered.
It seems that gcc does not know that getXXX() returns a value it r0.

getXXX2() does the trick, but has some overhead (sometimes).

I wonder where's the problem here...

Thanks for replying!

von Martin Thomas (Guest)


Rate this post
useful
not useful
Geroge Miller wrote:
> At offset 1e8 the r0 register is clobbered.
> It seems that gcc does not know that getXXX() returns a value it r0.
>
> getXXX2() does the trick, but has some overhead (sometimes).

Yes, sorry, I should have mentioned that "getXXX2" is my proposal to
implement this. I try to avoid "register" where possible. This might
produce an overhead but I think this approach is "cleaner" - But this is
just a personal "style".

> I wonder where's the problem here...

Currently I do not know either. Maybe someone else can jump in here and
give an explanation.

Martin Thomas

von Bill B. (auldreekie)


Rate this post
useful
not useful
Martin Thomas wrote:
> Geroge Miller wrote:
>> At offset 1e8 the r0 register is clobbered.
>> It seems that gcc does not know that getXXX() returns a value it r0.
>>
>> getXXX2() does the trick, but has some overhead (sometimes).

> Currently I do not know either. Maybe someone else can jump in here and
> give an explanation.

Looking for help with my own swi/gcc problem I came across this
long-dormant thread.

Surely by now it's been figured out, but anyway, getXXX() does not
provide gcc with a %0 placeholder to represent the output argument.  So
it doesn't surprise me that gcc assumes there is no output.

getXXX2() works because it has a %0 placeholder for which gcc can
substitute the output argument.

I'm not aware that gcc is obliged to assume an output in r0 if not
explicitly told.

The extra overhead is annoying all right but if you really crank up the
optimizer a lot of it goes away.  Which leads me to another problem I'm
putting in a new thread.

von Bill B. (auldreekie)


Rate this post
useful
not useful
I take it back, yes according to the manual this should work.  The
manual gives the explicit example

register int *p1 asm ("r0") = ...;
register int *p2 asm ("r1") = ...;
register int *result asm ("r0");
asm ("sysint" : "=r" (result) : "0" (p1), "r" (p2));

For the case of a software interrupt (although for a different
architecture).  No % placeholders anywhere and the manual authors
expected this to work.

And for the record, I'm having trouble with this too (arm-elf-gcc
4.1.0).

von Bill B. (auldreekie)


Rate this post
useful
not useful
George, I don't know if you're tracking this, but I think I found a
solution.

You need to modify your swi call from this:

 asm volatile ("swi 42"
                : "=r" (res_r0)
                :
                : "r12", "cc");

to this:

 asm volatile ("swi 42 @ %0"
                : "=r" (res_r0)
                :
                : "r12", "cc");

Apparently the inline assembler, when it sees no %0, %1, etc.-type
arguments in the assembler text, decides to ignore any input and output
arguments that follow.  Including a %0 in the assembler text assures the
inline assembler that this argument really does get used.  The fact that
it is in a comment (it comes after the @ character) keeps other errors
at bay.

If you want to pass pointers to an SWI there are other compilation
issues, at least if you optimize.  See my thread on gcc and swi for
details (http://en.mikrocontroller.net/topic/146800)

Please log in before posting. Registration is free and takes only a minute.
Existing account
Do you have a Google/GoogleMail account? No registration required!
Log in with Google account
No account? Register here.