Inline assembler

From Wikipedia, the free encyclopedia

In computer programming, the inline assembler is a feature of some compilers that allows very low level code written in assembly to be embedded in a high level language like C or Ada. This embedding is usually done for one of three reasons:

  1. optimization
  2. access to processor specific instructions
  3. system calls

When assembly language is inlined for optimization, the most performance-sensitive parts of an algorithm are replaced by hand-written assembly. This allows the programmer to use the full extent of his ingenuity, without being limited by a compiler's higher-level constructs.

Some processors offer special instructions, such as Compare and Swap and Test and Set — instructions which may be used to construct semaphores or other synchronization and locking primitives. Nearly every modern processor has these or similar instructions, as they are necessary to implement multitasking. To name a few, specialized instructions are found in the SPARC VIS, Intel MMX and SSE, and Motorola Altivec instruction sets.

Lastly, high-level languages rarely have a direct facility to make system calls, so assembly code is used.

[edit] Example of optimization and processor-specific instructions

This example of inline assembler is from the D programming language and computes the tangent of x using the x86's FPU instructions. This is faster than using the floating-point operations that would be emitted by the compiler, and it allows the programmer to make use of the fldpi instruction, which loads the closest approximation of pi possible on the x86 architecture.

// Compute the tangent of x
real tan(real x)
{
   asm
   {
       fld     x[EBP]                  ; // load x
       fxam                            ; // test for oddball values
       fstsw   AX                      ;
       sahf                            ;
       jc      trigerr                 ; // x is NAN, infinity, or empty
                                         // 387's can handle denormals
SC18:  fptan                           ;
       fstp    ST(0)                   ; // dump X, which is always 1
       fstsw   AX                      ;
       sahf                            ;
       jnp     Lret                    ; // C2 = 1 (x is out of range)
       // Do argument reduction to bring x into range
       fldpi                           ;
       fxch                            ;
SC17:  fprem1                          ;
       fstsw   AX                      ;
       sahf                            ;
       jp      SC17                    ;
       fstp    ST(1)                   ; // remove pi from stack
       jmp     SC18                    ;
   }
trigerr:
   return real.nan;
Lret:
   ;
}

[edit] Example of a system call

Directly calling an operating system is generally impossible in the presence of protected memory. The OS runs at a more privileged level (kernel mode) than the user (user mode); a (software) interrupt is used to make requests to the operating system. This is rarely a feature in a higher-level language, and so wrapper functions for system calls are written using inline assembler.

The following C programming language code is a sample system call wrapper. They are normally written with the aid of macros; the full code is included for clarity.

extern int errno;

int funcname(int arg1, int *arg2, int arg3) {
  long __res;
  __asm__ volatile 
    ("movl $128, %%eax;" /* call system call 128 */
     "movl %1, %%ebx;"   /* pass arg1 as the first argument */
     "movl %2, %%ecx;"   /*      arg2 */
     "movl %3, %%edx;"   /*      arg3 */
     "int $0x80;"        /* make the request to the OS */
     "movl %%eax,%0"     /* save the result in __res */
     : "=m" (__res) /* bind %0 */
     : "m" (arg1), "m" ((long) arg2), "m" (arg3) /* bind %1, %2, %3 */
     : "%eax","%ebx","%ecx","%edx"); /* announce to the compiler that eax...edx are dirtied */

  /* the operating system will return a negative value on error;
     wrappers return -1 on error and set the errno global variable */
  if ((unsigned long)(__res) >= (unsigned long)(-125)) { \
    errno = (int) -(__res); \
    __res = -1; \
  }  
  return (int)__res;
}

[edit] External links

In other languages