Skip to content

Commit 540d5fa

Browse files
JaroslavTulachjdunkerley
authored andcommitted
Provide names of local variables via FramePointerAnalysis (#10906)
(cherry picked from commit d37b8f3)
1 parent 373fc1a commit 540d5fa

File tree

34 files changed

+537
-235
lines changed

34 files changed

+537
-235
lines changed

docs/runtime/compiler-ir.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ for future goals.
1111

1212
## Visualization
1313

14-
The IR can be visualized using `-Denso.compiler.dumpIr` system property. This
15-
will output a `.dot` file in [GraphViz](www.graphviz.org) format in the
14+
The IR can be visualized using `--vm.D=enso.compiler.dumpIr` system property.
15+
This will output a `.dot` file in [GraphViz](www.graphviz.org) format in the
1616
`ir-dumps` directory for each IR in the program. The _dot_ file format is a
1717
minimal textual format, that can be converted to a graphical representation
1818
using the `dot` command from the GraphViz package. For example, on Ubuntu,
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package org.enso.compiler.context;
2+
3+
import java.util.Objects;
4+
import java.util.function.Supplier;
5+
6+
final class ContextUtils {
7+
private ContextUtils() {}
8+
9+
static <V> V assertSame(String msg, V actual, Supplier<V> expectedSupplier) {
10+
assert checkEquality(actual, expectedSupplier)
11+
: msg + "\nexpected: " + expectedSupplier.get() + "\nactual: " + actual;
12+
return actual;
13+
}
14+
15+
private static <V> boolean checkEquality(V actual, Supplier<V> expectedSupplier) {
16+
if (!Objects.equals(expectedSupplier.get(), actual)) {
17+
return false;
18+
} else {
19+
return true;
20+
}
21+
}
22+
}

engine/runtime-compiler/src/main/java/org/enso/compiler/context/FramePointer.java

Lines changed: 0 additions & 12 deletions
This file was deleted.
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package org.enso.compiler.pass.analyse;
2+
3+
import org.enso.compiler.core.ir.ProcessingPass;
4+
5+
public sealed interface FrameAnalysisMeta extends ProcessingPass.Metadata
6+
permits FramePointer, FrameVariableNames {}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package org.enso.compiler.pass.analyse;
2+
3+
import org.enso.compiler.core.CompilerStub;
4+
import org.enso.compiler.core.ir.ProcessingPass;
5+
import org.enso.persist.Persistable;
6+
import scala.Option;
7+
8+
/**
9+
* A representation of a pointer into a stack frame at a given number of levels above the current.
10+
*/
11+
@Persistable(clazz = FramePointer.class, id = 1283)
12+
public record FramePointer(int parentLevel, int frameSlotIdx) implements FrameAnalysisMeta {
13+
14+
public FramePointer {
15+
assert parentLevel >= 0;
16+
assert frameSlotIdx >= 0;
17+
}
18+
19+
@Override
20+
public String metadataName() {
21+
return getClass().getSimpleName();
22+
}
23+
24+
@Override
25+
public ProcessingPass.Metadata prepareForSerialization(CompilerStub compiler) {
26+
return this;
27+
}
28+
29+
@Override
30+
public Option<ProcessingPass.Metadata> restoreFromSerialization(CompilerStub compiler) {
31+
return Option.apply(this);
32+
}
33+
34+
@Override
35+
public Option<ProcessingPass.Metadata> duplicate() {
36+
return Option.apply(this);
37+
}
38+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
package org.enso.compiler.pass.analyse;
2+
3+
import java.util.List;
4+
import org.enso.compiler.core.CompilerStub;
5+
import org.enso.compiler.core.ir.ProcessingPass;
6+
import org.enso.persist.Persistable;
7+
import scala.Option;
8+
import scala.jdk.javaapi.CollectionConverters;
9+
10+
@Persistable(id = 1286)
11+
public final class FrameVariableNames implements FrameAnalysisMeta {
12+
private final List<String> names;
13+
14+
public FrameVariableNames(List<String> variableNames) {
15+
this.names = variableNames;
16+
}
17+
18+
public static FrameVariableNames create(scala.collection.immutable.List<String> names) {
19+
return new FrameVariableNames(CollectionConverters.asJava(names));
20+
}
21+
22+
public List<String> variableNames() {
23+
return names;
24+
}
25+
26+
@Override
27+
public String metadataName() {
28+
return getClass().getSimpleName();
29+
}
30+
31+
@Override
32+
public ProcessingPass.Metadata prepareForSerialization(CompilerStub compiler) {
33+
return this;
34+
}
35+
36+
@Override
37+
public Option<ProcessingPass.Metadata> restoreFromSerialization(CompilerStub compiler) {
38+
return Option.apply(this);
39+
}
40+
41+
@Override
42+
public Option<ProcessingPass.Metadata> duplicate() {
43+
return Option.apply(new FrameVariableNames(names));
44+
}
45+
}

engine/runtime-compiler/src/main/java/org/enso/compiler/pass/analyse/PassPersistance.java

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package org.enso.compiler.pass.analyse;
22

33
import java.io.IOException;
4-
import org.enso.compiler.context.FramePointer;
54
import org.enso.compiler.pass.analyse.alias.AliasMetadata;
65
import org.enso.compiler.pass.analyse.alias.graph.Graph;
76
import org.enso.compiler.pass.analyse.alias.graph.GraphOccurrence;
@@ -67,8 +66,6 @@
6766
@Persistable(clazz = Graph.Link.class, id = 1266, allowInlining = false)
6867
@Persistable(clazz = TypeInference.class, id = 1280)
6968
@Persistable(clazz = FramePointerAnalysis$.class, id = 1281)
70-
@Persistable(clazz = FramePointerAnalysis.FramePointerMeta.class, id = 1282)
71-
@Persistable(clazz = FramePointer.class, id = 1283)
7269
public final class PassPersistance {
7370
private PassPersistance() {}
7471

@@ -155,19 +152,16 @@ public PersistAliasAnalysisGraph() {
155152

156153
@SuppressWarnings("unchecked")
157154
protected Graph readObject(Input in) throws IOException {
158-
var g = new Graph();
159155

160156
var rootScope = (Graph.Scope) in.readObject();
161157
assignParents(rootScope);
162-
g.rootScope_$eq(rootScope);
163158

164159
var links =
165160
(scala.collection.immutable.Set) in.readInline(scala.collection.immutable.Set.class);
166-
g.initLinks(links);
167161

168162
var nextIdCounter = in.readInt();
169-
g.nextIdCounter_$eq(nextIdCounter);
170-
163+
var g = new Graph(rootScope, nextIdCounter, links);
164+
g.freeze();
171165
return g;
172166
}
173167

engine/runtime-compiler/src/main/scala/org/enso/compiler/context/LocalScope.scala

Lines changed: 71 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,16 @@
11
package org.enso.compiler.context
22

3+
import org.enso.compiler.pass.analyse.FrameAnalysisMeta
4+
import org.enso.compiler.pass.analyse.FramePointer
5+
import org.enso.compiler.pass.analyse.FrameVariableNames
36
import org.enso.compiler.pass.analyse.DataflowAnalysis
47
import org.enso.compiler.pass.analyse.alias.graph.{
58
GraphOccurrence,
69
Graph => AliasGraph
710
}
811

912
import scala.jdk.CollectionConverters._
13+
import java.util.function.BiFunction
1014

1115
/** A representation of an Enso local scope.
1216
*
@@ -34,20 +38,55 @@ class LocalScope(
3438
final val aliasingGraph: () => AliasGraph,
3539
final private val scopeProvider: () => AliasGraph.Scope,
3640
final private val dataflowInfoProvider: () => DataflowAnalysis.Metadata,
37-
final val flattenToParent: Boolean = false,
38-
private val parentFrameSlotIdxs: Map[AliasGraph.Id, Int] = Map()
41+
final private val symbolsProvider: () => FrameAnalysisMeta = null,
42+
final val flattenToParent: Boolean = false,
43+
private val parentFrameSlotIdxs: () => Map[AliasGraph.Id, Int] = () => Map()
3944
) {
4045
lazy val scope: AliasGraph.Scope = scopeProvider()
4146
lazy val dataflowInfo: DataflowAnalysis.Metadata = dataflowInfoProvider()
4247

48+
/** Computes allSymbols needed by this scope. Either the value is obtained
49+
* from symbolsProvider or (as a fallback) a computation from aliasingGraph
50+
* is performed - the latter situation is logged, when log is not null.
51+
*
52+
* @param where human "friendly" identification of the caller
53+
* @param log function to log or null if no logging is needed
54+
*/
55+
def allSymbols(
56+
where: String,
57+
log: BiFunction[String, Array[Object], Void]
58+
): java.util.List[String] = {
59+
def symbols(): java.util.List[String] = {
60+
val r = scope.allDefinitions.map(_.symbol)
61+
r.asJava
62+
}
63+
val meta = if (symbolsProvider == null) null else symbolsProvider()
64+
if (meta.isInstanceOf[FrameVariableNames]) {
65+
val cached = meta.asInstanceOf[FrameVariableNames].variableNames()
66+
if (log != null) {
67+
ContextUtils.assertSame(where, cached, () => symbols())
68+
}
69+
cached
70+
} else {
71+
val result = symbols()
72+
if (log != null) {
73+
log(
74+
"Scope computed from AliasAnalysis at {0} = {1}",
75+
Array(where, result)
76+
)
77+
}
78+
result
79+
}
80+
}
81+
4382
private lazy val localFrameSlotIdxs: Map[AliasGraph.Id, Int] =
4483
gatherLocalFrameSlotIdxs()
4584

4685
/** All frame slot indexes, including local and all the parents.
4786
* Useful for quick searching for [[FramePointer]] of parent scopes.
4887
*/
4988
private lazy val allFrameSlotIdxs: Map[AliasGraph.Id, Int] =
50-
parentFrameSlotIdxs ++ localFrameSlotIdxs
89+
parentFrameSlotIdxs() ++ localFrameSlotIdxs
5190

5291
/** Creates a new child with a new aliasing scope.
5392
*
@@ -64,15 +103,23 @@ class LocalScope(
64103
*/
65104
def createChild(
66105
childScope: () => AliasGraph.Scope,
67-
flattenToParent: Boolean = false
106+
flattenToParent: Boolean = false,
107+
symbolsProvider: () => FrameVariableNames = null
68108
): LocalScope = {
109+
val sp = if (flattenToParent) {
110+
assert(symbolsProvider == null)
111+
this.symbolsProvider
112+
} else {
113+
symbolsProvider
114+
}
69115
new LocalScope(
70116
Some(this),
71117
aliasingGraph,
72118
childScope,
73119
() => dataflowInfo,
120+
sp,
74121
flattenToParent,
75-
allFrameSlotIdxs
122+
() => allFrameSlotIdxs
76123
)
77124
}
78125

@@ -125,11 +172,27 @@ class LocalScope(
125172
}
126173
object LocalScope {
127174

128-
/** Constructs a local scope for an [[EnsoRootNode]].
175+
/** Empty and immutable singleton scope.
176+
*/
177+
val empty: LocalScope = {
178+
val graph = new AliasGraph
179+
graph.freeze()
180+
val info = DataflowAnalysis.DependencyInfo()
181+
val emptyVariableNames = FrameVariableNames.create(List())
182+
new LocalScope(
183+
None,
184+
() => graph,
185+
() => graph.rootScope,
186+
() => info,
187+
() => emptyVariableNames
188+
)
189+
}
190+
191+
/** Constructs a new local scope for an [[EnsoRootNode]] that can then be modified.
129192
*
130-
* @return a defaulted local scope
193+
* @return a new empty scope ready for additional modifications.
131194
*/
132-
def root: LocalScope = {
195+
def createEmpty: LocalScope = {
133196
val graph = new AliasGraph
134197
val info = DataflowAnalysis.DependencyInfo()
135198
new LocalScope(

0 commit comments

Comments
 (0)