What is inline assembly good for ?

Programming, for all ages and all languages.
User avatar
Neolander
Member
Member
Posts: 228
Joined: Tue Mar 23, 2010 3:01 pm
Location: Uppsala, Sweden
Contact:

Re: What is inline assembly good for ?

Post by Neolander »

Good. Tried to merge some things and clean the article up a bit.
Can someone check the new version, especially for English correctness since I am not a native English writer?
User avatar
Thomas
Member
Member
Posts: 281
Joined: Thu Jun 04, 2009 11:12 pm

Re: What is inline assembly good for ?

Post by Thomas »

Hi,
That's just gcc,These are the compilers that I have predominantly worked with
Turbo C / Borland C / Digital Mars use asm{}

Code: Select all

asm{
mov ax , 0x4c00
int 0x21
} 
Note that the position of the brace is important esp in the freely available Turbo C++ 1.01 Compiler ,the brace should come right after the asm keyword.Possibly a bug :wink:

Visual C++ uses __asm

Code: Select all

__asm
{
   mov al, 2
   mov dx, 0xD009
   out dx, al
}
--Thomas
Last edited by Thomas on Wed May 05, 2010 2:18 am, edited 1 time in total.
User avatar
xenos
Member
Member
Posts: 1118
Joined: Thu Aug 11, 2005 11:00 pm
Libera.chat IRC: xenos1984
Location: Tartu, Estonia
Contact:

Re: What is inline assembly good for ?

Post by xenos »

Code: Select all

int current_task;
asm( "ltr %[output]"
   : [output] "=r" (current_task)
    );
should probably be

Code: Select all

int current_task;
asm( "str %[output]"
   : [output] "=r" (current_task)
    );
You store the task register, you don't load it ;) And another one:

Code: Select all

"movl %0, %%es" : "=r" (x)
I guess there is no output operand here, but an input operand instead.

As for the original topic, I like the way GCC handles inline assembly. GCC's operand constraint syntax offers the possibility to let the compiler choose which register(s) to use from a given set of registers, or choose memory or constant operands instead. The compiler can thus optimize the assembler code as well, which would not be possible if it was located in a separate file with fixed registers.

Of course there are some drawbacks with inline assembly. The first one is that inline assembly syntax is highly compiler dependent, so before using it, one must choose a compiler. This is not a major drawback for me, since I use GCC most of the time, as it fits my needs: It is fast, optimizing, and targets plenty different architectures. And of course it is open source and so on.

Another drawback that has been mentioned in this thread is that inline assembly is not portable between different target architectures. Of course this is true. This is the reason why I use inline assembly only for architecture specific code. (A typical example is x86 paging code: Of course, an instruction like "movl %0, %%cr3" works only for x86 or x86_64, but this paging code will be used only for these, anyway.) There are some situations where a similar operation can be performed on different architectures, but with different instructions. An example for this are spinlocks. Most architectures support an atomic test-and-set instruction, but these may look completely different. In this case, I use something similar to this:

Code: Select all

// AtomicLock.h
#include <config.h>

class AtomicLock
{
private:
	volatile int key;

public:
	void Enter(void);
	void Exit(void);
};

#include INC_ARCH(AtomicLock.h)
Here, INC_ARCH is defined in config.h, which is generated by configure, and points to some architecture specific include directory, like this:

Code: Select all

#define INC_ARCH(x) <arch/x86/x>
Finally, arch/x86/AtomicLock.h contains the architecture specific code:

Code: Select all

// arch/x86/AtomicLock.h

inline void AtomicLock::Enter(void)
{
	asm volatile ("1: \n"
		"pause \n"
		"lock btsl $0, %[key] \n"
		"jc 1b" : : [key] "m" (key));
}

inline void AtomicLock::Exit(void)
{
	asm volatile ("movl $0, %[key]" : : [key] "m" (key));
}
On a different architecture (say, M68K) it looks completely different:

Code: Select all

// arch/m68k/AtomicLock.h

inline void AtomicLock::Enter(void)
{
	asm volatile ("1: \n"
		"bsetb #0, %[key] \n"
		"beqs 1b" : : [key] "m" (key));
}

inline void AtomicLock::Exit(void)
{
	asm volatile ("movel #0, %[key]" : : [key] "m" (key));
}
(Note that constants look like $0 in x86 assembly, but #0 in M68K assembly.)
Programmers' Hardware Database // GitHub user: xenos1984; OS project: NOS
User avatar
Neolander
Member
Member
Posts: 228
Joined: Tue Mar 23, 2010 3:01 pm
Location: Uppsala, Sweden
Contact:

Re: What is inline assembly good for ?

Post by Neolander »

About other compilers, guess it should be interesting to write some separate articles for MS and Borland inline assembly and mention it in this article (ex : in the new overview section, by something like "this is about GCC, if you're using MSVC go here"). Since I don't know these much, I let you do it.
GCC should remain in the "Inline Assembly" article, IMO, as it's the most used compiler, and the most documented compiler in the wiki. This way, one gets quick access to the most probable answer, while still having access to the less common options.
XenOS wrote:

Code: Select all

int current_task;
asm( "ltr %[output]"
   : [output] "=r" (current_task)
    );
should probably be

Code: Select all

int current_task;
asm( "str %[output]"
   : [output] "=r" (current_task)
    );
You store the task register, you don't load it ;) And another one:

Code: Select all

"movl %0, %%es" : "=r" (x)
I guess there is no output operand here, but an input operand instead.
Gee... I didn't check the examples from the original article enough, even though I already found some horrors in places :| Now it's fixed. Thanks for your participation !
As for the original topic, I like the way GCC handles inline assembly. GCC's operand constraint syntax offers the possibility to let the compiler choose which register(s) to use from a given set of registers, or choose memory or constant operands instead. The compiler can thus optimize the assembler code as well, which would not be possible if it was located in a separate file with fixed registers.
Yup, I'm starting to sort of like it too as I begin to understand how it works, but I still think that the %x thing is a horrible mistake.
1/Because it changes original GAS register syntax, where they could have used a new syntax like <variable name> instead.
2/Because it is error-prone : if you add an output operand in a code using input operands, you must rewrite the whole part of your code which uses them.
I don't fully understand the logic behind the new [] syntax either : it's better, but why not just use the output operand names right away ? It sounds more intuitive and less error-prone, while the benefit of doing otherwise is very small... (EDIT : Looking at the new wiki entries, I wonder if it's not possible already)
Another drawback that has been mentioned in this thread is that inline assembly is not portable between different target architectures. Of course this is true. This is the reason why I use inline assembly only for architecture specific code. (A typical example is x86 paging code: Of course, an instruction like "movl %0, %%cr3" works only for x86 or x86_64, but this paging code will be used only for these, anyway.) There are some situations where a similar operation can be performed on different architectures, but with different instructions. (...)
Well, this is more of a general problem with assembly, and not specific to inline assembly, isn't it ?

EDIT : Well, it looks like Creature joined the rewrite effort, and started improving the wiki too. That's great ! I opened a page in order to discuss the wiki issue in more details here : http://forum.osdev.org/viewtopic.php?f=8&p=175981
User avatar
xenos
Member
Member
Posts: 1118
Joined: Thu Aug 11, 2005 11:00 pm
Libera.chat IRC: xenos1984
Location: Tartu, Estonia
Contact:

Re: What is inline assembly good for ?

Post by xenos »

Neolander wrote:Yup, I'm starting to sort of like it too as I begin to understand how it works, but I still think that the %x thing is a horrible mistake.
1/Because it changes original GAS register syntax, where they could have used a new syntax like <variable name> instead.
2/Because it is error-prone : if you add an output operand in a code using input operands, you must rewrite the whole part of your code which uses them.
I agree. At the beginning I was quite confused because of the additional %'s and used to forget them, with strange results.
I don't fully understand the logic behind the new [] syntax either : it's better, but why not just use the output operand names right away ? It sounds more intuitive and less error-prone, while the benefit of doing otherwise is very small...
In principle, yes. But the advantage of GCC's syntax is that you can have different operands sharing the same register. For example, you can have something like:

Code: Select all

asm ("cpuid" : "=a" (eax), "=b" (ebx), "=c" (ecx), "=d" (edx) : "0" (function));
So EAX is loaded with the value of the variable "function", and the result of cpuid is stored in "eax" to "edx". Now the compiler can assign either eax or function to the physical EAX register, or put them into memory and add some movl instructions to put the data in place - and it will do the right thing if you still want to use the value of "function" after that asm statement.

But I also like MSVC syntax, where you can to something like this:

Code: Select all

struct
{
	int first;
	int second;
} data;

...

__asm
{
	mov eax, &data
	mov ecx, [eax].first
	mov edx, [eax].second
}
Well, this is more of a general problem with assembly, and not specific to inline assembly, isn't it ?
Yes, that's true. The problem with inline assembly is that it introduces this architecture dependence into the C source code, which otherwise could in principle be compiled targetting any architecture (even though it doesn't make sense, e.g. compiling x86 paging code for a M68K target). But especially in OS development, there are some things that simply must be architecture specific, and I don't see any disadvantage in putting them directly into some (architecture specific) C file via inline assembly. (Apart from the compiler dependence, of course.)
Programmers' Hardware Database // GitHub user: xenos1984; OS project: NOS
User avatar
Sam111
Member
Member
Posts: 385
Joined: Mon Nov 03, 2008 6:06 pm

Re: What is inline assembly good for ?

Post by Sam111 »

I have used inline asm in some cases but I always favor just putting it in a .s or .asm file and call it from c using an extern command.

Note I don't know how much more performance benifit it gives but using the .s file way saves me a headache in most cases.

Can somebody tell me how much performance benifit you are getting from it?
Basically a function call only pushes the return address and the parameters on the stack and then does your code pop's a few things and returns so if you had N parameters their would be N+1 pushes and one mov for the return value into EAX or so... then N+1 pops and a jmp to the return address

So another words 2N+2 operations plus a 2 jmp command (one for jumping to the function another for jumping back)

So final formula 2N+4 and maybe some extra pushs and move's to manipulate the parameters ,...etc

I guess it can be come a big savings if you have alot of varibles or alot of function calls to this.
User avatar
Solar
Member
Member
Posts: 7615
Joined: Thu Nov 16, 2006 12:01 pm
Location: Germany
Contact:

Re: What is inline assembly good for ?

Post by Solar »

If you want to play it really ugly, you can put the inline assembly into a seperate file and #include that file where you need it. Add a couple of #ifdef's for compiler and platform, and you got portable "inline" assembly.

Sorry, nasty mood today. :twisted:
Every good solution is obvious once you've found it.
Post Reply