From e6b24d1a5dfffb9a5bdbd21f4208f2d29a1694a2 Mon Sep 17 00:00:00 2001 From: Remy Date: Tue, 29 Sep 2020 18:54:21 +0200 Subject: [PATCH] Speedy Compiler: compute variable indices more generally. (#7502) CHANGELOG_BEGIN CHANGELOG_END --- .../daml/lf/speedy/Compiler.scala | 830 ++++++++---------- 1 file changed, 358 insertions(+), 472 deletions(-) diff --git a/daml-lf/interpreter/src/main/scala/com/digitalasset/daml/lf/speedy/Compiler.scala b/daml-lf/interpreter/src/main/scala/com/digitalasset/daml/lf/speedy/Compiler.scala index 3238066336..094975d434 100644 --- a/daml-lf/interpreter/src/main/scala/com/digitalasset/daml/lf/speedy/Compiler.scala +++ b/daml-lf/interpreter/src/main/scala/com/digitalasset/daml/lf/speedy/Compiler.scala @@ -135,44 +135,55 @@ private[lf] final class Compiler( // corresponds to DAML-LF type variable. private[this] case class TVarRef(name: TypeVarName) extends VarRef + case class Position(idx: Int) + + @inline + private[this] def nextPosition(): Position = { + val p = env.position + env = env.copy(position = env.position + 1) + Position(p) + } + + @inline + private[this] def nextPositionWithExprName(name: ExprVarName): Position = { + val p = nextPosition() + addExprVar(name, p) + p + } + + private[this] def nextPositionWithOptionalExprName(name: Option[ExprVarName]): Position = + name.fold(nextPosition())(nextPositionWithExprName) + + private[this] def svar(p: Position): SEVar = SEVar(env.position - p.idx) + + private[this] def addVar(ref: VarRef, position: Position) = + env = env.copy(varIndices = env.varIndices.updated(ref, position)) + + private[this] def addExprVar(name: ExprVarName, position: Position) = + addVar(EVarRef(name), position) + + private[this] def addTypeVar(name: TypeVarName, position: Position) = + addVar(TVarRef(name), position) + + private[this] def hideTypeVar(name: TypeVarName) = + env = env.copy(varIndices = env.varIndices - TVarRef(name)) + + private[this] def vars: List[VarRef] = env.varIndices.keys.toList + + private[this] def lookupVar(varRef: VarRef): Option[SEVar] = + env.varIndices.get(varRef).map(svar) + + def lookupExprVar(name: ExprVarName): SEVar = + lookupVar(EVarRef(name)) + .getOrElse(throw CompilationError(s"Unknown variable: $name. Known: ${vars.mkString(",")}")) + + def lookupTypeVar(name: TypeVarName): Option[SEVar] = + lookupVar(TVarRef(name)) + private[this] case class Env( position: Int = 0, - varIndices: List[(VarRef, Option[Int])] = List.empty, - ) { - def incrPos: Env = copy(position = position + 1) - def addExprVar(name: Option[ExprVarName], index: Int): Env = - name.fold(this)(n => copy(varIndices = (EVarRef(n), Some(index)) :: varIndices)) - def addExprVar(name: ExprVarName, index: Int): Env = - addExprVar(Some(name), index) - def addExprVar(name: Option[ExprVarName]): Env = - incrPos.addExprVar(name, position) - def addExprVar(name: ExprVarName): Env = - addExprVar(Some(name)) - def addTypeVar(name: TypeVarName): Env = - incrPos.copy(varIndices = (TVarRef(name), Some(position)) :: varIndices) - def hideTypeVar(name: TypeVarName): Env = - copy(varIndices = (TVarRef(name), None) :: varIndices) - - def vars: List[VarRef] = varIndices.map(_._1) - - private[this] def lookUpVar(varRef: VarRef): Option[Int] = - varIndices - .find(_._1 == varRef) - .flatMap(_._2) - // The de Bruijin index for the binder, e.g. - // the distance to the binder. The closest binder - // is at distance 1. - .map(position - _) - - def lookUpExprVar(name: ExprVarName): Int = - lookUpVar(EVarRef(name)) - .getOrElse( - throw CompilationError(s"Unknown variable: $name. Known: ${env.vars.mkString(",")}")) - - def lookUpTypeVar(name: TypeVarName): Option[Int] = - lookUpVar(TVarRef(name)) - - } + varIndices: Map[VarRef, Position] = Map.empty, + ) /** Environment mapping names into stack positions */ private[this] var env = Env() @@ -243,18 +254,12 @@ private[lf] final class Compiler( builder += compileCreate(identifier, tmpl) builder += compileFetch(identifier, tmpl) - tmpl.choices.toList.foreach { - case (cname, choice) => - builder += compileChoice(identifier, tmpl, cname, choice) - } + tmpl.choices.values.foreach(builder += compileChoice(identifier, tmpl, _)) tmpl.key.foreach { tmplKey => builder += compileFetchByKey(identifier, tmpl, tmplKey) builder += compileLookupByKey(identifier, tmplKey) - tmpl.choices.foreach { - case (cname, choice) => - builder += compileChoiceByKey(identifier, tmpl, tmplKey, cname, choice) - } + tmpl.choices.values.foreach(builder += compileChoiceByKey(identifier, tmpl, tmplKey, _)) } builder.result() @@ -327,7 +332,7 @@ private[lf] final class Compiler( private[this] def compile(expr0: Expr): SExpr = expr0 match { case EVar(name) => - SEVar(env.lookUpExprVar(name)) + lookupExprVar(name) case EVal(ref) => SEVal(LfDefRef(ref)) case EBuiltin(bf) => @@ -649,7 +654,7 @@ private[lf] final class Compiler( bindings.map { case Binding(optBinder, _, bound) => val bound2 = withOptLabel(optBinder, compile(bound)) - env = env.addExprVar(optBinder) + nextPositionWithOptionalExprName(optBinder) bound2 }.toArray, compile(body), @@ -690,13 +695,13 @@ private[lf] final class Compiler( private[this] def compileAbss(expr0: Expr, arity: Int = 0): SExpr = expr0 match { case EAbs((binder, typ @ _), body, ref @ _) => - env = env.addExprVar(binder) + nextPositionWithExprName(binder) compileAbss(body, arity + 1) case ETyAbs((binder, KNat), body) => - env = env.addTypeVar(binder) + addTypeVar(binder, nextPosition()) compileAbss(body, arity + 1) case ETyAbs((binder, _), body) => - env = env.hideTypeVar(binder) + hideTypeVar(binder) compileAbss(body, arity) case _ if arity == 0 => compile(expr0) @@ -720,7 +725,7 @@ private[lf] final class Compiler( private[this] def translateType(typ: Type): Option[SExpr] = typ match { case TNat(n) => SENat(n) - case TVar(name) => env.lookUpTypeVar(name).map(SEVar) + case TVar(name) => lookupTypeVar(name) case _ => None } @@ -743,6 +748,7 @@ private[lf] final class Compiler( case ScenarioEmbedExpr(_, e) => compileEmbedExpr(e) } + @inline private[this] def compileCommit(partyE: Expr, updateE: Expr, optLoc: Option[Location]): SExpr = // let party = @@ -750,86 +756,77 @@ private[lf] final class Compiler( // in \token -> // let _ = $beginCommit party token // r = update token - // in $endCommit[mustFail = false] r token + // in $endCommit(mustFail = false) r token withEnv { _ => val party = compile(partyE) - env = env.incrPos // party + val partyPos = nextPosition() val update = compile(updateE) - env = env.incrPos // update - env = env.incrPos // $beginCommit + val updatePos = nextPosition() + SELet(party, update) in withLabel( "submit", SEAbs(1) { + val tokenPos = nextPosition() + val beginCommit = SBSBeginCommit(optLoc)(svar(partyPos), svar(tokenPos)) + nextPosition() + val result = SEApp(svar(updatePos), Array(svar(tokenPos))) + val resultPos = nextPosition() + SELet( - // stack: - SBSBeginCommit(optLoc)(SEVar(3), SEVar(1)), - // stack: () - SEApp(SEVar(3), Array(SEVar(2))), - // stack: () result + beginCommit, + result ) in - SBSEndCommit(false)(SEVar(1), SEVar(3)) + SBSEndCommit(mustFail = false)(svar(resultPos), svar(tokenPos)) } ) } @inline - private[this] def compileMustFail(partyE: Expr, updateE: Expr, optLoc: Option[Location]): SExpr = + private[this] def compileMustFail(party: Expr, update: Expr, optLoc: Option[Location]): SExpr = // \token -> - // let _ = $beginCommit token - // r = $catch ( token) true false - // in $endCommit[mustFail = true] r token + // let _ = $beginCommit [party] + // = $catch ([update] ) true false + // in $endCommit(mustFail = true) withEnv { _ => - env = env.incrPos // token - val party = compile(partyE) - env = env.incrPos // $beginCommit - val update = compile(updateE) withLabel( "submitMustFail", SEAbs(1) { + val tokenPos = nextPosition() + val beginCommit = SBSBeginCommit(optLoc)(compile(party), svar(tokenPos)) + nextPosition() + val result = + SECatch(SEApp(compile(update), Array(svar(tokenPos))), SEValue.True, SEValue.False) + val resultPos = nextPosition() + SELet( - SBSBeginCommit(optLoc)(party, SEVar(1)), - SECatch(SEApp(update, Array(SEVar(2))), SEValue.True, SEValue.False), - ) in SBSEndCommit(true)(SEVar(1), SEVar(3)) + beginCommit, + result, + ) in SBSEndCommit(mustFail = true)(svar(resultPos), svar(tokenPos)) } ) } @inline private[this] def compileGetParty(expr: Expr): SExpr = - withEnv { _ => - env = env.incrPos // token - withLabel( - "getParty", - SEAbs(1) { - SBSGetParty(compile(expr), SEVar(1)) - } - ) + function(1, "getPArty") { + case List(tokenPos) => SBSGetParty(compile(expr), svar(tokenPos)) } @inline private[this] def compilePass(time: Expr): SExpr = - withEnv { _ => - env = env.incrPos // token - withLabel( - "pass", - SEAbs(1) { - SBSPass(compile(time), SEVar(1)) - } - ) + function(1, "pass") { + case List(tokenPos) => SBSPass(compile(time), svar(tokenPos)) } @inline private[this] def compileEmbedExpr(expr: Expr): SExpr = - withEnv { _ => - env = env.incrPos // token + function(1) { // EmbedExpr's get wrapped into an extra layer of abstraction // to delay evaluation. // e.g. // embed (error "foo") => \token -> error "foo" - SEAbs(1) { - SEApp(compile(expr), Array(SEVar(1))) - } + case List(tokenPos) => SEApp(compile(expr), Array(svar(tokenPos))) } private[this] def compilePure(body: Expr): SExpr = @@ -851,208 +848,174 @@ private[lf] final class Compiler( // in z x y token withEnv { _ => val boundHead = compile(bindings.head.bound) - env = env.incrPos // evaluated body of first binding + val boundHeadPos = nextPosition() - val tokenPosition = env.position - env = env.incrPos // token - - // add the first binding into the environment - val appBoundHead = SEApp(SEVar(2), Array(SEVar(1))) - env = env.addExprVar(bindings.head.binder) - - // and then the rest - val boundTail = bindings.tail.toList.map { - case Binding(optB, _, bound) => - val sbound = compile(bound) - val tokenIndex = env.position - tokenPosition - env = env.addExprVar(optB) - SEApp(sbound, Array(SEVar(tokenIndex))) - } - val allBounds = appBoundHead +: boundTail SELet(boundHead) in SEAbs(1) { + val tokenPos = nextPosition() + + // add the first binding into the environment + val appBoundHead = SEApp(svar(boundHeadPos), Array(svar(tokenPos))) + nextPositionWithOptionalExprName(bindings.head.binder) + + // and then the rest + val boundTail = bindings.tail.toList.map { + case Binding(optB, _, bound) => + val expr = SEApp(compile(bound), Array(svar(tokenPos))) + nextPositionWithOptionalExprName(optB) + expr + } + val allBounds = appBoundHead +: boundTail SELet(allBounds: _*) in - SEApp(compile(body), Array(SEVar(env.position - tokenPosition))) + SEApp(compile(body), Array(svar(tokenPos))) } } - private[this] val keyWithMaintainersStruct: Struct[Int] = - Struct.assertFromSeq(List(keyFieldName, maintainersFieldName).zipWithIndex) + private[this] val KeyWithMaintainersStruct = + SBStructCon(Struct.assertFromSeq(List(keyFieldName, maintainersFieldName).zipWithIndex)) - private def encodeKeyWithMaintainers(key: SExpr, tmplKey: TemplateKey): SExpr = - SELet(key) in - SBStructCon(keyWithMaintainersStruct)( - SEVar(1), // key - SEApp(compile(tmplKey.maintainers), Array(SEVar(1) /* key */ )), + private[this] def encodeKeyWithMaintainers(keyPos: Position, tmplKey: TemplateKey): SExpr = + KeyWithMaintainersStruct(svar(keyPos), SEApp(compile(tmplKey.maintainers), Array(svar(keyPos)))) + + private[this] def compileKeyWithMaintainers(maybeTmplKey: Option[TemplateKey]): SExpr = + withEnv { _ => + maybeTmplKey match { + case None => SEValue.None + case Some(tmplKey) => + val key = compile(tmplKey.body) + val keyPos = nextPosition() + SELet(key) in SBSome(encodeKeyWithMaintainers(keyPos, tmplKey)) + } + } + + @inline + def function(arity: Int)(body: List[Position] => SExpr): SExpr = + withEnv { _ => + SEAbs(arity, body(List.fill(arity)(nextPosition()))) + } + + @inline + def function(arity: Int, label: String)(body: List[Position] => SExpr): SExpr = + function(arity)(x => withLabel(label, body(x))) + + @inline + private[this] def topLevelFunction(ref: SDefinitionRef, arity: Int, label: String)( + body: List[Position] => SExpr + ): (SDefinitionRef, SExpr) = + ref -> + validate( + closureConvert( + Map.empty, + function(arity, label)(body) + ) ) - private[this] def translateKeyWithMaintainers(tmplKey: TemplateKey): SExpr = - encodeKeyWithMaintainers(compile(tmplKey.body), tmplKey) - private[this] def compileChoice( tmplId: TypeConName, tmpl: Template, - cname: ChoiceName, choice: TemplateChoice, - ): (ChoiceDefRef, SExpr) = + ): (SDefinitionRef, SExpr) = // Compiles a choice into: // ChoiceDefRef(SomeTemplate, SomeChoice) = \ -> - // let targ = fetch - // _ = $beginExercise[tmplId, chId] sigs obs ctrls mbKey - // result = - // _ = $endExercise[tmplId] - // in result - ChoiceDefRef(tmplId, cname) -> - validate( - closureConvert( - Map.empty, - withEnv { _ => - env = env.incrPos /* */ - val selfBinderPos = env.position - env = env.incrPos /* */ - val choiceArgumentPos = env.position - env = env.incrPos // - env = env.incrPos /* */ - //