From e5f51f040de731d55b2b518c88e37036d3c18459 Mon Sep 17 00:00:00 2001 From: Rolph Recto Date: Wed, 2 Feb 2022 00:44:08 -0500 Subject: [PATCH 1/8] first draft --- .../cs/apl/viaduct/lowering/Flowchart.kt | 87 ++++++ .../apl/viaduct/lowering/PartialEvaluator.kt | 250 ++++++++++++++++++ .../backend/PlaintextProtocolInterpreter.kt | 2 +- 3 files changed, 338 insertions(+), 1 deletion(-) create mode 100644 compiler/src/main/kotlin/edu/cornell/cs/apl/viaduct/lowering/Flowchart.kt create mode 100644 compiler/src/main/kotlin/edu/cornell/cs/apl/viaduct/lowering/PartialEvaluator.kt diff --git a/compiler/src/main/kotlin/edu/cornell/cs/apl/viaduct/lowering/Flowchart.kt b/compiler/src/main/kotlin/edu/cornell/cs/apl/viaduct/lowering/Flowchart.kt new file mode 100644 index 0000000000..4e9fb6d3b9 --- /dev/null +++ b/compiler/src/main/kotlin/edu/cornell/cs/apl/viaduct/lowering/Flowchart.kt @@ -0,0 +1,87 @@ +package edu.cornell.cs.apl.viaduct.lowering + +import edu.cornell.cs.apl.viaduct.syntax.Host +import edu.cornell.cs.apl.viaduct.syntax.ObjectVariable +import edu.cornell.cs.apl.viaduct.syntax.Operator +import edu.cornell.cs.apl.viaduct.syntax.Protocol +import edu.cornell.cs.apl.viaduct.syntax.Temporary +import edu.cornell.cs.apl.viaduct.syntax.datatypes.ClassName +import edu.cornell.cs.apl.viaduct.syntax.datatypes.QueryName +import edu.cornell.cs.apl.viaduct.syntax.datatypes.UpdateName +import edu.cornell.cs.apl.viaduct.syntax.types.ValueType +import edu.cornell.cs.apl.viaduct.syntax.values.Value +import kotlinx.collections.immutable.PersistentList + +sealed class LoweredExpression + +data class LiteralNode(val value: Value): LoweredExpression() + +data class ReadNode(val temporary: Temporary): LoweredExpression() + +data class OperatorApplicationNode( + val operator: Operator, + val arguments: PersistentList +): LoweredExpression() + +data class QueryNode( + val variable: ObjectVariable, + val query: QueryName, + val arguments: PersistentList +): LoweredExpression() + +data class InputNode( + val type: ValueType, + val host: Host +): LoweredExpression() + +sealed class LoweredStatement + +object SkipNode: LoweredStatement() + +data class DeclarationNode( + val name: ObjectVariable, + val className: ClassName, + val typeArguments: PersistentList, + val arguments: PersistentList, + val protocol: Protocol +): LoweredStatement() + +data class LetNode( + val temporary: Temporary, + val value: LoweredExpression, + val protocol: Protocol +): LoweredStatement() + +data class UpdateNode( + val variable: ObjectVariable, + val update: UpdateName, + val arguments: PersistentList +): LoweredStatement() + +data class OutputNode( + val message: LoweredExpression, + val host: Host +): LoweredStatement() + +sealed class LoweredControl + +data class Goto(val label: BlockLabel): LoweredControl() + +data class GotoIf( + val guard: LoweredExpression, + val thenLabel: BlockLabel, + val elseLabel: BlockLabel +): LoweredControl() + +object Halt: LoweredControl() + +typealias BlockLabel = String + +data class LoweredBasicBlock( + val statements: List, + val jump: LoweredControl +) + +data class FlowchartProgram( + val blocks: Map +) diff --git a/compiler/src/main/kotlin/edu/cornell/cs/apl/viaduct/lowering/PartialEvaluator.kt b/compiler/src/main/kotlin/edu/cornell/cs/apl/viaduct/lowering/PartialEvaluator.kt new file mode 100644 index 0000000000..437a254c6e --- /dev/null +++ b/compiler/src/main/kotlin/edu/cornell/cs/apl/viaduct/lowering/PartialEvaluator.kt @@ -0,0 +1,250 @@ +package edu.cornell.cs.apl.viaduct.lowering + +import edu.cornell.cs.apl.viaduct.syntax.ObjectVariable +import edu.cornell.cs.apl.viaduct.syntax.Temporary +import edu.cornell.cs.apl.viaduct.syntax.datatypes.ClassName +import edu.cornell.cs.apl.viaduct.syntax.datatypes.Get +import edu.cornell.cs.apl.viaduct.syntax.datatypes.ImmutableCell +import edu.cornell.cs.apl.viaduct.syntax.datatypes.Modify +import edu.cornell.cs.apl.viaduct.syntax.datatypes.MutableCell +import edu.cornell.cs.apl.viaduct.syntax.datatypes.QueryName +import edu.cornell.cs.apl.viaduct.syntax.datatypes.UpdateName +import edu.cornell.cs.apl.viaduct.syntax.datatypes.Vector +import edu.cornell.cs.apl.viaduct.syntax.types.ValueType +import edu.cornell.cs.apl.viaduct.syntax.values.Value +import edu.cornell.cs.apl.viaduct.syntax.values.IntegerValue +import kotlinx.collections.immutable.PersistentList +import kotlinx.collections.immutable.PersistentMap +import kotlinx.collections.immutable.persistentListOf +import kotlinx.collections.immutable.toPersistentList + +sealed class InterpreterObject { + abstract fun query(query: QueryName, arguments: List): Value + + abstract fun update(update: UpdateName, arguments: List): InterpreterObject +} + +object NullObject : InterpreterObject() { + override fun query(query: QueryName, arguments: List): Value { + throw Exception("runtime error") + } + + override fun update(update: UpdateName, arguments: List): InterpreterObject { + throw Exception("runtime error") + } +} + +data class ImmutableCellObject(val value: Value) : InterpreterObject() { + override fun query(query: QueryName, arguments: List): Value { + return when (query) { + is Get -> value + + else -> { + throw Exception("unknown query ${query.name} for immutable cell") + } + } + } + + override fun update(update: UpdateName, arguments: List): InterpreterObject { + throw Exception("cannot update immutable cell") + } +} + +data class MutableCellObject(var value: Value) : InterpreterObject() { + override fun query(query: QueryName, arguments: List): Value { + return when (query) { + is Get -> this.value + + else -> { + throw Exception("unknown query ${query.name} for mutable cell") + } + } + } + + override fun update(update: UpdateName, arguments: List): InterpreterObject { + return MutableCellObject( + when (update) { + is edu.cornell.cs.apl.viaduct.syntax.datatypes.Set -> { + arguments[0] + } + + is Modify -> { + update.operator.apply(value, arguments[0]) + } + + else -> { + throw Exception("unknown update ${update.name} for mutable cell") + } + } + ) + } +} + +data class VectorObject(val size: Int, defaultValue: Value) : InterpreterObject() { + val values: PersistentList = persistentListOf() + + init { + for (i: Int in 0 until size) { + values = values.add(defaultValue) + } + } + + override fun query(query: QueryName, arguments: List): Value { + return when (query) { + // TODO: fail silently when index is out of bounds + is Get -> { + val index = arguments[0] as IntegerValue + values[index.value] + } + + else -> { + throw Exception("unknown query ${query.name} for vector") + } + } + } + + override fun update(update: UpdateName, arguments: List): InterpreterObject { + val index = arguments[0] as IntegerValue + + values[index.value] = when (update) { + is edu.cornell.cs.apl.viaduct.syntax.datatypes.Set -> { + arguments[1] + } + + is Modify -> { + update.operator.apply(values[index.value], arguments[1]) + } + + else -> { + throw Exception("unknown update ${update.name} for vector") + } + } + } +} + +data class PartialStore( + val objectStore: PersistentMap, + val temporaryStore: PersistentMap +) { + fun updateObject(variable: ObjectVariable, obj: InterpreterObject): PartialStore { + return this.copy(objectStore = objectStore.put(variable, obj)) + } + + fun deleteObject(variable: ObjectVariable): PartialStore { + return this.copy(objectStore = objectStore.remove(variable)) + } + + fun updateTemporary(temporary: Temporary, expr: LoweredExpression): PartialStore { + return this.copy(temporaryStore = temporaryStore.put(temporary, expr)) + } +} + +private fun LoweredExpression.isStatic(): Boolean = this is LiteralNode + +class PartialEvaluator( + val program: FlowchartProgram +) { + companion object { + fun evaluate(program: FlowchartProgram) { + PartialEvaluator(program).evaluate() + } + } + + private fun evaluate(store: PartialStore, expr: LoweredExpression): LoweredExpression { + return when (expr) { + is LiteralNode -> expr + + is ReadNode -> { + store.temporaryStore[expr.temporary] ?: + throw Error("cannot find temporary ${expr.temporary.name}") + } + + is OperatorApplicationNode -> { + val reducedArgs = expr.arguments.map { evaluate(store, it) } + val staticArgs = reducedArgs.all { it.isStatic() } + if (staticArgs) { + val valArgs = reducedArgs.map { (it as LiteralNode).value } + LiteralNode(expr.operator.apply(valArgs)) + + } else { + expr.copy(arguments = reducedArgs.toPersistentList()) + } + } + + is QueryNode -> { + val reducedArgs = expr.arguments.map { evaluate(store, it) } + val staticArgs = reducedArgs.all { it.isStatic() } + if (staticArgs && store.objectStore.contains(expr.variable)) { + val valArgs = reducedArgs.map { (it as LiteralNode).value } + val queryVal = store.objectStore[expr.variable]!!.query(expr.query, valArgs) + LiteralNode(queryVal) + + } else { + expr.copy(arguments = reducedArgs.toPersistentList()) + } + } + + is InputNode -> expr + } + } + + private fun buildObject( + className: ClassName, typeArguments: List, arguments: List + ): InterpreterObject { + return when (className) { + ImmutableCell -> ImmutableCellObject(arguments[0]) + + MutableCell -> MutableCellObject(arguments[0]) + + Vector -> { + val length = (arguments[0] as IntegerValue).value + VectorObject(length, typeArguments[0].defaultValue) + } + + else -> throw Exception("unknown class name $className") + } + } + + private fun evaluate(store: PartialStore, stmt: LoweredStatement): Pair { + return when (stmt) { + is SkipNode -> Pair(store, stmt) + + is DeclarationNode -> { + val reducedArgs = stmt.arguments.map { arg -> evaluate(store, arg) } + val staticArgs = reducedArgs.all { it.isStatic() } + if (staticArgs) { + val valArgs = reducedArgs.map { (it as LiteralNode).value } + val obj = buildObject(stmt.className, stmt.typeArguments, valArgs) + Pair(store.updateObject(stmt.name, obj), SkipNode) + + } else { + val reducedDecl = stmt.copy(arguments = reducedArgs.toPersistentList()) + Pair(store, reducedDecl) + } + } + + is LetNode -> { + val reducedExpr = evaluate(store, stmt.value) + Pair(store.updateTemporary(stmt.temporary, reducedExpr), SkipNode) + } + + is OutputNode -> { + val reducedMsg = evaluate(store, stmt.message) + Pair(store, stmt.copy(message = reducedMsg)) + } + + is UpdateNode -> { + val reducedArgs = stmt.arguments.map { arg -> evaluate(store, arg) } + val staticArgs = reducedArgs.all { it.isStatic() } + if (staticArgs && store.objectStore.contains(stmt.variable)) { + val valArgs = reducedArgs.map { (it as LiteralNode).value } + store.objectStore[stmt.variable]!!.update(stmt.update, valArgs) + Pair(store, SkipNode) + + } else { + Pair(store.deleteObject()) + } + } + } + } +} diff --git a/interpreter/src/main/kotlin/edu/cornell/cs/apl/viaduct/backend/PlaintextProtocolInterpreter.kt b/interpreter/src/main/kotlin/edu/cornell/cs/apl/viaduct/backend/PlaintextProtocolInterpreter.kt index 231ee7830c..5595bf3904 100644 --- a/interpreter/src/main/kotlin/edu/cornell/cs/apl/viaduct/backend/PlaintextProtocolInterpreter.kt +++ b/interpreter/src/main/kotlin/edu/cornell/cs/apl/viaduct/backend/PlaintextProtocolInterpreter.kt @@ -105,7 +105,7 @@ class PlaintextProtocolInterpreter( Vector -> { val length = runExpr(arguments[0]) as IntegerValue - VectorObject(length.value, length.type.defaultValue) + VectorObject(length.value, typeArguments[0].defaultValue) } else -> throw Exception("runtime error") From bb748f46455034c2b4ce8c86fadf159b1febc2a6 Mon Sep 17 00:00:00 2001 From: Rolph Recto Date: Wed, 2 Feb 2022 13:32:25 -0500 Subject: [PATCH 2/8] first complete draft of partial evaluator --- .../cs/apl/viaduct/lowering/Flowchart.kt | 3 + .../apl/viaduct/lowering/PartialEvaluator.kt | 244 +++++++++++++++--- 2 files changed, 206 insertions(+), 41 deletions(-) diff --git a/compiler/src/main/kotlin/edu/cornell/cs/apl/viaduct/lowering/Flowchart.kt b/compiler/src/main/kotlin/edu/cornell/cs/apl/viaduct/lowering/Flowchart.kt index 4e9fb6d3b9..463cc1d089 100644 --- a/compiler/src/main/kotlin/edu/cornell/cs/apl/viaduct/lowering/Flowchart.kt +++ b/compiler/src/main/kotlin/edu/cornell/cs/apl/viaduct/lowering/Flowchart.kt @@ -76,6 +76,9 @@ data class GotoIf( object Halt: LoweredControl() typealias BlockLabel = String +data class ResidualBlockLabel(val label: BlockLabel, val store: PartialStore) + +val ENTRY_POINT_LABEL: BlockLabel = "main" data class LoweredBasicBlock( val statements: List, diff --git a/compiler/src/main/kotlin/edu/cornell/cs/apl/viaduct/lowering/PartialEvaluator.kt b/compiler/src/main/kotlin/edu/cornell/cs/apl/viaduct/lowering/PartialEvaluator.kt index 437a254c6e..cdfe143b61 100644 --- a/compiler/src/main/kotlin/edu/cornell/cs/apl/viaduct/lowering/PartialEvaluator.kt +++ b/compiler/src/main/kotlin/edu/cornell/cs/apl/viaduct/lowering/PartialEvaluator.kt @@ -11,12 +11,15 @@ import edu.cornell.cs.apl.viaduct.syntax.datatypes.QueryName import edu.cornell.cs.apl.viaduct.syntax.datatypes.UpdateName import edu.cornell.cs.apl.viaduct.syntax.datatypes.Vector import edu.cornell.cs.apl.viaduct.syntax.types.ValueType +import edu.cornell.cs.apl.viaduct.syntax.values.BooleanValue import edu.cornell.cs.apl.viaduct.syntax.values.Value import edu.cornell.cs.apl.viaduct.syntax.values.IntegerValue -import kotlinx.collections.immutable.PersistentList +import edu.cornell.cs.apl.viaduct.util.FreshNameGenerator import kotlinx.collections.immutable.PersistentMap -import kotlinx.collections.immutable.persistentListOf +import kotlinx.collections.immutable.persistentHashMapOf import kotlinx.collections.immutable.toPersistentList +import java.util.LinkedList +import java.util.Queue sealed class InterpreterObject { abstract fun query(query: QueryName, arguments: List): Value @@ -24,16 +27,6 @@ sealed class InterpreterObject { abstract fun update(update: UpdateName, arguments: List): InterpreterObject } -object NullObject : InterpreterObject() { - override fun query(query: QueryName, arguments: List): Value { - throw Exception("runtime error") - } - - override fun update(update: UpdateName, arguments: List): InterpreterObject { - throw Exception("runtime error") - } -} - data class ImmutableCellObject(val value: Value) : InterpreterObject() { override fun query(query: QueryName, arguments: List): Value { return when (query) { @@ -80,21 +73,22 @@ data class MutableCellObject(var value: Value) : InterpreterObject() { } } -data class VectorObject(val size: Int, defaultValue: Value) : InterpreterObject() { - val values: PersistentList = persistentListOf() - - init { - for (i: Int in 0 until size) { - values = values.add(defaultValue) - } - } +data class VectorObject(val values: PersistentMap): InterpreterObject() { + constructor(size: Int, defaultValue: Value): this( + persistentHashMapOf( + *generateSequence(0) { it+1 } + .take(size) + .map { i -> i to defaultValue } + .toList().toTypedArray() + ) + ) override fun query(query: QueryName, arguments: List): Value { return when (query) { // TODO: fail silently when index is out of bounds is Get -> { - val index = arguments[0] as IntegerValue - values[index.value] + val index = (arguments[0] as IntegerValue).value + values[index]!! } else -> { @@ -104,21 +98,24 @@ data class VectorObject(val size: Int, defaultValue: Value) : InterpreterObject( } override fun update(update: UpdateName, arguments: List): InterpreterObject { - val index = arguments[0] as IntegerValue + val index = (arguments[0] as IntegerValue).value - values[index.value] = when (update) { - is edu.cornell.cs.apl.viaduct.syntax.datatypes.Set -> { - arguments[1] - } + val value = + when (update) { + is edu.cornell.cs.apl.viaduct.syntax.datatypes.Set -> { + arguments[1] + } - is Modify -> { - update.operator.apply(values[index.value], arguments[1]) - } + is Modify -> { + update.operator.apply(values[index]!!, arguments[1]) + } - else -> { - throw Exception("unknown update ${update.name} for vector") + else -> { + throw Exception("unknown update ${update.name} for vector") + } } - } + + return this.copy(values = this.values.put(index, value)) } } @@ -145,8 +142,8 @@ class PartialEvaluator( val program: FlowchartProgram ) { companion object { - fun evaluate(program: FlowchartProgram) { - PartialEvaluator(program).evaluate() + fun evaluate(program: FlowchartProgram): FlowchartProgram { + return PartialEvaluator(program).evaluate() } } @@ -205,6 +202,8 @@ class PartialEvaluator( } } + /* Evaluate a statement in a given partial store, + returning an updated store and a residual statement. */ private fun evaluate(store: PartialStore, stmt: LoweredStatement): Pair { return when (stmt) { is SkipNode -> Pair(store, stmt) @@ -236,15 +235,178 @@ class PartialEvaluator( is UpdateNode -> { val reducedArgs = stmt.arguments.map { arg -> evaluate(store, arg) } val staticArgs = reducedArgs.all { it.isStatic() } - if (staticArgs && store.objectStore.contains(stmt.variable)) { - val valArgs = reducedArgs.map { (it as LiteralNode).value } - store.objectStore[stmt.variable]!!.update(stmt.update, valArgs) - Pair(store, SkipNode) + val staticObject = store.objectStore.contains(stmt.variable) + + when { + // easy case: interpret update as in normal (full) evaluation + staticArgs && staticObject -> { + val valArgs = reducedArgs.map { (it as LiteralNode).value } + store.objectStore[stmt.variable]!!.update(stmt.update, valArgs) + Pair(store, SkipNode) + } + + !staticArgs && staticObject -> { + when (val obj = store.objectStore[stmt.variable]!!) { + is ImmutableCellObject -> + throw Exception("cannot update immutable cell object") + + // for mutable cells, remove cell from partial store + is MutableCellObject -> { + Pair( + store.deleteObject(stmt.variable), + stmt.copy(arguments = reducedArgs.toPersistentList()) + ) + } + + is VectorObject -> { + val reducedIndex = reducedArgs[0] + val reducedRHS = reducedArgs[1] + + when { + // if index is dynamic, remove all entries in vector + !reducedIndex.isStatic() -> { + Pair( + store.updateObject( + stmt.variable, + VectorObject(persistentHashMapOf()) + ), + stmt.copy(arguments = reducedArgs.toPersistentList()) + ) + } + + // if index is static but arg is dynamic, remove single index in vector + reducedIndex.isStatic() && !reducedRHS.isStatic() -> { + val index = ((reducedIndex as LiteralNode).value as IntegerValue).value + Pair( + store.updateObject( + stmt.variable, + obj.copy(values = obj.values.remove(index)) + ), + stmt.copy(arguments = reducedArgs.toPersistentList()) + ) + } + + else -> throw Exception("unreachable state") + } + } + } + } + + // add static object to partial store + // TODO: don't determine class name by number of arguments + staticArgs && !staticObject -> { + when (stmt.update) { + // set can be evaluated because it does not require a read + is edu.cornell.cs.apl.viaduct.syntax.datatypes.Set -> { + if (reducedArgs.size == 1) { // mutable cell + Pair( + store.updateObject( + stmt.variable, + MutableCellObject((reducedArgs[0] as LiteralNode).value) + ), + SkipNode + ) + } else { // vector + val index = ((reducedArgs[0] as LiteralNode).value as IntegerValue).value + val value = (reducedArgs[1] as LiteralNode).value + Pair( + store.updateObject( + stmt.variable, + VectorObject(persistentHashMapOf(index to value)) + ), + SkipNode + ) + } + } + + // modify requires a read so it can't be evaluated + is Modify -> { + Pair(store, stmt.copy(arguments = reducedArgs.toPersistentList())) + } + + else -> throw Exception("unknown update ${stmt.update.name}") + } + } + + // args and object are dynamic, can't be evaluated + else -> { + Pair(store, stmt.copy(arguments = reducedArgs.toPersistentList())) + } + } + } + } - } else { - Pair(store.deleteObject()) + } + + /* Evaluate a basic block starting a given partial store, + returning an updated store, residual block, and labels of successor blocks */ + private fun evaluate(store: PartialStore, block: LoweredBasicBlock): Triple> { + var curStore = store + val residualStmts = mutableListOf() + + // partially execute statements in block + for (stmt in block.statements) { + val (newStore, residualStmt) = evaluate(curStore, stmt) + curStore = newStore + if (residualStmt != SkipNode) { + residualStmts.add(residualStmt) + } + } + + // calculate successors + val successors: Set = + when (val jump = block.jump) { + is Goto -> setOf(jump.label) + is GotoIf -> { + val reducedGuard = evaluate(curStore, jump.guard) + if (reducedGuard.isStatic()) { + val guardValue = ((reducedGuard as LiteralNode).value as BooleanValue).value + setOf(if (guardValue) jump.thenLabel else jump.elseLabel) + + } else { + setOf(jump.thenLabel, jump.elseLabel) + } } + is Halt -> setOf() } + + val residualBlock = block.copy(statements = residualStmts) + return Triple(curStore, residualBlock, successors) + } + + /** Online partial evaluation of a flowchart program. */ + fun evaluate( + initialStore: PartialStore = + PartialStore(objectStore = persistentHashMapOf(), temporaryStore = persistentHashMapOf()) + ): FlowchartProgram { + val initialBlockLabel = ResidualBlockLabel(ENTRY_POINT_LABEL, initialStore) + val residualBlockMap = mutableMapOf() + val worklist: Queue = LinkedList() + worklist.add(initialBlockLabel) + + while (!worklist.isEmpty()) { + val residualBlockLabel = worklist.remove() + val (endStore, residualBlock, successors) = + evaluate(residualBlockLabel.store, program.blocks[residualBlockLabel.label]!!) + residualBlockMap[residualBlockLabel] = residualBlock + worklist.addAll( + successors + .map { label -> ResidualBlockLabel(label, endStore) } + .filter { residualBlockMap.containsKey(it) } + ) } + + // create fresh labels from label-partial store pairs + val nameGenerator = FreshNameGenerator() + val finalBlockMap = mutableMapOf() + finalBlockMap[ENTRY_POINT_LABEL] = residualBlockMap[initialBlockLabel]!! + residualBlockMap.remove(initialBlockLabel) + + for (kv in residualBlockMap) { + val newLabel: BlockLabel = nameGenerator.getFreshName(kv.key.label) + finalBlockMap[newLabel] = kv.value + } + + return FlowchartProgram(blocks = finalBlockMap) } } From 76218b19c6f8b755f0036684ff66668ec90123ee Mon Sep 17 00:00:00 2001 From: Rolph Recto Date: Thu, 3 Feb 2022 19:14:56 -0500 Subject: [PATCH 3/8] first completed version of intermediate -> flowchart lowering transformation --- .idea/runConfigurations.xml | 10 + .../cornell/cs/apl/viaduct/cli/Evaluate.kt | 29 +++ .../edu/cornell/cs/apl/viaduct/cli/Viaduct.kt | 2 +- .../cs/apl/viaduct/lowering/Flowchart.kt | 153 ++++++++++++--- .../lowering/FlowchartOptimizations.kt | 47 +++++ .../cs/apl/viaduct/lowering/LoweringPass.kt | 180 ++++++++++++++++++ .../apl/viaduct/lowering/PartialEvaluator.kt | 107 +++++++---- 7 files changed, 459 insertions(+), 69 deletions(-) create mode 100644 .idea/runConfigurations.xml create mode 100644 cli/src/main/kotlin/edu/cornell/cs/apl/viaduct/cli/Evaluate.kt create mode 100644 compiler/src/main/kotlin/edu/cornell/cs/apl/viaduct/lowering/FlowchartOptimizations.kt create mode 100644 compiler/src/main/kotlin/edu/cornell/cs/apl/viaduct/lowering/LoweringPass.kt diff --git a/.idea/runConfigurations.xml b/.idea/runConfigurations.xml new file mode 100644 index 0000000000..797acea53e --- /dev/null +++ b/.idea/runConfigurations.xml @@ -0,0 +1,10 @@ + + + + + + \ No newline at end of file diff --git a/cli/src/main/kotlin/edu/cornell/cs/apl/viaduct/cli/Evaluate.kt b/cli/src/main/kotlin/edu/cornell/cs/apl/viaduct/cli/Evaluate.kt new file mode 100644 index 0000000000..5c3b561f31 --- /dev/null +++ b/cli/src/main/kotlin/edu/cornell/cs/apl/viaduct/cli/Evaluate.kt @@ -0,0 +1,29 @@ +package edu.cornell.cs.apl.viaduct.cli + +import com.github.ajalt.clikt.core.CliktCommand +import edu.cornell.cs.apl.viaduct.analysis.main +import edu.cornell.cs.apl.viaduct.backends.DefaultCombinedBackend +import edu.cornell.cs.apl.viaduct.lowering.LoweringPass +import edu.cornell.cs.apl.viaduct.lowering.PartialEvaluator +import edu.cornell.cs.apl.viaduct.parsing.parse +import edu.cornell.cs.apl.viaduct.passes.elaborated +import java.io.File + +class Evaluate : CliktCommand(help = "Partially evaluate compiled program.") { + private val input: File? by inputProgram() + + override fun run() { + val program = + input.sourceFile() + .parse(DefaultCombinedBackend.protocolParsers) + .elaborated() + + val flowchart = LoweringPass.lower(program.main.body) + println("original") + println(flowchart.toDocument().print()) + + val residualFlowchart = PartialEvaluator.evaluate(flowchart) + println("residual") + println(residualFlowchart.toDocument().print()) + } +} diff --git a/cli/src/main/kotlin/edu/cornell/cs/apl/viaduct/cli/Viaduct.kt b/cli/src/main/kotlin/edu/cornell/cs/apl/viaduct/cli/Viaduct.kt index 99cad960ad..9be07f335a 100644 --- a/cli/src/main/kotlin/edu/cornell/cs/apl/viaduct/cli/Viaduct.kt +++ b/cli/src/main/kotlin/edu/cornell/cs/apl/viaduct/cli/Viaduct.kt @@ -11,7 +11,7 @@ class Viaduct : NoOpCliktCommand(help = "Compile high level specifications to se init { versionOption(version) - subcommands(Format(), Compile(), CompletionCommand(), Run()) + subcommands(Format(), Compile(), CompletionCommand(), Run(), Evaluate()) // TODO: Help, Interpret, commands } } diff --git a/compiler/src/main/kotlin/edu/cornell/cs/apl/viaduct/lowering/Flowchart.kt b/compiler/src/main/kotlin/edu/cornell/cs/apl/viaduct/lowering/Flowchart.kt index 463cc1d089..70b1fc9100 100644 --- a/compiler/src/main/kotlin/edu/cornell/cs/apl/viaduct/lowering/Flowchart.kt +++ b/compiler/src/main/kotlin/edu/cornell/cs/apl/viaduct/lowering/Flowchart.kt @@ -1,5 +1,12 @@ package edu.cornell.cs.apl.viaduct.lowering +import edu.cornell.cs.apl.prettyprinting.Document +import edu.cornell.cs.apl.prettyprinting.PrettyPrintable +import edu.cornell.cs.apl.prettyprinting.bracketed +import edu.cornell.cs.apl.prettyprinting.concatenated +import edu.cornell.cs.apl.prettyprinting.plus +import edu.cornell.cs.apl.prettyprinting.times +import edu.cornell.cs.apl.prettyprinting.tupled import edu.cornell.cs.apl.viaduct.syntax.Host import edu.cornell.cs.apl.viaduct.syntax.ObjectVariable import edu.cornell.cs.apl.viaduct.syntax.Operator @@ -11,32 +18,48 @@ import edu.cornell.cs.apl.viaduct.syntax.datatypes.UpdateName import edu.cornell.cs.apl.viaduct.syntax.types.ValueType import edu.cornell.cs.apl.viaduct.syntax.values.Value import kotlinx.collections.immutable.PersistentList +import java.util.LinkedList +import java.util.Queue -sealed class LoweredExpression +sealed class LoweredExpression : PrettyPrintable -data class LiteralNode(val value: Value): LoweredExpression() +data class LiteralNode(val value: Value) : LoweredExpression() { + override fun toDocument(): Document = value.toDocument() +} -data class ReadNode(val temporary: Temporary): LoweredExpression() +data class ReadNode(val temporary: Temporary) : LoweredExpression() { + override fun toDocument(): Document = temporary.toDocument() +} data class OperatorApplicationNode( val operator: Operator, val arguments: PersistentList -): LoweredExpression() +) : LoweredExpression() { + override fun toDocument(): Document = operator.toDocument(arguments) +} data class QueryNode( val variable: ObjectVariable, val query: QueryName, val arguments: PersistentList -): LoweredExpression() +) : LoweredExpression() { + override fun toDocument(): Document = + variable + Document(".") + query + arguments.tupled() +} data class InputNode( val type: ValueType, val host: Host -): LoweredExpression() +) : LoweredExpression() { + override fun toDocument(): Document = + Document("input") * type * Document("from") * host +} -sealed class LoweredStatement +sealed class LoweredStatement : PrettyPrintable -object SkipNode: LoweredStatement() +object SkipNode : LoweredStatement() { + override fun toDocument(): Document = Document("skip") +} data class DeclarationNode( val name: ObjectVariable, @@ -44,47 +67,123 @@ data class DeclarationNode( val typeArguments: PersistentList, val arguments: PersistentList, val protocol: Protocol -): LoweredStatement() +) : LoweredStatement() { + override fun toDocument(): Document = + Document("val") * name + Document("@") + protocol * + Document("=") * className + typeArguments.bracketed() + arguments.tupled() +} data class LetNode( val temporary: Temporary, val value: LoweredExpression, val protocol: Protocol -): LoweredStatement() +) : LoweredStatement() { + override fun toDocument(): Document = + Document("let") * temporary + Document("@") + protocol * Document("=") * value +} data class UpdateNode( val variable: ObjectVariable, val update: UpdateName, val arguments: PersistentList -): LoweredStatement() +) : LoweredStatement() { + override fun toDocument(): Document = + variable + Document(".") + update + arguments.tupled() +} data class OutputNode( val message: LoweredExpression, val host: Host -): LoweredStatement() +) : LoweredStatement() { + override fun toDocument(): Document = + Document("output") * message * Document("to") * host +} -sealed class LoweredControl +sealed class LoweredControl : PrettyPrintable { + abstract fun successors(): Set +} -data class Goto(val label: BlockLabel): LoweredControl() +data class Goto(val label: T) : LoweredControl() { + override fun toDocument(): Document = + Document("goto") * label -data class GotoIf( + override fun successors(): Set = setOf(label) +} + +data class GotoIf( val guard: LoweredExpression, - val thenLabel: BlockLabel, - val elseLabel: BlockLabel -): LoweredControl() + val thenLabel: T, + val elseLabel: T +) : LoweredControl() { + override fun toDocument(): Document = + Document("if") * guard * + Document("then goto") * thenLabel * + Document("else goto") * elseLabel + + override fun successors(): Set = setOf(thenLabel, elseLabel) +} + +// HACK! need a singleton paramerized on type T : BlockLabel, +// but Kotlin doesn't allow generic singletons. So instead +// we create two different singletons for each known BlockLabel class + +object RegularHalt : LoweredControl() { + override fun toDocument(): Document = Document("halt") + override fun successors(): Set = setOf() +} + +object ResidualHalt : LoweredControl() { + override fun toDocument(): Document = Document("halt") + override fun successors(): Set = setOf() +} + +sealed class BlockLabel : PrettyPrintable -object Halt: LoweredControl() +data class RegularBlockLabel(val label: String) : BlockLabel() { + override fun toDocument(): Document = Document(label) +} -typealias BlockLabel = String -data class ResidualBlockLabel(val label: BlockLabel, val store: PartialStore) +data class ResidualBlockLabel(val label: RegularBlockLabel, val store: PartialStore) : BlockLabel() { + override fun toDocument(): Document = + listOf(label.toDocument(), Document("store")).tupled() +} -val ENTRY_POINT_LABEL: BlockLabel = "main" +val ENTRY_POINT_LABEL = RegularBlockLabel("main") -data class LoweredBasicBlock( +data class LoweredBasicBlock( val statements: List, - val jump: LoweredControl -) + val jump: LoweredControl +) : PrettyPrintable { + override fun toDocument(): Document = + if (statements.isNotEmpty()) { + statements.concatenated(separator = Document.forcedLineBreak) + Document.forcedLineBreak + jump + } else { + jump.toDocument() + } + + fun successors(): Set = jump.successors() +} data class FlowchartProgram( - val blocks: Map -) + val blocks: Map> +) : PrettyPrintable { + override fun toDocument(): Document { + val blockOrder = mutableListOf() + val worklist: Queue = LinkedList() + worklist.add(ENTRY_POINT_LABEL) + + while (worklist.isNotEmpty()) { + val label = worklist.remove() + blockOrder.add(label) + for (successor in blocks[label]!!.successors()) { + if (!blockOrder.contains(successor)) { + worklist.add(successor) + } + } + } + + return blockOrder.map { label -> + Document("label") * label + ":" + Document.forcedLineBreak + blocks[label]!! + }.concatenated(Document.forcedLineBreak + Document.forcedLineBreak) + } +} diff --git a/compiler/src/main/kotlin/edu/cornell/cs/apl/viaduct/lowering/FlowchartOptimizations.kt b/compiler/src/main/kotlin/edu/cornell/cs/apl/viaduct/lowering/FlowchartOptimizations.kt new file mode 100644 index 0000000000..5d72c0a3e1 --- /dev/null +++ b/compiler/src/main/kotlin/edu/cornell/cs/apl/viaduct/lowering/FlowchartOptimizations.kt @@ -0,0 +1,47 @@ +package edu.cornell.cs.apl.viaduct.lowering + +/* Instead of jumping to an empty block, jump to its successor instead. */ +fun FlowchartProgram.removeEmptyBlocks(): FlowchartProgram { + // find empty blocks with a unique successor + val initEmptyBlockMap: Map = + this.blocks + .filter { kv -> kv.value.statements.isEmpty() && kv.value.jump is Goto } + .map { kv -> kv.key to (kv.value.jump as Goto).label } + .toMap() + + // chain relabelings together + val emptyBlockMap = mutableMapOf() + for (kv in initEmptyBlockMap) { + var relabel = kv.value + while (initEmptyBlockMap.containsKey(relabel)) { + relabel = initEmptyBlockMap[relabel]!! + } + emptyBlockMap[kv.key] = relabel + } + + return FlowchartProgram( + this.blocks + .filter { kv -> !emptyBlockMap.containsKey(kv.key) } + .map { kv -> + val renamedJump = + when (val jump = kv.value.jump) { + is Goto -> emptyBlockMap[jump.label]?.let { Goto(it) } ?: jump + + is GotoIf -> { + val renamedThenLabel = emptyBlockMap[jump.thenLabel] ?: jump.thenLabel + val renamedElseLabel = emptyBlockMap[jump.elseLabel] ?: jump.elseLabel + jump.copy(thenLabel = renamedThenLabel, elseLabel = renamedElseLabel) + } + + RegularHalt -> RegularHalt + + // Kotlin compiler is too dumb to recognize that ResidualHalt + // is not a valid case, so we need this else case + else -> TODO() + } + + kv.key to kv.value.copy(jump = renamedJump) + } + .toMap() + ) +} diff --git a/compiler/src/main/kotlin/edu/cornell/cs/apl/viaduct/lowering/LoweringPass.kt b/compiler/src/main/kotlin/edu/cornell/cs/apl/viaduct/lowering/LoweringPass.kt new file mode 100644 index 0000000000..41b33ea83a --- /dev/null +++ b/compiler/src/main/kotlin/edu/cornell/cs/apl/viaduct/lowering/LoweringPass.kt @@ -0,0 +1,180 @@ +package edu.cornell.cs.apl.viaduct.lowering + +import edu.cornell.cs.apl.viaduct.syntax.JumpLabel +import edu.cornell.cs.apl.viaduct.syntax.intermediate.AssertionNode +import edu.cornell.cs.apl.viaduct.syntax.intermediate.BlockNode +import edu.cornell.cs.apl.viaduct.syntax.intermediate.BreakNode +import edu.cornell.cs.apl.viaduct.syntax.intermediate.DeclarationNode +import edu.cornell.cs.apl.viaduct.syntax.intermediate.DeclassificationNode +import edu.cornell.cs.apl.viaduct.syntax.intermediate.EndorsementNode +import edu.cornell.cs.apl.viaduct.syntax.intermediate.ExpressionNode +import edu.cornell.cs.apl.viaduct.syntax.intermediate.FunctionCallNode +import edu.cornell.cs.apl.viaduct.syntax.intermediate.IfNode +import edu.cornell.cs.apl.viaduct.syntax.intermediate.InfiniteLoopNode +import edu.cornell.cs.apl.viaduct.syntax.intermediate.InputNode +import edu.cornell.cs.apl.viaduct.syntax.intermediate.LetNode +import edu.cornell.cs.apl.viaduct.syntax.intermediate.LiteralNode +import edu.cornell.cs.apl.viaduct.syntax.intermediate.OperatorApplicationNode +import edu.cornell.cs.apl.viaduct.syntax.intermediate.OutParameterInitializationNode +import edu.cornell.cs.apl.viaduct.syntax.intermediate.OutputNode +import edu.cornell.cs.apl.viaduct.syntax.intermediate.QueryNode +import edu.cornell.cs.apl.viaduct.syntax.intermediate.ReadNode +import edu.cornell.cs.apl.viaduct.syntax.intermediate.SimpleStatementNode +import edu.cornell.cs.apl.viaduct.syntax.intermediate.StatementNode +import edu.cornell.cs.apl.viaduct.syntax.intermediate.UpdateNode +import edu.cornell.cs.apl.viaduct.util.FreshNameGenerator +import kotlinx.collections.immutable.toPersistentList +import edu.cornell.cs.apl.viaduct.lowering.DeclarationNode as LDeclarationNode +import edu.cornell.cs.apl.viaduct.lowering.InputNode as LInputNode +import edu.cornell.cs.apl.viaduct.lowering.LetNode as LLetNode +import edu.cornell.cs.apl.viaduct.lowering.LiteralNode as LLiteralNode +import edu.cornell.cs.apl.viaduct.lowering.OperatorApplicationNode as LOperatorApplicationNode +import edu.cornell.cs.apl.viaduct.lowering.OutputNode as LOutputNode +import edu.cornell.cs.apl.viaduct.lowering.QueryNode as LQueryNode +import edu.cornell.cs.apl.viaduct.lowering.ReadNode as LReadNode +import edu.cornell.cs.apl.viaduct.lowering.UpdateNode as LUpdateNode + +class LoweringPass(val block: BlockNode) { + companion object { + fun lower(block: BlockNode): FlowchartProgram { + return LoweringPass(block).lower() + } + } + + private val nameGenerator = FreshNameGenerator() + private val blockMap = mutableMapOf>() + private val breakLabelMap = mutableMapOf() + + private fun freshBlockLabel(): RegularBlockLabel { + return RegularBlockLabel(nameGenerator.getFreshName("block")) + } + + private fun lowerExpression(expr: ExpressionNode): LoweredExpression { + return when (expr) { + is InputNode -> LInputNode(expr.type.value, expr.host.value) + is LiteralNode -> LLiteralNode(expr.value) + is ReadNode -> LReadNode(expr.temporary.value) + is DeclassificationNode -> lowerExpression(expr.expression) + is EndorsementNode -> lowerExpression(expr.expression) + is OperatorApplicationNode -> { + val loweredArgs = expr.arguments.map { lowerExpression(it) } + LOperatorApplicationNode(expr.operator, loweredArgs.toPersistentList()) + } + is QueryNode -> { + val loweredArgs = expr.arguments.map { lowerExpression(it) } + LQueryNode(expr.variable.value, expr.query.value, loweredArgs.toPersistentList()) + } + } + } + + private fun lowerSimpleStatement(stmt: SimpleStatementNode): LoweredStatement? { + return when (stmt) { + is DeclarationNode -> { + val loweredArgs = stmt.arguments.map { lowerExpression(it) } + LDeclarationNode( + stmt.name.value, + stmt.className.value, + stmt.typeArguments.map { it.value }.toPersistentList(), + loweredArgs.toPersistentList(), + stmt.protocol!!.value + ) + } + + is LetNode -> + LLetNode( + stmt.temporary.value, + lowerExpression(stmt.value), + stmt.protocol!!.value + ) + + is OutParameterInitializationNode -> null + + is OutputNode -> + LOutputNode(lowerExpression(stmt.message), stmt.host.value) + + is UpdateNode -> { + val loweredArgs = stmt.arguments.map { lowerExpression(it) } + LUpdateNode( + stmt.variable.value, + stmt.update.value, + loweredArgs.toPersistentList() + ) + } + } + } + + private fun lower( + currentLabel: RegularBlockLabel, + currentBlock: MutableList, + stmt: StatementNode + ): Pair> { + return when (stmt) { + // TODO: make these no-ops for now + is AssertionNode -> Pair(currentLabel, currentBlock) + is FunctionCallNode -> Pair(currentLabel, currentBlock) + is OutParameterInitializationNode -> Pair(currentLabel, currentBlock) + + is SimpleStatementNode -> + lowerSimpleStatement(stmt)?.let { loweredStmt -> + currentBlock.add(loweredStmt) + Pair(currentLabel, currentBlock) + } ?: Pair(currentLabel, currentBlock) + + is BlockNode -> { + var lastLabel = currentLabel + var lastBlock = currentBlock + for (blockStmt in stmt.statements) { + val (outLabel, outBlock) = lower(lastLabel, lastBlock, blockStmt) + lastLabel = outLabel + lastBlock = outBlock + } + + Pair(lastLabel, lastBlock) + } + + is BreakNode -> { + val gotoLabel = breakLabelMap[stmt.jumpLabel.value]!! + blockMap[currentLabel] = LoweredBasicBlock(currentBlock, Goto(gotoLabel)) + return Pair(freshBlockLabel(), mutableListOf()) + } + + is IfNode -> { + val loweredGuard = lowerExpression(stmt.guard) + val thenLabel = freshBlockLabel() + val elseLabel = freshBlockLabel() + val joinLabel = freshBlockLabel() + + val ifJump = GotoIf(loweredGuard, thenLabel, elseLabel) + blockMap[currentLabel] = LoweredBasicBlock(currentBlock, ifJump) + + val (lastThenLabel, lastThenBlock) = lower(thenLabel, mutableListOf(), stmt.thenBranch) + blockMap[lastThenLabel] = LoweredBasicBlock(lastThenBlock, Goto(joinLabel)) + + val (lastElseLabel, lastElseBlock) = lower(elseLabel, mutableListOf(), stmt.elseBranch) + blockMap[lastElseLabel] = LoweredBasicBlock(lastElseBlock, Goto(joinLabel)) + + Pair(joinLabel, mutableListOf()) + } + + is InfiniteLoopNode -> { + val startLabel = freshBlockLabel() + val exitLabel = freshBlockLabel() + + blockMap[currentLabel] = LoweredBasicBlock(currentBlock, Goto(startLabel)) + breakLabelMap[stmt.jumpLabel.value] = exitLabel + + val (lastBodyLabel, lastBodyBlock) = lower(startLabel, mutableListOf(), stmt.body) + breakLabelMap.remove(stmt.jumpLabel.value) + blockMap[lastBodyLabel] = LoweredBasicBlock(lastBodyBlock, Goto(startLabel)) + + Pair(exitLabel, mutableListOf()) + } + } + } + + fun lower(): FlowchartProgram { + val (lastLabel, lastBlock) = lower(ENTRY_POINT_LABEL, mutableListOf(), block) + blockMap[lastLabel] = LoweredBasicBlock(lastBlock, RegularHalt) + return FlowchartProgram(blockMap).removeEmptyBlocks() + } +} diff --git a/compiler/src/main/kotlin/edu/cornell/cs/apl/viaduct/lowering/PartialEvaluator.kt b/compiler/src/main/kotlin/edu/cornell/cs/apl/viaduct/lowering/PartialEvaluator.kt index cdfe143b61..19d1e93756 100644 --- a/compiler/src/main/kotlin/edu/cornell/cs/apl/viaduct/lowering/PartialEvaluator.kt +++ b/compiler/src/main/kotlin/edu/cornell/cs/apl/viaduct/lowering/PartialEvaluator.kt @@ -12,8 +12,8 @@ import edu.cornell.cs.apl.viaduct.syntax.datatypes.UpdateName import edu.cornell.cs.apl.viaduct.syntax.datatypes.Vector import edu.cornell.cs.apl.viaduct.syntax.types.ValueType import edu.cornell.cs.apl.viaduct.syntax.values.BooleanValue -import edu.cornell.cs.apl.viaduct.syntax.values.Value import edu.cornell.cs.apl.viaduct.syntax.values.IntegerValue +import edu.cornell.cs.apl.viaduct.syntax.values.Value import edu.cornell.cs.apl.viaduct.util.FreshNameGenerator import kotlinx.collections.immutable.PersistentMap import kotlinx.collections.immutable.persistentHashMapOf @@ -73,10 +73,10 @@ data class MutableCellObject(var value: Value) : InterpreterObject() { } } -data class VectorObject(val values: PersistentMap): InterpreterObject() { - constructor(size: Int, defaultValue: Value): this( - persistentHashMapOf( - *generateSequence(0) { it+1 } +data class VectorObject(val values: PersistentMap) : InterpreterObject() { + constructor(size: Int, defaultValue: Value) : this( + persistentHashMapOf( + *generateSequence(0) { it + 1 } .take(size) .map { i -> i to defaultValue } .toList().toTypedArray() @@ -147,35 +147,33 @@ class PartialEvaluator( } } - private fun evaluate(store: PartialStore, expr: LoweredExpression): LoweredExpression { + private fun evaluateExpression(store: PartialStore, expr: LoweredExpression): LoweredExpression { return when (expr) { is LiteralNode -> expr is ReadNode -> { - store.temporaryStore[expr.temporary] ?: - throw Error("cannot find temporary ${expr.temporary.name}") + store.temporaryStore[expr.temporary] + ?: throw Error("cannot find temporary ${expr.temporary.name}") } is OperatorApplicationNode -> { - val reducedArgs = expr.arguments.map { evaluate(store, it) } + val reducedArgs = expr.arguments.map { evaluateExpression(store, it) } val staticArgs = reducedArgs.all { it.isStatic() } if (staticArgs) { val valArgs = reducedArgs.map { (it as LiteralNode).value } LiteralNode(expr.operator.apply(valArgs)) - } else { expr.copy(arguments = reducedArgs.toPersistentList()) } } is QueryNode -> { - val reducedArgs = expr.arguments.map { evaluate(store, it) } + val reducedArgs = expr.arguments.map { evaluateExpression(store, it) } val staticArgs = reducedArgs.all { it.isStatic() } if (staticArgs && store.objectStore.contains(expr.variable)) { val valArgs = reducedArgs.map { (it as LiteralNode).value } val queryVal = store.objectStore[expr.variable]!!.query(expr.query, valArgs) LiteralNode(queryVal) - } else { expr.copy(arguments = reducedArgs.toPersistentList()) } @@ -186,7 +184,9 @@ class PartialEvaluator( } private fun buildObject( - className: ClassName, typeArguments: List, arguments: List + className: ClassName, + typeArguments: List, + arguments: List ): InterpreterObject { return when (className) { ImmutableCell -> ImmutableCellObject(arguments[0]) @@ -209,13 +209,12 @@ class PartialEvaluator( is SkipNode -> Pair(store, stmt) is DeclarationNode -> { - val reducedArgs = stmt.arguments.map { arg -> evaluate(store, arg) } + val reducedArgs = stmt.arguments.map { arg -> evaluateExpression(store, arg) } val staticArgs = reducedArgs.all { it.isStatic() } if (staticArgs) { val valArgs = reducedArgs.map { (it as LiteralNode).value } val obj = buildObject(stmt.className, stmt.typeArguments, valArgs) Pair(store.updateObject(stmt.name, obj), SkipNode) - } else { val reducedDecl = stmt.copy(arguments = reducedArgs.toPersistentList()) Pair(store, reducedDecl) @@ -223,17 +222,17 @@ class PartialEvaluator( } is LetNode -> { - val reducedExpr = evaluate(store, stmt.value) + val reducedExpr = evaluateExpression(store, stmt.value) Pair(store.updateTemporary(stmt.temporary, reducedExpr), SkipNode) } is OutputNode -> { - val reducedMsg = evaluate(store, stmt.message) + val reducedMsg = evaluateExpression(store, stmt.message) Pair(store, stmt.copy(message = reducedMsg)) } is UpdateNode -> { - val reducedArgs = stmt.arguments.map { arg -> evaluate(store, arg) } + val reducedArgs = stmt.arguments.map { arg -> evaluateExpression(store, arg) } val staticArgs = reducedArgs.all { it.isStatic() } val staticObject = store.objectStore.contains(stmt.variable) @@ -335,12 +334,14 @@ class PartialEvaluator( } } } - } /* Evaluate a basic block starting a given partial store, returning an updated store, residual block, and labels of successor blocks */ - private fun evaluate(store: PartialStore, block: LoweredBasicBlock): Triple> { + private fun evaluate( + store: PartialStore, + block: LoweredBasicBlock + ): LoweredBasicBlock { var curStore = store val residualStmts = mutableListOf() @@ -354,24 +355,35 @@ class PartialEvaluator( } // calculate successors - val successors: Set = + val residualJump: LoweredControl = when (val jump = block.jump) { - is Goto -> setOf(jump.label) + is Goto -> { + Goto(ResidualBlockLabel(jump.label, curStore)) + } + is GotoIf -> { - val reducedGuard = evaluate(curStore, jump.guard) + val reducedGuard = evaluateExpression(curStore, jump.guard) if (reducedGuard.isStatic()) { val guardValue = ((reducedGuard as LiteralNode).value as BooleanValue).value - setOf(if (guardValue) jump.thenLabel else jump.elseLabel) - + val successor = if (guardValue) jump.thenLabel else jump.elseLabel + Goto(ResidualBlockLabel(successor, curStore)) } else { - setOf(jump.thenLabel, jump.elseLabel) + GotoIf( + reducedGuard, + ResidualBlockLabel(jump.thenLabel, curStore), + ResidualBlockLabel(jump.elseLabel, curStore) + ) } } - is Halt -> setOf() + + is RegularHalt -> ResidualHalt + + // Kotlin compiler is too dumb to recognize that ResidualHalt + // is not a valid case, so we need this else case + else -> TODO() } - val residualBlock = block.copy(statements = residualStmts) - return Triple(curStore, residualBlock, successors) + return LoweredBasicBlock(residualStmts, residualJump) } /** Online partial evaluation of a flowchart program. */ @@ -380,33 +392,46 @@ class PartialEvaluator( PartialStore(objectStore = persistentHashMapOf(), temporaryStore = persistentHashMapOf()) ): FlowchartProgram { val initialBlockLabel = ResidualBlockLabel(ENTRY_POINT_LABEL, initialStore) - val residualBlockMap = mutableMapOf() + val residualBlockMap = mutableMapOf>() val worklist: Queue = LinkedList() worklist.add(initialBlockLabel) while (!worklist.isEmpty()) { val residualBlockLabel = worklist.remove() - val (endStore, residualBlock, successors) = - evaluate(residualBlockLabel.store, program.blocks[residualBlockLabel.label]!!) + val residualBlock = evaluate(residualBlockLabel.store, program.blocks[residualBlockLabel.label]!!) residualBlockMap[residualBlockLabel] = residualBlock + worklist.addAll( - successors - .map { label -> ResidualBlockLabel(label, endStore) } - .filter { residualBlockMap.containsKey(it) } + residualBlock.successors().filter { !residualBlockMap.containsKey(it) } ) } // create fresh labels from label-partial store pairs val nameGenerator = FreshNameGenerator() - val finalBlockMap = mutableMapOf() - finalBlockMap[ENTRY_POINT_LABEL] = residualBlockMap[initialBlockLabel]!! - residualBlockMap.remove(initialBlockLabel) + val finalLabelMap = mutableMapOf() + finalLabelMap[initialBlockLabel] = ENTRY_POINT_LABEL + for (residualLabel in residualBlockMap.keys) { + val newLabel = RegularBlockLabel(nameGenerator.getFreshName(residualLabel.label.label)) + finalLabelMap[residualLabel] = newLabel + } + + val finalBlockMap = mutableMapOf>() for (kv in residualBlockMap) { - val newLabel: BlockLabel = nameGenerator.getFreshName(kv.key.label) - finalBlockMap[newLabel] = kv.value + val relabeledJump: LoweredControl = + when (val jump = kv.value.jump) { + is Goto -> Goto(finalLabelMap[jump.label]!!) + is GotoIf -> GotoIf(jump.guard, finalLabelMap[jump.thenLabel]!!, finalLabelMap[jump.elseLabel]!!) + is ResidualHalt -> RegularHalt + + // Kotlin compiler is too dumb to recognize that RegularHalt + // is not a valid case, so we need this else case + else -> TODO() + } + + finalBlockMap[finalLabelMap[kv.key]!!] = LoweredBasicBlock(kv.value.statements, relabeledJump) } - return FlowchartProgram(blocks = finalBlockMap) + return FlowchartProgram(blocks = finalBlockMap).removeEmptyBlocks() } } From a1528f48564ae4d3ba132ca28e3b737838a1a803 Mon Sep 17 00:00:00 2001 From: Rolph Recto Date: Fri, 4 Feb 2022 12:32:54 -0500 Subject: [PATCH 4/8] first working version of partial evaluation + optimizations for removing empty blocks and inlining blocks --- .../cornell/cs/apl/viaduct/cli/Evaluate.kt | 7 +- .../cs/apl/viaduct/lowering/Flowchart.kt | 33 +++++++- .../lowering/FlowchartOptimizations.kt | 79 ++++++++++++++++--- .../cs/apl/viaduct/lowering/LoweringPass.kt | 2 +- .../apl/viaduct/lowering/PartialEvaluator.kt | 28 +++++-- 5 files changed, 127 insertions(+), 22 deletions(-) diff --git a/cli/src/main/kotlin/edu/cornell/cs/apl/viaduct/cli/Evaluate.kt b/cli/src/main/kotlin/edu/cornell/cs/apl/viaduct/cli/Evaluate.kt index 5c3b561f31..b3004865e1 100644 --- a/cli/src/main/kotlin/edu/cornell/cs/apl/viaduct/cli/Evaluate.kt +++ b/cli/src/main/kotlin/edu/cornell/cs/apl/viaduct/cli/Evaluate.kt @@ -5,6 +5,7 @@ import edu.cornell.cs.apl.viaduct.analysis.main import edu.cornell.cs.apl.viaduct.backends.DefaultCombinedBackend import edu.cornell.cs.apl.viaduct.lowering.LoweringPass import edu.cornell.cs.apl.viaduct.lowering.PartialEvaluator +import edu.cornell.cs.apl.viaduct.lowering.optimize import edu.cornell.cs.apl.viaduct.parsing.parse import edu.cornell.cs.apl.viaduct.passes.elaborated import java.io.File @@ -18,12 +19,12 @@ class Evaluate : CliktCommand(help = "Partially evaluate compiled program.") { .parse(DefaultCombinedBackend.protocolParsers) .elaborated() - val flowchart = LoweringPass.lower(program.main.body) + val flowchart = LoweringPass.lower(program.main.body).optimize() println("original") println(flowchart.toDocument().print()) - val residualFlowchart = PartialEvaluator.evaluate(flowchart) - println("residual") + val residualFlowchart = PartialEvaluator.evaluate(flowchart).optimize() + println("\nresidual") println(residualFlowchart.toDocument().print()) } } diff --git a/compiler/src/main/kotlin/edu/cornell/cs/apl/viaduct/lowering/Flowchart.kt b/compiler/src/main/kotlin/edu/cornell/cs/apl/viaduct/lowering/Flowchart.kt index 70b1fc9100..e16a5184d1 100644 --- a/compiler/src/main/kotlin/edu/cornell/cs/apl/viaduct/lowering/Flowchart.kt +++ b/compiler/src/main/kotlin/edu/cornell/cs/apl/viaduct/lowering/Flowchart.kt @@ -1,5 +1,6 @@ package edu.cornell.cs.apl.viaduct.lowering +import com.ibm.icu.text.CaseMap import edu.cornell.cs.apl.prettyprinting.Document import edu.cornell.cs.apl.prettyprinting.PrettyPrintable import edu.cornell.cs.apl.prettyprinting.bracketed @@ -145,7 +146,7 @@ data class RegularBlockLabel(val label: String) : BlockLabel() { data class ResidualBlockLabel(val label: RegularBlockLabel, val store: PartialStore) : BlockLabel() { override fun toDocument(): Document = - listOf(label.toDocument(), Document("store")).tupled() + listOf(label.toDocument(), store).tupled() } val ENTRY_POINT_LABEL = RegularBlockLabel("main") @@ -167,6 +168,36 @@ data class LoweredBasicBlock( data class FlowchartProgram( val blocks: Map> ) : PrettyPrintable { + /** Return the entry point basic block for the program. */ + val entryPointBlock: LoweredBasicBlock = + blocks[ENTRY_POINT_LABEL]!! + + /** Adjecency list representation of the program's CFG. */ + val successorMap: Map> by lazy { + blocks.map { kv -> kv.key to kv.value.successors() }.toMap() + } + + + /** Like successorMap, but for predecessors. */ + val predecessorMap: Map> by lazy { + val predecessors = mutableMapOf>() + for (kv in successorMap) { + for (successor in kv.value) { + if (predecessors.containsKey(successor)) { + predecessors[successor]!!.add(kv.key) + + } else { + predecessors[successor] = mutableSetOf(kv.key) + } + } + } + + predecessors + } + + fun block(label: RegularBlockLabel): LoweredBasicBlock? = + blocks[label] + override fun toDocument(): Document { val blockOrder = mutableListOf() val worklist: Queue = LinkedList() diff --git a/compiler/src/main/kotlin/edu/cornell/cs/apl/viaduct/lowering/FlowchartOptimizations.kt b/compiler/src/main/kotlin/edu/cornell/cs/apl/viaduct/lowering/FlowchartOptimizations.kt index 5d72c0a3e1..81487f66a5 100644 --- a/compiler/src/main/kotlin/edu/cornell/cs/apl/viaduct/lowering/FlowchartOptimizations.kt +++ b/compiler/src/main/kotlin/edu/cornell/cs/apl/viaduct/lowering/FlowchartOptimizations.kt @@ -1,35 +1,47 @@ package edu.cornell.cs.apl.viaduct.lowering +import java.util.LinkedList +import java.util.Queue +import java.util.concurrent.Flow + /* Instead of jumping to an empty block, jump to its successor instead. */ fun FlowchartProgram.removeEmptyBlocks(): FlowchartProgram { // find empty blocks with a unique successor - val initEmptyBlockMap: Map = + val emptyBlockMap: Map = this.blocks .filter { kv -> kv.value.statements.isEmpty() && kv.value.jump is Goto } .map { kv -> kv.key to (kv.value.jump as Goto).label } .toMap() // chain relabelings together - val emptyBlockMap = mutableMapOf() - for (kv in initEmptyBlockMap) { + val jumpRelabelMap = mutableMapOf() + val blockRelableMap = mutableMapOf() + for (kv in emptyBlockMap) { var relabel = kv.value - while (initEmptyBlockMap.containsKey(relabel)) { - relabel = initEmptyBlockMap[relabel]!! + while (emptyBlockMap.containsKey(relabel)) { + relabel = emptyBlockMap[relabel]!! } - emptyBlockMap[kv.key] = relabel + jumpRelabelMap[kv.key] = relabel + } + + // if entry point is removed, new entry point is the block it points to + if (jumpRelabelMap.containsKey(ENTRY_POINT_LABEL)) { + val newEntryPointLabel = jumpRelabelMap[ENTRY_POINT_LABEL]!! + jumpRelabelMap[newEntryPointLabel] = ENTRY_POINT_LABEL + blockRelableMap[newEntryPointLabel] = ENTRY_POINT_LABEL } - return FlowchartProgram( + val newBlocks = this.blocks .filter { kv -> !emptyBlockMap.containsKey(kv.key) } .map { kv -> val renamedJump = when (val jump = kv.value.jump) { - is Goto -> emptyBlockMap[jump.label]?.let { Goto(it) } ?: jump + is Goto -> jumpRelabelMap[jump.label]?.let { Goto(it) } ?: jump is GotoIf -> { - val renamedThenLabel = emptyBlockMap[jump.thenLabel] ?: jump.thenLabel - val renamedElseLabel = emptyBlockMap[jump.elseLabel] ?: jump.elseLabel + val renamedThenLabel = jumpRelabelMap[jump.thenLabel] ?: jump.thenLabel + val renamedElseLabel = jumpRelabelMap[jump.elseLabel] ?: jump.elseLabel jump.copy(thenLabel = renamedThenLabel, elseLabel = renamedElseLabel) } @@ -40,8 +52,51 @@ fun FlowchartProgram.removeEmptyBlocks(): FlowchartProgram { else -> TODO() } - kv.key to kv.value.copy(jump = renamedJump) + val newLabel = blockRelableMap[kv.key] ?: kv.key + + newLabel to kv.value.copy(jump = renamedJump) } .toMap() - ) + + return FlowchartProgram(newBlocks) +} + +/** Inline "linear" set of blocks + * e.g. a CFG A -> B -> C should just be one block A */ +fun FlowchartProgram.inlineBlocks(): FlowchartProgram { + val newBlocks = mutableMapOf>() + + val worklist: Queue = LinkedList() + worklist.add(ENTRY_POINT_LABEL) + + while (worklist.isNotEmpty()) { + val label = worklist.remove() + var curBlock = this.block(label)!! + val curStmts = mutableListOf() + + while (true) { + curStmts.addAll(curBlock.statements) + + val jump = curBlock.jump + if (jump is Goto) { + // if the current block is the only predecessor for the successor, + // inline the successor + if (this.predecessorMap[jump.label]!!.size == 1) { + curBlock = this.block(jump.label)!! + + } else break + } else break + } + + newBlocks[label] = LoweredBasicBlock(curStmts, curBlock.jump) + worklist.addAll( + curBlock.successors().filter { !newBlocks.containsKey(it) } + ) + } + + return FlowchartProgram(newBlocks) +} + +fun FlowchartProgram.optimize(): FlowchartProgram { + return this.removeEmptyBlocks().inlineBlocks() } diff --git a/compiler/src/main/kotlin/edu/cornell/cs/apl/viaduct/lowering/LoweringPass.kt b/compiler/src/main/kotlin/edu/cornell/cs/apl/viaduct/lowering/LoweringPass.kt index 41b33ea83a..030221996d 100644 --- a/compiler/src/main/kotlin/edu/cornell/cs/apl/viaduct/lowering/LoweringPass.kt +++ b/compiler/src/main/kotlin/edu/cornell/cs/apl/viaduct/lowering/LoweringPass.kt @@ -175,6 +175,6 @@ class LoweringPass(val block: BlockNode) { fun lower(): FlowchartProgram { val (lastLabel, lastBlock) = lower(ENTRY_POINT_LABEL, mutableListOf(), block) blockMap[lastLabel] = LoweredBasicBlock(lastBlock, RegularHalt) - return FlowchartProgram(blockMap).removeEmptyBlocks() + return FlowchartProgram(blockMap) } } diff --git a/compiler/src/main/kotlin/edu/cornell/cs/apl/viaduct/lowering/PartialEvaluator.kt b/compiler/src/main/kotlin/edu/cornell/cs/apl/viaduct/lowering/PartialEvaluator.kt index 19d1e93756..69ebd85c42 100644 --- a/compiler/src/main/kotlin/edu/cornell/cs/apl/viaduct/lowering/PartialEvaluator.kt +++ b/compiler/src/main/kotlin/edu/cornell/cs/apl/viaduct/lowering/PartialEvaluator.kt @@ -1,5 +1,10 @@ package edu.cornell.cs.apl.viaduct.lowering +import edu.cornell.cs.apl.prettyprinting.Document +import edu.cornell.cs.apl.prettyprinting.PrettyPrintable +import edu.cornell.cs.apl.prettyprinting.bracketed +import edu.cornell.cs.apl.prettyprinting.plus +import edu.cornell.cs.apl.prettyprinting.times import edu.cornell.cs.apl.viaduct.syntax.ObjectVariable import edu.cornell.cs.apl.viaduct.syntax.Temporary import edu.cornell.cs.apl.viaduct.syntax.datatypes.ClassName @@ -21,7 +26,7 @@ import kotlinx.collections.immutable.toPersistentList import java.util.LinkedList import java.util.Queue -sealed class InterpreterObject { +sealed class InterpreterObject: PrettyPrintable { abstract fun query(query: QueryName, arguments: List): Value abstract fun update(update: UpdateName, arguments: List): InterpreterObject @@ -41,6 +46,8 @@ data class ImmutableCellObject(val value: Value) : InterpreterObject() { override fun update(update: UpdateName, arguments: List): InterpreterObject { throw Exception("cannot update immutable cell") } + + override fun toDocument(): Document = value.toDocument() } data class MutableCellObject(var value: Value) : InterpreterObject() { @@ -71,6 +78,8 @@ data class MutableCellObject(var value: Value) : InterpreterObject() { } ) } + + override fun toDocument(): Document = value.toDocument() } data class VectorObject(val values: PersistentMap) : InterpreterObject() { @@ -117,12 +126,17 @@ data class VectorObject(val values: PersistentMap) : InterpreterObje return this.copy(values = this.values.put(index, value)) } + + override fun toDocument(): Document = + values.map { kv -> + Document(kv.key.toString()) + Document(":") * kv.value.toDocument() + }.bracketed() } data class PartialStore( val objectStore: PersistentMap, val temporaryStore: PersistentMap -) { +): PrettyPrintable { fun updateObject(variable: ObjectVariable, obj: InterpreterObject): PartialStore { return this.copy(objectStore = objectStore.put(variable, obj)) } @@ -134,6 +148,10 @@ data class PartialStore( fun updateTemporary(temporary: Temporary, expr: LoweredExpression): PartialStore { return this.copy(temporaryStore = temporaryStore.put(temporary, expr)) } + + override fun toDocument(): Document = + objectStore.map { kv -> Document(kv.key.name) * Document("=>") * kv.value }.bracketed() * + temporaryStore.map { kv -> Document(kv.key.name) * Document("=>") * kv.value }.bracketed() } private fun LoweredExpression.isStatic(): Boolean = this is LiteralNode @@ -240,8 +258,8 @@ class PartialEvaluator( // easy case: interpret update as in normal (full) evaluation staticArgs && staticObject -> { val valArgs = reducedArgs.map { (it as LiteralNode).value } - store.objectStore[stmt.variable]!!.update(stmt.update, valArgs) - Pair(store, SkipNode) + val updatedObj = store.objectStore[stmt.variable]!!.update(stmt.update, valArgs) + Pair(store.updateObject(stmt.variable, updatedObj), SkipNode) } !staticArgs && staticObject -> { @@ -432,6 +450,6 @@ class PartialEvaluator( finalBlockMap[finalLabelMap[kv.key]!!] = LoweredBasicBlock(kv.value.statements, relabeledJump) } - return FlowchartProgram(blocks = finalBlockMap).removeEmptyBlocks() + return FlowchartProgram(blocks = finalBlockMap) } } From 63a3fe70b36c694971d875890dcc721b7b82df0e Mon Sep 17 00:00:00 2001 From: Rolph Recto Date: Fri, 4 Feb 2022 20:00:03 -0500 Subject: [PATCH 5/8] dominator and postdominator analysis --- .../cornell/cs/apl/viaduct/cli/Evaluate.kt | 15 +++- .../apl/viaduct/lowering/DominatorAnalysis.kt | 70 +++++++++++++++++++ .../cs/apl/viaduct/lowering/Flowchart.kt | 18 +++-- .../lowering/FlowchartOptimizations.kt | 1 - .../cs/apl/viaduct/lowering/LoweringPass.kt | 21 +++--- 5 files changed, 107 insertions(+), 18 deletions(-) create mode 100644 compiler/src/main/kotlin/edu/cornell/cs/apl/viaduct/lowering/DominatorAnalysis.kt diff --git a/cli/src/main/kotlin/edu/cornell/cs/apl/viaduct/cli/Evaluate.kt b/cli/src/main/kotlin/edu/cornell/cs/apl/viaduct/cli/Evaluate.kt index b3004865e1..17a518497e 100644 --- a/cli/src/main/kotlin/edu/cornell/cs/apl/viaduct/cli/Evaluate.kt +++ b/cli/src/main/kotlin/edu/cornell/cs/apl/viaduct/cli/Evaluate.kt @@ -5,6 +5,8 @@ import edu.cornell.cs.apl.viaduct.analysis.main import edu.cornell.cs.apl.viaduct.backends.DefaultCombinedBackend import edu.cornell.cs.apl.viaduct.lowering.LoweringPass import edu.cornell.cs.apl.viaduct.lowering.PartialEvaluator +import edu.cornell.cs.apl.viaduct.lowering.computeDominators +import edu.cornell.cs.apl.viaduct.lowering.computePostdominators import edu.cornell.cs.apl.viaduct.lowering.optimize import edu.cornell.cs.apl.viaduct.parsing.parse import edu.cornell.cs.apl.viaduct.passes.elaborated @@ -19,10 +21,21 @@ class Evaluate : CliktCommand(help = "Partially evaluate compiled program.") { .parse(DefaultCombinedBackend.protocolParsers) .elaborated() - val flowchart = LoweringPass.lower(program.main.body).optimize() + val flowchart = LoweringPass.get(program).flowchartProgram.optimize() println("original") println(flowchart.toDocument().print()) + + println("dominators") + for (kv in flowchart.computeDominators()) { + println("${kv.key} => ${kv.value}") + } + + println("postdominators") + for (kv in flowchart.computePostdominators()) { + println("${kv.key} => ${kv.value}") + } + val residualFlowchart = PartialEvaluator.evaluate(flowchart).optimize() println("\nresidual") println(residualFlowchart.toDocument().print()) diff --git a/compiler/src/main/kotlin/edu/cornell/cs/apl/viaduct/lowering/DominatorAnalysis.kt b/compiler/src/main/kotlin/edu/cornell/cs/apl/viaduct/lowering/DominatorAnalysis.kt new file mode 100644 index 0000000000..9192232bad --- /dev/null +++ b/compiler/src/main/kotlin/edu/cornell/cs/apl/viaduct/lowering/DominatorAnalysis.kt @@ -0,0 +1,70 @@ +package edu.cornell.cs.apl.viaduct.lowering + +import edu.cornell.cs.apl.viaduct.algebra.MeetSemiLattice +import edu.cornell.cs.apl.viaduct.util.dataflow.DataFlowNode +import edu.cornell.cs.apl.viaduct.util.dataflow.IdentityEdge +import edu.cornell.cs.apl.viaduct.util.dataflow.solveDataFlow +import kotlinx.collections.immutable.PersistentSet +import kotlinx.collections.immutable.intersect +import kotlinx.collections.immutable.persistentSetOf +import kotlinx.collections.immutable.toPersistentSet +import org.jgrapht.graph.DefaultDirectedGraph + +private data class DataFlowSet(val set: PersistentSet): MeetSemiLattice>{ + override fun meet(that: DataFlowSet): DataFlowSet { + return DataFlowSet(this.set.intersect(that.set)) + } + + override fun lessThanOrEqualTo(that: DataFlowSet): Boolean = + this.set.containsAll(that.set) +} + +private data class DominatorBlockNode( + val label: RegularBlockLabel, + val isEntryPoint: Boolean +): DataFlowNode> { + override fun transfer(input: DataFlowSet): DataFlowSet { + // recall that the top element is all labels; if we don't special case entry points, + // then the analysis will compute top for all nodes + return if (isEntryPoint) { + DataFlowSet(persistentSetOf(label)) + + } else { + DataFlowSet(input.set.add(label)) + } + } +} + +private fun computeDominators( + successorMap: Map>, + entryPoints: Set +): Map> { + val graph = DefaultDirectedGraph>>(null, null, false) + val nodeMap = successorMap.keys.associateWith { label -> + DominatorBlockNode(label, entryPoints.contains(label)) + } + + for (kv in successorMap) { + val src = nodeMap[kv.key]!! + graph.addVertex(src) + + for (successor in kv.value) { + val dst = nodeMap[successor]!! + val edge = IdentityEdge>() + graph.addVertex(dst) + graph.addEdge(src, dst, edge) + } + } + + return solveDataFlow(DataFlowSet(nodeMap.keys.toPersistentSet()), graph).map { kv -> + kv.key.label to kv.value.set + }.toMap() +} + +fun FlowchartProgram.computeDominators(): Map> { + return computeDominators(this.successorMap, setOf(ENTRY_POINT_LABEL)) +} + +fun FlowchartProgram.computePostdominators(): Map> { + return computeDominators(this.predecessorMap, this.exitPoints) +} diff --git a/compiler/src/main/kotlin/edu/cornell/cs/apl/viaduct/lowering/Flowchart.kt b/compiler/src/main/kotlin/edu/cornell/cs/apl/viaduct/lowering/Flowchart.kt index e16a5184d1..f4aa9ef8c9 100644 --- a/compiler/src/main/kotlin/edu/cornell/cs/apl/viaduct/lowering/Flowchart.kt +++ b/compiler/src/main/kotlin/edu/cornell/cs/apl/viaduct/lowering/Flowchart.kt @@ -177,24 +177,28 @@ data class FlowchartProgram( blocks.map { kv -> kv.key to kv.value.successors() }.toMap() } - /** Like successorMap, but for predecessors. */ val predecessorMap: Map> by lazy { val predecessors = mutableMapOf>() + + for (label in successorMap.keys) { + predecessors[label] = mutableSetOf() + } + for (kv in successorMap) { for (successor in kv.value) { - if (predecessors.containsKey(successor)) { - predecessors[successor]!!.add(kv.key) - - } else { - predecessors[successor] = mutableSetOf(kv.key) - } + predecessors[successor]!!.add(kv.key) } } predecessors } + /** The set of basic blocks that transition to HALT and exit the program. */ + val exitPoints: Set by lazy { + blocks.filter { kv -> kv.value.jump is RegularHalt }.map { kv -> kv.key }.toSet() + } + fun block(label: RegularBlockLabel): LoweredBasicBlock? = blocks[label] diff --git a/compiler/src/main/kotlin/edu/cornell/cs/apl/viaduct/lowering/FlowchartOptimizations.kt b/compiler/src/main/kotlin/edu/cornell/cs/apl/viaduct/lowering/FlowchartOptimizations.kt index 81487f66a5..b0edebe084 100644 --- a/compiler/src/main/kotlin/edu/cornell/cs/apl/viaduct/lowering/FlowchartOptimizations.kt +++ b/compiler/src/main/kotlin/edu/cornell/cs/apl/viaduct/lowering/FlowchartOptimizations.kt @@ -2,7 +2,6 @@ package edu.cornell.cs.apl.viaduct.lowering import java.util.LinkedList import java.util.Queue -import java.util.concurrent.Flow /* Instead of jumping to an empty block, jump to its successor instead. */ fun FlowchartProgram.removeEmptyBlocks(): FlowchartProgram { diff --git a/compiler/src/main/kotlin/edu/cornell/cs/apl/viaduct/lowering/LoweringPass.kt b/compiler/src/main/kotlin/edu/cornell/cs/apl/viaduct/lowering/LoweringPass.kt index 030221996d..b0ff65b603 100644 --- a/compiler/src/main/kotlin/edu/cornell/cs/apl/viaduct/lowering/LoweringPass.kt +++ b/compiler/src/main/kotlin/edu/cornell/cs/apl/viaduct/lowering/LoweringPass.kt @@ -1,5 +1,7 @@ package edu.cornell.cs.apl.viaduct.lowering +import edu.cornell.cs.apl.viaduct.analysis.AnalysisProvider +import edu.cornell.cs.apl.viaduct.analysis.main import edu.cornell.cs.apl.viaduct.syntax.JumpLabel import edu.cornell.cs.apl.viaduct.syntax.intermediate.AssertionNode import edu.cornell.cs.apl.viaduct.syntax.intermediate.BlockNode @@ -17,6 +19,7 @@ import edu.cornell.cs.apl.viaduct.syntax.intermediate.LiteralNode import edu.cornell.cs.apl.viaduct.syntax.intermediate.OperatorApplicationNode import edu.cornell.cs.apl.viaduct.syntax.intermediate.OutParameterInitializationNode import edu.cornell.cs.apl.viaduct.syntax.intermediate.OutputNode +import edu.cornell.cs.apl.viaduct.syntax.intermediate.ProgramNode import edu.cornell.cs.apl.viaduct.syntax.intermediate.QueryNode import edu.cornell.cs.apl.viaduct.syntax.intermediate.ReadNode import edu.cornell.cs.apl.viaduct.syntax.intermediate.SimpleStatementNode @@ -34,13 +37,7 @@ import edu.cornell.cs.apl.viaduct.lowering.QueryNode as LQueryNode import edu.cornell.cs.apl.viaduct.lowering.ReadNode as LReadNode import edu.cornell.cs.apl.viaduct.lowering.UpdateNode as LUpdateNode -class LoweringPass(val block: BlockNode) { - companion object { - fun lower(block: BlockNode): FlowchartProgram { - return LoweringPass(block).lower() - } - } - +class LoweringPass private constructor (val block: BlockNode) { private val nameGenerator = FreshNameGenerator() private val blockMap = mutableMapOf>() private val breakLabelMap = mutableMapOf() @@ -172,9 +169,15 @@ class LoweringPass(val block: BlockNode) { } } - fun lower(): FlowchartProgram { + val flowchartProgram: FlowchartProgram by lazy { val (lastLabel, lastBlock) = lower(ENTRY_POINT_LABEL, mutableListOf(), block) blockMap[lastLabel] = LoweredBasicBlock(lastBlock, RegularHalt) - return FlowchartProgram(blockMap) + FlowchartProgram(blockMap) + } + + companion object : AnalysisProvider { + private fun construct(program: ProgramNode) = LoweringPass(program.main.body) + + override fun get(program: ProgramNode): LoweringPass = program.cached(::construct) } } From ccff258861c102544e16d3619f87fa9e92c1913a Mon Sep 17 00:00:00 2001 From: Rolph Recto Date: Sat, 5 Feb 2022 19:38:20 -0500 Subject: [PATCH 6/8] binding time analysis --- .../cornell/cs/apl/viaduct/cli/Evaluate.kt | 8 +- .../viaduct/lowering/BindingTimeAnalysis.kt | 184 ++++++++++++++++++ .../apl/viaduct/lowering/DominatorAnalysis.kt | 65 +++++-- .../cs/apl/viaduct/lowering/Flowchart.kt | 3 +- .../lowering/FlowchartOptimizations.kt | 1 - .../cs/apl/viaduct/lowering/LoweringPass.kt | 2 +- .../apl/viaduct/lowering/PartialEvaluator.kt | 14 +- 7 files changed, 250 insertions(+), 27 deletions(-) create mode 100644 compiler/src/main/kotlin/edu/cornell/cs/apl/viaduct/lowering/BindingTimeAnalysis.kt diff --git a/cli/src/main/kotlin/edu/cornell/cs/apl/viaduct/cli/Evaluate.kt b/cli/src/main/kotlin/edu/cornell/cs/apl/viaduct/cli/Evaluate.kt index 17a518497e..726c551067 100644 --- a/cli/src/main/kotlin/edu/cornell/cs/apl/viaduct/cli/Evaluate.kt +++ b/cli/src/main/kotlin/edu/cornell/cs/apl/viaduct/cli/Evaluate.kt @@ -1,12 +1,12 @@ package edu.cornell.cs.apl.viaduct.cli import com.github.ajalt.clikt.core.CliktCommand -import edu.cornell.cs.apl.viaduct.analysis.main import edu.cornell.cs.apl.viaduct.backends.DefaultCombinedBackend import edu.cornell.cs.apl.viaduct.lowering.LoweringPass import edu.cornell.cs.apl.viaduct.lowering.PartialEvaluator import edu.cornell.cs.apl.viaduct.lowering.computeDominators import edu.cornell.cs.apl.viaduct.lowering.computePostdominators +import edu.cornell.cs.apl.viaduct.lowering.computeTransitiveSuccessors import edu.cornell.cs.apl.viaduct.lowering.optimize import edu.cornell.cs.apl.viaduct.parsing.parse import edu.cornell.cs.apl.viaduct.passes.elaborated @@ -25,7 +25,6 @@ class Evaluate : CliktCommand(help = "Partially evaluate compiled program.") { println("original") println(flowchart.toDocument().print()) - println("dominators") for (kv in flowchart.computeDominators()) { println("${kv.key} => ${kv.value}") @@ -36,6 +35,11 @@ class Evaluate : CliktCommand(help = "Partially evaluate compiled program.") { println("${kv.key} => ${kv.value}") } + println("transitive successors") + for (kv in flowchart.computeTransitiveSuccessors()) { + println("${kv.key} => ${kv.value}") + } + val residualFlowchart = PartialEvaluator.evaluate(flowchart).optimize() println("\nresidual") println(residualFlowchart.toDocument().print()) diff --git a/compiler/src/main/kotlin/edu/cornell/cs/apl/viaduct/lowering/BindingTimeAnalysis.kt b/compiler/src/main/kotlin/edu/cornell/cs/apl/viaduct/lowering/BindingTimeAnalysis.kt new file mode 100644 index 0000000000..6f154475df --- /dev/null +++ b/compiler/src/main/kotlin/edu/cornell/cs/apl/viaduct/lowering/BindingTimeAnalysis.kt @@ -0,0 +1,184 @@ +package edu.cornell.cs.apl.viaduct.lowering + +import edu.cornell.cs.apl.viaduct.algebra.MeetSemiLattice +import edu.cornell.cs.apl.viaduct.syntax.ObjectVariable +import edu.cornell.cs.apl.viaduct.syntax.Temporary +import edu.cornell.cs.apl.viaduct.util.FreshNameGenerator +import edu.cornell.cs.apl.viaduct.util.dataflow.DataFlowNode +import edu.cornell.cs.apl.viaduct.util.dataflow.IdentityEdge +import edu.cornell.cs.apl.viaduct.util.dataflow.solveDataFlow +import org.jgrapht.graph.DefaultDirectedGraph + +enum class BindingTime : MeetSemiLattice { + STATIC, DYNAMIC; + + override fun meet(that: BindingTime): BindingTime = + if (this == DYNAMIC || that == DYNAMIC) DYNAMIC else STATIC + + override fun lessThanOrEqualTo(that: BindingTime): Boolean = + this == DYNAMIC || that == STATIC +} + +sealed class BindingTimeAnalysisNode : DataFlowNode + +data class ObjectBindingTimeNode(val variable: ObjectVariable) : BindingTimeAnalysisNode() { + override fun transfer(input: BindingTime): BindingTime = input +} + +data class TemporaryBindingTimeNode(val temporary: Temporary) : BindingTimeAnalysisNode() { + override fun transfer(input: BindingTime): BindingTime = input +} + +data class BlockBindingTimeNode(val block: RegularBlockLabel) : BindingTimeAnalysisNode() { + override fun transfer(input: BindingTime): BindingTime = input +} + +data class ConstantBindingTimeNode(val value: BindingTime) : BindingTimeAnalysisNode() { + override fun transfer(input: BindingTime): BindingTime = value +} + +data class VariableBindingTimeNode(val name: String) : BindingTimeAnalysisNode() { + override fun transfer(input: BindingTime): BindingTime = input +} + +class BindingTimeAnalysis private constructor(val program: FlowchartProgram) { + companion object { + fun computeBindingTime(program: FlowchartProgram): Map { + return BindingTimeAnalysis(program).computeObjectBindingTime() + } + } + + private val graph = DefaultDirectedGraph>(null, null, false) + private val nameGenerator = FreshNameGenerator() + + private val objectNodes = mutableMapOf() + private val temporaryNodes = mutableMapOf() + private val blockNodes = mutableMapOf() + + private fun objectNode(variable: ObjectVariable): ObjectBindingTimeNode = + objectNodes.getOrPut(variable) { ObjectBindingTimeNode(variable) } + + private fun temporaryNode(temporary: Temporary): TemporaryBindingTimeNode = + temporaryNodes.getOrPut(temporary) { TemporaryBindingTimeNode(temporary) } + + private fun blockNode(label: RegularBlockLabel): BlockBindingTimeNode = + blockNodes.getOrPut(label) { BlockBindingTimeNode(label) } + + private fun getFreshVariable(): VariableBindingTimeNode { + return VariableBindingTimeNode(nameGenerator.getFreshName("var")) + } + + private fun flowsTo(lhs: BindingTimeAnalysisNode, rhs: BindingTimeAnalysisNode) { + graph.addVertex(lhs) + graph.addVertex(rhs) + graph.addEdge(lhs, rhs, IdentityEdge()) + } + + private fun generateBindingTimeConstraints(expr: LoweredExpression): BindingTimeAnalysisNode = + when (expr) { + is InputNode -> ConstantBindingTimeNode(BindingTime.DYNAMIC) + + is LiteralNode -> ConstantBindingTimeNode(BindingTime.STATIC) + + is OperatorApplicationNode -> { + val node = getFreshVariable() + + expr.arguments.map { + generateBindingTimeConstraints(it) + }.forEach { argNode -> flowsTo(argNode, node) } + + node + } + + is QueryNode -> { + val node = getFreshVariable() + + flowsTo(objectNode(expr.variable), node) + expr.arguments.map { + generateBindingTimeConstraints(it) + }.forEach { argNode -> flowsTo(argNode, node) } + + node + } + + is ReadNode -> temporaryNode(expr.temporary) + } + + private fun generateBindingTimeConstraints(block: RegularBlockLabel, stmt: LoweredStatement) { + when (stmt) { + is DeclarationNode -> { + val node = objectNode(stmt.name) + + stmt.arguments.map { + generateBindingTimeConstraints(it) + }.forEach { argNode -> flowsTo(argNode, node) } + + // flowsTo(blockNode(block), node) + } + + is LetNode -> { + val node = temporaryNode(stmt.temporary) + val valueNode = generateBindingTimeConstraints(stmt.value) + flowsTo(valueNode, node) + } + + is OutputNode -> {} + + SkipNode -> {} + + is UpdateNode -> { + val node = objectNode(stmt.variable) + stmt.arguments.map { + generateBindingTimeConstraints(it) + }.forEach { argNode -> flowsTo(argNode, node) } + + flowsTo(blockNode(block), node) + } + } + } + + private fun generateBindingTimeConstraints( + transitiveSuccessors: Set, + postdominators: Set, + label: RegularBlockLabel, + block: LoweredBasicBlock + ) { + // generate constraints from statements + for (stmt in block.statements) { + generateBindingTimeConstraints(label, stmt) + } + + // add PC label constraints: if this block has a conditional jump, + // there is an implicit flow from the guard's label to + // the PC labels of transitive successor blocks that are *not* postdominators + when (val jump = block.jump) { + is GotoIf -> { + val guardNode = generateBindingTimeConstraints(jump.guard) + for (successor in transitiveSuccessors) { + if (!postdominators.contains(successor)) { + flowsTo(guardNode, blockNode(successor)) + } + } + } + + else -> {} + } + } + + fun computeObjectBindingTime(): Map { + val postdominatorMap = program.computePostdominators() + val transitiveSuccessorMap = program.computeTransitiveSuccessors() + + for (kv in program.blocks) { + generateBindingTimeConstraints( + postdominatorMap[kv.key]!!, + transitiveSuccessorMap[kv.key]!!, + kv.key, + kv.value + ) + } + + val solution = solveDataFlow(BindingTime.STATIC, graph) + return objectNodes.map { kv -> kv.key to solution[kv.value]!! }.toMap() + } +} diff --git a/compiler/src/main/kotlin/edu/cornell/cs/apl/viaduct/lowering/DominatorAnalysis.kt b/compiler/src/main/kotlin/edu/cornell/cs/apl/viaduct/lowering/DominatorAnalysis.kt index 9192232bad..350e934a2c 100644 --- a/compiler/src/main/kotlin/edu/cornell/cs/apl/viaduct/lowering/DominatorAnalysis.kt +++ b/compiler/src/main/kotlin/edu/cornell/cs/apl/viaduct/lowering/DominatorAnalysis.kt @@ -10,7 +10,12 @@ import kotlinx.collections.immutable.persistentSetOf import kotlinx.collections.immutable.toPersistentSet import org.jgrapht.graph.DefaultDirectedGraph -private data class DataFlowSet(val set: PersistentSet): MeetSemiLattice>{ +private sealed class AbstractDataFlowSet> : MeetSemiLattice { + abstract val set: PersistentSet +} + +/** Powerset element. */ +private data class DataFlowSet(override val set: PersistentSet) : AbstractDataFlowSet>() { override fun meet(that: DataFlowSet): DataFlowSet { return DataFlowSet(this.set.intersect(that.set)) } @@ -19,29 +24,47 @@ private data class DataFlowSet(val set: PersistentSet): MeetSemiLattice(override val set: PersistentSet) : AbstractDataFlowSet>() { + override fun meet(that: InvertedDataFlowSet): InvertedDataFlowSet { + return InvertedDataFlowSet(this.set.addAll(that.set)) + } + + override fun lessThanOrEqualTo(that: InvertedDataFlowSet): Boolean = + that.set.containsAll(this.set) +} + +private data class DominatorAnalysisNode>( val label: RegularBlockLabel, - val isEntryPoint: Boolean -): DataFlowNode> { - override fun transfer(input: DataFlowSet): DataFlowSet { + val isEntryPoint: Boolean, + val nodeBuilder: (PersistentSet) -> S +) : DataFlowNode { + override fun transfer(input: S): S { // recall that the top element is all labels; if we don't special case entry points, // then the analysis will compute top for all nodes return if (isEntryPoint) { - DataFlowSet(persistentSetOf(label)) - + nodeBuilder(persistentSetOf(label)) } else { - DataFlowSet(input.set.add(label)) + nodeBuilder(input.set.add(label)) } } } -private fun computeDominators( +/** Perform dominator analysis by dataflow analysis. + * The lattice is the powerset lattice (top = set of all block labels), + * while the transfer function is defined by: + * - in(in_nodes) = fold in_nodes TOP (acc in_node -> intersect acc out(in_node)) + * - out(n) = {n} union in(n) + * - out(START) = {START} (important that you special case entry points!) + * */ +private fun > computeDominators( successorMap: Map>, - entryPoints: Set + entryPoints: Set, + nodeBuilder: (PersistentSet) -> S ): Map> { - val graph = DefaultDirectedGraph>>(null, null, false) + val graph = DefaultDirectedGraph, IdentityEdge>(null, null, false) val nodeMap = successorMap.keys.associateWith { label -> - DominatorBlockNode(label, entryPoints.contains(label)) + DominatorAnalysisNode(label, entryPoints.contains(label), nodeBuilder) } for (kv in successorMap) { @@ -50,21 +73,31 @@ private fun computeDominators( for (successor in kv.value) { val dst = nodeMap[successor]!! - val edge = IdentityEdge>() + val edge = IdentityEdge() graph.addVertex(dst) graph.addEdge(src, dst, edge) } } - return solveDataFlow(DataFlowSet(nodeMap.keys.toPersistentSet()), graph).map { kv -> + return solveDataFlow(nodeBuilder(nodeMap.keys.toPersistentSet()), graph).map { kv -> kv.key.label to kv.value.set }.toMap() } +/** Compute dominators for a flowchart program. */ fun FlowchartProgram.computeDominators(): Map> { - return computeDominators(this.successorMap, setOf(ENTRY_POINT_LABEL)) + return computeDominators(this.successorMap, setOf(ENTRY_POINT_LABEL)) { set -> DataFlowSet(set) } } +/** Compute postdominators for a flowchart program + * by computing dominators in inverse CFG. */ fun FlowchartProgram.computePostdominators(): Map> { - return computeDominators(this.predecessorMap, this.exitPoints) + return computeDominators(this.predecessorMap, this.exitPoints) { set -> DataFlowSet(set) } +} +/** Compute transitive successors for a flowchart program. + * Similar to postdominator analysis, except invert lattice so the meet operator is union instead of intersect: + * We care about successors from *any* path, not *all* paths. + * */ +fun FlowchartProgram.computeTransitiveSuccessors(): Map> { + return computeDominators(this.predecessorMap, this.exitPoints) { set -> InvertedDataFlowSet(set) } } diff --git a/compiler/src/main/kotlin/edu/cornell/cs/apl/viaduct/lowering/Flowchart.kt b/compiler/src/main/kotlin/edu/cornell/cs/apl/viaduct/lowering/Flowchart.kt index f4aa9ef8c9..c77083eda9 100644 --- a/compiler/src/main/kotlin/edu/cornell/cs/apl/viaduct/lowering/Flowchart.kt +++ b/compiler/src/main/kotlin/edu/cornell/cs/apl/viaduct/lowering/Flowchart.kt @@ -1,6 +1,5 @@ package edu.cornell.cs.apl.viaduct.lowering -import com.ibm.icu.text.CaseMap import edu.cornell.cs.apl.prettyprinting.Document import edu.cornell.cs.apl.prettyprinting.PrettyPrintable import edu.cornell.cs.apl.prettyprinting.bracketed @@ -159,7 +158,7 @@ data class LoweredBasicBlock( if (statements.isNotEmpty()) { statements.concatenated(separator = Document.forcedLineBreak) + Document.forcedLineBreak + jump } else { - jump.toDocument() + jump.toDocument() } fun successors(): Set = jump.successors() diff --git a/compiler/src/main/kotlin/edu/cornell/cs/apl/viaduct/lowering/FlowchartOptimizations.kt b/compiler/src/main/kotlin/edu/cornell/cs/apl/viaduct/lowering/FlowchartOptimizations.kt index b0edebe084..4c896c1bbf 100644 --- a/compiler/src/main/kotlin/edu/cornell/cs/apl/viaduct/lowering/FlowchartOptimizations.kt +++ b/compiler/src/main/kotlin/edu/cornell/cs/apl/viaduct/lowering/FlowchartOptimizations.kt @@ -82,7 +82,6 @@ fun FlowchartProgram.inlineBlocks(): FlowchartProgram { // inline the successor if (this.predecessorMap[jump.label]!!.size == 1) { curBlock = this.block(jump.label)!! - } else break } else break } diff --git a/compiler/src/main/kotlin/edu/cornell/cs/apl/viaduct/lowering/LoweringPass.kt b/compiler/src/main/kotlin/edu/cornell/cs/apl/viaduct/lowering/LoweringPass.kt index b0ff65b603..125f0823ec 100644 --- a/compiler/src/main/kotlin/edu/cornell/cs/apl/viaduct/lowering/LoweringPass.kt +++ b/compiler/src/main/kotlin/edu/cornell/cs/apl/viaduct/lowering/LoweringPass.kt @@ -37,7 +37,7 @@ import edu.cornell.cs.apl.viaduct.lowering.QueryNode as LQueryNode import edu.cornell.cs.apl.viaduct.lowering.ReadNode as LReadNode import edu.cornell.cs.apl.viaduct.lowering.UpdateNode as LUpdateNode -class LoweringPass private constructor (val block: BlockNode) { +class LoweringPass private constructor(val block: BlockNode) { private val nameGenerator = FreshNameGenerator() private val blockMap = mutableMapOf>() private val breakLabelMap = mutableMapOf() diff --git a/compiler/src/main/kotlin/edu/cornell/cs/apl/viaduct/lowering/PartialEvaluator.kt b/compiler/src/main/kotlin/edu/cornell/cs/apl/viaduct/lowering/PartialEvaluator.kt index 69ebd85c42..01c4b8eb03 100644 --- a/compiler/src/main/kotlin/edu/cornell/cs/apl/viaduct/lowering/PartialEvaluator.kt +++ b/compiler/src/main/kotlin/edu/cornell/cs/apl/viaduct/lowering/PartialEvaluator.kt @@ -26,7 +26,7 @@ import kotlinx.collections.immutable.toPersistentList import java.util.LinkedList import java.util.Queue -sealed class InterpreterObject: PrettyPrintable { +sealed class InterpreterObject : PrettyPrintable { abstract fun query(query: QueryName, arguments: List): Value abstract fun update(update: UpdateName, arguments: List): InterpreterObject @@ -136,7 +136,7 @@ data class VectorObject(val values: PersistentMap) : InterpreterObje data class PartialStore( val objectStore: PersistentMap, val temporaryStore: PersistentMap -): PrettyPrintable { +) : PrettyPrintable { fun updateObject(variable: ObjectVariable, obj: InterpreterObject): PartialStore { return this.copy(objectStore = objectStore.put(variable, obj)) } @@ -165,6 +165,8 @@ class PartialEvaluator( } } + private val objectBindingTimeMap: Map = BindingTimeAnalysis.computeBindingTime(program) + private fun evaluateExpression(store: PartialStore, expr: LoweredExpression): LoweredExpression { return when (expr) { is LiteralNode -> expr @@ -229,7 +231,9 @@ class PartialEvaluator( is DeclarationNode -> { val reducedArgs = stmt.arguments.map { arg -> evaluateExpression(store, arg) } val staticArgs = reducedArgs.all { it.isStatic() } - if (staticArgs) { + val staticObject = objectBindingTimeMap[stmt.name] == BindingTime.STATIC + + if (staticArgs && staticObject) { val valArgs = reducedArgs.map { (it as LiteralNode).value } val obj = buildObject(stmt.className, stmt.typeArguments, valArgs) Pair(store.updateObject(stmt.name, obj), SkipNode) @@ -252,7 +256,7 @@ class PartialEvaluator( is UpdateNode -> { val reducedArgs = stmt.arguments.map { arg -> evaluateExpression(store, arg) } val staticArgs = reducedArgs.all { it.isStatic() } - val staticObject = store.objectStore.contains(stmt.variable) + val staticObject = objectBindingTimeMap[stmt.variable]!! == BindingTime.STATIC when { // easy case: interpret update as in normal (full) evaluation @@ -404,7 +408,7 @@ class PartialEvaluator( return LoweredBasicBlock(residualStmts, residualJump) } - /** Online partial evaluation of a flowchart program. */ + /** Offline partial evaluation of a flowchart program. */ fun evaluate( initialStore: PartialStore = PartialStore(objectStore = persistentHashMapOf(), temporaryStore = persistentHashMapOf()) From 59dfd90834936c72eddc1964b367a53687496c14 Mon Sep 17 00:00:00 2001 From: Rolph Recto Date: Fri, 11 Feb 2022 17:31:49 -0500 Subject: [PATCH 7/8] start object renaming --- .../apl/viaduct/lowering/PartialEvaluator.kt | 37 +++++++++++++------ 1 file changed, 26 insertions(+), 11 deletions(-) diff --git a/compiler/src/main/kotlin/edu/cornell/cs/apl/viaduct/lowering/PartialEvaluator.kt b/compiler/src/main/kotlin/edu/cornell/cs/apl/viaduct/lowering/PartialEvaluator.kt index 01c4b8eb03..297c1e6e62 100644 --- a/compiler/src/main/kotlin/edu/cornell/cs/apl/viaduct/lowering/PartialEvaluator.kt +++ b/compiler/src/main/kotlin/edu/cornell/cs/apl/viaduct/lowering/PartialEvaluator.kt @@ -137,6 +137,8 @@ data class PartialStore( val objectStore: PersistentMap, val temporaryStore: PersistentMap ) : PrettyPrintable { + fun getObject(variable: ObjectVariable): InterpreterObject? = objectStore[variable] + fun updateObject(variable: ObjectVariable, obj: InterpreterObject): PartialStore { return this.copy(objectStore = objectStore.put(variable, obj)) } @@ -166,6 +168,10 @@ class PartialEvaluator( } private val objectBindingTimeMap: Map = BindingTimeAnalysis.computeBindingTime(program) + private val objectRenameMap = mutableMapOf() + private val nameGenerator = FreshNameGenerator() + + private fun getObjectName(variable: ObjectVariable) = objectRenameMap[variable] ?: variable private fun evaluateExpression(store: PartialStore, expr: LoweredExpression): LoweredExpression { return when (expr) { @@ -195,7 +201,10 @@ class PartialEvaluator( val queryVal = store.objectStore[expr.variable]!!.query(expr.query, valArgs) LiteralNode(queryVal) } else { - expr.copy(arguments = reducedArgs.toPersistentList()) + expr.copy( + variable = objectRenameMap[expr.variable] ?: expr.variable, + arguments = reducedArgs.toPersistentList() + ) } } @@ -236,7 +245,8 @@ class PartialEvaluator( if (staticArgs && staticObject) { val valArgs = reducedArgs.map { (it as LiteralNode).value } val obj = buildObject(stmt.className, stmt.typeArguments, valArgs) - Pair(store.updateObject(stmt.name, obj), SkipNode) + Pair(store.updateObject(getObjectName(stmt.name), obj), SkipNode) + } else { val reducedDecl = stmt.copy(arguments = reducedArgs.toPersistentList()) Pair(store, reducedDecl) @@ -262,20 +272,25 @@ class PartialEvaluator( // easy case: interpret update as in normal (full) evaluation staticArgs && staticObject -> { val valArgs = reducedArgs.map { (it as LiteralNode).value } - val updatedObj = store.objectStore[stmt.variable]!!.update(stmt.update, valArgs) - Pair(store.updateObject(stmt.variable, updatedObj), SkipNode) + val updatedObj = + store.getObject(getObjectName(stmt.variable))!! + .update(stmt.update, valArgs) + Pair(store.updateObject(getObjectName(stmt.variable), updatedObj), SkipNode) } !staticArgs && staticObject -> { - when (val obj = store.objectStore[stmt.variable]!!) { + when (val obj = store.getObject(getObjectName(stmt.variable))!!) { is ImmutableCellObject -> throw Exception("cannot update immutable cell object") // for mutable cells, remove cell from partial store is MutableCellObject -> { Pair( - store.deleteObject(stmt.variable), - stmt.copy(arguments = reducedArgs.toPersistentList()) + store.deleteObject(getObjectName(stmt.variable)), + stmt.copy( + variable = getObjectName(stmt.variable), + arguments = reducedArgs.toPersistentList() + ) ) } @@ -288,7 +303,7 @@ class PartialEvaluator( !reducedIndex.isStatic() -> { Pair( store.updateObject( - stmt.variable, + getObjectName(stmt.variable), VectorObject(persistentHashMapOf()) ), stmt.copy(arguments = reducedArgs.toPersistentList()) @@ -300,7 +315,7 @@ class PartialEvaluator( val index = ((reducedIndex as LiteralNode).value as IntegerValue).value Pair( store.updateObject( - stmt.variable, + getObjectName(stmt.variable), obj.copy(values = obj.values.remove(index)) ), stmt.copy(arguments = reducedArgs.toPersistentList()) @@ -322,7 +337,7 @@ class PartialEvaluator( if (reducedArgs.size == 1) { // mutable cell Pair( store.updateObject( - stmt.variable, + getObjectName(stmt.variable), MutableCellObject((reducedArgs[0] as LiteralNode).value) ), SkipNode @@ -332,7 +347,7 @@ class PartialEvaluator( val value = (reducedArgs[1] as LiteralNode).value Pair( store.updateObject( - stmt.variable, + getObjectName(stmt.variable), VectorObject(persistentHashMapOf(index to value)) ), SkipNode From 97974de6b19697116d6a6b98730c66f7505e79b8 Mon Sep 17 00:00:00 2001 From: Rolph Recto Date: Mon, 14 Feb 2022 11:08:58 -0500 Subject: [PATCH 8/8] object renaming for partial evaluator --- .../apl/viaduct/lowering/DominatorAnalysis.kt | 5 +++-- .../apl/viaduct/lowering/PartialEvaluator.kt | 19 +++++++++++++++---- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/compiler/src/main/kotlin/edu/cornell/cs/apl/viaduct/lowering/DominatorAnalysis.kt b/compiler/src/main/kotlin/edu/cornell/cs/apl/viaduct/lowering/DominatorAnalysis.kt index 350e934a2c..643780e190 100644 --- a/compiler/src/main/kotlin/edu/cornell/cs/apl/viaduct/lowering/DominatorAnalysis.kt +++ b/compiler/src/main/kotlin/edu/cornell/cs/apl/viaduct/lowering/DominatorAnalysis.kt @@ -50,7 +50,7 @@ private data class DominatorAnalysisNode intersect acc out(in_node)) @@ -79,8 +79,9 @@ private fun > computeDominators( } } + // return blocks that are *strictly* dominated return solveDataFlow(nodeBuilder(nodeMap.keys.toPersistentSet()), graph).map { kv -> - kv.key.label to kv.value.set + kv.key.label to kv.value.set.filter { v -> v != kv.key.label }.toSet() }.toMap() } diff --git a/compiler/src/main/kotlin/edu/cornell/cs/apl/viaduct/lowering/PartialEvaluator.kt b/compiler/src/main/kotlin/edu/cornell/cs/apl/viaduct/lowering/PartialEvaluator.kt index 297c1e6e62..68fff9a973 100644 --- a/compiler/src/main/kotlin/edu/cornell/cs/apl/viaduct/lowering/PartialEvaluator.kt +++ b/compiler/src/main/kotlin/edu/cornell/cs/apl/viaduct/lowering/PartialEvaluator.kt @@ -242,14 +242,25 @@ class PartialEvaluator( val staticArgs = reducedArgs.all { it.isStatic() } val staticObject = objectBindingTimeMap[stmt.name] == BindingTime.STATIC + val objName = + if (objectRenameMap.containsKey(stmt.name)) { + val freshName = nameGenerator.getFreshName(stmt.name.name) + objectRenameMap[stmt.name] = ObjectVariable(freshName) + ObjectVariable(freshName) + } else { + objectRenameMap[stmt.name] = stmt.name + stmt.name + } + if (staticArgs && staticObject) { val valArgs = reducedArgs.map { (it as LiteralNode).value } val obj = buildObject(stmt.className, stmt.typeArguments, valArgs) - Pair(store.updateObject(getObjectName(stmt.name), obj), SkipNode) - + Pair(store.updateObject(objName, obj), SkipNode) } else { - val reducedDecl = stmt.copy(arguments = reducedArgs.toPersistentList()) - Pair(store, reducedDecl) + Pair( + store, + stmt.copy(name = objName, arguments = reducedArgs.toPersistentList()) + ) } }