What should happen here?

Steven Schveighoffer schveiguy at gmail.com
Tue Sep 21 18:55:12 UTC 2021


On 9/21/21 12:19 PM, Johan wrote:
> On Tuesday, 21 September 2021 at 12:02:05 UTC, Steven Schveighoffer wrote:
>> I would say if you can somehow find a way to trigger the optimizer not 
>> to avoid that stack push, in all compilers, we should do that. IMO, 
>> the cost of a stack pointer is minimal compared to the surprising 
>> result that we currently see. But I don't know enough about compilers 
>> implementation to know whether this is a reasonable ask.
> 
> I think this is not unreasonable to implement, it is similar to keeping 
> track of what destructors to use: just doing a noop/keepalive on the 
> variable at the end of scope.
> I can think of hypothetical cases where this would impact performance. 
> For example,
> the function `void foo(S* s)` receives the pointer in a register, and 
> would have to keep it alive in a register or push it to stack to 
> preserve it for duration of the function; in a tight loop one may not 
> expect that (and there would be no way to _not_ do that). We also don't 
> want this for just any kind of parameter (e.g. not for an int), so would 
> need some smartness on which types to apply this to. I think this would 
> cover it: (pointers to, arrays of) struct, class, slice, AA.
> 
> Test and see?

Probably you don't need to push to the stack unless the last usage is 
sending the variable to a function, but even that could be more 
expensive than just keeping in a register.

The more I think about it (and finding out that other languages have a 
keepAlive feature), this really should just be changed to something 
that's opt-in. A way to signal to the compiler to ensure the thing gets 
onto the stack. And then we change the spec to say "use this feature to 
keep pointers alive during a scope".

If that's not the thing I posted below, then maybe even a special symbol 
name can be used to signal to the compiler.

>> I just thought of a possible easy and effective way to ensure the 
>> thing isn't collected early:
>>
>> ```d
>> struct Pin(T)
>> {
>>    T t;
>>    @nogc nothrow pure @safe ~this() {}
>>    alias t this;
>> }
>>
>> ...
>> // usage
>> auto c = Pin!C(new C); // now it needs to be held until the scope ends
>> ```
>>
>> This seems to work on LDC with -O3 to prevent the early collection, so 
>> maybe it is sound?
> 
> I don't think it is, and I am surprised it works.
> You can trivially inline the destructor, see that it does nothing, and 
> then the liveness of the variable is very short indeed...

Yeah, if the inliner elides the entire function, it could potentially be 
collected, maybe there's something about the fact that the struct has a 
destructor that forces the compiler to store on the stack?

-Steve


More information about the Digitalmars-d mailing list