July 16, 2014
On 7/16/2014 7:14 AM, Daniel Murphy wrote:
> Intrinsics get us away from our current 'there is no portable way to do this'
> situation.  They are _trivial_ to implement for any of the compilers. I have
> complete faith that it is possible to make a performant wrapper for both the
> single-register read/write and the struct pattern you posted.  And if I'm wrong
> - we can add a volatile type modifier later!

I'll add that if it turns out we can't do a wrapper because of some limitations/bugs in D's expressiveness, that is the problem with D we need to fix, not adding another type modifier.

It all comes down to leverage. Making user defined types more capable has far, far more productive leverage than welding specifics into the compiler.

July 16, 2014
On 7/16/2014 7:31 AM, Iain Buclaw via Digitalmars-d wrote:
>> They are _trivial_ to implement for any of the compilers.
> No they aren't, unless you are talking specifically about volatile.,
> in which case, that depends...

If the compiler back end doesn't support volatile, then it's a problem regardless of how the front end handles it, so the point is moot.

In any case, the compiler front end can always implement an intrinsic by just calling a function.


July 16, 2014
On 16 July 2014 19:58, Walter Bright via Digitalmars-d <digitalmars-d@puremagic.com> wrote:
> On 7/16/2014 7:31 AM, Iain Buclaw via Digitalmars-d wrote:
>>>
>>> They are _trivial_ to implement for any of the compilers.
>>
>> No they aren't, unless you are talking specifically about volatile.,
>>
>> in which case, that depends...
>
>
> If the compiler back end doesn't support volatile, then it's a problem regardless of how the front end handles it, so the point is moot.
>
> In any case, the compiler front end can always implement an intrinsic by just calling a function.
>
>

I was thinking: http://bugzilla.gdcproject.org/show_bug.cgi?id=84
July 16, 2014
On 16 July 2014 20:03, Iain Buclaw <ibuclaw@gdcproject.org> wrote:
> On 16 July 2014 19:58, Walter Bright via Digitalmars-d <digitalmars-d@puremagic.com> wrote:
>> On 7/16/2014 7:31 AM, Iain Buclaw via Digitalmars-d wrote:
>>>>
>>>> They are _trivial_ to implement for any of the compilers.
>>>
>>> No they aren't, unless you are talking specifically about volatile.,
>>>
>>> in which case, that depends...
>>
>>
>> If the compiler back end doesn't support volatile, then it's a problem regardless of how the front end handles it, so the point is moot.
>>
>> In any case, the compiler front end can always implement an intrinsic by just calling a function.
>>
>>
>
> I was thinking: http://bugzilla.gdcproject.org/show_bug.cgi?id=84

By the way, it's 'so-called' bugs like this that make we want to remove any notion of volatile from shared.

Regards
Iain
July 16, 2014
On 7/16/2014 5:59 AM, Johannes Pfau wrote:
> And how do you implement this with a wrapper? This is common in C/C++:
>
> struct Timer
> {
>      uint control;
>      uint data;
> }
>
> enum timerA = (Volatile!Timer)* = cast()oxDEADBEAF;
>
> timerA.control |= 0b1;

  auto timerA = VolatileStruct!Timer(0xDEADBEAF);
  timerA.control |= 1;

i.e. with introspection, the template can automatically generate the getters/setters and operator overloads.


> The more I think about it the more corner cases appear. After all
> implementing this in the library is much more complex than a type
> qualifier. Some of the complexity is already implemented (alias
> this, ...) so there's less changes in DMD. But any bug in alias this,
> templates, etc will immediately leak into Volatile!T.

It's true that VolatileStruct will be more complex than the average template. But it's quite doable, and the user won't see that complexity.


>> The point is it is not perfectly safe. There is no definition, and
>> certainly no portable definition, as to how many read/write cycles
>> such an operation entails. That's what I meant, and DIP62 says pretty
>> much said the same thing. Please allow me to quote:
>
> In cases where you use |= the exact number of accesses usually does not
> matter. What matters is that there's at least one access and that it's
> not reordered with other reads/writes to volatile.

I understand, but the pedantic in me notes the word "usually".


> For example |= is used on control registers. But I don't care if I
> read/set the bit twice or once, I just doesn't matter.

I understand that it usually does not matter. But sometimes it does. It should be under user control rather than "whatever the compiler does" which can change with optimization settings. C compiler semantics for volatile are "implementation defined", which pretty much means "random".


> And what does peek/poke guarantee in that case?

It guarantees semantics for (A op= B) to be (A = A op B).


> One important point is that this must be as effective as
> c+volatile (in terms of code size and execution cycles). This is an
> important aspect for embedded programming

I fully agree.


> and it'll take ages and many
> language changes to make a type wrapper as efficient.

This is unduly pessimistic.


> If it's even possible at all.

I see no reason to quit before we start.


>> That is not what I'm saying at all. Please do not conflate
>> disagreement about the best way to achieve a goal with disagreement
>> about the goal.
>
> So you do think peek/poke is a better solution than DIP62? Then you
> have to explain this. All arguments so far have been it's too
> complex in the compiler implementation, but never that peek/poke is a
> better way to achieve that goal of accessing volatile memory.
>
> In the end it's a judgement call between complexity and more
> convenience for embedded programming and it's absolutely your decision.
> But as long as I'm not convinced that peek/poke is a _better_ solution
> I'll think that embedded programming is not important enough for you to
> implement the _best_ solution, as it causes some implementation
> complexity. And this in the end means it's of lower priority to you
> than keeping some complexity out of the compiler.

I think it unfair to impute motives when I've specifically said otherwise.


>> I understand that you and others put a lot of work into DIP62. As I
>> said before, it's a very nice example of a well-done DIP.
>>
>
> That's not the point.

I think it is fair to acknowledge the good work you've done.


> If I were convinced there's a better solution,
> I'd be glad to accept it. But I'm not convinced at all, the only
> argument against DIP62 so far was complexity.

Complexity is not a minor issue. Complexity means time and bugs. I am all too familiar with how volatile in C++ has been costly in a lot of effort, bugs, specification, and code that had no use for volatile, yet had to account for it.

BTW, testing of "volatile" behavior in the compiler is problematic as there just doesn't seem to be a reasonable way to do it. This engenders a regular risk of compiler breakage, and shows up to the end user as erratic behavior and lots of cursing at the compiler vendor.
July 16, 2014
On 7/16/2014 12:10 PM, Iain Buclaw via Digitalmars-d wrote:
> By the way, it's 'so-called' bugs like this that make we want to
> remove any notion of volatile from shared.

Right.

July 16, 2014
On 07/16/14 15:02, Johannes Pfau via Digitalmars-d wrote:
> Am Tue, 15 Jul 2014 19:44:51 +0200
> schrieb Artur Skawina via Digitalmars-d <digitalmars-d@puremagic.com>:
> 
>> Could you show one (concrete) example where using 'volatile' is better
>> than my approach?
> 
> I experimented a little more with Volatile!T and I don't think you can make this example work:
> 
> struct Timer
> {
>     uint control;
>     uint data;
> }
> 
> enum timerA = (Volatile!Timer)* = cast()0xDEADBEAF;
> 
> timerA.control |= 0b1;

Thank you very much for showing actual code. There are many (wrong) assumptions being made in this thread (and the one on the gdc list). Claims like "A is required" or "B is impossible", where A and B are not actually defined and only vaguely described, do not usually lead to a productive dialogue. Concrete examples and code make it possible for the discussion to be about facts and not perceptions.

   struct Timer
   {
       uint control;
       uint data;
   }

   enum timerA = cast(Volatile!(Timer)*)0xDEADBEAF;
   //enum timerA = volatile_(cast(Timer*)0xDEADBEAF); // works too.

   void main() {
      timerA.control |= 0b1;
   }

   struct Volatile(PT) {
      PT raw;

      auto opDispatch(string M)() @property { return volatile_(mixin(q{&raw.}~M)); }

      auto opOpAssign(string OP, B)(B b) {
         auto a = load(*raw);
         auto r = mixin("a "~OP~" b");
         store(*raw, r);
         return this;
      }

   static:
      T load(T)(ref T v) {
         asm { "" : "+m" v; }
         T res = v;
         asm { "" : "+g" res; }
         return res;
      }

      T store(T)(ref T v, const T a) {
         asm { "" : : "m" v; }
         v = a;
         asm { "" : "+m" v; }
         return a;
      }
   }
   auto volatile_(A)(A a) { return Volatile!A(a); } // Just for IFTI.


when compiled w/ "gdc -O1" results in

0000000000402949 <_Dmain>:
  402949:       b8 af be ad de          mov    $0xdeadbeaf,%eax
  40294e:       8b 10                   mov    (%rax),%edx
  402950:       83 ca 01                or     $0x1,%edx
  402953:       89 10                   mov    %edx,(%rax)
  402955:       b8 00 00 00 00          mov    $0x0,%eax
  40295a:       c3                      retq

which is the exact op sequence you'd expect. Doing it as just one
R-M-W op would be possible too, but that might not be supported by
all HW/platforms.
And, yes, gdc inlines it even at -O0, if @inline is used. But at -O0
the generated code is /already/ huge and inefficient, hence not really
usable in constrained mem environments.

Obviously, this is just a proof of concept and the minimal implementation that can only handle your example. But it's enough to disprove the it-cannot-be-made-to-work-without-c-volatile claim. Are there any other examples of things that cannot be efficiently handled w/o a built-in volatile type qualifier?

artur
July 16, 2014
Am Wed, 16 Jul 2014 21:59:57 +0200
schrieb Artur Skawina via Digitalmars-d <digitalmars-d@puremagic.com>:

> On 07/16/14 15:02, Johannes Pfau via Digitalmars-d wrote:
> > Am Tue, 15 Jul 2014 19:44:51 +0200
> > schrieb Artur Skawina via Digitalmars-d
> > <digitalmars-d@puremagic.com>:
> > 
> >> Could you show one (concrete) example where using 'volatile' is
> >> better than my approach?
> > 
> > I experimented a little more with Volatile!T and I don't think you can make this example work:
> > 
> > struct Timer
> > {
> >     uint control;
> >     uint data;
> > }
> > 
> > enum timerA = (Volatile!Timer)* = cast()0xDEADBEAF;
> > 
> > timerA.control |= 0b1;
> 
> Thank you very much for showing actual code. There are many (wrong) assumptions being made in this thread (and the one on the gdc list). Claims like "A is required" or "B is impossible", where A and B are not actually defined and only vaguely described, do not usually lead to a productive dialogue. Concrete examples and code make it possible for the discussion to be about facts and not perceptions.
> 
>    struct Timer
>    {
>        uint control;
>        uint data;
>    }
> 
>    enum timerA = cast(Volatile!(Timer)*)0xDEADBEAF;
>    //enum timerA = volatile_(cast(Timer*)0xDEADBEAF); // works too.
> 
>    void main() {
>       timerA.control |= 0b1;
>    }
> 
>    struct Volatile(PT) {
>       PT raw;
> 
>       auto opDispatch(string M)() @property { return
> volatile_(mixin(q{&raw.}~M)); }
> 
>       auto opOpAssign(string OP, B)(B b) {
>          auto a = load(*raw);
>          auto r = mixin("a "~OP~" b");
>          store(*raw, r);
>          return this;
>       }
> 
>    static:
>       T load(T)(ref T v) {
>          asm { "" : "+m" v; }
>          T res = v;
>          asm { "" : "+g" res; }
>          return res;
>       }
> 
>       T store(T)(ref T v, const T a) {
>          asm { "" : : "m" v; }
>          v = a;
>          asm { "" : "+m" v; }
>          return a;
>       }
>    }
>    auto volatile_(A)(A a) { return Volatile!A(a); } // Just for IFTI.
> 
> 
> when compiled w/ "gdc -O1" results in
> 
> 0000000000402949 <_Dmain>:
>   402949:       b8 af be ad de          mov    $0xdeadbeaf,%eax
>   40294e:       8b 10                   mov    (%rax),%edx
>   402950:       83 ca 01                or     $0x1,%edx
>   402953:       89 10                   mov    %edx,(%rax)
>   402955:       b8 00 00 00 00          mov    $0x0,%eax
>   40295a:       c3                      retq
> 
> which is the exact op sequence you'd expect. Doing it as just one
> R-M-W op would be possible too, but that might not be supported by
> all HW/platforms.
> And, yes, gdc inlines it even at -O0, if @inline is used. But at -O0
> the generated code is /already/ huge and inefficient, hence not really
> usable in constrained mem environments.
> 
> Obviously, this is just a proof of concept and the minimal implementation that can only handle your example. But it's enough to disprove the it-cannot-be-made-to-work-without-c-volatile claim. Are there any other examples of things that cannot be efficiently handled w/o a built-in volatile type qualifier?
> 
> artur

Well I guess this is already decided anyway.

I think it's kinda ridiculous that D embedded code will only be usable with strong optimization flags, but whatever. Maybe it works better if the usage is restricted to basic types only.
July 16, 2014
Am Wed, 16 Jul 2014 11:49:21 -0700
schrieb Walter Bright <newshound2@digitalmars.com>:

> On 7/16/2014 7:14 AM, Daniel Murphy wrote:
> > Intrinsics get us away from our current 'there is no portable way
> > to do this' situation.  They are _trivial_ to implement for any of
> > the compilers. I have complete faith that it is possible to make a
> > performant wrapper for both the single-register read/write and the
> > struct pattern you posted.  And if I'm wrong
> > - we can add a volatile type modifier later!
> 
> I'll add that if it turns out we can't do a wrapper because of some limitations/bugs in D's expressiveness, that is the problem with D we need to fix, not adding another type modifier.
> 
> It all comes down to leverage. Making user defined types more capable has far, far more productive leverage than welding specifics into the compiler.
> 

I'll take this as you pre-approve all the mentioned extensions?

* way to disable typeinfo for struct
* way to disable initializer
* force inlining
* (way to omit copy constructor function / force inline)
July 16, 2014
On 07/16/14 22:09, Johannes Pfau via Digitalmars-d wrote:
> Well I guess this is already decided anyway.

Well, peek/poke is not the right solution either...

> I think it's kinda ridiculous that D embedded code will only be usable with strong optimization flags, but whatever. Maybe it works better

Why would it be unusable without "strong optimization flags"? At -O0 the
generated code will be so slow and bloated that it will be unusable in
a constrained embedded environment. Yes, the implicitly generated asserts
are a problem - but _this is not a problem specific to volatile_. Ditto
for RTTI, unwinding tables, dead code etc. Those issues needs to be dealt
with anyway.

artur