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.
Related
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");
#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 have some PowerPC assembly code translated with a gcc cross compiler with this function:
uint32_t fill_cache(void)
{
__asm__ ("addi 3, 0, 0\n"); /* R3 = 0 */
/* More asm here modifying R3 and filling the cache lines. */
}
which, under the PowerPC EABI, returns the value computed in R3. When compiling I get
foo.c:105: warning: control reaches end of non-void function
Is there a way to teach gcc that a value is actually returned? Or is there a way to suppress the warning (without removing -Wall or adding -Wno-*)? I would like to very selectively suppress this warning for only this function in order to leave the general warning level as high as possible.
It is not an option to make this function return void since the value computed is required by the caller.
Solution 1: with diagnostic pragmas you can locally suppress certain diagnostic checks. The specific option (which also is implied by -Wall) that complains for no return in a non-void function is -Wreturn-type. So the specific code to suppress the warning is:
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wreturn-type"
/* Your code here */
#pragma GCC diagnostic pop
You can find out which option is causing the warning by compiling with -fdiagnostics-show-option. It will simply append the option to the warning message.
Solution 2: define a register variable and put it in the desired register. Refer to the variable in an inline assembler template, with the resulting code:
uint32_t fill_cache(void)
{
register uint32_t cacheVal __asm__ ("r3");
__asm__ __volatile__ ("addi %0, 0, 0" : "=r" (cacheVal));
/* More code here */
return cacheVal;
}
The volatile modifier is to ensure that the instruction is not removed or in some other way affected undesirably by the optimization strategy.
Solution 2 is preferred for at least two reasons:
The value of a no returning non-void function is undefined as far as the standard is concerned.
There's no risk of suppressing (new) diagnostic warnings there was no intention to suppress in the first place.
Function could be declared as naked, in this case compiler would not generate prolog & epilog and would assume that programmer preserves all necessary registers and puts output value into correct register(s) before return.
uint32_t fill_cache(void) __attribute__((naked)); // Declaration
// attribute should be specified in declaration not in implementation
uint32_t fill_cache(void)
{
__asm__ ("addi 3, 0, 0\n"); /* R3 = 0 */
/* More asm here modifying R3 and filling the cache lines. */
}
A bit late but maybe someone will step in this as well :)
PS: For my best knowledge __asm__ as well as __volatile__ are std=c89 syntax. Practically there is not difference between __asm__ & asm in GNU GCC. But the modern approach is underscoreless style: asm volatile.
asm_language
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 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.