I'm making a compiler using LLVM and LLVM's shadow-stack. I thought I might easy the load of the GC by using escape analysis and allocate some variables on the stack. Can I use my typed AST for this algorithm, or must I use a graph data-structure?
1 Answer
Escape analysis is a static analysis that determines whether the lifetime of data exceeds its static scope. As such, it is not really about stack vs heap allocation, just about lifetimes.
I don't think you can use your typed AST for escape analysis, because what you are analyzing is the data flow of the program. The appropriate structure for that is a graph, not a tree, because the data flow is cyclic. So what you want is to build a control flow graph (cfg), and perform data flow analysis on it.
Data flow analysis is a method or family of methods used for implementing a whole range of compiler optimizations. Among them, escape analysis. In it, you keep track of all objects, variables and references of your program in a points-to graph. Each object and variable is a node in the graph and, depending on how you implement it, references between them can either be nodes or edges.
For example, consider this Java method:
public Pair myMethod(Pair[] pairs) {
Foo f = new Foo();
Pair p1 = new Pair(1, 2);
Pair p2 = new Pair(3, 4);
p1.value = f;
pairs[0] = p1;
return new Pair(5, 6);
}
Escape analysis would work roughly like this:
- Line 1: An array of type
Pair
is passed into the method and aPair
is returned from it. Add two nodes to the graph, one labelledpairs
and another labelledRETURN
both with flaggedforeigner
. Line 2: An object called
f
is created in themyMethod
method- Add a node labelledf
with flagnative
to the graph.Line 3-4: Add two more
native
nodes labelledp1
andp2
.Line 5: The field
value
ofp1
is set to refer tof
. Add an edge from nodep1
tof
.Line 6: First element of the
pairs
array is set to refer top1
. Add an edge frompairs
top1
.Line 7: A new pair object is returned. Add an anonymous
native
node representing the pair object to the graph. Then add an edge from theRETURN
node to this anonymous node.
After this step, traverse the graph beginning at the foreigner
nodes
and flag all nodes visited as foreigner
. It is exactly the same task
as the mark step in a mark and sweep garbage collector.
Then all objects that weren't reached by the traversal retain their
native
flag and are eligible for stack allocation provided that the
compiler can statically determine their sizes. The rest must be
allocated on the heap.
I highly recommend watching the Youtube lectures of this professor:
The paper Escape analysis for Java also provides a fairly good description on how the algorithm works.