Re: Specifying different registers in inline asm

David Brown via Gcc-help <[email protected]>
Newsgroups gmane.comp.gcc.help
Message-ID <[email protected]>
On 19/11/2025 18:22, jh--- via Gcc-help wrote:
> Le 2025-11-19 16:19, Richard Earnshaw (foss) a écrit :
> [snip]
> 
> Thank you all very much, I am slowly (the slowness comes entirely from 
> my side) understanding what I should do, both on a conceptual and on a 
> practical levels.
> 
> I still need a little push.
> 
>>>
>>> #define byte_from_user(addr)     ({uint8_t val; __asm__ 
>>> __volatile__("LDRBT    %0, [%1]\n" : "=&r"(val) : "r" (addr), 
>>> "m"(*addr)); val;})
>>>
>>> void dump(const uint8_t * src, size_t count;
>>>     for(size_t cntsrc=0;cntsrc<count;cntsrc++){
>>>         uint8_t data = byte_from_user(&src[cntsrc]);
>>>         printf("%02X ", data);
>>>     }
>>>     printf("\n");
>>> }
>>>
>>> So the only way to be theoretically correct is to copy addr to a 
>>> dummy variable and say it will come out of the asm block? Like this:
>>>
>>> #define byte_from_user(addr)     ({uint8_t val; uintptr_t clob_addr = 
>>> addr; __asm__ __volatile__("LDRBT    %0, [%1]\n" : "=&r"(val), 
>>> "+r"(clob_addr) : "m"(*clob_addr)); val;})
>>>
>>
>> With the '+r' variant you don't need to mention clob_addr twice.  You
>> also don't need '&' on the first value now because the compiler will
>> never assign two outputs to the same register.
>>
>> So I think you're best solution would be:
>>
>>
>> static inline uint8_t
>> byte_from_user(uint8_t *addr)
>> {
>>   uint8_t val;
>>   void *unused;
>>   __asm__ __volatile__ ("LDRBT    %0, [%1]\n"
>>             : "=r"(val), "=r" (unused)
>>             : "1" (addr)
>>             : "memory");
>>   return val;
>> }
>>
>> void dump(const uint8_t * src, size_t count;
>>     for(size_t cntsrc=0;cntsrc<count;cntsrc++){
>>         uint8_t data = byte_from_user(&src[cntsrc]);
>>         printf("%02X ", data);
>>     }
>>     printf("\n");
>> }
>>
>> You probably won't need the clobber of 'memory' in addition to the
>> 'volatile' qualifier, but you certainly need at least one.
>>
> About memory access, I think I made a mistake, *addr is not clobbered, 
> but will be read, should that be mentioned in the input list?
> In the converse function byte_to_user, *addr would be written, should it 
> then be marked as clobber?
> Is there a sense in saying that my code only reads/writes *addr and not 
> any other memory (i.e. does it make a difference for clobber to write 
> "m"(*addr) rather then "memory")?
> 
> Cheers,
> JH
> 

Your memory does not need to be clobbered - a general memory clobber can 
be an expensive thing.  If you imagine the CPU registers as a sort of 
"level -1 cache", then a memory clobber is a full cache flush - any 
outstanding data that should logically be written to memory, is written 
out, and anything that had previously been read and kept in a register 
is dropped.  (Local data that only ever existed in registers is unaffected.)

What you want to tell the compiler is that the data at "*addr" must be 
ready to read - thus if it was due to write something to that address, 
it must do that write before your "ldrbt" operation.  And it can't 
re-order other accesses back and forth across the "ldrbt".

The simplest and surest way is to be careful to use volatile accesses 
for all your reads to or writes from the address pointed to are volatile 
accesses, and that your "ldrbt" function uses volatile pointers and 
assembly :

static inline uint32_t ldrbt3(volatile uint8_t * addr) {
     uint32_t x;
     asm volatile ("ldrbt %[rt], [%[rn]]"
             : [rt] "=&r" (x)
             : [rn] "r" (addr));
     return x;
}


But the most efficient manner in terms of the generated assembly is 
simply to tell the compiler that the data pointed at by "*addr" is an 
input to the assembly - but don't make use of that input so that the 
compiler will not generate code to read it :

static inline uint32_t ldrbt4(uint8_t * addr) {
     uint32_t x;
     asm ("ldrbt %[rt], [%[rn]]"
             : [rt] "=&r" (x)
             : [rn] "r" (addr), "m" (*addr));
     return x;
}


<https://godbolt.org/z/hWMeGxT7q>
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.