200 likes | 464 Views
Machine-Level Programming VIII: inline Assembly Comp 21000: Introduction to Computer Systems & Assembly Lang On-Line resources*. * See see http://www.codeproject.com/Articles/15971/Using-Inline-Assembly-in-C- C. Today. Inline assembly Example. Why use assembly?. To access hardware directly
E N D
Machine-Level Programming VIII:inline Assembly Comp 21000: Introduction to Computer Systems & Assembly LangOn-Line resources* * See see http://www.codeproject.com/Articles/15971/Using-Inline-Assembly-in-C-C
Today • Inline assembly • Example
Why use assembly? • To access hardware directly • linux OS code sometimes does this • See usr/include/asm/io.h • contains assembly to directly access i/o ports. • See usr/src/linux/arch/i386/kernel/process.s • uses hlt instruction in idle loop code. • Many examples in usr/src/linux/arch • Many examples in usr/src/linux/drivers • often in drivers need to specify how to access registers in the driver.
Why use assembly? • Speed • Be careful! Optimizing compilers are almost always better! • useful when you know an assembly language instruction that can replace a library call • Example: transcendental function computation • See /usr/include/bits/mathinline.h • has macros for some inline assembly squences • Example: if spend most of the time in a loop computing the sine and cosine of the same angles, could use the fsincos assembly function • We’ll see an example later
inline assembly code* • Example: • mov instruction /* put this line in your C program*/asm("assemblycode"); /* alternative syntax */__asm__ ("assemblycode"); asm("movl %ebx, %eax"); /* moves the contents of ebx register to eax */ __asm__("movb %ch, (%ebx)"); /* moves the byte from ch to the memory pointed by ebx */ * see http://www.codeproject.com/Articles/15971/Using-Inline-Assembly-in-C-C
inline assembly • More sophisticated assembly • For more than one assembly instruction, use semicolon at the end of each instruction of inserted code • see example on next slide
Example #include <stdio.h> int main() { /* Add 10 and 20 and store result into register %eax */ __asm__ ( "movl $10, %eax;" "movl $20, %ebx;" "addl %ebx, %eax;" ); /* Subtract 20 from 10 and storeresultintoregister %eax */ __asm__ ( "movl $10, %eax;" "movl $20, %ebx;" "subl %ebx, %eax;" ); /* Multiply 10 and 20 and store result into register %eax */ __asm__ ( "movl $10, %eax;" "movl $20, %ebx;" "imull %ebx, %eax;" ); return 0 ; } compile with –m32 –O1 flags trace in gdb to see registers change.
Extended inline assembly • Idea • In extended assembly, we can also specify the operands • can specify the input registers, output registers and a list of clobbered registers. • If there are no output operands but there are input operands, we must place two consecutive colons surrounding the place where the output operands would go. • Can omit list of clobbered registers to use, • GCC and GCC’s optimization scheme will take care of the reg. asm ( "assemblycode" : output operands /* optional */ : input operands /* optional */ : list of clobbered registers /* optional */ );
Example 1 • The variable "val" is kept in a register • val is a C variable that must be declared earlier in the C program • the value in register %eaxis copied onto that register, • and the value of "val" is updated into the memory from that register. • note that eax is preceded by 2 percent signs • differentiate from a asm parameter (like printf) asm ("movl %%eax, %0;" : "=r" ( val ));
Example 1 • the %0 indicates the first operand of asm, it is associated with the first parameter, i.e., val • “=r” indicates a register constraint • see chart on next page for all possible register specifications • “r” indicates that gccmay keep the variable in any available General Purpose Registers • the “=“ indicates write only mode. asm ("movl %%eax, %0;" : "=r" ( val )); see ~barr/Student/Comp210/inline directory for all examples
Example 2 int no = 100, val ; asm("movl %1, %%ebx;" "movl %%ebx, %0;" : "=r" ( val ) /* output */ : "r" ( no ) /* input */ : "%ebx" /* clobbered register */ ); • Two variables are declared in the C code, no and val • %0 is the first operand to asm and thus refers to the C variable val(the output variable) • %1 is the second operand and thus refers to the C variable no (the input variable) • “=r” and “r” say that gcc can use any registers to store the corresponding variable (either valor no ) • the clobbered variable is %ebx so gcc should not use that variable anywhere else.
Example 3 int arg1, arg2, add ; arg1 = 10; arg2 = 25; __asm__ ( "addl %%ebx, %%eax;" : "=a" (add) : "a" (arg1), "b" (arg2) ); • The C code declares three variables: arg1, arg2, add • The input variables will use %eax (for arg1) and %ebx (for arg2) • the output variable is add and will use register %eax • no clobber register is set; gcc can determine
#include <stdio.h> int main() { int arg1, arg2, add, sub, mul, quo, rem ; printf( "Enter two integer numbers : " ); scanf( "%d%d", &arg1, &arg2 ); /* PerformAddition, Subtraction, Multiplication & Division */ __asm__ ( "addl %%ebx, %%eax;" : "=a" (add) : "a" (arg1) , "b" (arg2) ); __asm__ ( "subl %%ebx, %%eax;" : "=a" (sub) : "a" (arg1) , "b" (arg2) ); __asm__ ( "imull %%ebx, %%eax;" : "=a" (mul) : "a" (arg1) , "b" (arg2) ); __asm__ ( "movl $0x0, %%edx;" "movl %2, %%eax;" "movl %3, %%ebx;" "idivl %%ebx;" : "=a" (quo), "=d" (rem) : "g" (arg1), "g" (arg2) ); printf( "%d + %d = %d\n", arg1, arg2, add ); printf( "%d - %d = %d\n", arg1, arg2, sub ); printf( "%d * %d = %d\n", arg1, arg2, mul ); printf( "%d / %d = %d\n", arg1, arg2, quo ); printf( "%d %% %d = %d\n", arg1, arg2, rem ); return 0 ; } Example 4 Example 4 idivlS # Signed divide R[%edx] R[%edx]:R[%eax] mod S; R[%eax] R[%edx]:R[%eax] / S
#include <stdio.h> #include <stdlib.h> int main (intargc, char* argv[]) { long max = atoi (argv[1]); long number; long i; unsigned position; volatile unsigned result; /* Repeat the operation for a large number of values. */ for (number = 1; number <= max; ++number) { /* Repeatedly shift the number to the right, until the result is zero. Keep count of the number of shifts this requires. */ for (i = (number >> 1), position = 0; i != 0; ++position) i>>= 1; /* The position of the most significant set bit is the number of shifts we needed after the first one. */ result = position; } // end outer for loop return 0; } // end main Example 4 Example 5 why use inline assembly? no assembly in this code % gcc-O2 -o bit-pos-loop bit-pos-loop.c % time ./bit-pos-loop 250000000 19.51user 0.00 system 0:20.40 elapsed 95%CPU (0avgtext+0avgdata 0maxresident)k0inputs+0outputs (73major+11minor)pagefaults 0swaps
#include <stdio.h> #include <stdlib.h> int main (intargc, char* argv[]) { long max = atoi (argv[1]); long number; unsigned position; volatile unsigned result; /* Repeat the operation for a large number of values. */ for (number = 1; number <= max; ++number) { /* Compute the position of the most significant set bit using the bsrl assembly instruction. */ asm(“bsrl %1, %0” : “=r” (position) : “r” (number)); result = position; } // end for loop return 0; } Example 4 Example 5 why use inline assembly? assembly used for inner loop %gcc-O2 -o bit-pos-asm bit-pos-asm.c % time ./bit-pos-asm250000000 3.19user 0.00system 0:03.32elapsed 95%CPU (0avgtext+0avgdata 0maxresident)k0inputs+0outputs (73major+11minor)pagefaults 0swaps Compare to the 19.51user of the previous C only example!
Volitile • If our assembly statement must execute where we put it, (i.e. must not be moved out of a loop as an optimization), put the keyword "volatile" or "__volatile__" after "asm" or "__asm__" and before the ()s. asm volatile ( "...;” "...;" : ... ); __asm__ __volatile__ ( "...;" "...;" : ... );
#include <stdio.h> intgcd( int a, int b ) { int result ; /* Compute Greatest Common Divisor using Euclid's Algorithm */ __asm__ __volatile__ ( "movl %1, %%eax;" "movl %2, %%ebx;" "CONTD: cmpl $0, %%ebx;" "je DONE;" "xorl %%edx, %%edx;" "idivl %%ebx;" "movl %%ebx, %%eax;" "movl %%edx, %%ebx;" "jmp CONTD;" "DONE: movl %%eax, %0;" : "=g" (result) : "g" (a), "g" (b) ); return result; } intmain() { intfirst, second ; printf( "Enter twointegers : " ) ; scanf( "%d%d", &first, &second ); printf( "GCD of %d & %d is %d\n", first, second, gcd(first, second) ) ; return 0 ; } Example 4 Example 6 Compute GCD with Euclid’s Algm Note that we can put labels and jump to them! Note that we can put several asm instructions in one __asm__ function call
#include <stdio.h> int main() { int x, y, rslt, rtnval; inti; printf("Enter two integers\n"); rslt = scanf("%d%d", &x, &y); /* change into assembly code */ rtnval = 0; for(i = 0; i < y; i++) { rtnval += x; } /* end change */ printf("%d * %d = %d\n", x, y, rtnval); return 0; } Example 6 convert the for loop to assembly code (should be no more than 11 lines of assembly code)