July 16, 2014
On 7/16/2014 1:35 AM, Iain Buclaw via Digitalmars-d wrote:
> Which would you prefer?

The simplest, which is just to use a version declaration to select whatever works for any particular compiler/CPU.

July 16, 2014
On Tuesday, 15 July 2014 at 21:28:24 UTC, Walter Bright wrote:
> Peek/poke has a 40 year history of being used for MMIO, and I've never heard of it being used for concurrency. I don't think there's endemic confusion, quite unlike volatile.

There is a huge confusion about volatile in the native space. No one really knows what it does and when it should be used, because it's kind of rare.

I've seen pervasive volatile use make some GPU kernels 100x slower. Programming by coincidence, sure, but _explicit_ barriers, being read/write/optimizer barriers, I find them much better. volatile is more like denial, and importing the history behind the keyword would negative PR. Pascal had no volatile, and using &= from a memory-mapped location doesn't exactly scream "performance".


July 16, 2014
Am Wed, 16 Jul 2014 15:59:46 +1000
schrieb "Daniel Murphy" <yebbliesnospam@gmail.com>:

> The problem is not 'this feature is too complex', the problems is that it's more complex than necessary.

So what's necessary in this specific case?
July 16, 2014
Am Tue, 15 Jul 2014 23:38:21 -0700
schrieb Walter Bright <newshound2@digitalmars.com>:

> On 7/15/2014 10:15 PM, Johannes Pfau wrote:
> > Yes, I actually tried tested that simple implementation with gdc. With aggressive optimizations the code inlines just fine and it'll generate the same asm as an access to a volatile variable in C. But it's not space-effecient: Even if it inlines, the function is still in the object file as a distinct function. There's TypeInfo and there's the assert(this), there's an initializer symbol and I guess there could be copy constructors as well. I expect many bug reports/fixes till we get a Volatile!T wrapper to be really space-efficient.
> 
> D is intended to have a robust enough feature set in order to implement types as library types rather than builtin, and have them operate as good as a builtin type would. These are more general issues, and are not specific to volatile.
> 

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;

Your peek/poke intrinsics don't even consider this example. If you extend peek/poke to T, then it'll always load the complete type, whereas the C code loads only the field. And you can't even do anything about it, cause peek!T explicitly tells the compiler to load the complete type and prevent optimization of this load...

And it seems timerA.control |= 0b1; produces absolute nonsense. It
compiles, but
* The codegen is horrible. In C this compiles to 3 instructions, in D
  30-40 instructions
* This doesn't do what it's supposed to do at all. For example it calls
  the getter twice and never calls the setter.

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.

> 
> >>>> 3. if you really hate peek/poke calls appearing in the code, you can use UFCS to make them look like variables
> >>> But you cant do REGISTER.peek() |= 0b1;
> >> As you mentioned in the DIP, read-modify-write operations should be split up, as their semantics are murky.
> > But not necessarily in user code. REGISTER |= is quite common in C and it's perfectly safe.
> 
> 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.

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.

> 
> "CISC machines may allow other operations to memory than simple load/store operations. The question is then if a read-modify-write operation like 'x++' should translate to 'r = load x; r++; x = store r' or to a single add instruction 'add x;'. This DIP does not dictate any specific behaviour, but this is recommended: If it is known that instructions operating on memory do not work for memory mapped IO as expected, compilers should default to generating a load/modify/store sequence. Otherwise compilers should generate the same code sequence as for regular variables. It is recommended to provide a compiler switch on CISC architectures to allow the user to choose between these two different behaviours."

And what does peek/poke guarantee in that case? Everything you could specify for peek/poke can be specified for volatile variables as well.

> 
> > If people will have to split this up into peek |=
> > poke that's a step backwards from C.
> 
> Support for op= is fairly simple for VolatilePointerToUint to implement, and it can implement them in terms of peek/poke for precise and portable semantics.
See above

> I do believe that a wrapper type around peek/poke satisfies essentially all your necessary requirements, except for transitivity.

See above. 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 and it'll take ages and many language changes to make a type wrapper as efficient. If it's even possible at all.

> > I think it's a correct argument to say that you think embedded system programming is not important enough for the additional complexity introduced by a new qualifier.
> 
> 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 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. 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.
July 16, 2014
Am Tue, 15 Jul 2014 19:44:51 +0200
schrieb Artur Skawina via Digitalmars-d <digitalmars-d@puremagic.com>:

> it originally and also the gdc ml discussion that followed. 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?
> 
> artur

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;
July 16, 2014
On Wednesday, 16 July 2014 at 13:04:37 UTC, Johannes Pfau wrote:
> 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;

How about

struct VolatileTimer
{
   Volatile!uint control;
   Volatile!uint data;
}

enum timerA = cast(VolatileTimer*)0xDEADBEAF;

timerA.control |= 0b1;
July 16, 2014
On Wednesday, 16 July 2014 at 13:04:37 UTC, Johannes Pfau wrote:
> 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;

The actual code is a little different: http://forum.dlang.org/thread/lq3kq8$pa3$1@digitalmars.com?page=5#post-fkgjcnqyoqltftbfmwqd:40forum.dlang.org
July 16, 2014
On Wednesday, 16 July 2014 at 13:51:49 UTC, Kagamin wrote:
> On Wednesday, 16 July 2014 at 13:04:37 UTC, Johannes Pfau wrote:
>> 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;
>
> The actual code is a little different: http://forum.dlang.org/thread/lq3kq8$pa3$1@digitalmars.com?page=5#post-fkgjcnqyoqltftbfmwqd:40forum.dlang.org

I think, the idea behind such declarations is to not let user to set volatile qualifiers. What if he forgets?
July 16, 2014
"Johannes Pfau"  wrote in message news:lq5pv1$2nfb$1@digitalmars.com...

> > The problem is not 'this feature is too complex', the problems is
> > that it's more complex than necessary.
>
> So what's necessary in this specific case?

Use intrinsics with a nice template wrapper.  They get inlined, the unused function bodies get stripped, and the debug info/typeinfo is just as big a problem as it is for every other function in your program.

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 just don't see volatile pulling it's weight. 

July 16, 2014
On 16 July 2014 15:14, Daniel Murphy via Digitalmars-d <digitalmars-d@puremagic.com> wrote:
> "Johannes Pfau"  wrote in message news:lq5pv1$2nfb$1@digitalmars.com...
>
>
>> > The problem is not 'this feature is too complex', the problems is that it's more complex than necessary.
>>
>> So what's necessary in this specific case?
>
>
> Use intrinsics with a nice template wrapper.  They get inlined, the unused function bodies get stripped, and the debug info/typeinfo is just as big a problem as it is for every other function in your program.
>
> Intrinsics get us away from our current 'there is no portable way to do this' situation.

No they don't.  Intrinsics make things worse.

> 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...

> 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 just don't see volatile pulling it's weight.

Hardware access is supposed to be correct, not performant.

Regards
Iain