Is it possible to manufacture, as a macro, a sort of zero-cost integer upcast via inline assembly?
In many instances, I would like to pass 32 bit integers, which are already "in place" through interfaces that take 64 bit integers. But C doesn't like leaving upper bits indeterminate and casting or trying to extend the 32 bits to 64 via a union makes compilers either zero-extend or sign-extend the 32 bit number.
Dummy example:
void g(unsigned long );
void f(int X){
unsigned long XX = (unsigned)X; //mov %edi, %edi
g(XX);
}
Is it possible to replace the cast with some JUST_PRETEND_ITS_AN_UNSIGNED_LONG_WITHOUT_DOING_ANYTHING(X)
?
A combo of unions and inline assembly matching constraints seems to have done it:
#include <stdint.h>
void takeUptr(uintptr_t U);
#define upcast(X) ({ union { int x; uintptr_t xx; } u; u.x = X; \
asm("":"=r"(u.xx):"0"(u.x)); /*make C think the upper bits are inited*/ \
u.xx; })
void passAsUptr(int x){ takeUptr(upcast(x)); }
//^jmp takeUptr; //no zero/sign-extending mov ✓
uintptr_t retAsUptr(int x){ return upcast(x); }
//^movl %edi, %eax; ret; //correctly moved to the output register ✓
https://godbolt.org/z/vz9W5Mq8M
On gcc (sadly not clang) this eliminates the zero-extending mov when moving from
a 32-bit to a 64-bit number when they're both mapped to the same register. The key was using the positional 0 constraint to make the register of the input (here u.x
) match the register of the output (u.xx
, at position 0, hence constraint 0
).