Am Wed, 31 Aug 2016 09:07:49 +0000 schrieb Timo Sintonen <t.sinto...@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.