mirror of
https://github.com/digital-asset/daml.git
synced 2024-11-13 00:16:19 +03:00
Stack safe speedy compilation (#12925)
* Rework speedy compilation phase1 to be stack safe. changelog_begin changelog_end
This commit is contained in:
parent
15b54a870e
commit
b19567aa7a
File diff suppressed because it is too large
Load Diff
@ -2,6 +2,8 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package com.daml.lf.speedy
|
||||
import com.daml.lf.data.ImmArray
|
||||
import com.daml.lf.data.Ref._
|
||||
import com.daml.lf.data.Ref.PackageId
|
||||
import com.daml.lf.language.Ast._
|
||||
import com.daml.lf.speedy.SExpr0._
|
||||
@ -15,10 +17,6 @@ import scala.annotation.tailrec
|
||||
|
||||
class PhaseOneTest extends AnyFreeSpec with Matchers with TableDrivenPropertyChecks {
|
||||
|
||||
// Construct one level of source-expression at various 'recursion-points'.
|
||||
private val app1 = (x: Expr) => EApp(x, leaf)
|
||||
private val app2 = (x: Expr) => EApp(leaf, x)
|
||||
|
||||
"compilation phase #1 (stack-safety)" - {
|
||||
|
||||
val phase1 = {
|
||||
@ -44,25 +42,78 @@ class PhaseOneTest extends AnyFreeSpec with Matchers with TableDrivenPropertyChe
|
||||
def runTest(depth: Int, cons: Expr => Expr) = {
|
||||
// Make an expression by iterating the 'cons' function, 'depth' times
|
||||
@tailrec def loop(x: Expr, n: Int): Expr = if (n == 0) x else loop(cons(x), n - 1)
|
||||
val exp: Expr = loop(leaf, depth)
|
||||
val _: SExpr = transform(exp)
|
||||
val source: Expr = loop(exp, depth)
|
||||
val _: SExpr = transform(source)
|
||||
true
|
||||
}
|
||||
|
||||
// TODO https://github.com/digital-asset/daml/issues/11561
|
||||
// - add testcases for all Ast.Expr recursion points
|
||||
val testCases = {
|
||||
Table[String, Expr => Expr](
|
||||
("name", "recursion-point"),
|
||||
("app1", app1),
|
||||
("app2", app2),
|
||||
)
|
||||
}
|
||||
|
||||
{
|
||||
// TODO https://github.com/digital-asset/daml/issues/11561
|
||||
// -- tests should run to a depth of 10000 (currently this causes stack overflow)
|
||||
val depth = 100
|
||||
val depth = 10000 // 10k plenty to prove stack-safety (but we can do a million)
|
||||
val testCases = {
|
||||
Table[String, Expr => Expr](
|
||||
("name", "recursion-point"),
|
||||
("tyApp", tyApp),
|
||||
("app1", app1),
|
||||
("app2", app2),
|
||||
("app1of3", app1of3),
|
||||
("app2of3", app2of3),
|
||||
("app3of3", app3of3),
|
||||
("esome", esome),
|
||||
("eabs", eabs),
|
||||
("etyabs", etyabs),
|
||||
("struct1", struct1),
|
||||
("struct2", struct2),
|
||||
("consH", consH),
|
||||
("consT", consT),
|
||||
("scenPure", scenPure),
|
||||
("scenBlock1", scenBlock1),
|
||||
("scenBlock2", scenBlock2),
|
||||
("scenCommit1", scenCommit1),
|
||||
("scenCommit2", scenCommit2),
|
||||
("scenMustFail1", scenMustFail1),
|
||||
("scenMustFail2", scenMustFail2),
|
||||
("scenPass", scenPass),
|
||||
("scenParty", scenParty),
|
||||
("scenEmbed", scenEmbed),
|
||||
("upure", upure),
|
||||
("ublock1", ublock1),
|
||||
("ublock2", ublock2),
|
||||
("ublock3", ublock3),
|
||||
("ucreate", ucreate),
|
||||
("ucreateI", ucreateI),
|
||||
("ufetch", ufetch),
|
||||
("ufetchI", ufetchI),
|
||||
("uexercise1", uexercise1),
|
||||
("uexercise2", uexercise2),
|
||||
("uexerciseI1", uexerciseI1),
|
||||
("uexerciseI2", uexerciseI2),
|
||||
("uexerciseI3", uexerciseI3),
|
||||
("uexerciseI4", uexerciseI4),
|
||||
("uexbykey1", uexbykey1),
|
||||
("uexbykey2", uexbykey2),
|
||||
("ufetchbykey", ufetchbykey),
|
||||
("ulookupbykey", ulookupbykey),
|
||||
("uembed", uembed),
|
||||
("utrycatch1", utrycatch1),
|
||||
("utrycatch2", utrycatch2),
|
||||
("structUpd1", structUpd1),
|
||||
("structUpd2", structUpd2),
|
||||
("recCon1", recCon1),
|
||||
("recCon2", recCon2),
|
||||
("caseScrut", caseScrut),
|
||||
("caseAlt1", caseAlt1),
|
||||
("caseAlt2", caseAlt2),
|
||||
("let1", let1),
|
||||
("let2", let2),
|
||||
("eabs_esome", eabs_esome),
|
||||
("etyabs_esome", etyabs_esome),
|
||||
("app1_esome", app1_esome),
|
||||
("app2_esome", app2_esome),
|
||||
("tyApp_esome", tyApp_esome),
|
||||
("let1_esome", let1_esome),
|
||||
("let2_esome", let2_esome),
|
||||
)
|
||||
}
|
||||
s"depth = $depth" - {
|
||||
forEvery(testCases) { (name: String, recursionPoint: Expr => Expr) =>
|
||||
name in {
|
||||
@ -73,6 +124,95 @@ class PhaseOneTest extends AnyFreeSpec with Matchers with TableDrivenPropertyChe
|
||||
}
|
||||
}
|
||||
|
||||
private def leaf: Expr = EPrimLit(PLText("leaf"))
|
||||
// Construct one level of source-expression at various 'recursion-points'.
|
||||
private def app1 = (x: Expr) => EApp(x, exp)
|
||||
private def app2 = (x: Expr) => EApp(exp, x)
|
||||
private def app1of3 = (x: Expr) => EApp(x, EApp(exp, exp))
|
||||
private def app2of3 = (x: Expr) => EApp(exp, EApp(x, exp))
|
||||
private def app3of3 = (x: Expr) => EApp(exp, EApp(exp, x))
|
||||
private def tyApp = (x: Expr) => ETyApp(x, ty)
|
||||
private def esome = (x: Expr) => ESome(ty, x)
|
||||
private def eabs = (x: Expr) => EAbs(binder, x, None)
|
||||
private def etyabs = (x: Expr) => ETyAbs(tvBinder, x)
|
||||
private def struct1 = (x: Expr) => EStructCon(ImmArray((field, x), (field2, exp)))
|
||||
private def struct2 = (x: Expr) => EStructCon(ImmArray((field, exp), (field2, x)))
|
||||
private def structUpd1 = (x: Expr) => EStructUpd(field, x, exp)
|
||||
private def structUpd2 = (x: Expr) => EStructUpd(field, exp, x)
|
||||
private def consH = (x: Expr) => ECons(ty, ImmArray(x), exp)
|
||||
private def consT = (x: Expr) => ECons(ty, ImmArray(exp), x)
|
||||
private def scenPure = (x: Expr) => EScenario(ScenarioPure(ty, x))
|
||||
private def scenBlock1 = (x: Expr) =>
|
||||
EScenario(ScenarioBlock(ImmArray(Binding(None, ty, x)), exp))
|
||||
private def scenBlock2 = (x: Expr) =>
|
||||
EScenario(ScenarioBlock(ImmArray(Binding(None, ty, exp)), x))
|
||||
private def scenCommit1 = (x: Expr) => EScenario(ScenarioCommit(x, exp, ty))
|
||||
private def scenCommit2 = (x: Expr) => EScenario(ScenarioCommit(exp, x, ty))
|
||||
private def scenMustFail1 = (x: Expr) => EScenario(ScenarioMustFailAt(x, exp, ty))
|
||||
private def scenMustFail2 = (x: Expr) => EScenario(ScenarioMustFailAt(exp, x, ty))
|
||||
private def scenPass = (x: Expr) => EScenario(ScenarioPass(x))
|
||||
private def scenParty = (x: Expr) => EScenario(ScenarioGetParty(x))
|
||||
private def scenEmbed = (x: Expr) => EScenario(ScenarioEmbedExpr(ty, x))
|
||||
private def upure = (x: Expr) => EUpdate(UpdatePure(ty, x))
|
||||
private def ublock1 = (x: Expr) =>
|
||||
EUpdate(UpdateBlock(ImmArray(Binding(None, ty, x), Binding(None, ty, exp)), exp))
|
||||
private def ublock2 = (x: Expr) =>
|
||||
EUpdate(UpdateBlock(ImmArray(Binding(None, ty, exp), Binding(None, ty, x)), exp))
|
||||
private def ublock3 = (x: Expr) =>
|
||||
EUpdate(UpdateBlock(ImmArray(Binding(None, ty, exp), Binding(None, ty, exp)), x))
|
||||
private def ucreate = (x: Expr) => EUpdate(UpdateCreate(tcon, x))
|
||||
private def ucreateI = (x: Expr) => EUpdate(UpdateCreateInterface(tcon, x))
|
||||
private def ufetch = (x: Expr) => EUpdate(UpdateFetch(tcon, x))
|
||||
private def ufetchI = (x: Expr) => EUpdate(UpdateFetchInterface(tcon, x))
|
||||
private def uexercise1 = (x: Expr) => EUpdate(UpdateExercise(tcon, choice, x, exp))
|
||||
private def uexercise2 = (x: Expr) => EUpdate(UpdateExercise(tcon, choice, exp, x))
|
||||
private def uexerciseI1 = (x: Expr) =>
|
||||
EUpdate(UpdateExerciseInterface(tcon, choice, x, exp, exp, exp))
|
||||
private def uexerciseI2 = (x: Expr) =>
|
||||
EUpdate(UpdateExerciseInterface(tcon, choice, exp, x, exp, exp))
|
||||
private def uexerciseI3 = (x: Expr) =>
|
||||
EUpdate(UpdateExerciseInterface(tcon, choice, exp, exp, x, exp))
|
||||
private def uexerciseI4 = (x: Expr) =>
|
||||
EUpdate(UpdateExerciseInterface(tcon, choice, exp, exp, exp, x))
|
||||
private def uexbykey1 = (x: Expr) => EUpdate(UpdateExerciseByKey(tcon, choice, x, exp))
|
||||
private def uexbykey2 = (x: Expr) => EUpdate(UpdateExerciseByKey(tcon, choice, exp, x))
|
||||
private def ufetchbykey = (x: Expr) => EUpdate(UpdateFetchByKey(RetrieveByKey(tcon, x)))
|
||||
private def ulookupbykey = (x: Expr) => EUpdate(UpdateLookupByKey(RetrieveByKey(tcon, x)))
|
||||
private def uembed = (x: Expr) => EUpdate(UpdateEmbedExpr(ty, x))
|
||||
private def utrycatch1 = (x: Expr) => EUpdate(UpdateTryCatch(ty, x, varname, exp))
|
||||
private def utrycatch2 = (x: Expr) => EUpdate(UpdateTryCatch(ty, exp, varname, x))
|
||||
|
||||
private def recCon1 = (x: Expr) => ERecCon(tapp, ImmArray((field, x), (field2, exp)))
|
||||
private def recCon2 = (x: Expr) => ERecCon(tapp, ImmArray((field, exp), (field2, x)))
|
||||
|
||||
// We dont test the recursion points ERecProj and ERecUpd, because the compiler requires
|
||||
// access to a package signature containing info for the type constructor.
|
||||
|
||||
private def caseScrut = (x: Expr) => ECase(x, ImmArray())
|
||||
private def caseAlt1 = (x: Expr) => ECase(exp, ImmArray(CaseAlt(CPNil, x), alt))
|
||||
private def caseAlt2 = (x: Expr) => ECase(exp, ImmArray(alt, CaseAlt(CPNil, x)))
|
||||
private def let1 = (x: Expr) => ELet(Binding(None, ty, x), exp)
|
||||
private def let2 = (x: Expr) => ELet(Binding(None, ty, exp), x)
|
||||
|
||||
// Alterating construction for applicaton & abstraction with some
|
||||
private def eabs_esome = (x: Expr) => eabs(esome(x))
|
||||
private def etyabs_esome = (x: Expr) => etyabs(esome(x))
|
||||
private def app1_esome = (x: Expr) => app1(esome(x))
|
||||
private def app2_esome = (x: Expr) => app2(esome(x))
|
||||
private def tyApp_esome = (x: Expr) => tyApp(esome(x))
|
||||
private def let1_esome = (x: Expr) => let1(esome(x))
|
||||
private def let2_esome = (x: Expr) => let2(esome(x))
|
||||
|
||||
// Leaves for various types
|
||||
private def alt: CaseAlt = CaseAlt(CPNil, exp)
|
||||
private def exp: Expr = EPrimLit(PLText("exp"))
|
||||
private def ty: Type = TVar(Name.assertFromString("ty"))
|
||||
private def binder: (ExprVarName, Type) = (varname, ty)
|
||||
private def tvBinder: (TypeVarName, Kind) = (tvar, KStar)
|
||||
private def varname: ExprVarName = Name.assertFromString("x")
|
||||
private def tvar: TypeVarName = Name.assertFromString("T")
|
||||
private def tcon: TypeConName = Identifier.assertFromString("P:M:tcon")
|
||||
private def choice: ChoiceName = Name.assertFromString("choice")
|
||||
private def tapp: TypeConApp = TypeConApp(tcon, ImmArray.empty)
|
||||
private def field: FieldName = Name.assertFromString("field")
|
||||
private def field2: FieldName = Name.assertFromString("field2")
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user