[LLVMdev] Named register variables GNU-style

Krzysztof Parzyszek kparzysz at codeaurora.org
Thu Mar 27 14:20:23 PDT 2014


On 3/27/2014 3:15 PM, Renato Golin wrote:
> On 27 March 2014 19:35, Krzysztof Parzyszek <kparzysz at codeaurora.org> wrote:
>> Is there any sane reason to actually implement it?
>
> Bare metal code is never sane, but that's not an excuse not to write
> it. C is a very complete language, especially modern variations like
> C11, but there's still a lot that can't be done in C and for good
> reasons. Kernel code and optimal libraries can stretch the compiler a
> lot more than user code, and often enough, there's simply no way to
> represent ideas in C. One of these examples is unwind code.

That's all fine, but I'm not sure how this supports having named 
register builtins. The problem is that once we implement a feature, it 
may be impossible to get rid of it, should it turn out to be a flop.  Do 
we understand all intended and unintended consequences of implementing this?


> Then you ask...
>
>> Are there any cases when inline asm would work well enough?

Sorry, that was meant to be "would not work well enough".


> Well, assembly is very powerful and so, but it's also very hard to
> understand what's going on. Inline assembly is an extension to the
> language, and because of that, different compilers implement them in
> different ways.

True, but I know of only one other compiler that allows register 
variables (and it's not even a C compiler). The portability argument is 
not a very strong one here, at least as I understand it.


> Most of GCC's implementation is hidden in layers upon
> layers of legacy code that not many people dare to touch, but that
> compiles code that can't stop being compiled, nor easily migrated (for
> technical and legal reasons).
>
> The interaction between inline assembly and C is, therefore, not easy
> to implement, and it's even harder to get it "right" (ie. similar to
> the "other" compiler). The most you could write in C the better for
> *compilers*, and minimising the exposure by using register variables
> is actually not a bad idea.

I'm not sure if I'm following your argument. The code that uses inline 
asm that cannot be easily migrated will likely not be rewritten to use 
named registers.  Specifically, for those reasons, we need to be 
implementation-compatible with GCC when it comes to inline asm.  So, 
whether we like it or not, we have to have that part working.

Maybe I'm missing something, but in the previous comments, you mention 
__builtin_<register_name>, and in the examples given by others, the 
register name is given as a string.  Also, there is some example where 
the register name is associated with a variable via "asm".  All these 
options are not exactly equivalent, but they all come with some issues.

If a register name is given via a string, as in "register long a 
asm("rsp")", who will check the type of "a"?  On PowerPC, "fpr0" is a 
floating point register.  Would it be legal to have "uint64_t a 
asm("fpr0")"?  How about "float a asm("fpr0")"?  What's funny here is 
that fpr0 is 64-bit long and is incapable of holding a 32-bit IEEE 
value.  If you load a 32-bit fp value into it, it will be automatically 
extended to 64 bits.  With VSX things are different, and if I remember 
correctly, it is now possible to have a single-precision values in some 
set of registers. Do you want the front-end to deal with all this? 
Actually, what's even funnier is that the official PPC assembler syntax 
does not define "fpr0".  It's just 0, and the meaning of it depends on 
where it's placed.  But I digress...

Another example: on Hexagon you can use pairs of registers, for example 
r15:14. Some instructions can use 64-bit values given in even-odd pairs 
like that. At the same time, you can use r14 and r15 separately, and 
both of them will be aliased with the r15:14...


> But mostly, the __builtin_stack_frame is, in essence, a special case
> of the generic pattern of named registers, and gives us the same level
> of guarantees, so, in the end, there isn't much *technical* difference
> in doing one and the other, with the added benefit that named
> registers are already widely used and you won't need to add ifdefs for
> new compilers.

__builtin_frame_pointer specifies a register via the functionality, not 
by name.  In that sense, it is actually something more general than 
named registers.  While many architectures have something like "frame 
pointer", not a lot of them have "rax".


>> Argument against it..? "Why?"
>
> Why? Because the less asm and more C code we have, the better.

Yes, but making it "more C" by keeping direct uses of registers and only 
changing how that is accomplished, is, ahem, akin to porting code from C 
to C++ by changing the file name from .c to .cpp.


> Because
> inline asm semantics is a lot more obtuse than named registers.

I'm not sure if that's true. The compiler still needs to conform to the 
user's desire to have some some value in R3, even if everything except 
R3 would be a better choice.


> Because builtins are just special cases of named registers. Because
> target-independence is not possible in that kind of code.

Ok, so by now I'm confused.  What exactly are we discussing here:
1. unsigned a asm("rax") : make a be an alias for "rax",
2. a = __builtin_register("rax")  : copy "rax" to a,
3. a = __builtin_rax() : copy "rax" to a?

Is it about which of the above is superior to others?

As you can see, I don't like any of those, but some of them I like even 
less than others. :)


The option 3 makes it easier to type-check, since each target can define 
its own set of builtins with proper types.  On the other hand, what do 
you expect to get?  The value of "rax" at that particular place in the 
code?  How would you control what is being loaded into rax?


-Krzysztof


-- 
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, 
hosted by The Linux Foundation



More information about the llvm-dev mailing list