July 15, 2014
Am Tue, 15 Jul 2014 17:42:47 +0000
schrieb "Kagamin" <spam@here.lot>:

> On Tuesday, 15 July 2014 at 17:13:09 UTC, Johannes Pfau wrote:
> > Why do shared variables have the privilege to prevent accessing
> > shared
> > memory in inappropriate ways but it's fine to place the burden
> > of
> > checking that all accesses to volatile memory are backed by
> > compiler
> > barriers to the user? How is that 'first class' support?
> 
> Maybe it can be automated by a wrapper template?
> 
> struct Volatile(T)
> {
>    T val;
>    void opAssign(T v)
>    {
>      volatile_store(val, v);
>    }
> }
> 
> extern(C) Volatile!byte led;
> 
> foreach(i;0..10)
> {
>    led=0;
>    led=1; //blink
> }

Then we have a templated struct. Which generates TypeInfo for every instance. Which might generate an Initializer for every instance and it might generate extended debug info for every instance.

These are exactly the kind of workarounds I don't want. Sure we can make it work: @noTypeInfo @noInit @noDebug (struct volatile). But then I don't see why we don't just use the proposal in this DIP. It's less complicated, has got fewer corner case, ...

And that's why I say first class support: We don't have shared!(T) we have shared T. And volatile should get it's own qualifier as well.

Also some things will just not work with Volatile!T, for example volatile/nonvolatile member function overloading.
July 15, 2014
On Tuesday, 15 July 2014 at 17:50:13 UTC, Johannes Pfau wrote:
> And that's why I say first class support: We don't have shared!(T) we
> have shared T. And volatile should get it's own qualifier as well.
>
> Also some things will just not work with Volatile!T, for example
> volatile/nonvolatile member function overloading.

Volatile has probably much smaller scope than shared. Shared memory is used more often and in more non-trivial ways, it's not unthinkable for even complex data structures to be shared or abstracted with an interface, so it makes sense that interfaces should support shared methods. Volatile is usually a small range of bytes (a communication device), which should be read once or written once.

D doesn't have a way of saving on typeinfos, so volatile is not alone in this respect. I'd prefer to see it applicable to anything (or rather everything).
July 15, 2014
On Tuesday, 15 July 2014 at 17:50:13 UTC, Johannes Pfau wrote:
> Then we have a templated struct. Which generates TypeInfo for every
> instance. Which might generate an Initializer for every instance and
> it might generate extended debug info for every instance.
>
> These are exactly the kind of workarounds I don't want. Sure we can
> make it work: @noTypeInfo @noInit @noDebug (struct volatile). But then
> I don't see why we don't just use the proposal in this DIP. It's less
> complicated, has got fewer corner case, ...
>
> And that's why I say first class support: We don't have shared!(T) we
> have shared T. And volatile should get it's own qualifier as well.
>
> Also some things will just not work with Volatile!T, for example
> volatile/nonvolatile member function overloading.

Functions can take Volatile!T, of course, and you can always have "set/volatileSet". You might even be able to use opDispatch to make it less DRY.
July 15, 2014
Am Tue, 15 Jul 2014 19:44:51 +0200
schrieb Artur Skawina via Digitalmars-d <digitalmars-d@puremagic.com>:

> On 07/15/14 19:11, Johannes Pfau via Digitalmars-d wrote:
> > Am Tue, 15 Jul 2014 19:03:52 +0200
> > schrieb Artur Skawina via Digitalmars-d
> > <digitalmars-d@puremagic.com>:
> > 
> >> Compiler barriers are not "workarounds". "volatile" is not a "better solution". It's used in C only because it's defined and reasonably portable; barriers are a language extension, hence dialect specific.
> >>
> >> First class support for "embedded programming" would be defining barriers, not transplanting C's volatile. You can already express all the described volatile semantics in GDC's D dialect, in a completely portable way and without using a single asm instruction. What's missing is just a defined standard interface which can be supported by all D compilers.
> > 
> > Did you even read the section that explains why volatility is a property of the memory address and not of the access (4.2.3)? What's your response to that?
> 
> You're assuming that the raw memory needs to be exposed. It doesn't.
> 
> IOW, instead of:
> 
>    struct HWRegs { uint a; uint b; /*...*/ }
>    auto ptr = cast(HWReg*)0xc0000000;
> 
> do:
> 
>    struct HWRegs { uint a; uint b; /*...*/ }
>    auto ptr = cast(Volatile!HwRegs*)0xc0000000;

You didn't say anything about a template in your last response. See my
reply to Kagamin:
- Template bloat: TypeInfo, Initializer, DebugInfo
- Requires inlining to produce decent results
- Requires force inlining: You wouldn't want any method implementations
  of Volatile to actually end up in object files.

> 
> and the Volatile template will magically add all the access barriers.
> 
> > Why do shared variables have the privilege to prevent accessing shared memory in inappropriate ways but it's fine to place the burden of checking that all accesses to volatile memory are backed by compiler barriers to the user? How is that 'first class' support?
> 
> See above; just don't expose mmapped regs as normal memory.

This is not only for mmaped regs. It's also for memory used in interrupt handlers with indirection.

> I have
> not seen any good argument for introducing a volatile type qualifier.
> Could you show one (concrete) example where using 'volatile' is better
> than my approach?
> 

For one thing we should not dictate usage patterns (like don't expose raw memory) on users. Then volatile is very simple for every compiler to implement and optimize, your idea requires multiple pragmas/attributes to make sure there's no bloat and inlining always works.

(For example, in gcc even a force inline attribute does not inline if -finline is not used. I don't consider that acceptable for this kind of code. So we might have to special case Volatile!T even more in the compiler, to really always inline)

Otherwise Volatile!T is OK, but it's a new, fancy idea and might need lots of work, whereas a volatile qualifer could be implemented easily and we know right now for sure that it'll work 100% as expected.
July 15, 2014
Am Wed, 16 Jul 2014 03:33:35 +1000
schrieb "Daniel Murphy" <yebbliesnospam@gmail.com>:

> "Johannes Pfau"  wrote in message news:lq3nf4$rbp$1@digitalmars.com...
> 
> > Did you even read the section that explains why volatility is a property of the memory address and not of the access (4.2.3)? What's your response to that?
> 
> This is true, and it's the ideal I guess, but I'm not sure that it makes it worth extending the type system for.
> 
> My use of volatile in C consists entirely of
> 
> #define SOME_REGISTER (*(volatile unsigned *)(BASE + OFFSET))
> 
> And this could easily be done in D with a wrapper around peek/poke functions. (assuming they're intrinsics and inlining/optimization works correctly)
> 
> So, the complexity it adds to the type system and overloading etc is not worth it for my use cases.  What are the other use cases where it pulls its weight?
> 

That's a judgement call of course and the main reason why I constantly say 'first class support'. This is the only language change we need for embedded systems, do we really want to compromise here? Especially if C has already got a better solution?

There's some complexity in the compiler implementation, indeed. There's also some complexity in the compiler for -cov, for @nogc and for shared. And we recently added complexity to support C++ namespaces. All of these things are completely useless on embedded systems. So I think we could add one slightly complex thing for embedded development.


Also there's effectively no increase in language complexity for users, as nobody will use volatile with desktop applications.

Volatile is already a reserved & deprecated keyword. We don't break any code.

Code on embedded systems will usually be less complex with volatile and it'll be safer.

For some examples where this matters:
- Memory used in interrupt handlers with redirection
- registers with the same layout/type could be referred by pointer
July 15, 2014
Am Tue, 15 Jul 2014 18:13:26 +0000
schrieb "Meta" <jared771@gmail.com>:

> >
> > Also some things will just not work with Volatile!T, for example volatile/nonvolatile member function overloading.
> 
> Functions can take Volatile!T, of course, and you can always have "set/volatileSet". You might even be able to use opDispatch to make it less DRY.

No, this doesn't work with Volatile!:

struct A
{
    private int _val;
    int read(){ return val+val };
    int read() volatile { auto tmp = val; return tmp*tmp; };
}

opDispatch sounds _awesome_ for systems with a few KB of SRAM. People here always have nice ideas but will you implement it and tune it until it has as little overhead as the C version?
July 15, 2014
On Tuesday, 15 July 2014 at 18:30:11 UTC, Johannes Pfau wrote:
> No, this doesn't work with Volatile!:
>
> struct A
> {
>     private int _val;
>     int read(){ return val+val };
>     int read(bool volatile)() { auto tmp = val; return tmp*tmp; };
> }

struct A
{
    private int _val;
    int read(bool volatile)()
    {
        static if (volatile)
        {
            auto tmp = val; return tmp*tmp;
        }
        else
        {
            return val+val;
        }
    };
}

And you are no worse off than before. This template can only generate 2 possible functions, and the instantiations are also cached so as far as I know, there's no code bloat.


> opDispatch sounds _awesome_ for systems with a few KB of SRAM. People here always have nice ideas but will you implement it and tune it until it has as little overhead as the C version?

That's not something I'm currently interested in, but it's something to look into if someone wants to use it in embedded programming.
July 15, 2014
On 07/15/14 20:20, Johannes Pfau via Digitalmars-d wrote:
> (For example, in gcc even a force inline attribute does not inline if -finline is not used. I don't consider that acceptable for this kind of code. So we might have to special case Volatile!T even more in the compiler, to really always inline)

Not inlining in unoptimized (-O0) builds wouldn't be a problem, it could even be useful sometimes (setting breakpoints for specific hw accesses etc). But is that actually the case? I practically never use -O0, so you just made me check - and indeed the GDC here *always* inlines always_inline functions, at "-O0", even when explicitly given '-fno-inline'...

Anyway, the language *relies* on automatic inlining happening, this case isn't special.


> Otherwise Volatile!T is OK, but it's a new, fancy idea and might need lots of work, whereas a volatile qualifer could be implemented easily and we know right now for sure that it'll work 100% as expected.

There's nothing new about it - it's how it has been done in gcc-ese, for many, many years; it's just combining it with templates, which aren't exactly novel either.


> Also there's effectively no increase in language complexity for users, as nobody will use volatile with desktop applications.

Actually, there are quite a few "normal" uses. The most obvious example probably being communication with signal handlers. Then there are various kinds of inter-thread/shared-mem communication schemes.


Yes, D has many problems, some of which you have mentioned in this thread. [1] But introducing additional complexity to a language just to avoid other problematic areas is not a good idea; that approach does not scale. The result are many half-finished features, every one with some kind of problem.

Could a 'volatile' type qualifier be made to work, and are there technical
problems with the DIP? Yes, and No. I'm not ignoring the details; I probably
agree with most, if not all, of them.
I'm saying the DIP is a good answer, to the wrong question.

artur

[1] Which I didn't respond to, to avoid derailing the thread. ;)
July 15, 2014
On 15 July 2014 19:38, Meta via Digitalmars-d <digitalmars-d@puremagic.com> wrote:
> On Tuesday, 15 July 2014 at 18:30:11 UTC, Johannes Pfau wrote:
>>
>> No, this doesn't work with Volatile!:
>>
>> struct A
>> {
>>     private int _val;
>>     int read(){ return val+val };
>>     int read(bool volatile)() { auto tmp = val; return tmp*tmp; };
>> }
>
>
> struct A
> {
>     private int _val;
>     int read(bool volatile)()
>     {
>         static if (volatile)
>         {
>
>             auto tmp = val; return tmp*tmp;
>         }
>         else
>         {
>             return val+val;
>         }
>     };
> }
>
> And you are no worse off than before. This template can only generate 2 possible functions, and the instantiations are also cached so as far as I know, there's no code bloat.
>


Worse, there's no code because the compiler optimises everything away!
July 15, 2014
On Tuesday, 15 July 2014 at 19:23:36 UTC, Iain Buclaw via Digitalmars-d wrote:
> Worse, there's no code because the compiler optimises everything away!

I was assuming you'd do something in the static if (volatile) section to stop it from doing that. I know very little when it comes to these things.