Escape analysis (full scope analysis proposal)
Michel Fortin
michel.fortin at michelf.com
Fri Nov 14 04:45:41 PST 2008
On 2008-11-09 08:59:18 -0500, Christopher Wright <dhasenan at gmail.com> said:
> Michel Fortin wrote:
>> I don't see a problem at all. The compiler would expand the lifetime of
>> x to the outer scope, and do the same for y. Basically, the compiler
>> would make it this way in the compiled code:
>>
>> int * p;
>> float * q;
>> int x;
>> float y;
>> if (condition) {
>> p = &x;
>> } else {
>> q = &y;
>> }
>
> In point of fact, it's expensive to extend the stack, so any compiler
> would do that, even without escape analysis.
Indeed.
> On the other hand, what about nested functions? I don't think they'd
> cause any trouble, but I'm not certain.
If you mean there could be a problem with functions referring to the
pointer, I'd say that with properly propagated escape constrains, it's
safe. But it's an interesting case nonetheless. Consider this:
int * p;
if (condition) {
int x;
p = &x;
} else {
int y;
p = &y;
}
int f() { return *p; }
return &f;
Now returning &f forces p to dynamically allocate on the heap, which
puts a constrain on p forcing it to point only to variables on the
heap, which in turn forces x and y to be allocated on the heap.
I haven't verified, but I'm pretty certain this doesn't work correctly
with the current dynamic closures in D2 however (because escape
analysis doesn't see through pointers).
Also, if you made p point to a value it received in argument, and the
scope of that argument isn't the global scope, it'd be an error. For
instance, this wouldn't work:
int delegate() foo1(int* arg) {
int f() { return *arg; }
return &f; // error, returned closure may live longer than *arg; need
constraint
}
Constraining the lifetime of the returned value to be no longer than
the one of the argument would allow it to work safely (disregard the
bizarre syntax for expressing the constrain on the delegate):
int delegate(arg)() foo2(int* arg) {
int f() { return *arg; }
return &f;
// ok, returned closure lifetime guarantied to be
// at most as long as the lifetime of *arg.
}
int globalInt;
int delegate() globalDelegate;
void bar() {
int localInt;
int delegate() localDelegate;
globalDelegate = foo2(globalInt); // ok, same lifetime
localDelegate = foo2(globalInt); // ok, delegate lifetime shorter
localDelegate = foo2(localInt); // ok, same lifetime
globalDelegate = foo2(localInt);
// ok, but forces bar to allocate localInt on the heap since otherwise
// localInt lifetime would be shorter than lifetime of the delegate
}
Note that what I want to demonstrate is that the compiler can see
pretty clearly what needs and what doesn't need to be allocated on the
heap to guaranty safety. Whether we decide it does allocate
automatically or it generate an error is of lesser concern to me. (And
I'll add that some other issues with templates may make this automatic
allocation scheme unworkable.)
--
Michel Fortin
michel.fortin at michelf.com
http://michelf.com/
More information about the Digitalmars-d
mailing list