Date: Mon, 18 Mar 1996 19:36:42 +1100 From: Bruce Evans <bde@zeta.org.au> To: bde@zeta.org.au, terry@lambert.org Cc: freebsd-hackers@FreeBSD.org, jdp@polstra.com, nate@sneezy.sri.com Subject: Re: GAS question Message-ID: <199603180836.TAA15110@godzilla.zeta.org.au>
next in thread | raw e-mail | index | archive | help
>> This is very primitive. Instead of letting the compiler decide which >> registers to use, you have to do all the register loading yourself. >> This wastes your time, and wastes the cpu's time doing unnecessary >> moves if the registers are already in suitable places (delta in eax >> and locklist in ebx in the above). >Excuse me. If I wanted the compiler to decide this, I would have >written it in C. It can't be written in C. >I wrote it in assembly because there exists an API of which the >compiler is ignorant that depends on particular registes being >used. That's why it can't be written in C. apm_int() is not a good way to glue to an external API. Inline C functions, like all C functions, can only return one value so they can't directly deal with several values returned in registers except possibly by returning a struct that the compiler optimizes away. DOS interfaces such as int86() have the same problems. It's very easy to write everything inline using gcc asm: u_long eax, ebx, ecx, esi, edi, cf; ... __asm __volatile(" pushl %ebp /* preserved from original (PFO) */ lcall _apm_addr /* or whatever the API wants */ popl %ebp /* PFO */ jnc 1f /* PFO */ incl %3 /* PFO */ " : "=a" (eax), "=b" (ebx), "=c" (ecx), "=D" (cf)/* PFO */ : "0" (eax), "1" (ebx), "2" (ecx), "3" (0)/* PFO */, "S" (0) : "dx"/* PFO */, "si/* PFO */ ); The PFO code can probably be done better (register-based API's probably guarantee not to touch the registers that they don't return a value in, so you don't need to preserve them; %edi can be initialized later unless it is also a 0-valued arg to the API and gcc should be allowed to pich the register that the result is returned in...). Since this is called from several places, it should be turned into a macro. It really is simple - you load a bunch of registers, call the API, and the results come back in a bunch of registers. Simple API calls are even simpler to call. E.g., the BIOS call to read a byte from the BIOS, which takes 30 lines of assembler in /sys/i386/boot/biosboot/bios.S (cheating by counting the comments that document the interface in the wrong place and the mode switch calls :-), can be written in 1 line of inline asm char c; __asm __volatile("int $0x16" : "=a" (c) : "0" (0)); >> >And Assembly for a C callable function (Win95 blue screen from a VXD, >> >actually): >> Assembly for a C callable function in gas: >> >> foo: >> ret >> >> :-) >I don't see you handling C parameters or obeying register saves for >calling conventions or dealing automatically with "callee pop" or >any of a large number of other issues that using a real optimizing >macroassembler would handle for you. See the smiley. Using gcc inline assembler avoids all these problems. >> >Variable references are plain old variable references. Why is GCC so >> >complicated? >> >> It's more powerful. Plain old variable references are used, just not >> in the part that looks like it will be processed by the assembler. >I'm missing something. Why does complexity == power? Some complexity is required for multiple return values and avoiding fixed register allocations. The fairly ugly syntax for gcc asms is partly because it has to be acceptable to cpp (it must consist of cpp tokens). Bruce
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?199603180836.TAA15110>