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