[Info-vax] Rust as a HS language, was: Re: Quiet?

Dan Cross cross at spitfire.i.gajendra.net
Mon Apr 11 09:44:53 EDT 2022


In article <t2soi8$los$1 at dont-email.me>,
Simon Clubley  <clubley at remove_me.eisner.decus.org-Earth.UFP> wrote:
>On 2022-04-09, Dan Cross <cross at spitfire.i.gajendra.net> wrote:
>> In article <t2pn8f$r5q$1 at news.misty.com>,
>> Johnny Billquist  <bqt at softjar.se> wrote:
>>>
>>>volatile is simple enough. Every reference have to stay, and be kept in 
>>>the same order, and order also kept in relation to other volatile 
>>>variables, and the value cannot be "cached" or placed in a register, or 
>>>whatever. Memory barriers all around.
>>
>> The last time this came up where I was involved, the murky stuff
>> revolved around precise semantics of generated loads and stores.
>> What you wrote above is true, but suppose I'm doing multiple
>> stores into aligned buffers; can the compiler lower into stores
>> into a larger datum, or must they be kept distinct?  That is,
>> suppose I'm writing a bunch of uint16_t's from an aligned source
>> into an aligned destination; can the compiler turn that into
>> corresponding loads/stores of uint32_t?  Can it write to the
>> same byte twice?  And how do I work with volatile byte buffers
>> without memcpy?
>>
>
>Volatile is very simple as far as I am concerned when answering
>the above questions.
>
>One read in the source code is exactly one read in the generated code.
>
>One write in the source code is exactly one write in the generated code.
>
>A set of reads or a set of writes cannot be combined.

Consider the case of structure assignment to a
volatile-qualified object.  There's nothing in the standard
that guarantees that the above properties hold.

>If the source code asks for two 16-bit reads or writes, then that's
>exactly what it gets. This cannot be converted to a single 32-bit
>read or write.

Sadly, the standard makes no such guarantee, and cases have been
seen in the wild where that does not hold:

https://lists.llvm.org/pipermail/llvm-dev/2019-June/132817.html
https://llvm.org/pubs/2008-10-EMSOFT-Volatiles.pdf

>If the compiler does anything else, it's broken.
>
>Reasoning: the most common use of volatile I have in my code is
>for reading and writing to device registers. If the compiler did
>any of the above in your questions, it would break my code.

In the standard gives no such guarantee, then I'm afraid the
compiler isn't broken.  That's what makes C so damned tricky
these days.

Your reasoning is sound.  The compiler folks just don't care,
and the behavior is "implementation defined" (C11 6.7.3, par 7).

>I would never use memcpy() with volatile BTW, I would always read
>and write the volatile area directly in my source code if I was
>working with something larger than a set of registers.

Indeed, you _cannot_ use memcpy() with volatile; either pointer
argument would discard the `volatile` qualifier which is instant
UB.  (C11 sec 6.7.3, par 6).

>Also, when it comes to talking to hardware, the compiler has no
>idea what the hardware alignment is or what the size of the
>register the code is talking to is. It has to use the information
>in the source code and nothing else.

The trouble is, the compiler doesn't know whether you're talking
to hardware or not; it just knows that something outside of the
view of the program _may_ change the value of an object that is
volatile-qualified.

Sadly, when dealing with things like, say, misaligned hardware
registers on some peripheral, about the only well defined thing
you can do is synthesize a char* that points to that register
and memcpy() into it.  Even creating a misaligned pointer is UB
(C11 6.3.2.3 par 7).  But since you can't memcpy() through a
volatile-qualified pointer, you can't do that.

Of course, most compilers will give you an escape hatch.  But
relying on the semantics of `volatile` is fraught.  See also
DR 476:
http://www.open-std.org/jtc1/sc22/wg14/www/docs/summary.htm#dr_476

	- Dan C.




More information about the Info-vax mailing list