Thursday, December 10, 2015

Portable Inline Assembly

This is a fun little tidbit.  Assembly language is, obviously, not portable.  If you use inline assembly code in a C program, you won't expect it to run on multiple processors without special handling for each one.  Well, there's an exception to that.

First, the trivial exception:

__asm__("");

That's valid code (assuming the gcc compiler, as I will throughout this discussion).  But it doesn't do anything, so it's rather pointless.  The magic comes in when you specify the input and output parameters.  For output, I'll specify two registers, assigned to the variables 'a' and 'b'.  For input, I'll use the same registers.  The syntax GCC uses is that zero is the first output register, and so-forth.  Note that you specify the output first.

__asm__("" \
 :"=r"(a), "=r"(b) \
 :"0"(b),"1"(a) );

Now this does something!  We've told it that the input is two registers with the values from variables 'b' and 'a', and the output is the same two registers which are variables 'a' and 'b'.  Notice the order.  We've swapped two variables.  No intermediary storage.  No fancy xors.  No actual code.  What we've done is tell the compiler to swap it's notion of where 'a' and 'b' are stored (with both being registers).

We can make this into a fancy macro:

#define SWAP_ASM(_a,_b) __asm__("" \
    :"=r"(_a), "=r"(_b) \
    :"0"(_b),"1"(_a) );

And demonstrate that it works:

#include <stdio.h>

#define SWAP_ASM(_a,_b) __asm__("" \
    :"=r"(_a), "=r"(_b) \
    :"0"(_b),"1"(_a) );

int main(int argc,char *argv[])
{
   int a='a', b='b';
   (void)argc;(void)argv; // no "unused" warning
   SWAP_ASM(a,b);
   printf("a contains '%c'\n"
          "b contains '%c'\n",a,b);
   return 0;
}

Now admittedly, this isn't very useful.  If the variables aren't in registers, the compiler has to generate load and store instructions before and after the swap.  Using inline assembly can mess up optimization (probably more so in older releases), so using a variable to do a swap with all your code being in C is likely a better choice.  Still, a neat little hack.

No comments:

Post a Comment