Accessing peripheral registers - the next version

Johannes Pfau via D.gnu d.gnu at puremagic.com
Wed Aug 31 03:12:21 PDT 2016


Am Wed, 31 Aug 2016 09:07:49 +0000
schrieb Timo Sintonen <t.sintonen at luukku.com>:

> Some thing that I have noticed in the original struct definition
> 
> > struct Volatile(T) {
> >      T raw;
> >      nothrow:
> >      @disable this(this);
> >      A opAssign(A)(A a) { volatileStore(&raw, a); return a; }
> >      T load() @property { return volatileLoad(&raw); }
> >      alias load this;
> >
> >      void opOpAssign(string OP)(const T rhs) {
> >           auto v = volatileLoad(&raw);
> >           mixin("v " ~ OP ~ "= rhs;");
> >           volatileStore(&raw, v);
> >      }
> > }  
> 
> There was @disable this(this)
> This prevents to use a location as argument to a function, like: 
> print(timer.count). Is there any reason to have this line or not? 

I guess timer is a (enum) pointer to a struct and count is a Volatile!T
field in that struct?
But then timer.count does not return a address, it returns the value of
count? Unless of course print(X) takes its argument by ref or alias.

The reason for @disable this(this) is to disable copying of the struct.
The compiler otherwise accesses T raw in a 'non-volatile' way when
copying the value. In D we can only have a postblit function, but we
cannot reimplement the copying. opAssign can only be used in some cases:
    Foo f;
    auto f2 = Foo();
    auto f3 = f2;
    f = Foo();
    f = f2;

Only the last two lines call opAssign. So "auto f3 = f2" could copy a
Volatile!T in a non-volatile way. This is the reason for @disable
this(this), AFAIK.

> What about disable this() ? I will never need to create any 
> instance of this type.

The only usecase are variables for interrupts AFAIK:

shared|__gshared Volatile!bool sendDone = false;

@interrupt(Interrupt.uartReady) void onUartReady()
{
    sendDone = true;
}

void startSendData(ubyte[]...)
{
    sendDone = false;
    ...
    while(!sendDone)
}

> 
> OpAssign did originally return void. Then I had a piece of code 
> like cr1=cr2=cr3=0 which did not work. Now I return the same type 
> but I think I have seen that it should return this. What should 
> it return and would it make shorter code if it does not return 
> anything?
Both cases are valid D code, AFAIK. But let's consider this example
cr1=cr2=0:

First of all, there are two possible, different interpretations of the
code: Assign 0 to cr2 and 0 to cr1 OR assign 0 to cr2 and cr2 to cr1.
For non-shared, non-volatile variables this is the same. But for shared
or volatile variables the result could be different. 

Consider this example is lowered into
cr2 = 0;
cr1 = cr2;

This is difficult to get right:
1) Volatile!T opAssign(...) {return this;}
   Is wrong: The "return this" part will do a non-volatile read/copy.
2) ref Volatile!T opAssign(...) {return this;}
   Should be correct. But then the example above will additionally
   produce a volatile read from cr2 when setting cr1. This implements
   the second behaviour mentioned above.
3) T opAssign(T rhs){return rhs} implements behaviour 1 mentioned above.

I think this is too confusing so we should return void and simply
disallow the pattern (AFAICS it also doesn't work with @disable
this(this), though the could be a DMD bug).

> 
> The original functions had force_inline attribute. The compiler 
> said it can not inline because the body is not available. Is this 
> because they are templates or is this because of the intrinsic 
> call? These calls are often in time critical places and inlining 
> them is very important.
> 

This is probably a old GDC/DMDFE bug. Cross-module inlining (i.e.
function with force-inline in one module, caller in another module)
wasn't possible with older frontend versions. It should be possible
now, but it is not implemented in GDC. Can you post a simple test case?
Usually templated methods are the only methods which should work with
cross module inlining.

> When these are all templated, all modules have their own 
> instances of all templates for all data and operator types. That 
> is lots of extra code. There are intrinsics for only four data 
> types. Should I make overloads for these 4 types instead of 
> templates? These functions are so short that code duplication 
> does not matter.

Those templates are emitted to every module, but they should have the
same name and be marked as weak, so the linker should merge them into a
single instance. So as long as Volatile!T is restricted to ubyte,
ushort, uint, ulong* there shouldn't be additional bloat.

* I wonder whether using Volatile!T with types larger than the
  atomically readable type is useful. Probably not.

> For the outer struct (like uartregs struct in my example) there 
> is compiler generated stuff like opEquals and toHash. I will 
> never need them and they take a lot of space. How can I disable 
> them?

AFAIK there's no way to disable this function. Extending @disable to
these functions (=> @disable toHash...) should hopefully be
uncontroversial, but needs to be done in DMD first.


More information about the D.gnu mailing list