HOWTO: Write ASM routines for GCC && VC++ [ a .aware presentation ] =====================================================[ rattle|awarenetwork|org ] The issue. As you might know, I study mathematics. Sometimes more, somtetimes less related to my studies are certain numerical algorithms, which I enjoy to implement in my free time. Also, I do not entrust compilers with the optimization of my cautiously crafted routines, I'd rather implement them directly in Assembler. Now, since I am a Windows kind of guy, I got used to Visual C++, which allows for this kind of code: double sine (double x) { __asm { finit ; initialize FPU fld qword ptr x ; load x onto FPU stack fsin ; calculate sine lea eax, x ; get address of x fst qword ptr [eax] ; store sine in x on the stack } } This is just a small example, because we do not really want to look at a complex algorithm here - I am just trying to point out two major advantages of this way to implement an inline assembler: a) We do not have to care about calling conventions, or the stack, we simply use C variable names inside our inline assembler intuitively. b) The inline assembler can be actually formatted like regular ASM - it is not enclosed by quotes, and we can use regular ";" ASM comments. c) We can write actual assembler. Use all the registers as we please, and generally do as we please, inside those __asm {} blocks. I obviously included (b) and (c) only because GCC is fundamentally different. All inline assembler in GCC has to be enclosed in double quotes and must follow very strict formatting rules. Due to the nature of GCC, you will not get as far as worrying about (a): GCC's inline assembler was clearly built to implement syscalls, or anything else that is basically just a single opcode. Now, for calculating a sine, a syscall, or using a CMPXCHG opcode which is otherwise unknown to the compiler, GCC's inline assembler is perfectly sufficient. However, as soon as you start considering large algorithms, entirely written in ASM - which is comfortably possible in VC++ - you will be totally lost with GCC. Not only for the already named reasons either - there is also the additional obstacle that GCC is an optimizing compiler. Thus, you cannot freely use registers in your ASM code: You will have to specify input and output variables with their respective, related registers. All of that is very boring and annoying, so I will not go into further detail here. What really matters is this - if you want the Assembler component of a large algorithm to work in a linux environment as well, you will have to come up with a solution. Since GCC is the more problematic compiler, we will first look at a way to incorporate ASM components with other C code under gcc conditions. After a lot of RTFM'ing, trial, error, and diet coke, I ended up feeding .s files to GCC directly. GCC forwards these ASM files to AS, the gnu portable assembler. It would work like this: -------------------------------------------------------------[ snip. ]---------- rattle@nana ~/asmtest$ cat sine.s .intel_syntax noprefix .globl _sine _sine: finit /* initialize FPU */ fld qword ptr [esp+4] /* load x onto FPU stack */ fsin /* calculate sine */ lea eax, [esp+4] /* get address of x */ fst qword ptr [eax] /* store sine in x on the stack */ ret /* return */ rattle@nana ~/asmtest$ cat main.c #include double sine(double); int main() { double d = sine(.45); printf("%f\n",d); } rattle@nana ~/asmtest$ gcc -masm=intel -o sine sine.s main.c rattle@nana ~/asmtest$ ./sine 0.434966 --------------------------------------------------------------[ end. ]---------- IMPORTANT NOTE: It took me about an hour to figure out that /*...*/ comments are the only comments accepted by AS. C++ style // comments will throw a syntax error, and semicolons are actually considered SEPARATORS, like a line break. Yes, that's right, this is correct AS syntax: finit ; fld qword ptr [esp+4] ; fsin ; lea eax, [esp+4] ; ... So it's actually quite simple, except we do not have the benefit of (a), but that was to be expected. Now, how can we make this thing work with Visual C++ as well? First of all, how would we use the same, raw ASM use with VC++ inline ´ assembler? It would work like this: -------------------------------------------------------------[ snip. ]---------- __declspec ( naked ) // do not generate prolog and epilog double __cdecl // C default calling convention sine (double x) { __asm { // inline assembler finit /* initialize FPU */ fld qword ptr [esp+4] /* load x onto FPU stack */ fsin /* calculate sine */ lea eax, [esp+4] /* get address of x */ fst qword ptr [eax] /* store sine in x on the stack */ ret /* return */ } } --------------------------------------------------------------[ end. ]---------- This does exactly the same thing as sine.s up there. More actually, it is almost exactly the same text, too. Scroll back up to the IMPORTANT NOTE. Remember that semicolons can be used as a comment in Visual C++, but is interpreted as a command separator in AS? Enter the dirty hack: -------------------------------------------------------------[ snip. ]---------- rattle@nana ~/asmtest$ cat sine.s ; .intel_syntax noprefix ; .globl _sine ; _sine: finit /* initialize FPU */ fld qword ptr [esp+4] /* load x onto FPU stack */ fsin /* calculate sine */ lea eax, [esp+4] /* get address of x */ fst qword ptr [eax] /* store sine in x on the stack */ ret /* return */ rattle@nana ~/asmtest$ cat main.c #include double __cdecl sine(double); #ifdef _WIN32 __declspec ( naked ) double __cdecl sine (double x) { __asm { # pragma warning( push ) # pragma warning( disable: 4068 ) # include "sine.s" # pragma warning ( pop ) } } #endif int main() { double d = sine(.45); printf("%f\n",d); } --------------------------------------------------------------[ end. ]---------- See what's happening? In Windows, we will include the sine.s file to our C file and wrap it inside an inline assembler instruction. The AS instructions at the beginning as well as the (unreferenced) label _sine will be commented out in VC++ - but when you compile this with "gcc -masm=intel -o sine sine.s main.c" in linus, it will still work as expected because ";" is just like a newline to AS. The only actual disadvantage is now that we will have to work with the stack as in ancient times, and when there's more than 3 paramters, probably have a notebook next to the computer with stack shemes drawn all over it. But hey. Isn't it worth the cross-platform compatibility? I say it is. I also attached a different example with corresponding VC++ workspace. ---[ eof. ]------------------------------------------------[ /rattle ]----------