How do memory operands work in avr-gcc inline assembly? - gcc

I'm trying to write a custom memory-copy function for AVR as inline assembly, because avr-gcc will always use a loop for memcpy and struct assignment, which is inefficient in terms of time. I want to use memory operands to avoid having to add a "memory" clobber. I currently have this:
void copy_2_bytes (char *restrict dst, char *restrict src)
{
struct S {
char x[2];
};
__asm__(
" ld __tmp_reg__,%[src]+\n"
" st %[dst]+,__tmp_reg__\n"
" ld __tmp_reg__,%[src]+\n"
" st %[dst]+,__tmp_reg__\n"
: [dst] "=m" ( *(struct S *)dst )
: [src] "m" ( *(struct S *)src )
);
}
This compiles, but it's incorrect in general because it modifies the pointer register pairs corresponding to the memory operands. It's easy to see that gcc assumes that the registers stay unchanged, for example by adding "*dst = 0;" after the assembly.
On the other hand, the Y and Z registers support the "ldd" and "std" instructions, which also take an immediate offset, so they can be used to access multiple bytes without being modified. But then there doesn't seem to be a way to force gcc to not select the X register, which doesn't support that.
UPDATE
Actually, if gcc determines that the address of the memory operand is constant, it will pass the constant address into the assembly, instead of a register pair. So now, I have absolutely no idea how to deal with this. Are there some magic instructions or assembly macros which can deal with both pointer registers and constant addresses at the same time?

Related

Confusion about different clobber description for arm inline assembly

I'm learning ARM inline assembly, and is confused about a very simple function: assign the value of x to y (both are int type), on arm32 and arm64 why different clobber description required?
Here is the code:
#include <arm_neon.h>
#include <stdio.h>
void asm_test()
{
int x = 10;
int y = 0;
#ifdef __aarch64__
asm volatile(
"mov %w[in], %w[out]"
: [out] "=r"(y)
: [in] "r"(x)
: "r0" // r0 not working, but r1 or x1 works
);
#else
asm volattile(
"mov %[in], %[out]"
: [out] "=r"(y)
: [in] "r"(x)
: "r0" // r0 works, but r1 not working
);
#endif
printf("y is %d\n", y);
}
int main() {
arm_test();
return 0;
}
Tested on my rooted android phone, for arm32, r0 generates correct result but r1 won't. For arm64, r1 or x1 generate correct result, and r0 won't. Why on arm32 and arm64 they are different? What is the concrete rule for this and where can I find it?
ARM / AArch64 syntax is mov dst, src
Your asm statement only works if the compiler happens to pick the same register for both "=r" output and "r" input (or something like that, given extra copies of x floating around).
Different clobbers simply perturb the compiler's register-allocation choices. Look at the generated asm (gcc -S or on https://godbolt.org/, especially with -fverbose-asm.)
Undefined Behaviour from getting the constraints mismatched with the instructions in the template string can still happen to work; never assume that an asm statement is correct just because it works with one set of compiler options and surrounding code.
BTW, x86 AT&T syntax does use mov src, dst, and many GNU C inline-asm examples / tutorials are written for that. Assembly language is specific to the ISA and the toolchain, but a lot of architectures have an instruction called mov. Seeing a mov does not mean this is an ARM example.
Also, you don't actually need a mov instruction to use inline asm to copy a valid. Just tell the compiler you want the input to be in the same register it picks for the output, whatever that happens to be:
// not volatile: has no side effects and produces the same output if the input is the same; i.e. the output is a pure function of the input.
asm (""
: "=r"(output) // pick any register
: "0"(input) // pick the same register as operand 0
: // no clobbers
);

GCC Inline Assembly 'Nd' constraint

I'm developing a small toy kernel in C. I'm at the point where I need to get user input from the keyboard. So far, I have implemented inb using the following code:
static inline uint8_t inb(uint16_t port) {
uint8_t ret;
asm volatile("inb %1, %0" : "=a"(ret) : "Nd"(port));
return ret;
}
I know that the "=a" constraint means that al/ax/eax will be copied to ret as output, but I'm still confused about the "Nd" constraint. Can anyone provide some insight on why this constraint is necessary? Or why I can't just use a general purpose register constraint like "r" or "b"? Any help would be appreciated.
The in instruction (returning a byte) can either take an immediate 8 bit value as a port number, or a port specified in the dx register. More on the in instruction can be found in the instruction reference (Intel syntax) . The machine constraints being used can be found in the GCC docs . If you scroll down to x86 family you'll see:
d
The d register
N
Unsigned 8-bit integer constant (for in and out instructions).

Loading SSE registers

I'm working on homework project for OS development class. One task is to save context of SSE registers upon interrupt. Now, saving and restoring context is easy (fxsave/fxsave). But I have problem with testing. I want to put same sample date into one of registers, but all I get is error interrupt 6. Here is code:
// load some SSE registers
struct Vec4 {
int x, y, z, w;
} vec = { 0, 1, 2, 3 };
asm volatile ( "movl %0, %%eax"
: /* no output */
: "r"( &vec )
:
);
asm volatile ( "movups (%eax), %xmm0" );
I searched on internet for solution. All I got is that it might something to do with effective address space. But I don't know what it is.
You need to use a memory operand as a constraint in the inline assembly. This is much better than generating the address by yourself (as you tried with the & operator) and loading in in a register, because the latter will not work if the address is rip relative or relocatable.
asm volatile ( "movups %0, %%xmm0"
: /* no output */
: "m"( vec )
:
);
And you need to use two "%%" before register names.
Read more about gcc's constraints here: http://gcc.gnu.org/onlinedocs/gcc/Simple-Constraints.html#Simple-Constraints . The title is somewhat misleading, as this concept is far from simple :-)
I found out what is problem. Execution of SSE instructions must be enabled by setting some flags in CR0 and CR4 registers. More info here: http://wiki.osdev.org/SSE
You're making this way harder than it needs to be - just use the intrinsics in the *mmintrin.h headers, e.g.
#include <emmintrin.h>
__m128i vec = _mm_set_epi32(3, 2, 1, 0);
If you need to put this in a specific XMM register then use the above example as a starting point, then generate asm, e.g. using gcc -S and use the generated asm as a template for your own code.

Rewrite Intel-style assembly code into GCC inline assembly

How to write this assembly code as inline assembly? Compiler: gcc(i586-elf-gcc). The GAS syntax confuses me. Please give tell me how to write this as inline assembly that works for gcc.
.set_video_mode:
mov ah,00h
mov al,13h
int 10h
.init_mouse:
mov ax,0
int 33h
Similar one I have in assembly. I wrote them separate as assembly routines to call them from my C program. I need to call these and some more interrupts from C itself.
Also I need to put some values in some registers depending on which interrupt routine I'm calling. Please tell me how to do it.
All that I want to do is call interrupt routines from C. It's OK for me even to do it using int86() but i don't have source code of that function.
I want int86() so that i can call interrupts from C.
I am developing my own tiny OS so i got no restrictions for calling interrupts or for any direct hardware access.
I've not tested this, but it should get you started:
void set_video_mode (int x, int y) {
register int ah asm ("ah") = x;
register int al asm ("al") = y;
asm volatile ("int $0x10"
: /* no outputs */
: /* no inputs */
: /* clobbers */ "ah", "al");
}
I've put in two 'clobbers' as an example, but you'll need to set the correct list of clobbers so that the compiler knows you've overwritten register values (maybe none).
First, keep in mind GCC doesn't support 16-bit code yet, so you'll end up compiling 32-bit code in 16-bit mode, which is very inefficient but doable (it is used, for example, by Linux and SeaBIOS). It can be done with the following at the begging of each file:
__asm__ (".code16gcc");
Newer GCC versions (since 4.9 IIRC) support the -m16 flag that does the same thing.
Also, there's no mouse driver available unless you load it previous to your kernel running init_mouse.
You seem to be using an API commonly available in several x86 DOS.
asm can take care of the register assignments, so the code can be reduced to:
void set_video_mode(int mode)
{
mode &= 255;
__asm__ __volatile__ (
"int $0x10"
: "+a" (mode) /* %eax = mode & 255 => %ah = 0, %al = mode */
);
}
void init_mouse(void)
{
/* XXX it is really important to check the IDT entry isn't 0 */
int tmp = 0;
__asm__ __volatile__ (
"int $0x33"
: "+a" (tmp) /* %eax = 0*/
:: "ebx" /* %ebx is also clobbered by DOS mouse drivers */
);
}
The asm statement is documented in the GCC manual, although perhaps not in enough depth and lacks x86 examples. The outputs (after first colon) have a distinctively obscure syntax, while the rest is far easier to understand (the second colon specifies the inputs and the third the clobbered registers, flags and/or memory).
The outputs must be prefixed with =, meaning you don't care the previous value it may have had, or +, meaning you want to use it as an input too. In this context we use that instead of an input because the value is modified by the interrupt and you're not allowed to specify input registers in the clobbered list (because the compiler is forbidden from using them).

Basic GCC inline assembly question

I want to move the value of the variable "userstack" inside the ESP register and then do an absolute jump to the memory address contained in the variable "location".
This is what I've got:
// These are the two variables that contains memory addresses
uint32_t location = current_running->LOCATION;
uint32_t userstack = current_running->user_stack;
// And then something like this
__asm__ volatile ("movl userstack, %esp");
__asm__ volatile ("ljmp $0x0000, location");
However when I try to compile I get the errors:
"Error: suffix or operands invalid for ljmp" and "undefined reference to `userstack'".
Any help would be very much appreciated.
Take a look at the manual.
I think you'd need something like this:
asm volatile ("movl %0, %esp" : "g" (userstack));
asm volatile ("ljmp $0x0000, %0" : "g" (location));
Basically GCC needs know what and where userstack and location may be (registers, memory operands, floating, restricted subset of registers, etc.) and that is specified by "g", in this case meaning a general operand.

Resources