Skip to content

Commit

Permalink
Provide names of local variables via FramePointerAnalysis (#10906)
Browse files Browse the repository at this point in the history
(cherry picked from commit d37b8f3)
  • Loading branch information
JaroslavTulach authored and jdunkerley committed Sep 9, 2024
1 parent 373fc1a commit 540d5fa
Show file tree
Hide file tree
Showing 34 changed files with 537 additions and 235 deletions.
4 changes: 2 additions & 2 deletions docs/runtime/compiler-ir.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ for future goals.

## Visualization

The IR can be visualized using `-Denso.compiler.dumpIr` system property. This
will output a `.dot` file in [GraphViz](www.graphviz.org) format in the
The IR can be visualized using `--vm.D=enso.compiler.dumpIr` system property.
This will output a `.dot` file in [GraphViz](www.graphviz.org) format in the
`ir-dumps` directory for each IR in the program. The _dot_ file format is a
minimal textual format, that can be converted to a graphical representation
using the `dot` command from the GraphViz package. For example, on Ubuntu,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package org.enso.compiler.context;

import java.util.Objects;
import java.util.function.Supplier;

final class ContextUtils {
private ContextUtils() {}

static <V> V assertSame(String msg, V actual, Supplier<V> expectedSupplier) {
assert checkEquality(actual, expectedSupplier)
: msg + "\nexpected: " + expectedSupplier.get() + "\nactual: " + actual;
return actual;
}

private static <V> boolean checkEquality(V actual, Supplier<V> expectedSupplier) {
if (!Objects.equals(expectedSupplier.get(), actual)) {
return false;
} else {
return true;
}
}
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package org.enso.compiler.pass.analyse;

import org.enso.compiler.core.ir.ProcessingPass;

public sealed interface FrameAnalysisMeta extends ProcessingPass.Metadata
permits FramePointer, FrameVariableNames {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package org.enso.compiler.pass.analyse;

import org.enso.compiler.core.CompilerStub;
import org.enso.compiler.core.ir.ProcessingPass;
import org.enso.persist.Persistable;
import scala.Option;

/**
* A representation of a pointer into a stack frame at a given number of levels above the current.
*/
@Persistable(clazz = FramePointer.class, id = 1283)
public record FramePointer(int parentLevel, int frameSlotIdx) implements FrameAnalysisMeta {

public FramePointer {
assert parentLevel >= 0;
assert frameSlotIdx >= 0;
}

@Override
public String metadataName() {
return getClass().getSimpleName();
}

@Override
public ProcessingPass.Metadata prepareForSerialization(CompilerStub compiler) {
return this;
}

@Override
public Option<ProcessingPass.Metadata> restoreFromSerialization(CompilerStub compiler) {
return Option.apply(this);
}

@Override
public Option<ProcessingPass.Metadata> duplicate() {
return Option.apply(this);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package org.enso.compiler.pass.analyse;

import java.util.List;
import org.enso.compiler.core.CompilerStub;
import org.enso.compiler.core.ir.ProcessingPass;
import org.enso.persist.Persistable;
import scala.Option;
import scala.jdk.javaapi.CollectionConverters;

@Persistable(id = 1286)
public final class FrameVariableNames implements FrameAnalysisMeta {
private final List<String> names;

public FrameVariableNames(List<String> variableNames) {
this.names = variableNames;
}

public static FrameVariableNames create(scala.collection.immutable.List<String> names) {
return new FrameVariableNames(CollectionConverters.asJava(names));
}

public List<String> variableNames() {
return names;
}

@Override
public String metadataName() {
return getClass().getSimpleName();
}

@Override
public ProcessingPass.Metadata prepareForSerialization(CompilerStub compiler) {
return this;
}

@Override
public Option<ProcessingPass.Metadata> restoreFromSerialization(CompilerStub compiler) {
return Option.apply(this);
}

@Override
public Option<ProcessingPass.Metadata> duplicate() {
return Option.apply(new FrameVariableNames(names));
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package org.enso.compiler.pass.analyse;

import java.io.IOException;
import org.enso.compiler.context.FramePointer;
import org.enso.compiler.pass.analyse.alias.AliasMetadata;
import org.enso.compiler.pass.analyse.alias.graph.Graph;
import org.enso.compiler.pass.analyse.alias.graph.GraphOccurrence;
Expand Down Expand Up @@ -67,8 +66,6 @@
@Persistable(clazz = Graph.Link.class, id = 1266, allowInlining = false)
@Persistable(clazz = TypeInference.class, id = 1280)
@Persistable(clazz = FramePointerAnalysis$.class, id = 1281)
@Persistable(clazz = FramePointerAnalysis.FramePointerMeta.class, id = 1282)
@Persistable(clazz = FramePointer.class, id = 1283)
public final class PassPersistance {
private PassPersistance() {}

Expand Down Expand Up @@ -155,19 +152,16 @@ public PersistAliasAnalysisGraph() {

@SuppressWarnings("unchecked")
protected Graph readObject(Input in) throws IOException {
var g = new Graph();

var rootScope = (Graph.Scope) in.readObject();
assignParents(rootScope);
g.rootScope_$eq(rootScope);

var links =
(scala.collection.immutable.Set) in.readInline(scala.collection.immutable.Set.class);
g.initLinks(links);

var nextIdCounter = in.readInt();
g.nextIdCounter_$eq(nextIdCounter);

var g = new Graph(rootScope, nextIdCounter, links);
g.freeze();
return g;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
package org.enso.compiler.context

import org.enso.compiler.pass.analyse.FrameAnalysisMeta
import org.enso.compiler.pass.analyse.FramePointer
import org.enso.compiler.pass.analyse.FrameVariableNames
import org.enso.compiler.pass.analyse.DataflowAnalysis
import org.enso.compiler.pass.analyse.alias.graph.{
GraphOccurrence,
Graph => AliasGraph
}

import scala.jdk.CollectionConverters._
import java.util.function.BiFunction

/** A representation of an Enso local scope.
*
Expand Down Expand Up @@ -34,20 +38,55 @@ class LocalScope(
final val aliasingGraph: () => AliasGraph,
final private val scopeProvider: () => AliasGraph.Scope,
final private val dataflowInfoProvider: () => DataflowAnalysis.Metadata,
final val flattenToParent: Boolean = false,
private val parentFrameSlotIdxs: Map[AliasGraph.Id, Int] = Map()
final private val symbolsProvider: () => FrameAnalysisMeta = null,
final val flattenToParent: Boolean = false,
private val parentFrameSlotIdxs: () => Map[AliasGraph.Id, Int] = () => Map()
) {
lazy val scope: AliasGraph.Scope = scopeProvider()
lazy val dataflowInfo: DataflowAnalysis.Metadata = dataflowInfoProvider()

/** Computes allSymbols needed by this scope. Either the value is obtained
* from symbolsProvider or (as a fallback) a computation from aliasingGraph
* is performed - the latter situation is logged, when log is not null.
*
* @param where human "friendly" identification of the caller
* @param log function to log or null if no logging is needed
*/
def allSymbols(
where: String,
log: BiFunction[String, Array[Object], Void]
): java.util.List[String] = {
def symbols(): java.util.List[String] = {
val r = scope.allDefinitions.map(_.symbol)
r.asJava
}
val meta = if (symbolsProvider == null) null else symbolsProvider()
if (meta.isInstanceOf[FrameVariableNames]) {
val cached = meta.asInstanceOf[FrameVariableNames].variableNames()
if (log != null) {
ContextUtils.assertSame(where, cached, () => symbols())
}
cached
} else {
val result = symbols()
if (log != null) {
log(
"Scope computed from AliasAnalysis at {0} = {1}",
Array(where, result)
)
}
result
}
}

private lazy val localFrameSlotIdxs: Map[AliasGraph.Id, Int] =
gatherLocalFrameSlotIdxs()

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

/** Creates a new child with a new aliasing scope.
*
Expand All @@ -64,15 +103,23 @@ class LocalScope(
*/
def createChild(
childScope: () => AliasGraph.Scope,
flattenToParent: Boolean = false
flattenToParent: Boolean = false,
symbolsProvider: () => FrameVariableNames = null
): LocalScope = {
val sp = if (flattenToParent) {
assert(symbolsProvider == null)
this.symbolsProvider
} else {
symbolsProvider
}
new LocalScope(
Some(this),
aliasingGraph,
childScope,
() => dataflowInfo,
sp,
flattenToParent,
allFrameSlotIdxs
() => allFrameSlotIdxs
)
}

Expand Down Expand Up @@ -125,11 +172,27 @@ class LocalScope(
}
object LocalScope {

/** Constructs a local scope for an [[EnsoRootNode]].
/** Empty and immutable singleton scope.
*/
val empty: LocalScope = {
val graph = new AliasGraph
graph.freeze()
val info = DataflowAnalysis.DependencyInfo()
val emptyVariableNames = FrameVariableNames.create(List())
new LocalScope(
None,
() => graph,
() => graph.rootScope,
() => info,
() => emptyVariableNames
)
}

/** Constructs a new local scope for an [[EnsoRootNode]] that can then be modified.
*
* @return a defaulted local scope
* @return a new empty scope ready for additional modifications.
*/
def root: LocalScope = {
def createEmpty: LocalScope = {
val graph = new AliasGraph
val info = DataflowAnalysis.DependencyInfo()
new LocalScope(
Expand Down
Loading

0 comments on commit 540d5fa

Please sign in to comment.