mirror of
https://github.com/enso-org/enso.git
synced 2024-11-27 05:15:42 +03:00
Removal of useless ApplicationSaturation phase (#8181)
This commit is contained in:
parent
646b47b246
commit
3d23c6a8d0
@ -11,7 +11,6 @@ import org.enso.compiler.pass.lint.{
|
||||
UnusedBindings
|
||||
}
|
||||
import org.enso.compiler.pass.optimise.{
|
||||
ApplicationSaturation,
|
||||
LambdaConsolidate,
|
||||
UnreachableMatchBranches
|
||||
}
|
||||
@ -114,8 +113,7 @@ class Passes(
|
||||
|
||||
/** Configuration for the passes. */
|
||||
private val passConfig: PassConfiguration = PassConfiguration(
|
||||
ApplicationSaturation -->> ApplicationSaturation.Configuration(),
|
||||
AliasAnalysis -->> AliasAnalysis.Configuration()
|
||||
AliasAnalysis -->> AliasAnalysis.Configuration()
|
||||
)
|
||||
|
||||
/** The pass manager for running compiler passes. */
|
||||
|
@ -26,10 +26,7 @@ import org.enso.compiler.pass.analyse.{
|
||||
TailCall
|
||||
}
|
||||
import org.enso.compiler.pass.lint.UnusedBindings
|
||||
import org.enso.compiler.pass.optimise.{
|
||||
ApplicationSaturation,
|
||||
LambdaConsolidate
|
||||
}
|
||||
import org.enso.compiler.pass.optimise.{LambdaConsolidate}
|
||||
import org.enso.compiler.pass.resolve.{
|
||||
DocumentationComments,
|
||||
IgnoredBindings,
|
||||
@ -58,7 +55,6 @@ case object ComplexType extends IRPass {
|
||||
override lazy val invalidatedPasses: Seq[IRPass] =
|
||||
List(
|
||||
AliasAnalysis,
|
||||
ApplicationSaturation,
|
||||
DataflowAnalysis,
|
||||
DemandAnalysis,
|
||||
FunctionBinding,
|
||||
|
@ -21,10 +21,7 @@ import org.enso.compiler.pass.analyse.{
|
||||
TailCall
|
||||
}
|
||||
import org.enso.compiler.pass.lint.UnusedBindings
|
||||
import org.enso.compiler.pass.optimise.{
|
||||
ApplicationSaturation,
|
||||
LambdaConsolidate
|
||||
}
|
||||
import org.enso.compiler.pass.optimise.{LambdaConsolidate}
|
||||
import org.enso.compiler.pass.resolve.{
|
||||
DocumentationComments,
|
||||
IgnoredBindings,
|
||||
@ -53,7 +50,6 @@ case object LambdaShorthandToLambda extends IRPass {
|
||||
)
|
||||
override lazy val invalidatedPasses: Seq[IRPass] = List(
|
||||
AliasAnalysis,
|
||||
ApplicationSaturation,
|
||||
DataflowAnalysis,
|
||||
DemandAnalysis,
|
||||
IgnoredBindings,
|
||||
|
@ -1,278 +0,0 @@
|
||||
package org.enso.compiler.pass.optimise
|
||||
|
||||
import org.enso.compiler.context.{InlineContext, ModuleContext}
|
||||
import org.enso.compiler.core.Implicits.AsMetadata
|
||||
import org.enso.compiler.core.ir.{Expression, Module, Name}
|
||||
import org.enso.compiler.core.ir.MetadataStorage._
|
||||
import org.enso.compiler.core.CompilerError
|
||||
import org.enso.compiler.core.ir.expression.Application
|
||||
import org.enso.compiler.pass.IRPass
|
||||
import org.enso.compiler.pass.analyse.AliasAnalysis
|
||||
import org.enso.compiler.pass.desugar._
|
||||
import org.enso.interpreter.node.{ExpressionNode => RuntimeExpression}
|
||||
import org.enso.interpreter.runtime.callable.argument.CallArgument
|
||||
import org.enso.interpreter.runtime.scope.{LocalScope, ModuleScope}
|
||||
|
||||
/** This optimisation pass recognises fully-saturated applications of known
|
||||
* functions and writes analysis data that allows optimisation of them to
|
||||
* specific nodes at codegen time.
|
||||
*
|
||||
* This pass requires the context to provide:
|
||||
*
|
||||
* - A [[org.enso.compiler.pass.PassConfiguration]] containing an instance of
|
||||
* [[ApplicationSaturation.Configuration]].
|
||||
*/
|
||||
case object ApplicationSaturation extends IRPass {
|
||||
|
||||
/** Information on the saturation state of a function. */
|
||||
override type Metadata = CallSaturation
|
||||
override type Config = Configuration
|
||||
|
||||
override lazy val precursorPasses: Seq[IRPass] = List(
|
||||
AliasAnalysis,
|
||||
ComplexType,
|
||||
FunctionBinding,
|
||||
GenerateMethodBodies,
|
||||
LambdaConsolidate,
|
||||
LambdaShorthandToLambda,
|
||||
NestedPatternMatch,
|
||||
OperatorToFunction,
|
||||
SectionsToBinOp
|
||||
)
|
||||
override lazy val invalidatedPasses: Seq[IRPass] = List()
|
||||
|
||||
/** Executes the analysis pass, marking functions with information about their
|
||||
* argument saturation.
|
||||
*
|
||||
* @param ir the Enso IR to process
|
||||
* @param moduleContext a context object that contains the information needed
|
||||
* to process a module
|
||||
* @return `ir`, possibly having made transformations or annotations to that
|
||||
* IR.
|
||||
*/
|
||||
override def runModule(
|
||||
ir: Module,
|
||||
moduleContext: ModuleContext
|
||||
): Module = {
|
||||
val passConfig = moduleContext.passConfiguration
|
||||
ir.mapExpressions(
|
||||
runExpression(
|
||||
_,
|
||||
new InlineContext(
|
||||
moduleContext,
|
||||
passConfiguration = passConfig,
|
||||
compilerConfig = moduleContext.compilerConfig
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/** Executes the analysis pass, marking functions with information about their
|
||||
* argument saturation.
|
||||
*
|
||||
* @param ir the Enso IR to process
|
||||
* @return `ir`, possibly having made transformations or annotations to that
|
||||
* IR.
|
||||
*/
|
||||
//noinspection DuplicatedCode
|
||||
override def runExpression(
|
||||
ir: Expression,
|
||||
inlineContext: InlineContext
|
||||
): Expression = {
|
||||
val knownFunctions =
|
||||
inlineContext.passConfiguration
|
||||
.flatMap(configs => configs.get(this))
|
||||
.getOrElse(
|
||||
throw new CompilerError("Pass configuration is missing.")
|
||||
)
|
||||
.knownFunctions
|
||||
|
||||
ir.transformExpressions {
|
||||
case func @ Application.Prefix(fn, args, _, _, _, _) =>
|
||||
fn match {
|
||||
case name: Name =>
|
||||
val aliasInfo =
|
||||
name
|
||||
.unsafeGetMetadata(
|
||||
AliasAnalysis,
|
||||
"Name occurrence with missing alias information."
|
||||
)
|
||||
.unsafeAs[AliasAnalysis.Info.Occurrence]
|
||||
|
||||
if (!aliasInfo.graph.linkedToShadowingBinding(aliasInfo.id)) {
|
||||
knownFunctions.get(name.name) match {
|
||||
case Some(FunctionSpec(arity, codegenHelper)) =>
|
||||
if (args.length == arity) {
|
||||
val argsArePositional = args.forall(arg => arg.name.isEmpty)
|
||||
|
||||
// TODO [AA] In future this should work regardless of the
|
||||
// application style. Needs interpreter changes.
|
||||
val saturationInfo = if (argsArePositional) {
|
||||
CallSaturation.Exact(codegenHelper)
|
||||
} else {
|
||||
CallSaturation.ExactButByName()
|
||||
}
|
||||
|
||||
func
|
||||
.copy(
|
||||
arguments = args.map(
|
||||
_.mapExpressions((ir: Expression) =>
|
||||
runExpression(ir, inlineContext)
|
||||
)
|
||||
)
|
||||
)
|
||||
.updateMetadata(this -->> saturationInfo)
|
||||
|
||||
} else if (args.length > arity) {
|
||||
func
|
||||
.copy(
|
||||
arguments = args.map(
|
||||
_.mapExpressions((ir: Expression) =>
|
||||
runExpression(ir, inlineContext)
|
||||
)
|
||||
)
|
||||
)
|
||||
.updateMetadata(
|
||||
this -->> CallSaturation.Over(args.length - arity)
|
||||
)
|
||||
} else {
|
||||
func
|
||||
.copy(
|
||||
arguments = args.map(
|
||||
_.mapExpressions((ir: Expression) =>
|
||||
runExpression(ir, inlineContext)
|
||||
)
|
||||
)
|
||||
)
|
||||
.updateMetadata(
|
||||
this -->> CallSaturation.Partial(arity - args.length)
|
||||
)
|
||||
}
|
||||
case None =>
|
||||
func
|
||||
.copy(
|
||||
arguments = args.map(
|
||||
_.mapExpressions((ir: Expression) =>
|
||||
runExpression(ir, inlineContext)
|
||||
)
|
||||
)
|
||||
)
|
||||
.updateMetadata(this -->> CallSaturation.Unknown())
|
||||
}
|
||||
} else {
|
||||
func
|
||||
.copy(
|
||||
function = runExpression(fn, inlineContext),
|
||||
arguments =
|
||||
args.map(_.mapExpressions(runExpression(_, inlineContext)))
|
||||
)
|
||||
.updateMetadata(this -->> CallSaturation.Unknown())
|
||||
}
|
||||
case _ =>
|
||||
func
|
||||
.copy(
|
||||
function = runExpression(fn, inlineContext),
|
||||
arguments =
|
||||
args.map(_.mapExpressions(runExpression(_, inlineContext)))
|
||||
)
|
||||
.updateMetadata(this -->> CallSaturation.Unknown())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Configuration for this pass
|
||||
*
|
||||
* @param knownFunctions the mapping of known functions
|
||||
*/
|
||||
sealed case class Configuration(
|
||||
knownFunctions: KnownFunctionsMapping = Map()
|
||||
) extends IRPass.Configuration {
|
||||
override var shouldWriteToContext: Boolean = false
|
||||
}
|
||||
|
||||
/** A function for constructing the optimised node for a function. */
|
||||
type CodegenHelper =
|
||||
ModuleScope => LocalScope => List[CallArgument] => RuntimeExpression
|
||||
|
||||
/** The configuration for this pass.
|
||||
*
|
||||
* The [[String]] is the name of the known function, while the
|
||||
* [[FunctionSpec]] describes said function.
|
||||
*/
|
||||
type KnownFunctionsMapping = Map[String, FunctionSpec]
|
||||
|
||||
/** Describes the saturation state of a function application. */
|
||||
sealed trait CallSaturation extends IRPass.IRMetadata {
|
||||
override def duplicate(): Option[IRPass.IRMetadata] = Some(this)
|
||||
}
|
||||
object CallSaturation {
|
||||
sealed case class Over(additionalArgCount: Int) extends CallSaturation {
|
||||
override val metadataName: String =
|
||||
"ApplicationSaturation.CallSaturation.Over"
|
||||
|
||||
/** @inheritdoc */
|
||||
override def prepareForSerialization(compiler: Compiler): Over = this
|
||||
|
||||
/** @inheritdoc */
|
||||
override def restoreFromSerialization(compiler: Compiler): Option[Over] =
|
||||
Some(this)
|
||||
}
|
||||
sealed case class Exact(helper: CodegenHelper) extends CallSaturation {
|
||||
override val metadataName: String =
|
||||
"ApplicationSaturation.CallSaturation.Exact"
|
||||
|
||||
/** @inheritdoc */
|
||||
override def prepareForSerialization(compiler: Compiler): Exact = this
|
||||
|
||||
/** @inheritdoc */
|
||||
override def restoreFromSerialization(
|
||||
compiler: Compiler
|
||||
): Option[Exact] = Some(this)
|
||||
}
|
||||
sealed case class ExactButByName() extends CallSaturation {
|
||||
override val metadataName: String =
|
||||
"ApplicationSaturation.CallSaturation.ExactButByName"
|
||||
|
||||
/** @inheritdoc */
|
||||
override def prepareForSerialization(compiler: Compiler): ExactButByName =
|
||||
this
|
||||
|
||||
/** @inheritdoc */
|
||||
override def restoreFromSerialization(
|
||||
compiler: Compiler
|
||||
): Option[ExactButByName] = Some(this)
|
||||
}
|
||||
sealed case class Partial(unappliedArgCount: Int) extends CallSaturation {
|
||||
override val metadataName: String =
|
||||
"ApplicationSaturation.CallSaturation.Partial"
|
||||
|
||||
/** @inheritdoc */
|
||||
override def prepareForSerialization(compiler: Compiler): Partial = this
|
||||
|
||||
/** @inheritdoc */
|
||||
override def restoreFromSerialization(
|
||||
compiler: Compiler
|
||||
): Option[Partial] = Some(this)
|
||||
}
|
||||
sealed case class Unknown() extends CallSaturation {
|
||||
override val metadataName: String =
|
||||
"ApplicationSaturation.CallSaturation.Unknown"
|
||||
|
||||
/** @inheritdoc */
|
||||
override def prepareForSerialization(compiler: Compiler): Unknown = this
|
||||
|
||||
/** @inheritdoc */
|
||||
override def restoreFromSerialization(
|
||||
compiler: Compiler
|
||||
): Option[Unknown] = Some(this)
|
||||
}
|
||||
}
|
||||
|
||||
/** A description of a known function
|
||||
*
|
||||
* @param arity the number of arguments the function expects
|
||||
* @param codegenHelper a function that can construct the optimised node to
|
||||
* represent the function at codegen time.
|
||||
*/
|
||||
sealed case class FunctionSpec(arity: Int, codegenHelper: CodegenHelper)
|
||||
}
|
@ -51,7 +51,6 @@ import org.enso.compiler.pass.analyse.{
|
||||
DataflowAnalysis,
|
||||
TailCall
|
||||
}
|
||||
import org.enso.compiler.pass.optimise.ApplicationSaturation
|
||||
import org.enso.compiler.pass.resolve.{
|
||||
ExpressionAnnotations,
|
||||
GenericAnnotations,
|
||||
@ -2072,18 +2071,11 @@ class IrToTruffle(
|
||||
InvokeCallableNode.DefaultsExecutionMode.EXECUTE
|
||||
}
|
||||
|
||||
val appNode = application.getMetadata(ApplicationSaturation) match {
|
||||
case Some(
|
||||
ApplicationSaturation.CallSaturation.Exact(createOptimised)
|
||||
) =>
|
||||
createOptimised(moduleScope)(scope)(callArgs.toList)
|
||||
case _ =>
|
||||
ApplicationNode.build(
|
||||
this.run(fn, subjectToInstrumentation),
|
||||
callArgs.toArray,
|
||||
defaultsExecutionMode
|
||||
)
|
||||
}
|
||||
val appNode = ApplicationNode.build(
|
||||
this.run(fn, subjectToInstrumentation),
|
||||
callArgs.toArray,
|
||||
defaultsExecutionMode
|
||||
)
|
||||
|
||||
setLocation(appNode, loc)
|
||||
}
|
||||
|
@ -16,10 +16,7 @@ import org.enso.compiler.pass.analyse.{
|
||||
}
|
||||
import org.enso.compiler.pass.desugar._
|
||||
import org.enso.compiler.pass.lint.UnusedBindings
|
||||
import org.enso.compiler.pass.optimise.{
|
||||
ApplicationSaturation,
|
||||
LambdaConsolidate
|
||||
}
|
||||
import org.enso.compiler.pass.optimise.{LambdaConsolidate}
|
||||
import org.enso.compiler.pass.resolve.{IgnoredBindings, OverloadsResolution}
|
||||
import org.enso.compiler.test.CompilerTest
|
||||
|
||||
@ -39,7 +36,6 @@ class PassManagerTest extends CompilerTest {
|
||||
LambdaConsolidate,
|
||||
OverloadsResolution,
|
||||
DemandAnalysis,
|
||||
ApplicationSaturation,
|
||||
TailCall,
|
||||
AliasAnalysis,
|
||||
DataflowAnalysis,
|
||||
|
@ -24,7 +24,6 @@ import org.enso.compiler.pass.analyse.DataflowAnalysis.{
|
||||
DependencyMapping
|
||||
}
|
||||
import org.enso.compiler.pass.analyse.{AliasAnalysis, DataflowAnalysis}
|
||||
import org.enso.compiler.pass.optimise.ApplicationSaturation
|
||||
import org.enso.compiler.pass.{PassConfiguration, PassGroup, PassManager}
|
||||
import org.enso.compiler.test.CompilerTest
|
||||
import org.enso.interpreter.runtime.scope.LocalScope
|
||||
@ -44,8 +43,7 @@ class DataflowAnalysisTest extends CompilerTest {
|
||||
passes.getPrecursors(DataflowAnalysis).get
|
||||
|
||||
val passConfig: PassConfiguration = PassConfiguration(
|
||||
AliasAnalysis -->> AliasAnalysis.Configuration(),
|
||||
ApplicationSaturation -->> ApplicationSaturation.Configuration()
|
||||
AliasAnalysis -->> AliasAnalysis.Configuration()
|
||||
)
|
||||
|
||||
implicit val passManager: PassManager =
|
||||
|
@ -16,7 +16,6 @@ import org.enso.compiler.core.ir.expression.Case
|
||||
import org.enso.compiler.pass.PassConfiguration._
|
||||
import org.enso.compiler.pass.analyse.TailCall.TailPosition
|
||||
import org.enso.compiler.pass.analyse.{AliasAnalysis, TailCall}
|
||||
import org.enso.compiler.pass.optimise.ApplicationSaturation
|
||||
import org.enso.compiler.pass.{PassConfiguration, PassGroup, PassManager}
|
||||
import org.enso.compiler.test.CompilerTest
|
||||
import org.enso.interpreter.runtime.scope.LocalScope
|
||||
@ -49,8 +48,7 @@ class TailCallTest extends CompilerTest {
|
||||
val precursorPasses: PassGroup = passes.getPrecursors(TailCall).get
|
||||
|
||||
val passConfiguration: PassConfiguration = PassConfiguration(
|
||||
AliasAnalysis -->> AliasAnalysis.Configuration(),
|
||||
ApplicationSaturation -->> ApplicationSaturation.Configuration()
|
||||
AliasAnalysis -->> AliasAnalysis.Configuration()
|
||||
)
|
||||
|
||||
implicit val passManager: PassManager =
|
||||
|
@ -8,7 +8,6 @@ import org.enso.compiler.core.ir.expression.{warnings, Case}
|
||||
import org.enso.compiler.pass.PassConfiguration._
|
||||
import org.enso.compiler.pass.analyse._
|
||||
import org.enso.compiler.pass.lint.UnusedBindings
|
||||
import org.enso.compiler.pass.optimise.ApplicationSaturation
|
||||
import org.enso.compiler.pass.{PassConfiguration, PassGroup, PassManager}
|
||||
import org.enso.compiler.test.CompilerTest
|
||||
import org.enso.interpreter.runtime.scope.LocalScope
|
||||
@ -23,8 +22,7 @@ class UnusedBindingsTest extends CompilerTest with Inside {
|
||||
val precursorPasses: PassGroup = passes.getPrecursors(UnusedBindings).get
|
||||
|
||||
val passConfiguration: PassConfiguration = PassConfiguration(
|
||||
ApplicationSaturation -->> ApplicationSaturation.Configuration(),
|
||||
AliasAnalysis -->> AliasAnalysis.Configuration()
|
||||
AliasAnalysis -->> AliasAnalysis.Configuration()
|
||||
)
|
||||
|
||||
implicit val passManager: PassManager =
|
||||
|
@ -1,327 +0,0 @@
|
||||
package org.enso.compiler.test.pass.optimise
|
||||
|
||||
import org.enso.compiler.Passes
|
||||
import org.enso.compiler.context.FreshNameSupply
|
||||
import org.enso.compiler.core.Implicits.AsMetadata
|
||||
import org.enso.compiler.core.ir.{CallArgument, Empty, Expression, Name}
|
||||
import org.enso.compiler.core.ir.expression.Application
|
||||
import org.enso.compiler.pass.PassConfiguration._
|
||||
import org.enso.compiler.pass.analyse.{AliasAnalysis, TailCall}
|
||||
import org.enso.compiler.pass.optimise.ApplicationSaturation
|
||||
import org.enso.compiler.pass.optimise.ApplicationSaturation.{
|
||||
CallSaturation,
|
||||
FunctionSpec,
|
||||
Metadata
|
||||
}
|
||||
import org.enso.compiler.pass.{PassConfiguration, PassManager}
|
||||
import org.enso.compiler.test.CompilerTest
|
||||
import org.enso.interpreter.node.ExpressionNode
|
||||
import org.enso.interpreter.runtime.scope.{LocalScope, ModuleScope}
|
||||
import org.enso.interpreter.runtime.callable
|
||||
|
||||
import scala.annotation.unused
|
||||
|
||||
class ApplicationSaturationTest extends CompilerTest {
|
||||
|
||||
// === Utilities ============================================================
|
||||
|
||||
/** Generates n arguments that _do not_ contain function applications
|
||||
* themselves.
|
||||
*
|
||||
* @param n the number of arguments to generate
|
||||
* @param positional whether or not the arguments should be generated by name
|
||||
* or positionally
|
||||
* @return a list containing `n` arguments
|
||||
*/
|
||||
def genNArgs(n: Int, positional: Boolean = true): List[CallArgument] = {
|
||||
val name = if (positional) {
|
||||
None
|
||||
} else {
|
||||
Some(Name.Literal("a", isMethod = false, None))
|
||||
}
|
||||
|
||||
List.fill(n)(CallArgument.Specified(name, Empty(None), None))
|
||||
}
|
||||
|
||||
// === Test Setup ===========================================================
|
||||
|
||||
// The functions are unused, so left undefined for ease of testing
|
||||
def dummyFn(@unused mod: ModuleScope)(@unused loc: LocalScope)(
|
||||
@unused args: List[callable.argument.CallArgument]
|
||||
): ExpressionNode = ???
|
||||
|
||||
val knownFunctions: ApplicationSaturation.Configuration =
|
||||
ApplicationSaturation.Configuration(
|
||||
Map(
|
||||
"+" -> FunctionSpec(2, dummyFn),
|
||||
"baz" -> FunctionSpec(3, dummyFn),
|
||||
"foo" -> FunctionSpec(4, dummyFn)
|
||||
)
|
||||
)
|
||||
|
||||
val passes: Passes = new Passes(defaultConfig)
|
||||
|
||||
val precursorPasses = passes.getPrecursors(TailCall).get
|
||||
|
||||
val knownPassConfig: PassConfiguration = PassConfiguration(
|
||||
ApplicationSaturation -->> knownFunctions,
|
||||
AliasAnalysis -->> AliasAnalysis.Configuration()
|
||||
)
|
||||
val passManagerKnown = new PassManager(List(precursorPasses), knownPassConfig)
|
||||
|
||||
val localScope: Option[LocalScope] = Some(LocalScope.root)
|
||||
|
||||
val knownCtx = buildInlineContext(
|
||||
localScope = localScope,
|
||||
freshNameSupply = Some(new FreshNameSupply),
|
||||
passConfiguration = Some(knownPassConfig)
|
||||
)
|
||||
|
||||
val moduleCtx = buildModuleContext(
|
||||
passConfiguration = Some(knownPassConfig),
|
||||
freshNameSupply = Some(new FreshNameSupply)
|
||||
)
|
||||
|
||||
// === The Tests ============================================================
|
||||
|
||||
"Known applications" should {
|
||||
val plusFn = Application
|
||||
.Prefix(
|
||||
Name.Literal("+", isMethod = true, None),
|
||||
genNArgs(2),
|
||||
hasDefaultsSuspended = false,
|
||||
None
|
||||
)
|
||||
.runPasses(passManagerKnown, knownCtx)
|
||||
.asInstanceOf[Application.Prefix]
|
||||
|
||||
val bazFn = Application
|
||||
.Prefix(
|
||||
Name.Literal("baz", isMethod = false, None),
|
||||
genNArgs(2),
|
||||
hasDefaultsSuspended = false,
|
||||
None
|
||||
)
|
||||
.runPasses(passManagerKnown, knownCtx)
|
||||
.asInstanceOf[Application.Prefix]
|
||||
|
||||
val fooFn = Application
|
||||
.Prefix(
|
||||
Name.Literal("foo", isMethod = false, None),
|
||||
genNArgs(5),
|
||||
hasDefaultsSuspended = false,
|
||||
None
|
||||
)
|
||||
.runPasses(passManagerKnown, knownCtx)
|
||||
.asInstanceOf[Application.Prefix]
|
||||
|
||||
val fooFnByName = Application
|
||||
.Prefix(
|
||||
Name.Literal("foo", isMethod = false, None),
|
||||
genNArgs(4, positional = false),
|
||||
hasDefaultsSuspended = false,
|
||||
None
|
||||
)
|
||||
.runPasses(passManagerKnown, knownCtx)
|
||||
.asInstanceOf[Application.Prefix]
|
||||
|
||||
"be tagged with full saturation where possible" in {
|
||||
val resultIR =
|
||||
ApplicationSaturation.runExpression(plusFn, knownCtx)
|
||||
|
||||
resultIR.getMetadata(ApplicationSaturation).foreach {
|
||||
case _: CallSaturation.Exact => succeed
|
||||
case _ => fail()
|
||||
}
|
||||
}
|
||||
|
||||
"be tagged with partial saturation where possible" in {
|
||||
val resultIR =
|
||||
ApplicationSaturation.runExpression(bazFn, knownCtx)
|
||||
val expected = Some(CallSaturation.Partial(1))
|
||||
|
||||
resultIR.getMetadata(ApplicationSaturation) shouldEqual expected
|
||||
}
|
||||
|
||||
"be tagged with over saturation where possible" in {
|
||||
val resultIR =
|
||||
ApplicationSaturation.runExpression(fooFn, knownCtx)
|
||||
val expected = Some(CallSaturation.Over(1))
|
||||
|
||||
resultIR.getMetadata(ApplicationSaturation) shouldEqual expected
|
||||
}
|
||||
|
||||
"be tagged with by name if applied by name" in {
|
||||
val resultIR =
|
||||
ApplicationSaturation.runExpression(fooFnByName, knownCtx)
|
||||
val expected = Some(CallSaturation.ExactButByName())
|
||||
|
||||
resultIR.getMetadata(ApplicationSaturation) shouldEqual expected
|
||||
}
|
||||
}
|
||||
|
||||
"Unknown applications" should {
|
||||
val unknownFn = Application
|
||||
.Prefix(
|
||||
Name.Literal("unknown", isMethod = false, None),
|
||||
genNArgs(10),
|
||||
hasDefaultsSuspended = false,
|
||||
None
|
||||
)
|
||||
.runPasses(passManagerKnown, knownCtx)
|
||||
.asInstanceOf[Application.Prefix]
|
||||
|
||||
"be tagged with unknown saturation" in {
|
||||
val resultIR =
|
||||
ApplicationSaturation.runExpression(unknownFn, knownCtx)
|
||||
val expected = Some(CallSaturation.Unknown())
|
||||
|
||||
resultIR.getMetadata(ApplicationSaturation) shouldEqual expected
|
||||
}
|
||||
}
|
||||
|
||||
"Known applications containing known applications" should {
|
||||
val empty = Empty(None)
|
||||
val knownPlus = Application
|
||||
.Prefix(
|
||||
Name.Literal("+", isMethod = true, None),
|
||||
genNArgs(2),
|
||||
hasDefaultsSuspended = false,
|
||||
None
|
||||
)
|
||||
.runPasses(passManagerKnown, knownCtx)
|
||||
.asInstanceOf[Application.Prefix]
|
||||
|
||||
val undersaturatedPlus = Application
|
||||
.Prefix(
|
||||
Name.Literal("+", isMethod = true, None),
|
||||
genNArgs(1),
|
||||
hasDefaultsSuspended = false,
|
||||
None
|
||||
)
|
||||
.runPasses(passManagerKnown, knownCtx)
|
||||
.asInstanceOf[Application.Prefix]
|
||||
|
||||
val oversaturatedPlus = Application
|
||||
.Prefix(
|
||||
Name.Literal("+", isMethod = true, None),
|
||||
genNArgs(3),
|
||||
hasDefaultsSuspended = false,
|
||||
None
|
||||
)
|
||||
.runPasses(passManagerKnown, knownCtx)
|
||||
.asInstanceOf[Application.Prefix]
|
||||
|
||||
implicit class InnerMeta(ir: Expression) {
|
||||
def getInnerMetadata: Option[Metadata] = {
|
||||
ir.asInstanceOf[Application.Prefix]
|
||||
.arguments
|
||||
.head
|
||||
.asInstanceOf[CallArgument.Specified]
|
||||
.value
|
||||
.getMetadata(ApplicationSaturation)
|
||||
}
|
||||
}
|
||||
|
||||
def outerPlus(argExpr: Expression): Application.Prefix = {
|
||||
Application
|
||||
.Prefix(
|
||||
Name.Literal("+", isMethod = true, None),
|
||||
List(
|
||||
CallArgument.Specified(None, argExpr, None),
|
||||
CallArgument.Specified(None, empty, None)
|
||||
),
|
||||
hasDefaultsSuspended = false,
|
||||
None
|
||||
)
|
||||
.runPasses(passManagerKnown, knownCtx)
|
||||
.asInstanceOf[Application.Prefix]
|
||||
}
|
||||
|
||||
"have fully saturated applications tagged correctly" in {
|
||||
val result =
|
||||
ApplicationSaturation.runExpression(outerPlus(knownPlus), knownCtx)
|
||||
|
||||
// The outer should be reported as fully saturated
|
||||
result.getMetadata(ApplicationSaturation).foreach {
|
||||
case _: CallSaturation.Exact => succeed
|
||||
case _ => fail()
|
||||
}
|
||||
|
||||
// The inner should be reported as fully saturated
|
||||
result.getInnerMetadata.foreach {
|
||||
case _: CallSaturation.Exact => succeed
|
||||
case _ => fail()
|
||||
}
|
||||
}
|
||||
|
||||
"have non-fully saturated applications tagged correctly" in {
|
||||
val result =
|
||||
ApplicationSaturation.runExpression(
|
||||
outerPlus(undersaturatedPlus),
|
||||
knownCtx
|
||||
)
|
||||
val expectedInnerMeta = CallSaturation.Partial(1)
|
||||
|
||||
// The outer should be reported as fully saturated
|
||||
result.getMetadata(ApplicationSaturation).foreach {
|
||||
case _: CallSaturation.Exact => succeed
|
||||
case _ => fail()
|
||||
}
|
||||
|
||||
// The inner should be reported as under saturateD
|
||||
result.getInnerMetadata
|
||||
.foreach(t => t shouldEqual expectedInnerMeta)
|
||||
}
|
||||
|
||||
"have a mixture of application saturations tagged correctly" in {
|
||||
val result =
|
||||
ApplicationSaturation.runExpression(
|
||||
outerPlus(oversaturatedPlus),
|
||||
knownCtx
|
||||
)
|
||||
val expectedInnerMeta = CallSaturation.Over(1)
|
||||
|
||||
// The outer should be reported as fully saturated
|
||||
result.getMetadata(ApplicationSaturation).foreach {
|
||||
case _: CallSaturation.Exact => succeed
|
||||
case _ => fail()
|
||||
}
|
||||
|
||||
// The inner should be reported as under saturateD
|
||||
result.getInnerMetadata
|
||||
.foreach(t => t shouldEqual expectedInnerMeta)
|
||||
}
|
||||
}
|
||||
|
||||
"Shadowed known functions" should {
|
||||
val rawIR =
|
||||
"""
|
||||
|main =
|
||||
| foo = x -> y -> z -> x + y + z
|
||||
|
|
||||
| foo a b c
|
||||
|""".stripMargin.toIrExpression
|
||||
|
||||
val inputIR = rawIR.get
|
||||
.runPasses(passManagerKnown, knownCtx)
|
||||
.asInstanceOf[Expression]
|
||||
|
||||
val result = ApplicationSaturation
|
||||
.runExpression(inputIR, knownCtx)
|
||||
.asInstanceOf[Expression.Binding]
|
||||
|
||||
"be tagged as unknown even if their name is known" in {
|
||||
// Needs alias analysis to work
|
||||
result.expression
|
||||
.asInstanceOf[Expression.Block]
|
||||
.returnValue
|
||||
.getMetadata(ApplicationSaturation)
|
||||
.foreach {
|
||||
case _: CallSaturation.Unknown => succeed
|
||||
case _ => fail()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -8,7 +8,6 @@ import org.enso.compiler.core.ir.expression.{errors, Case}
|
||||
import org.enso.compiler.core.ir.module.scope.definition
|
||||
import org.enso.compiler.pass.PassConfiguration.ToPair
|
||||
import org.enso.compiler.pass.analyse.AliasAnalysis
|
||||
import org.enso.compiler.pass.optimise.ApplicationSaturation
|
||||
import org.enso.compiler.pass.resolve.Patterns
|
||||
import org.enso.compiler.pass.{PassConfiguration, PassGroup, PassManager}
|
||||
import org.enso.compiler.test.CompilerTest
|
||||
@ -28,8 +27,7 @@ class PatternsTest extends CompilerTest {
|
||||
passes.getPrecursors(Patterns).get
|
||||
|
||||
val passConfiguration: PassConfiguration = PassConfiguration(
|
||||
AliasAnalysis -->> AliasAnalysis.Configuration(),
|
||||
ApplicationSaturation -->> ApplicationSaturation.Configuration()
|
||||
AliasAnalysis -->> AliasAnalysis.Configuration()
|
||||
)
|
||||
|
||||
implicit val passManager: PassManager =
|
||||
|
Loading…
Reference in New Issue
Block a user