C and Assembly

No one wants to write assembly code!!!. It is complex, hard to code, hard to debug and not portable. Sometimes we have no choice for example if performance is super critical or if we want to use some specific SIMD instructions etc.

So why we need to learn and write assembly code?

  1. Optimization – in some critical points we don’t want the compiler to generate the code for us
  2. Eliminate compiler changes – for example in a critical safety systems we don’t want the compiler to generate the code for us because we need a full control of the generated code
  3. Special CPU Instructions – some instructions are not used by the compiler such as software interrupt , synchronization instructions, barriers etc. if we want to use these instructions we have to write assembly code

If we want to mix C and Assembly we can do that using 2 method:

  1. Separate files – C source in .c files and Assembly code in .s files
  2. Inline Assembly – mix C and Assembly on the same .c file

For example we want to create a function in ARM processor assembly we will write the following fn.s file:

    .globl addfn;

addfn:
        add r0,r0,r1;
        mov pc,lr;

The above file declares a simple function takes 2 arguments and returns their sum. The parameters is stored in r0,r1 general purpose registers and the return value in r0

then we will write the C code that use the function

#include <stdio.h>
#include <stdlib.h>
 
extern int addfn(int,int);
 
int main(void) {
    int res;
    res=addfn(10,20);
    printf("res=%d\n",res); /* prints !!!Hello World!!! */
    return EXIT_SUCCESS;
}

We need to know some details about the C compiler in ARM:

  • Up to 4 parameters, The compiler is using registers r0, r1, r2, r3
  • If we use more than 4 parameters , the 5th+ parameters will be stored in the stack
  • the return value is stored in r0

To compile the code:

arm-none-linux-gnueabi-gcc -o app1 ./test.c ./addfn.s

MIPS Processor Example

The main problem with assembly code is the  platform dependency – if we want to switch from one architecture to another we need to rewrite our code. Also the compiler has different rules how to use the registers, stack etc.

For example to write the same simple function on MIPS:

        .globl    addfn;
        .type addfn, @function;
        .ent addfn
addfn:
        add $a0,$a0,$a1;
        move $v0,$a0;
        .end addfn;
        jr $ra;

C compiler on MIPS uses registers a0,a1,a2,a3 for function parameters and register v0 for return value

Inline Assembly

Writing inline assembly means writing the C + Assembly on the same file and both pieces of code compiled. It can be used for optimizations but the main reason we use this code is to generate instructions that never produced by the compiler. For Example if we want to write a system call in linux – function to use some kernel service, we need to change the processor state from user mode to privilege mode using Software Interrupt instruction – The compiler never generate this instruction so we need to do it manually

In this example we write the system call getpid():

#include <stdio.h>
#include <stdlib.h>
#include <sys/syscall.h>
#include <unistd.h>
 
int mygetpid()
{
    int res;
    asm volatile ("movl $20,%%eax;int $0x80; movl %%eax,%0;":
            "=r"(res):  : "eax");
    return res;
}
 
int main(void) {
    int z;
    printf("using getpid()=%d\n",getpid());
    z=mygetpid();
    printf("res=%d\n",z);
    return EXIT_SUCCESS;
}

The software interrupt instruction is “int $0x80” makes the switch , before that we tell the kernel which system call number we want to run (getpid = 20) and we get the result using EAX register

The syntax for inline assembly is:

asm volatile( INS : OUT : IN : MORE);

INS – the assembly instructions
OUT – output parameters (what the statement returns)
IN – input parameters (function parameters – here getpid gets no input parameter so its empty)
MORE – more info (memory barriers, other registers we use, …)

For more information – see GCC inline assembly documentation

More reasons to write inline assembly

  • Atomic operations
  • Memory barriers
  • CPU configuration
  • Caching configuration

We can find many examples in Linux kernel code (platform independent – /linux/arch folder)

 

 

 

 

 

Tagged ,

2 thoughts on “C and Assembly

  1. “No one wants to write assembly code!!!”

    Speak for yourself, a lot of people love Assembly.

    1. love is one thing, wants to write is another. But i can change it if you want to “most of the developers don’t want to write assembly code”

Comments are closed.