I'm trying to compile seabios in a more debug-able state, and so I want to cancel function inlining.
To do so, I have added -fon-inline to the compilation flag, but then I get compilation error:
error: can't find a register in class 'GENERAL_REGS' while reloading 'asm'
Which is complaning on the following code:
asm volatile(
"calll __call16big_from32"
: "+a" (callregs), "+m" (*callregs)
:
: "ebx", "ecx", "edx", "esi", "edi", "cc", "memory");
I've looked this error up and found this means that compiler has ran out of registers so it can't compile that asm statement.
The thing is, the exact same code compile just fine without -fon-inline, why is that?
Why doesn't compile?
The first argument to the asm may not share a register with the address of the second because the first is modified. If the function is inlined callregs may be a constant or an offset from the stack pointer, and therefore a separate register is not required.
How to fix the program so it compiles
Given the presence of volatile and "memory", and no reference to it the second argument may be removed.
asm volatile(
"call __call16big_from32"
: "+a" (callregs)
:
: "ebx", "ecx", "edx", "esi", "edi", "cc", "memory");
Related
I'm trying to do a simple inline asm command in C and compile it with gcc. I want to push the variable num to the stack:
asm (
"push %0"
: //output
: "r"(num) //input
: //clobber
);
The above is generating the error:
Error: expression too complex -- `push r3'
I'm following this tutorial and I found nothing about the push command.
I also tried:
asm ( "push %num" ); //Assembler Error: expression too complex -- `push %num'
and:
asm ( "push %[num]" ); //gcc error: undefined named operand 'num'
But none worked.
edit:
I'm using this compiler: arm-linux-gnueabihf-gcc
In ARM assembly, the push instruction is a shorthand for stmdb. It can push multiple registers at once. Thus, you have to use braces around the operand as it indicates a set of registers:
asm("push {%0}" : : "r"(num) : );
#include "stdio.h"
void fseek(void *, int, int);
main () {
FILE* f = fopen("myfile", "rb");
asm("push 2");
asm("push 0");
asm("push f");
asm("call fseek");
asm("add esp, 12");
}
gcc -masm=intel call.c
call.c:(.text+0x2c): undefined reference to `f'
call.c:(.text+0x31): undefined reference to `fseek'
I have been trying to use AT/T syntax but got the same result.
Well you can not write like this, since there is no grantee that symbol f would exist in the generated assembly -- it's merely a symbol in C.
The solution is to use GCC's extended asm syntax. For example, push f could be rewrited into this:
asm volatile ("pushl %0"
: /* no output operands */
: "m" (f)
: /* no clobbered operands */);
As for the function call fseek, I believed your code shall be alright (at least in my experience and on my laptop it works just now). What's your platform info? Do you have glibc or similar things providing the standard libraries of C?
Also Please notice you're using a weird declaration of fseek since it shall at least have a return value according to the C specification.
Just for your information, you may try this style of an indirect call:
asm volatile ("call *%0"
: /* no output operands */
: "r"(fseek)
: /* no clobbered operands */);
I am trying to make an earlier verion Linux got compiled, you can download the source code from git://github.com/azru0512/linux-0.12.git. While compiling ''kernel/blk_drv/ramdisk.c'', I got error message below,
ramdisk.c:36:10: error: can't find a register in class 'CREG' while reloading 'asm'
ramdisk.c:40:10: error: can't find a register in class 'CREG' while reloading 'asm'
ramdisk.c:36:10: error: 'asm' operand has impossible constraints
ramdisk.c:40:10: error: 'asm' operand has impossible constraints
What in ramdisk.c are,
if (CURRENT-> cmd == WRITE) {
(void) memcpy(addr,
CURRENT->buffer,
len);
} else if (CURRENT->cmd == READ) {
(void) memcpy(CURRENT->buffer,
addr,
len);
} else
panic("unknown ramdisk-command");
And the memcpy is,
extern inline void * memcpy(void * dest,const void * src, int n)
{
__asm__("cld\n\t"
"rep\n\t"
"movsb"
::"c" (n),"S" (src),"D" (dest)
:"cx","si","di");
return dest;
}
I guess it's memcpy (include/string.h) inline asm problem, so I remove the clobber list from it but without luck. Could you help me to find out what's going wrong? Thanks!
GCC's syntax for this has changed / evolved a bit.
You must now specify each of the special target registers as an output operand:
...("...instructions..."
: "=c"(n), "=S"(src), "=D"(dest)
and then additionally as the same registers as source operands:
: "0"(n), "1"(src), "2"(dest)
and finally you need to clobber "memory" (I can't remember offhand if this affects condition codes, if so you would also need "cc"):
: "memory")
Next, because this instruction should not be moved or deleted, you need to use either volatile or __volatile__ (I'm not entirely sure why but without this the instructions were deleted, in my test-case).
Last, it's no longer a good idea to attempt to override memcpy because gcc "knows" how to implement the function. You can override gcc's knowledge with -fno-builtin.
This compiles (for me anyway, with a somewhat old gcc on an x86-64 machine):
extern inline void * memcpy(void * dest,const void * src, int n)
{
__asm__ volatile("cld\n\t"
"rep\n\tmovsb\n\t"
: "=c" (n), "=S" (src), "=D" (dest)
: "0" (n), "1" (src), "2" (dest)
: "memory", "cc");
return dest;
}
This exact problem & its reasons are discussed on GCC's bugzilla :
Bug 43998 - inline assembler: can't set clobbering for input register
gcc wont allow input & output registers as clobbers.
If you corrupt input register, do a dummy output to same register :
unsigned int operation;
unsigned int dummy;
asm ("cpuid" : "=a" (dummy) : "0" ( operation) :);
I have this part of code that was compiling using ARMASM :
/* Software Interrupt */
/* we must save lr in case it is called from SVC mode */
#define ngARMSwi( code) __asm { SWI code,{},{},{lr} }
example of use :
ngARMSwi( 0x23);
I try to convert this to compile using gcc (code sourcery GCC-4.6.2 eabi). I found this link http://www.ethernut.de/en/documents/arm-inline-asm.html but I cannot find a way to compile this line correctly.
my best try is
#define ngARMSwi( code) __asm__ ("SWI " (code) : : :"lr" )
but I get compile error :
error: expected ':' or ')' before '(' token
Any help is appreciated!
You probably want
#define ngARMSwi(code) __asm__("SWI %0" : : "I"(code) : "lr")
Note that code is an input to the instruction, so it goes in the third section. Its place in the instuction is marked by the %0 in the string. The I is a constraint on code, indicating that it must be an 8-bit constant.
I have tried to mix SSE2 intrinsics and inline assembler in gcc. But if I specify a variable as xmm0/register as input then in some cases I get a compiler error. Example:
#include <emmintrin.h>
int main() {
__m128i test = _mm_setzero_si128();
asm ("pxor %%xmm0, %%xmm0" : : "xmm0" (test) : );
}
When compiled with gcc version 4.6.1 I get:
>gcc asm_xmm.c
asm_xmm.c: In function ‘main’:
asm_xmm.c:10:3: error: matching constraint references invalid operand number
asm_xmm.c:7:5: error: matching constraint references invalid operand number
The strange thing is that in same cases where I have other input variables/registers then it suddenly works with xmm0 as input but not xmm1, etc. And in another case I was able to specify xmm0-xmm4 but not above. A little confused/frustrated about this :S
Thanks :)
You should let the compiler do the register assignment. Here's an example of pshufb (for gcc too old to have tmmintrin for SSSE3):
static inline __m128i __attribute__((always_inline))
_mm_shuffle_epi8(__m128i xmm, __m128i xmm_shuf)
{
__asm__("pshufb %1, %0" : "+x" (xmm) : "xm" (xmm_shuf));
return xmm;
}
Note the "x" qualifier on the arguments and simply %0 in the assembly itself, where the compiler will substitute in the register it selected.
Be careful to use the right modifiers. "+x" means xmm is both an input and an output parameter. If you are sloppy with these modifiers (eg using "=x" meaning output only when you needed "+x") you will run into cases where it sometimes works and sometimes doesn't.