Escape analysis

Steven Schveighoffer schveiguy at yahoo.com
Tue Oct 28 13:09:03 PDT 2008


"Walter Bright" wrote
> Steven Schveighoffer wrote:
>> "Walter Bright" wrote
>>> The reason the scope/noscope needs to be part of the function signature 
>>> is because:
>>>
>>> 1. that makes it self-documenting
>>
>> But the documentation is not enough.  You cannot express the intricacies 
>> of what variables are scope escapes so that the compiler can make 
>> intelligent enough decisions.  What this will result in is slightly less 
>> unnecessary closures, but not enough to make a difference.  Or else you 
>> won't be able to declare things the way you want, so you will be forced 
>> to declare something that *could* result in an escape, but usually 
>> doesn't.
>
> I think it is conceptually straightforward whether a reference escapes or 
> not, though it is difficult for the compiler to detect it reliably.
>
>>> 2. function bodies may be external, i.e. not present
>>> 3. virtual functions
>>
>> Yes, so you are now implying a scope escape contract on all derived 
>> classes. But not a very expressive one.
>>
>>> 4. notifies the user if a library function parameter scope-ness changes 
>>> (you'll get a compile time error)
>>
>> Oh really?  I imagined that if the scope-ness changed it just results in 
>> a new heap allocation when I call the function.
>
> First off, the mangled names will be different, so it won't link until you 
> recompile. This is critical because the caller's code depends on the 
> scope/noscope characteristic.

This 'feature' is basically useless ;)  D has no shared libraries, so I 
don't think anyone generally keeps their stale object files around and tries 
to link with them instead of trying to recompile the sources.  You are 
asking for trouble otherwise.

> Secondly, passing a scoped reference to a noscope parameter should be a 
> compile time error.

OK, so when does a closure happen?  I thought the point of this was to 
specify when a closure was necessary...

compiler sees foo(noscope int *x)

I try to pass in an address to a local variable.  Compiler says, hm... I 
need a closure to convert my scope variable into a noscope.

>> The more I think about this, the more I'd rather have D1 behavior and 
>> some sort of way to indicate my function should allocate a heap frame 
>> (except on easily provable scope escapes).
>
> Having the caller specify it is not tenable, because the caller has no 
> control over (and likely no knowledge of) what the callee does. Functions 
> should be regarded as black boxes, where all you can know about them is in 
> the function signature.

But the compiler's lack of knowledge/proof about the escape intricacies of a 
function will cause either a) unnecessary closure allocation, or b) 
impossible specifications.  i.e. I want to specify that either a scope or 
noscope variable can be passed in, and the variable might escape depending 
on what you pass in for other arguments, how do I do that?

>> The most common case I think which will cause unnecessary allocations, is 
>> a very common case.  A class setter:
>>
>> class X
>> {
>>    private int *v_;
>>    int *v(int *newV) {return v_ = newV;}
>>    int *v() { return v_;}
>> }
>>
>> Clearly, newV escapes into the class instance,
>
> Then it's noscope.

So then to call X.v, the function must allocate a closure?  How does this 
improve the current situation where closures are allocated by default?

>
>> but how do we know what the scope of the class instance is to know if 
>> newV truly escapes its own scope?
>
> We take the conservative approach, and regard "might escape" and "don't 
> know if it escapes" as "treat as if it does escape".

Also untenable.  We have the same situation today.  You will have achieved 
nothing with this syntax except making people write scope or noscope 
everywhere to satisfy incomplete compiler rules.

-Steve 





More information about the Digitalmars-d mailing list