Can pretty variable names be used for registers in GCC inline assembly? - gcc

I have some inline assembly. I want GCC to have total freedom in choosing GP registers to allocate. I also want to use pretty names for registers inside the assembly for ease of comprehension for future maintainers. I think I did this previously (10+ years ago) for ARM 5te but am now scratching my head while writing some AArch64 code.
In a simpler example, this is what I want:
uint32_t arg1 = 1, arg2 = 2, result;
asm volatile(
"add %result, %arg1, %arg2\n"
// Outputs:
: ???
// Inputs:
: ???
// Clobbered:
: ???
);
I figure I need the right voodoo to go where I have written "???" above.
Is it possible?

Yes.
[arg1] "r" (arg1)
For example. The two names([arg1] and (arg1) above) can be different.
Inside the assembly code, you'd use:
add %[result], %[arg1], %[arg2]
Documentation link.
Here's your whole example reworked (case changed for the assembly variables just to illustrate that they needn't be the same):
uint32_t arg1 = 1, arg2 = 2, result;
asm volatile(
"add %[RESULT], %[ARG1], %[ARG2]\n"
: [RESULT]"=r"(result) /* output */
: [ARG1]"r"(arg1), [ARG2]"r"(arg2) /* inputs */
: /* no clobbers */
);

Related

Inline assembly using insl instruction - passing variables in C to registers

Recently, I read Xv6, a simple Unix-like teaching operating system and its source code. There is one example of inline assembly, which I cannot find answer to my question about the implementation. There is a stackoverflow article How to use ins instruction with GNU assembler, which cannot help answer that either.
The following is the code snippet I have trouble to understand how inline assembly works for insl instruction.
0461 static inline void
0462 insl(int port, void *addr, int cnt)
0463 {
0464 asm volatile("cld; rep insl" :
0465 "=D" (addr), "=c" (cnt) :
0466 "d" (port), "0" (addr), "1" (cnt) :
0467 "memory", "cc");
0468 }
And one calling function looks like this.
8573 insl(0x1F0, dst, SECTSIZE/4);
My understanding about insl instruction in GCC transfers a string (of 4 bytes) from a port specified in the DX register to the memory (of double words) pointed to by the ES:EDI as destination index. And, Use the rep prefix with the insl instruction for a block transfer of CX double words. The questions I have is, not sure how GCC will pass the values of variables port, *addr, and cnt to the registers of DX, EDI, and CX? I tried to find more information in GCC manual, but not. (Please forgive me as a GCC novice. Maybe I did not exhaust all the ways to find it yet.)
I tried to imagine that could be related to line 0466, "d" (port), "0" (addr) and "1" (cnt). If it is true, I still don’t understand why the first argument is listed as "d", and next ones starting from position 0 with "0" (addr) and "1" (cnt). Why not "0" (port), "1" (addr), "2" (cnt)? And any rule of the mapping to registers DX, EDI, and CX?

How to interact with RISC-V CSRs by using GCC C code?

this is my first question to be asked here on stackoverflow, so please be kind with me ;)
I'm new to RISC-V and low level C coding and I'm wondering how to manipulate the RISC-V CSRs using GCC C code.
A read of a specific CSR (e.g. MISA) looks easy: csrr rd, 0x301 which is short for csrrs rd, 0x301, x0 can be done e.g. with
int result;
asm("csrr %0, 0x301" : "=r"(result) : );
How can I convert the code above into some kind of function / callable unit with the following interface: int read_csr(int csr_number)?
Since the CSR number must be an immediate value in machine code, is it possible without generating the code on the fly (self modifying code)?
Thanks for your replies and discussion.
Joachim
You can use the I constraint for an immediate constant argument:
inline int read_csr(int csr_num) __attribute__((always_inline)) {
int result;
asm("csrr %0, %1" : "=r"(result) : "I"(csr_num));
return result; }
this will fail if you ever call it with an argument that is not a constant expression, as the I constraint requires a constant.

NEON inline assembly - store query

I am trying to learn how to utilize NEON using gcc and inline assembly.
While it is confusing and slow going, I making some progress (It's been 10 years since I last tried writing assembly).
My simple program loads a (small) vector, saturation sums it, and stores it. The problem I am having is that I cannot seem to store the result in the place I want.
When I use an unused array pointer (r) in my output list, I get an error "impossible constraint in asm". If I then create a second pointer to it (rptr), it assembles, but it re-uses an input register r2 which is a, effectively overwriting the input.
(I know my arrays are 32 elements in size and that I'm only processing one element, I plan to try to loop, or try load more registers for parallel processing next)
void vecSum()
{
//two input arrays of 32 bit types, one output
int32_t a[32];
int32_t b[32];
int32_t r[32];
//initialize
for(int cnt = 0; cnt < 32; cnt++)
{
a[cnt] = 0x33333333;
b[cnt] = 0x11111111;
r[cnt] = 0;
}
void *rptr = r;
__asm__ volatile(
"vld1.32 {d0},[%[ina]]!\n" //load the neon register with our data at a, post increment the reg
"vld1.32 {d1},[%[inb]]!\n"
"vqadd.s32 d0,d1\n" //perform the sat
"vst1.32 d0,[%[result]]\n" //store the answer
: [result]"=r" (rptr) /*r*/
: [ina] "r" (a), [inb] "r" (b)
: /*"d0", "d1", "d2"*/);
for(int g=0; g < 32; g++)
{
printf("0x[%d]%x ",g,a[g]);
}
}
Objdump:
for(int cnt = 0; cnt < 32; cnt++)
780: e3530080 cmp r3, #128 ; 0x80
784: 1afffff7 bne 768 <_Z8vecSum32v+0x28>
"vld1.32 {d1},[%[inb]]!\n"
"vqadd.s32 d0,d1\n" //perform the sat
"vst1.32 d0,[%[result]]\n"
: [result]"=r" (rptr)
: [ina] "r" (a), [inb] "r" (b)
: /*"d0", "d1", "d2"*/);
788: f422078f vld1.32 {d0}, [r2]
78c: f421178d vld1.32 {d1}, [r1]!
790: f2200011 vqadd.s32 d0, d0, d1
794: f402078f vst1.32 {d0}, [r2]
In summary, if I try vst1.32 d0,[%[result]] where result is the array pointer r, I get a compilation error. If I rptr ( another pointer to r) it comiles, but uses r2 (the array a) as the output.
Can anybody explain why I get the error outputting to r? And why the ptr to r is a?
rptr is declared as an output when it should be an input and "memory" is missing from the clobber list.
Alternatively you may put the arrays in structs and use the structs (rather than pointers) as arguments to the asm statement.
Consider if the asm contained add %[result], %[ina], %[inb]. There's no harm whatsoever in allocating r2 for both result and ina there. Since GCC doesn't go analysing the contents of the asm statement, its default assumption is that it contains a single instruction like that, so if yours is more complicated then you need to say so in order for things to work as expected.
Specifically, to prevent the problematic overlapping register allocation here, you need to be honest about the fact that you that your asm modifies the input registers - most simply via the + modifier (which then actually makes them outputs as far as GCC is concerned). Another unpleasant side effect of not doing that, is that the compiler would assume that e.g. r1 still holds the address of b afterwards, and may generate later code relying on that which will then go horribly wrong thanks to what the asm actually did.
Furthermore, you don't modify the result pointer, and only use its value as an input, so saying it's a write-only output operand is very wrong.
As for the issue with r, well, by specifying it as an output operand, you're saying that the asm writes a value back to that variable. Except you can't do that with an array variable in C (<languagelawyer> arrays are not modifiable lvalues) - you need to give the asm a variable which holds the address of the array and can be assigned back to, i.e. a pointer variable. The reason you can use the arrays directly as input operands, is because input operands are expressions, not variables, and an expression that evaluates to an array is automatically converted to a pointer to first element of that array (but is still not an lvalue </languagelawyer>).
All in all then, with appropriate pointer variables for a and b, suitable operands and constraints for this code as-is would look more like this:
: [ina] "+r" (aptr), [inb] "+r" (bptr)
: [result] "r" (r)
: "d0", "d1", "memory" /* getting clobbers right is also important */
Side note: if you just want to get to grips with NEON instructions rather than fighting with GCC, intrinsics are an alternative to consider.

what's meaning of f in "js 2f\n\t"?

the codes:
extern inline int strncmp(const char * cs, const char * ct, int count)
{
register int __res;
__asm__("cld\n"
"1:\tdecl %3\n\t"
"js 2f\n\t"
"lodsb\n\t"
"scasb\n\t"
"jne 3f\n\t"
"testb %%al, %%al\n\t"
"jne 1b\n"
"2:\txorl %%eax,%%eax\n\t"
"jmp 4f\n"
"3:\tmovl $1,%%eax\n\t"
"j1 4f\n\t"
"negl %%eax\n"
"4:"
:"=a" (__res):"D" (cs), "S" (ct), "c" (count):"si","di","cx");
return __res;
}
I don't understand the f in "js 2f\n\t" and the b in "jne 1b\n", How to
understand this ? which book I should look? Thank you.
In this context f means forward and b means backward. So js 2f means jump forward to label 2, if sign set.
You'll want to look into gcc inline assembly. I can't seem to find any reference online to include this bit, but I know you can find it in Professional Assembly Language.
Why can't we use named labels ? To quote from the book:
If you have another asm section in your C code, you cannot use the
same labels again, or an error message will result due to duplicate
use of labels.
So what can we do ?
The solution is to use local labels. Both conditional and
unconditional branches allow you to specify a number as a label, along
with a directional flag to indicate which way the processor should
look for the numerical label. The first occurrence of the label found
will be taken.
About modifiers:
Use the f modifier to indicate the label is forward from the jump
instruction. To move backward, you must use the b modifier.
This is documented in the manual for the assembler.

Retrieving the ZF in GCC inline assembly

I need to use some x86 instructions that have no GCC intrinsics, such as BSF and BSR.
With GCC inline assembly, I can write something like the following
__INTRIN_INLINE unsigned char bsf64(unsigned long* const index, const uint64_t mask)
{
__asm__("bsf %[mask], %[index]" : [index] "=r" (*index) : [mask] "mr" (mask));
return mask ? 1 : 0;
}
Code like if (bsf64(x, y)) { /* use x */ } is translated by GCC to something like
0x000000010001bf04 <bsf64+0>: bsf %rax,%rdx
0x000000010001bf08 <bsf64+4>: test %rax,%rax
0x000000010001bf0b <bsf64+7>: jne 0x10001bf44 <...>
However if mask is zero, BSF already sets the ZF flag, so the test after bsf is redundant.
Instead of returning mask ? 1 : 0, is it possible to retrieve the ZF flag and returning it, making GCC not generate the test?
EDIT: made the if example more clear
EDIT: In response to Damon, __builtin_ffsl generates even less optimal code. If I use the following code
int b = __builtin_ffsl(mask);
if (b) {
*index = b - 1;
return true;
} else {
return false;
}
GCC generates this assembly
0x000000000044736d <+1101>: bsf %r14,%r14
0x0000000000447371 <+1105>: cmove %r12,%r14
0x0000000000447375 <+1109>: add $0x1,%r14d
0x0000000000447379 <+1113>: je 0x4471c0 <...>
0x000000000044737f <+1119>: lea -0x1(%r14),%ecx
So the test is gone, but redundant conditional move, increment and decrement are generated.
A couple of remarks:
This is an "anti-optimization". You're trying to do a micro-optimization on something that the compiler already supports.
Your code does not generate the bsf instruction at all with my version of gcc with all optimization switches turned on. Looking at the code, that is not surprising, because you return mask, which is the source operand, not the destination operand (gcc uses AT&T syntax!). The compiler is intelligent enough to figure this out and drops the assembler code (which doesn't do anything) alltogether.
There is an intrinsic function __builtin_ffsl which does exactly the same as your inline assembly (though, correctly). An intrinsic is no less portable than inline assembler, but easier for the compiler to optimize.
Using the intrinsic function results in a bsf cmov sequence on my compiler (assuming the calling code forces it to actually emit the instruction), which shows that the compiler uses the zero-flag just fine without an additional test instruction.
Returning a char when you want a bool is not the best possible hint for the compiler, though it will probably figure it out anyway most of the time. However, telling the compiler to use a bitscan instruction when you are really only interested in "zero or not zero" is certainly sub-optimal. if(x) and if(!x) work perfectly well for that matter. It would be different if you returned the result as reference, so you could reuse it in another place, but as it is, your code is only a very complicated way of writing if(x).

Resources