Misc compiler enhancements (#1748)

This commit is contained in:
Ara Adkins 2021-05-21 16:53:44 +01:00 committed by GitHub
parent f34f8be895
commit 80eff9c017
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
63 changed files with 1916 additions and 355 deletions

View File

@ -6,6 +6,10 @@
[GraalVM 21.1.0](https://github.com/graalvm/graalvm-ce-builds/releases/tag/vm-21.1.0)
([#1738](https://github.com/enso-org/enso/pull/1738)). This brings a raft of
bug-fixes and improvements to how quickly Enso can reach its peak performance.
- Added support for bidirectional dataflow tracking to the `DataflowAnalysis`
pass ([#1748](https://github.com/enso-org/enso/pull/1748)). This will allow
the interpreter to perform more detailed analyses in the future to enable
optimisations and new features.
## Tooling
@ -17,6 +21,9 @@
- Implemented log masking ([#1732](https://github.com/enso-org/enso/pull/1732)).
This feature masks personally identifiable information in the logs, such as
code literals, computed values, and user environment variables.
- Added support for evaluating one-shot expressions on the result values of
arbitrary expressions ([#1749](https://github.com/enso-org/enso/pull/1749)).
This is very useful for enabling more advanced introspection in the IDE.
## Libraries

View File

@ -1,11 +1,10 @@
package org.enso.polyglot;
import org.graalvm.options.OptionDescriptor;
import org.graalvm.options.OptionDescriptors;
import org.graalvm.options.OptionKey;
import java.util.Arrays;
import java.util.logging.Level;
import org.graalvm.options.OptionDescriptor;
import org.graalvm.options.OptionDescriptors;
import org.graalvm.options.OptionKey;
/** Class representing runtime options supported by the Enso engine. */
public class RuntimeOptions {
@ -56,9 +55,9 @@ public class RuntimeOptions {
STRICT_ERRORS_DESCRIPTOR,
LOG_LEVEL_DESCRIPTOR,
DISABLE_INLINE_CACHES_DESCRIPTOR,
INTERPRETER_SEQUENTIAL_COMMAND_EXECUTION_DESCRIPTOR,
ENABLE_PROJECT_SUGGESTIONS_DESCRIPTOR,
ENABLE_GLOBAL_SUGGESTIONS_DESCRIPTOR));
ENABLE_GLOBAL_SUGGESTIONS_DESCRIPTOR,
INTERPRETER_SEQUENTIAL_COMMAND_EXECUTION_DESCRIPTOR));
/**
* Canonicalizes the option name by prefixing it with the language name.

View File

@ -61,14 +61,13 @@ public abstract class EvalNode extends BaseNode {
public abstract Stateful execute(CallerInfo callerInfo, Object state, Text expression);
RootCallTarget parseExpression(LocalScope scope, ModuleScope moduleScope, String expression) {
Context context = lookupContextReference(Language.class).get();
LocalScope localScope = scope.createChild();
InlineContext inlineContext = InlineContext.fromJava(localScope, moduleScope, getTailStatus());
InlineContext inlineContext =
InlineContext.fromJava(
localScope, moduleScope, getTailStatus(), context.getCompilerConfig());
ExpressionNode expr =
lookupContextReference(Language.class)
.get()
.getCompiler()
.runInline(expression, inlineContext)
.getOrElse(null);
context.getCompiler().runInline(expression, inlineContext).getOrElse(null);
if (expr == null) {
throw new RuntimeException("Invalid code passed to `eval`: " + expression);
}

View File

@ -18,6 +18,7 @@ import java.util.UUID;
import java.util.function.Function;
import java.util.stream.Collectors;
import org.enso.compiler.Compiler;
import org.enso.compiler.data.CompilerConfig;
import org.enso.home.HomeManager;
import org.enso.interpreter.Language;
import org.enso.interpreter.OptionsHelper;
@ -53,6 +54,7 @@ public class Context {
private final Builtins builtins;
private final String home;
private final List<ShadowedPackage> shadowedPackages;
private final CompilerConfig compilerConfig;
/**
* Creates a new Enso context.
@ -70,12 +72,13 @@ public class Context {
this.threadManager = new ThreadManager();
this.resourceManager = new ResourceManager(this);
this.isCachingDisabled = environment.getOptions().get(RuntimeOptions.DISABLE_INLINE_CACHES_KEY);
this.compilerConfig = new CompilerConfig(false, true);
this.home = home;
this.shadowedPackages = new ArrayList<>();
builtins = new Builtins(this);
this.compiler = new Compiler(this, builtins);
this.compiler = new Compiler(this, builtins, compilerConfig);
}
/** Perform expensive initialization logic for the context. */
@ -384,4 +387,9 @@ public class Context {
public List<ShadowedPackage> getShadowedPackages() {
return shadowedPackages;
}
/** @return the compiler configuration for this language */
public CompilerConfig getCompilerConfig() {
return compilerConfig;
}
}

View File

@ -5,6 +5,7 @@ import org.enso.compiler.codegen.{AstToIr, IrToTruffle, RuntimeStubsGenerator}
import org.enso.compiler.context.{FreshNameSupply, InlineContext, ModuleContext}
import org.enso.compiler.core.IR
import org.enso.compiler.core.IR.Expression
import org.enso.compiler.data.CompilerConfig
import org.enso.compiler.exception.{CompilationAbortedException, CompilerError}
import org.enso.compiler.pass.PassManager
import org.enso.compiler.pass.analyse._
@ -31,9 +32,13 @@ import scala.jdk.OptionConverters._
*
* @param context the language context
*/
class Compiler(val context: Context, private val builtins: Builtins) {
class Compiler(
val context: Context,
val builtins: Builtins,
config: CompilerConfig
) {
private val freshNameSupply: FreshNameSupply = new FreshNameSupply
private val passes: Passes = new Passes
private val passes: Passes = new Passes(config)
private val passManager: PassManager = passes.passManager
private val importResolver: ImportResolver = new ImportResolver(this)
private val stubsGenerator: RuntimeStubsGenerator =
@ -80,7 +85,8 @@ class Compiler(val context: Context, private val builtins: Builtins) {
val moduleContext = ModuleContext(
module = module,
freshNameSupply = Some(freshNameSupply)
freshNameSupply = Some(freshNameSupply),
compilerConfig = config
)
val compilerOutput = runCompilerPhases(module.getIr, moduleContext)
module.unsafeSetIr(compilerOutput)
@ -121,7 +127,8 @@ class Compiler(val context: Context, private val builtins: Builtins) {
module.getScope.reset()
val moduleContext = ModuleContext(
module = module,
freshNameSupply = Some(freshNameSupply)
freshNameSupply = Some(freshNameSupply),
compilerConfig = config
)
val parsedAST = parse(module.getSource)
val expr = generateIR(parsedAST)
@ -331,7 +338,10 @@ class Compiler(val context: Context, private val builtins: Builtins) {
if (context.isStrictErrors) {
val diagnostics = modules.map { module =>
val errors = GatherDiagnostics
.runModule(module.getIr, new ModuleContext(module))
.runModule(
module.getIr,
ModuleContext(module, compilerConfig = config)
)
.unsafeGetMetadata(
GatherDiagnostics,
"No diagnostics metadata right after the gathering pass."
@ -467,7 +477,7 @@ class Compiler(val context: Context, private val builtins: Builtins) {
source: Source,
scope: ModuleScope
): Unit = {
new IrToTruffle(context, source, scope).run(ir)
new IrToTruffle(context, source, scope, config).run(ir)
}
/** Generates code for the truffle interpreter in an inline context.
@ -486,7 +496,8 @@ class Compiler(val context: Context, private val builtins: Builtins) {
new IrToTruffle(
context,
source,
inlineContext.module.getScope
inlineContext.module.getScope,
config
).runInline(
ir,
inlineContext.localScope.getOrElse(LocalScope.root),

View File

@ -1,5 +1,6 @@
package org.enso.compiler
import org.enso.compiler.data.CompilerConfig
import org.enso.compiler.pass.PassConfiguration._
import org.enso.compiler.pass.analyse._
import org.enso.compiler.pass.desugar._
@ -17,7 +18,10 @@ import org.enso.compiler.pass.{
PassManager
}
class Passes(passes: Option[List[PassGroup]] = None) {
class Passes(
config: CompilerConfig,
passes: Option[List[PassGroup]] = None
) {
val moduleDiscoveryPasses = new PassGroup(
List(
@ -60,7 +64,16 @@ class Passes(passes: Option[List[PassGroup]] = None) {
Patterns,
AliasAnalysis,
UndefinedVariables,
DataflowAnalysis,
DataflowAnalysis
) ++
(if (config.autoParallelismEnabled) {
List(
AutomaticParallelism,
AliasAnalysis,
DataflowAnalysis
)
} else List()) ++
List(
CachePreferenceAnalysis,
UnusedBindings
)
@ -79,7 +92,10 @@ class Passes(passes: Option[List[PassGroup]] = None) {
/** Configuration for the passes. */
private val passConfig: PassConfiguration = PassConfiguration(
ApplicationSaturation -->> ApplicationSaturation.Configuration(),
AliasAnalysis -->> AliasAnalysis.Configuration()
AliasAnalysis -->> AliasAnalysis.Configuration(),
AutomaticParallelism -->> AutomaticParallelism.Configuration(
config.autoParallelismEnabled
)
)
/** The pass manager for running compiler passes. */

View File

@ -6,7 +6,7 @@ import com.oracle.truffle.api.source.{Source, SourceSection}
import org.enso.compiler.core.IR
import org.enso.compiler.core.IR.Module.Scope.Import
import org.enso.compiler.core.IR.{Error, IdentifiedLocation, Pattern}
import org.enso.compiler.data.BindingsMap
import org.enso.compiler.data.{BindingsMap, CompilerConfig}
import org.enso.compiler.exception.{BadPatternMatch, CompilerError}
import org.enso.compiler.pass.analyse.AliasAnalysis.Graph.{Scope => AliasScope}
import org.enso.compiler.pass.analyse.AliasAnalysis.{Graph => AliasGraph}
@ -85,11 +85,13 @@ import scala.collection.mutable.ArrayBuffer
* @param source the source code that corresponds to the text for which code
* is being generated
* @param moduleScope the scope of the module for which code is being generated
* @param compilerConfig the configuration for the compiler
*/
class IrToTruffle(
val context: Context,
val source: Source,
val moduleScope: ModuleScope
val moduleScope: ModuleScope,
val compilerConfig: CompilerConfig
) {
val language: Language = context.getLanguage

View File

@ -68,7 +68,7 @@ final class ChangesetBuilder[A: TextEditor: IndexedSource](
if (queue.isEmpty) visited.flatMap(_.externalId).toSet
else {
val elem = queue.dequeue()
val transitive = metadata.get(elem).getOrElse(Set())
val transitive = metadata.dependents.get(elem).getOrElse(Set())
val dynamic = transitive
.flatMap {
case DataflowAnalysis.DependencyInfo.Type.Static(int, _) =>
@ -80,7 +80,7 @@ final class ChangesetBuilder[A: TextEditor: IndexedSource](
case _ =>
None
}
.flatMap(metadata.get)
.flatMap(metadata.dependents.get)
.flatten
val combined = transitive.union(dynamic)

View File

@ -1,9 +1,10 @@
package org.enso.compiler.context
import org.enso.compiler.data.CompilerConfig
import org.enso.compiler.pass.PassConfiguration
import org.enso.interpreter.node.BaseNode.TailStatus
import org.enso.interpreter.runtime.scope.{LocalScope, ModuleScope}
import org.enso.interpreter.runtime.Module
import org.enso.interpreter.runtime.scope.{LocalScope, ModuleScope}
/** A type containing the information about the execution context for an inline
* expression.
@ -14,6 +15,7 @@ import org.enso.interpreter.runtime.Module
* position ([[None]] indicates no information)
* @param freshNameSupply the compiler's supply of fresh names
* @param passConfiguration the pass configuration
* @param compilerConfig the compiler configuration
*/
case class InlineContext(
module: Module,
@ -21,7 +23,7 @@ case class InlineContext(
isInTailPosition: Option[Boolean] = None,
freshNameSupply: Option[FreshNameSupply] = None,
passConfiguration: Option[PassConfiguration] = None,
noWarnings: Boolean = false
compilerConfig: CompilerConfig
)
object InlineContext {
@ -37,12 +39,31 @@ object InlineContext {
def fromJava(
localScope: LocalScope,
moduleScope: ModuleScope,
isInTailPosition: TailStatus
isInTailPosition: TailStatus,
compilerConfig: CompilerConfig
): InlineContext = {
InlineContext(
localScope = Option(localScope),
module = moduleScope.getModule,
isInTailPosition = Option(isInTailPosition != TailStatus.NOT_TAIL)
isInTailPosition = Option(isInTailPosition != TailStatus.NOT_TAIL),
compilerConfig = compilerConfig
)
}
/** Transform a module context into an inline context, retaining the useful
* information.
*
* @param moduleContext the module context
* @return an inline context wrapping the same data as `moduleContext`
*/
def fromModuleContext(moduleContext: ModuleContext): InlineContext = {
InlineContext(
localScope = None,
module = moduleContext.module,
isInTailPosition = None,
freshNameSupply = moduleContext.freshNameSupply,
passConfiguration = moduleContext.passConfiguration,
compilerConfig = moduleContext.compilerConfig
)
}
}

View File

@ -1,5 +1,6 @@
package org.enso.compiler.context
import org.enso.compiler.data.CompilerConfig
import org.enso.compiler.pass.PassConfiguration
import org.enso.interpreter.runtime.Module
@ -8,10 +9,11 @@ import org.enso.interpreter.runtime.Module
* @param module the current module scope
* @param freshNameSupply the compiler's supply of fresh names
* @param passConfiguration the pass configuration
* @param compilerConfig the compiler configuration
*/
case class ModuleContext(
module: Module,
freshNameSupply: Option[FreshNameSupply] = None,
passConfiguration: Option[PassConfiguration] = None,
noWarnings: Boolean = false
compilerConfig: CompilerConfig
)

View File

@ -5331,6 +5331,21 @@ object IR {
s"The pattern field $shadowedName is shadowed by $shadower."
}
}
/** A warning raised when a call is annotated with `@Auto_Parallel`, but the
* annotation cannot be obeyed.
*
* @param ir the annotated application
* @param reason the reason why the annotation cannot be obeyed
*/
case class FailedParallelism(
ir: IR,
reason: String
) extends Warning {
override val location: Option[IdentifiedLocation] = ir.location
override def message: String =
s"The expression ${ir.showCode()} could not be parallelised: $reason."
}
}
// === Errors ===============================================================

View File

@ -0,0 +1,12 @@
package org.enso.compiler.data
/** Configuration for the compiler.
*
* @param autoParallelismEnabled whether or not automatic parallelism detection
* is enabled.
* @param warningsEnabled whether or not warnings are enabled
*/
case class CompilerConfig(
autoParallelismEnabled: Boolean = false,
warningsEnabled: Boolean = true
)

View File

@ -0,0 +1,568 @@
package org.enso.compiler.pass.analyse
import org.enso.compiler.context.{InlineContext, ModuleContext}
import org.enso.compiler.core.IR
import org.enso.compiler.core.IR.Module.Scope.Definition
import org.enso.compiler.core.IR.{
Application,
CallArgument,
Case,
DefinitionArgument,
Error,
Name,
Type
}
import org.enso.compiler.exception.CompilerError
import org.enso.compiler.pass.IRPass
import org.enso.compiler.pass.analyse.DataflowAnalysis.DependencyInfo
import org.enso.compiler.pass.desugar.ComplexType
import org.enso.compiler.pass.resolve.ExpressionAnnotations
import scala.annotation.unused
import scala.collection.mutable
/** This pass is responsible for discovering occurrences of automatically
* parallelizable computations. If it finds a join point, annotated with the
* &#96;@Auto_Parallel&#96; annotation, it will discover the incoming
* computations that can safely be parallelized.
*
* This is a limited process, and operates under the following core
* assumptions:
*
* - The incoming edges are _entirely_ separate (this pass will not act on
* diamond patterns).
* - The incoming edges perform no side-effecting computations that would be
* observable from the other edges.
* - This functionality does not have to operate in the IDE.
*
* Additionally, it will only trigger when the following conditions hold:
*
* - The annotated call takes more than one argument.
* - All dependent names are defined in the same method.
* - The dependencies of the `@Auto_Parallel` computation may only be used
* once.
* - The dependencies must be able to be inlined.
*/
object AutomaticParallelism extends IRPass {
override type Metadata = IRPass.Metadata.Empty
override type Config = Configuration
override val precursorPasses: Seq[IRPass] = Seq(
AliasAnalysis,
DataflowAnalysis,
ComplexType
)
override val invalidatedPasses: Seq[IRPass] = Seq(
AliasAnalysis,
DataflowAnalysis
)
/** The specific dependency information. */
type DepInfoType = DataflowAnalysis.DependencyInfo.Type
/** Executes the pass on a module.
*
* @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: IR.Module,
@unused moduleContext: ModuleContext
): IR.Module = ir
/** Executes the pass on an expression.
*
* @param ir the Enso IR to process
* @param inlineContext a context object that contains the information needed
* for inline evaluation
* @return `ir`, possibly having made transformations or annotations to that
* IR.
*/
override def runExpression(
ir: IR.Expression,
@unused inlineContext: InlineContext
): IR.Expression = ir
// If I can do the limited form, then it is sufficient to have spawn/await on
// bindings combined with liberal inlining of the other parts of the
// computation.
// - At the binding, spawn a thread and store an identifier into the cell.
// - At the read emit a special ReadVariableNode that joins on that thread.
// This becomes even easier if I can make assumptions about where the
// annotated expression is, but that isn't necessary for this approach.
// === Pass Implementation ==================================================
@unused private def processModuleDefinition(
binding: Definition,
inlineContext: InlineContext
): Definition = {
binding match {
case method: Definition.Method.Explicit =>
processMethod(method, inlineContext)
case atom: Definition.Atom => atom
case _: Definition.Type =>
throw new CompilerError(
"Complex type definitions should not be present at the point of " +
"parallelism analysis."
)
case _: Definition.Method.Binding =>
throw new CompilerError(
"Binding-style methods should be desugared by the point of " +
"parallelism analysis."
)
case _: Name.Annotation =>
throw new CompilerError(
"Annotations should be desugared by the point of parallelism " +
"analysis."
)
case _: Type.Ascription =>
throw new CompilerError(
"Type ascriptions should be desugared by the point of parallelism " +
"analysis."
)
case _: IR.Comment =>
throw new CompilerError(
"Type ascriptions should be desugared by the point of parallelism " +
"analysis."
)
case err: Error => err
}
}
private def processMethod(
method: Definition.Method.Explicit,
context: InlineContext
): Definition.Method.Explicit = {
val body = method.body match {
case lam: IR.Function.Lambda => runExpression(lam, context)
case _ =>
throw new CompilerError("Explicit methods should only contain lambdas.")
}
method.copy(body = body)
}
private def processExpression(
expr: IR.Expression,
mutData: MutablePassData,
scopedData: ScopedPassData
): IR.Expression = {
val result = expr match {
case func: IR.Function => processFunction(func, mutData, scopedData)
case app: IR.Application => processApplication(app, mutData, scopedData)
case cse: IR.Case => processCase(cse, mutData, scopedData)
case block: IR.Expression.Block =>
try {
val newScopedData = scopedData.addEnclosingBlock(block)
val newExpressions =
block.expressions.map(processExpression(_, mutData, newScopedData))
val newRet =
processExpression(block.returnValue, mutData, newScopedData)
block.copy(expressions = newExpressions, returnValue = newRet)
} catch {
case _: RewriteException =>
block // TODO [AA] Do this properly
}
case binding: IR.Expression.Binding =>
val newExpr =
processExpression(binding.expression, mutData, scopedData)
binding.copy(expression = newExpr)
case name: IR.Name => name
case lit: IR.Literal => lit
case comm: IR.Comment => comm
case foreign: IR.Foreign => foreign
case empty: IR.Empty => empty
case error: IR.Error => error
case typ: Type => typ
}
mutData.putExpr(result)
result
}
private def checkParallelCall(
app: Application.Prefix,
mutData: MutablePassData,
scopedData: ScopedPassData
): IR.Expression = {
// Check that there are enough arguments.
if (app.arguments.length <= 1) {
val warning = IR.Warning.FailedParallelism(
app,
s"Cannot parallelize an application with ${app.arguments.length} " +
s"arguments."
)
app.diagnostics.add(warning)
return app
}
// Check that the incoming flows are distinct.
val dataflow = app
.getMetadata(DataflowAnalysis)
.getOrElse(
throw new CompilerError(
"Dataflow analysis metadata is required for parallelism analysis."
)
)
val args = app.arguments.collect { case a: CallArgument.Specified => a }
val argDepSets = args.map(a => {
val dep = mkStaticDep(a)
dataflow.dependencies
.get(dep)
.getOrElse(
throw new CompilerError(
s"Unable to find dataflow analysis data for $a."
)
)
})
if (!flowsDistinct(argDepSets)) {
val warning = IR.Warning.FailedParallelism(
app,
"Arguments to the parallel call are not distinct computations."
)
app.diagnostics.add(warning)
return app
}
// Check that the incoming flows do not depend on input arguments
if (dependsOnInputArguments(argDepSets, mutData)) {
val warning = IR.Warning.FailedParallelism(
app,
"Auto-parallel calls cannot depend on input arguments as these are " +
"not statically known."
)
app.diagnostics.add(warning)
return app
}
// Check if it is safe to inline the dependencies into the flows.
if (!canInline(argDepSets, dataflow)) {
val warning = IR.Warning.FailedParallelism(
app,
"Cannot inline the dependencies of the incoming flows. They are used " +
"in more than one place."
)
app.diagnostics.add(warning)
return app
}
// Preconditions are satisfied, so perform the rewrite.
throw rewriteParallelCall(app, mutData, scopedData, dataflow)
}
// TODO [AA] The steps are as follows:
// 3. Perform inlining.
// 4. Annotate the block with the parallel streams (structure TBC)
// 6. Docs and cleanup
// How do I get the inlined blocks to the right place? ====>>>> Throw exn
private def rewriteParallelCall(
app: Application.Prefix,
mutData: MutablePassData,
@unused scopedData: ScopedPassData,
dataflow: DataflowAnalysis.Metadata
): RewriteException = {
// TODO [AA] Create a new binding to hold the intermediary. Use fresh name.
// TODO [AA] Inline the expressions into the binding for each arg.
// TODO [AA] Annotate these bindings with a ParallelExecute metadata.
// TODO [AA] Rewrite the app to use the bindings
// TODO [AA] Rewrite the expressions to the top of the tree to remove the
// dependencies.
// Generate inlined and annotated bindings
val bindingNames = app.arguments.map(_ =>
mutData.context.freshNameSupply
.getOrElse(
throw new CompilerError(
"A fresh name supply is required for parallelism analysis."
)
)
.newName()
)
@unused val bindings = bindingNames.zip(app.arguments).map { case (bindName, arg) =>
makeInlinedBindingFor(bindName, arg, mutData, dataflow)
}
// Rewrite the application to use the bindings
val newArgs = app.arguments.zip(bindingNames).map {
case (arg: CallArgument.Specified, bindingName) =>
arg.copy(value = bindingName.duplicate())
}
@unused val newApp = app.copy(arguments = newArgs)
RewriteException()
}
private def makeInlinedBindingFor(
bindingName: IR.Name,
arg: CallArgument,
mutData: MutablePassData,
dataflow: DataflowAnalysis.Metadata
): IR.Expression.Binding = arg match {
case spec: CallArgument.Specified =>
val flattened = flattenExpr(spec.value, mutData, dataflow)
val binding = IR.Expression.Binding(bindingName, flattened, None)
binding
}
// TODO [AA] Write the actual flattening.
private def flattenExpr(
expr: IR.Expression,
@unused mutData: MutablePassData,
@unused dataflow: DataflowAnalysis.Metadata
): IR.Expression = {
expr match {
case lam: IR.Function.Lambda => lam
case pre: IR.Application.Prefix => pre
case force: IR.Application.Force => force
case seq: IR.Application.Literal.Sequence => seq
case caseBranch: IR.Case.Branch => caseBranch
case cse: IR.Case => cse
case block: IR.Expression.Block => block
case binding: IR.Expression.Binding => binding
case a => a
}
}
private def canInline(
dependenciesOfArgs: Seq[Set[DepInfoType]],
dataflow: DataflowAnalysis.Metadata
): Boolean = {
val flatDeps = dependenciesOfArgs.flatten
flatDeps.forall {
case d: DependencyInfo.Type.Static =>
dataflow.dependents.getDirect(d) match {
case Some(s) => s.size == 1
case _ => false
}
case _: DependencyInfo.Type.Dynamic => true
}
}
private def dependsOnInputArguments(
args: Seq[Set[DepInfoType]],
passData: MutablePassData
): Boolean = {
val identifiers: Seq[IR.Identifier] = args.flatMap(_.collect {
case s: DependencyInfo.Type.Static => s.id
})
identifiers.exists(id => passData.getDefinitionArg(id).isDefined)
}
private def flowsDistinct(args: Seq[Set[DepInfoType]]): Boolean = {
val noDynDeps = args.map(s => {
s.collect { case s: DependencyInfo.Type.Static => s }
})
val depsCount = noDynDeps.map(_.size).sum
noDynDeps.reduceLeft((l, r) => l union r).size == depsCount
}
private def mkStaticDep(ir: IR): DependencyInfo.Type.Static = {
DependencyInfo.Type.Static(ir.getId, ir.getExternalId)
}
// TODO [AA] Error case for annotation inside call (illegal)
private def processApplication(
app: IR.Application,
mutData: MutablePassData,
scopedData: ScopedPassData
): IR.Expression = {
val result: IR.Expression = app match {
case app @ Application.Prefix(function, arguments, _, _, _, _) =>
val isParAnnotated = app
.getMetadata(ExpressionAnnotations)
.exists(m =>
m.annotations.exists(ann =>
ann.name == ExpressionAnnotations.autoParallelName
)
)
if (isParAnnotated) {
checkParallelCall(app, mutData, scopedData)
} else {
val newFn = processExpression(function, mutData, scopedData)
val newArgs = arguments.map {
case spec @ IR.CallArgument.Specified(_, value, _, _, _, _) =>
val newExpr = processExpression(value, mutData, scopedData)
val result = spec.copy(value = newExpr)
mutData.putCallArgument(result)
result
}
app.copy(function = newFn, arguments = newArgs)
}
case force @ Application.Force(target, _, _, _) =>
force.copy(target = processExpression(target, mutData, scopedData))
case sequence: Application.Literal.Sequence =>
val newItems =
sequence.items.map(processExpression(_, mutData, scopedData))
sequence.copy(items = newItems)
case lit: Application.Literal.Typeset => lit
case _: Application.Operator =>
throw new CompilerError(
"Operators should be desugared to functions by the point of " +
"parallelism analysis."
)
}
mutData.putExpr(result)
result
}
private def processFunction(
function: IR.Function,
passData: MutablePassData,
scopedData: ScopedPassData
): IR.Expression = function match {
case fn @ IR.Function.Lambda(arguments, body, _, _, _, _) => {
val processedArguments = arguments.map {
case arg @ DefinitionArgument.Specified(_, default, _, _, _, _) =>
val newDefault =
default.map(processExpression(_, passData, scopedData))
val result = arg.copy(defaultValue = newDefault)
passData.putDefinitionArg(result)
result
}
val processedBody = processExpression(body, passData, scopedData)
fn.copy(arguments = processedArguments, body = processedBody)
}
case _: IR.Function.Binding =>
throw new CompilerError(
"Binding-style functions should be desugared by the point of " +
"parallelism analysis."
)
}
private def processCase(
cse: IR.Case,
mutData: MutablePassData,
scopedData: ScopedPassData
): IR.Expression = cse match {
case expr @ Case.Expr(scrutinee, branches, _, _, _) =>
val newScrut = processExpression(scrutinee, mutData, scopedData)
val newBranches = branches.map(processCaseBranch(_, mutData, scopedData))
expr.copy(scrutinee = newScrut, branches = newBranches)
case _: Case.Branch =>
throw new CompilerError("Unexpected case branch in processCase.")
}
private def processCaseBranch(
branch: IR.Case.Branch,
mutData: MutablePassData,
scopedData: ScopedPassData
): IR.Case.Branch = {
val result =
branch.copy(expression =
processExpression(branch.expression, mutData, scopedData)
)
mutData.putExpr(result)
result
}
// === Internal Data ========================================================
private case class RewriteException() extends Exception
/** Pass data that is immutable for the pass.
*
* @param enclosingBlocks the block that encloses the current expression
*/
private case class ScopedPassData(
enclosingBlocks: Seq[IR.Expression.Block] = Seq()
) {
/** Adds an enclosing block onto the stack.
*
* @param block the new enclosing block
* @return a new instance of the pass data
*/
def addEnclosingBlock(block: IR.Expression.Block): ScopedPassData = {
this.copy(enclosingBlocks = enclosingBlocks :+ block)
}
}
/** Mutable used to perform the analysis in this pass.
*
* @param context the compiler context for the pass
*/
// TODO [AA] Make private
class MutablePassData(val context: InlineContext) {
private[this] val callArgs: mutable.Map[IR.Identifier, IR.CallArgument] =
mutable.Map()
private[this] val definitionArgs
: mutable.Map[IR.Identifier, IR.DefinitionArgument] =
mutable.Map()
private[this] val expressions: mutable.Map[IR.Identifier, IR.Expression] =
mutable.Map()
/** Stores a call argument in the pass data.
*
* @param arg the argument to store
*/
def putCallArgument(arg: IR.CallArgument): Unit = {
callArgs += arg.getId -> arg
}
/** Gets a call argument from the pass data.
*
* @param id the identifier to get an argument for
* @return the argument associated with `id`, if it exists
*/
def getCallArg(id: IR.Identifier): Option[IR.CallArgument] = {
callArgs.get(id)
}
/** Stores a definition argument in the pass data.
*
* @param arg the argument to store
*/
def putDefinitionArg(arg: IR.DefinitionArgument): Unit = {
definitionArgs += arg.getId -> arg
}
/** Gets a definition argument from the pass data.
*
* @param id the identifier to get an argument for
* @return the argument associated with `id`, if it exists
*/
def getDefinitionArg(id: IR.Identifier): Option[IR.DefinitionArgument] = {
definitionArgs.get(id)
}
/** Store the expression in the pass data.
*
* @param ir the IR to store
*/
def putExpr(ir: IR.Expression): Unit = {
expressions += ir.getId -> ir
}
/** Get the expression by the specified `id`, if it exists.
*
* @param id the identifier to get the IR for
* @return the expression associated with `id`, if it exists
*/
def getExpr(id: IR.Identifier): Option[IR.Expression] = {
expressions.get(id)
}
/** Get the IR by the specified `id`, if it exists.
*
* @param id the identifier to get the IR for
* @return the IR associated with `id`, if it exists
*/
def getIr(id: IR.Identifier): Option[IR] = {
getDefinitionArg(id).orElse(getCallArg(id).orElse(getExpr(id)))
}
}
// === Pass Configuration ===================================================
/** The configuration for this pass.
*
* @param shouldWriteParallelScopes Whether or not the pass should write
* parallel scopes into the IR
*/
case class Configuration(shouldWriteParallelScopes: Boolean)
extends IRPass.Configuration {
override var shouldWriteToContext: Boolean = false
}
}

View File

@ -83,15 +83,19 @@ case object DataflowAnalysis extends IRPass {
* @param info the dependency information for the module
* @return `binding`, with attached dependency information
*/
// TODO [AA] Can I abstract the pattern here?
def analyseModuleDefinition(
binding: IR.Module.Scope.Definition,
info: DependencyInfo
): IR.Module.Scope.Definition = {
binding match {
case atom @ IR.Module.Scope.Definition.Atom(_, arguments, _, _, _) =>
arguments.foreach(arg =>
info.updateAt(asStatic(arg), Set(asStatic(atom)))
)
arguments.foreach(arg => {
val argDep = asStatic(arg)
val atomDep = asStatic(atom)
info.dependents.updateAt(argDep, Set(atomDep))
info.dependencies.updateAt(atomDep, Set(argDep))
})
atom
.copy(
@ -100,7 +104,10 @@ case object DataflowAnalysis extends IRPass {
.updateMetadata(this -->> info)
case method @ IR.Module.Scope.Definition.Method
.Explicit(_, body, _, _, _) =>
info.updateAt(asStatic(body), Set(asStatic(method)))
val bodyDep = asStatic(body)
val methodDep = asStatic(method)
info.dependents.updateAt(bodyDep, Set(methodDep))
info.dependencies.update(methodDep, Set(bodyDep))
method
.copy(body = analyseExpression(body, info))
@ -153,17 +160,16 @@ case object DataflowAnalysis extends IRPass {
case typ: IR.Type => analyseType(typ, info)
case name: IR.Name => analyseName(name, info)
case cse: IR.Case => analyseCase(cse, info)
case _: IR.Comment =>
throw new CompilerError(
"Comments should not be present during dataflow analysis."
)
case literal: IR.Literal =>
literal.updateMetadata(this -->> info)
case foreign: IR.Foreign =>
foreign.updateMetadata(this -->> info)
case block @ IR.Expression.Block(expressions, returnValue, _, _, _, _) =>
info.updateAt(asStatic(returnValue), Set(asStatic(block)))
val retValDep = asStatic(returnValue)
val blockDep = asStatic(block)
info.dependents.updateAt(retValDep, Set(blockDep))
info.dependencies.updateAt(blockDep, Set(retValDep))
block
.copy(
@ -172,8 +178,12 @@ case object DataflowAnalysis extends IRPass {
)
.updateMetadata(this -->> info)
case binding @ IR.Expression.Binding(name, expression, _, _, _) =>
info.updateAt(asStatic(expression), Set(asStatic(binding)))
info.updateAt(asStatic(name), Set(asStatic(binding)))
val expressionDep = asStatic(expression)
val nameDep = asStatic(name)
val bindingDep = asStatic(binding)
info.dependents.updateAt(expressionDep, Set(bindingDep))
info.dependents.updateAt(nameDep, Set(bindingDep))
info.dependencies.updateAt(bindingDep, Set(expressionDep, nameDep))
binding
.copy(
@ -183,6 +193,10 @@ case object DataflowAnalysis extends IRPass {
.updateMetadata(this -->> info)
case error: IR.Error => error
case _: IR.Comment =>
throw new CompilerError(
"Comments should not be present during dataflow analysis."
)
}
}
@ -201,12 +215,10 @@ case object DataflowAnalysis extends IRPass {
): IR.Function = {
function match {
case lam @ IR.Function.Lambda(arguments, body, _, _, _, _) =>
info.updateAt(asStatic(body), Set(asStatic(lam)))
arguments.foreach(arg =>
arg.defaultValue.foreach(d =>
info.updateAt(asStatic(d), Set(asStatic(lam)))
)
)
val bodyDep = asStatic(body)
val lamDep = asStatic(lam)
info.dependents.updateAt(bodyDep, Set(lamDep))
info.dependencies.updateAt(lamDep, Set(bodyDep))
lam
.copy(
@ -226,7 +238,7 @@ case object DataflowAnalysis extends IRPass {
* A prefix application depends on the values of the function and arguments,
* while a force depends purely on the term being forced.
*
* @param application the appliation to perform dependency analysis on
* @param application the application to perform dependency analysis on
* @param info the dependency information for the module
* @return `application`, with attached dependency information
*/
@ -236,8 +248,15 @@ case object DataflowAnalysis extends IRPass {
): IR.Application = {
application match {
case prefix @ IR.Application.Prefix(fn, args, _, _, _, _) =>
info.updateAt(asStatic(fn), Set(asStatic(prefix)))
args.foreach(arg => info.updateAt(asStatic(arg), Set(asStatic(prefix))))
val fnDep = asStatic(fn)
val prefixDep = asStatic(prefix)
info.dependents.updateAt(fnDep, Set(prefixDep))
info.dependencies.updateAt(prefixDep, Set(fnDep))
args.foreach(arg => {
val argDep = asStatic(arg)
info.dependents.updateAt(argDep, Set(prefixDep))
info.dependencies.updateAt(prefixDep, Set(argDep))
})
prefix
.copy(
@ -246,19 +265,32 @@ case object DataflowAnalysis extends IRPass {
)
.updateMetadata(this -->> info)
case force @ IR.Application.Force(target, _, _, _) =>
info.updateAt(asStatic(target), Set(asStatic(force)))
val targetDep = asStatic(target)
val forceDep = asStatic(force)
info.dependents.updateAt(targetDep, Set(forceDep))
info.dependencies.updateAt(forceDep, Set(targetDep))
force
.copy(target = analyseExpression(target, info))
.updateMetadata(this -->> info)
case vector @ IR.Application.Literal.Sequence(items, _, _, _) =>
items.foreach(it => info.updateAt(asStatic(it), Set(asStatic(vector))))
val vectorDep = asStatic(vector)
items.foreach(it => {
val itemDep = asStatic(it)
info.dependents.updateAt(itemDep, Set(vectorDep))
info.dependencies.updateAt(vectorDep, Set(itemDep))
})
vector
.copy(items = items.map(analyseExpression(_, info)))
.updateMetadata(this -->> info)
case tSet @ IR.Application.Literal.Typeset(expr, _, _, _) =>
expr.foreach(exp => info.updateAt(asStatic(exp), Set(asStatic(tSet))))
val tSetDep = asStatic(tSet)
expr.foreach(exp => {
val exprDep = asStatic(exp)
info.dependents.updateAt(exprDep, Set(tSetDep))
info.dependencies.updateAt(tSetDep, Set(exprDep))
})
tSet
.copy(expression = expr.map(analyseExpression(_, info)))
@ -279,8 +311,12 @@ case object DataflowAnalysis extends IRPass {
def analyseType(typ: IR.Type, info: DependencyInfo): IR.Type = {
typ match {
case asc @ IR.Type.Ascription(typed, signature, _, _, _) =>
info.updateAt(asStatic(typed), Set(asStatic(asc)))
info.updateAt(asStatic(signature), Set(asStatic(asc)))
val ascrDep = asStatic(asc)
val typedDep = asStatic(typed)
val sigDep = asStatic(signature)
info.dependents.updateAt(typedDep, Set(ascrDep))
info.dependents.updateAt(sigDep, Set(ascrDep))
info.dependencies.updateAt(ascrDep, Set(typedDep, sigDep))
asc
.copy(
@ -289,8 +325,12 @@ case object DataflowAnalysis extends IRPass {
)
.updateMetadata(this -->> info)
case ctx @ IR.Type.Context(typed, context, _, _, _) =>
info.updateAt(asStatic(typed), Set(asStatic(ctx)))
info.updateAt(asStatic(context), Set(asStatic(ctx)))
val ctxDep = asStatic(ctx)
val typedDep = asStatic(typed)
val contextDep = asStatic(context)
info.dependents.updateAt(typedDep, Set(ctxDep))
info.dependents.updateAt(contextDep, Set(ctxDep))
info.dependencies.updateAt(ctxDep, Set(typedDep, contextDep))
ctx
.copy(
@ -299,8 +339,12 @@ case object DataflowAnalysis extends IRPass {
)
.updateMetadata(this -->> info)
case err @ IR.Type.Error(typed, error, _, _, _) =>
info.updateAt(asStatic(typed), Set(asStatic(err)))
info.updateAt(asStatic(error), Set(asStatic(err)))
val errDep = asStatic(err)
val typedDep = asStatic(typed)
val errorDep = asStatic(error)
info.dependents.updateAt(typedDep, Set(errDep))
info.dependents.updateAt(errorDep, Set(errDep))
info.dependencies.updateAt(errDep, Set(typedDep, errorDep))
err
.copy(
@ -309,8 +353,12 @@ case object DataflowAnalysis extends IRPass {
)
.updateMetadata(this -->> info)
case member @ IR.Type.Set.Member(_, memberType, value, _, _, _) =>
info.updateAt(asStatic(memberType), Set(asStatic(member)))
info.updateAt(asStatic(value), Set(asStatic(member)))
val memberDep = asStatic(member)
val memberTypeDep = asStatic(memberType)
val valueDep = asStatic(value)
info.dependents.updateAt(memberTypeDep, Set(memberDep))
info.dependents.updateAt(valueDep, Set(memberDep))
info.dependencies.updateAt(memberDep, Set(memberTypeDep, valueDep))
member
.copy(
@ -319,8 +367,12 @@ case object DataflowAnalysis extends IRPass {
)
.updateMetadata(this -->> info)
case concat @ IR.Type.Set.Concat(left, right, _, _, _) =>
info.updateAt(asStatic(left), Set(asStatic(concat)))
info.updateAt(asStatic(right), Set(asStatic(concat)))
val concatDep = asStatic(concat)
val leftDep = asStatic(left)
val rightDep = asStatic(right)
info.dependents.updateAt(leftDep, Set(concatDep))
info.dependents.updateAt(rightDep, Set(concatDep))
info.dependencies.updateAt(concatDep, Set(rightDep, leftDep))
concat
.copy(
@ -329,16 +381,24 @@ case object DataflowAnalysis extends IRPass {
)
.updateMetadata(this -->> info)
case eq @ IR.Type.Set.Equality(left, right, _, _, _) =>
info.updateAt(asStatic(left), Set(asStatic(eq)))
info.updateAt(asStatic(right), Set(asStatic(eq)))
val eqDep = asStatic(eq)
val leftDep = asStatic(left)
val rightDep = asStatic(right)
info.dependents.updateAt(leftDep, Set(eqDep))
info.dependents.updateAt(rightDep, Set(eqDep))
info.dependencies.updateAt(eqDep, Set(leftDep, rightDep))
eq.copy(
left = analyseExpression(left, info),
right = analyseExpression(right, info)
).updateMetadata(this -->> info)
case intersect @ IR.Type.Set.Intersection(left, right, _, _, _) =>
info.updateAt(asStatic(left), Set(asStatic(intersect)))
info.updateAt(asStatic(right), Set(asStatic(intersect)))
val intersectDep = asStatic(intersect)
val leftDep = asStatic(left)
val rightDep = asStatic(right)
info.dependents.updateAt(leftDep, Set(intersectDep))
info.dependents.updateAt(rightDep, Set(intersectDep))
info.dependencies.updateAt(intersectDep, Set(leftDep, rightDep))
intersect
.copy(
@ -347,8 +407,12 @@ case object DataflowAnalysis extends IRPass {
)
.updateMetadata(this -->> info)
case union @ IR.Type.Set.Union(left, right, _, _, _) =>
info.updateAt(asStatic(left), Set(asStatic(union)))
info.updateAt(asStatic(right), Set(asStatic(union)))
val unionDep = asStatic(union)
val leftDep = asStatic(left)
val rightDep = asStatic(right)
info.dependents.updateAt(leftDep, Set(unionDep))
info.dependents.updateAt(rightDep, Set(unionDep))
info.dependencies.updateAt(unionDep, Set(leftDep, rightDep))
union
.copy(
@ -357,8 +421,12 @@ case object DataflowAnalysis extends IRPass {
)
.updateMetadata(this -->> info)
case subsumption @ IR.Type.Set.Subsumption(left, right, _, _, _) =>
info.updateAt(asStatic(left), Set(asStatic(subsumption)))
info.updateAt(asStatic(right), Set(asStatic(subsumption)))
val subDep = asStatic(subsumption)
val leftDep = asStatic(left)
val rightDep = asStatic(right)
info.dependents.updateAt(leftDep, Set(subDep))
info.dependents.updateAt(rightDep, Set(subDep))
info.dependencies.updateAt(subDep, Set(leftDep, rightDep))
subsumption
.copy(
@ -367,8 +435,12 @@ case object DataflowAnalysis extends IRPass {
)
.updateMetadata(this -->> info)
case subtraction @ IR.Type.Set.Subtraction(left, right, _, _, _) =>
info.updateAt(asStatic(left), Set(asStatic(subtraction)))
info.updateAt(asStatic(right), Set(asStatic(subtraction)))
val subDep = asStatic(subtraction)
val leftDep = asStatic(left)
val rightDep = asStatic(right)
info.dependents.updateAt(leftDep, Set(subDep))
info.dependents.updateAt(rightDep, Set(subDep))
info.dependencies.updateAt(subDep, Set(leftDep, rightDep))
subtraction
.copy(
@ -417,7 +489,9 @@ case object DataflowAnalysis extends IRPass {
DependencyInfo.Type.Dynamic(name.name, None)
}
info.updateAt(key, Set(asStatic(name)))
val nameDep = asStatic(name)
info.dependents.updateAt(key, Set(nameDep))
info.dependencies.updateAt(nameDep, Set(key))
name.updateMetadata(this -->> info)
}
@ -436,10 +510,15 @@ case object DataflowAnalysis extends IRPass {
def analyseCase(cse: IR.Case, info: DependencyInfo): IR.Case = {
cse match {
case expr @ IR.Case.Expr(scrutinee, branches, _, _, _) =>
info.updateAt(asStatic(scrutinee), Set(asStatic(expr)))
branches.foreach(branch =>
info.updateAt(asStatic(branch), Set(asStatic(expr)))
)
val exprDep = asStatic(expr)
val scrutDep = asStatic(scrutinee)
info.dependents.updateAt(scrutDep, Set(exprDep))
info.dependencies.updateAt(exprDep, Set(scrutDep))
branches.foreach(branch => {
val branchDep = asStatic(branch)
info.dependents.updateAt(branchDep, Set(exprDep))
info.dependencies.updateAt(exprDep, Set(branchDep))
})
expr
.copy(
@ -468,8 +547,12 @@ case object DataflowAnalysis extends IRPass {
val pattern = branch.pattern
val expression = branch.expression
info.updateAt(asStatic(pattern), Set(asStatic(branch)))
info.updateAt(asStatic(expression), Set(asStatic(branch)))
val branchDep = asStatic(branch)
val patternDep = asStatic(pattern)
val exprDep = asStatic(expression)
info.dependents.updateAt(patternDep, Set(branchDep))
info.dependents.updateAt(exprDep, Set(branchDep))
info.dependencies.updateAt(branchDep, Set(patternDep, exprDep))
branch
.copy(
@ -491,16 +574,23 @@ case object DataflowAnalysis extends IRPass {
pattern: IR.Pattern,
info: DependencyInfo
): IR.Pattern = {
val patternDep = asStatic(pattern)
pattern match {
case named @ Pattern.Name(name, _, _, _) =>
info.updateAt(asStatic(name), Set(asStatic(pattern)))
val nameDep = asStatic(name)
info.dependents.updateAt(nameDep, Set(patternDep))
info.dependencies.updateAt(patternDep, Set(nameDep))
named.updateMetadata(this -->> info)
case cons @ Pattern.Constructor(constructor, fields, _, _, _) =>
info.updateAt(asStatic(constructor), Set(asStatic(pattern)))
fields.foreach(field =>
info.updateAt(asStatic(field), Set(asStatic(pattern)))
)
val consDep = asStatic(constructor)
info.dependents.updateAt(consDep, Set(patternDep))
info.dependencies.updateAt(patternDep, Set(consDep))
fields.foreach(field => {
val fieldDep = asStatic(field)
info.dependents.updateAt(fieldDep, Set(patternDep))
info.dependencies.updateAt(patternDep, Set(fieldDep))
})
cons
.copy(
@ -531,9 +621,12 @@ case object DataflowAnalysis extends IRPass {
): IR.DefinitionArgument = {
argument match {
case spec @ IR.DefinitionArgument.Specified(_, defValue, _, _, _, _) =>
defValue.foreach(expr =>
info.updateAt(asStatic(expr), Set(asStatic(spec)))
)
val specDep = asStatic(spec)
defValue.foreach(expr => {
val exprDep = asStatic(expr)
info.dependents.updateAt(exprDep, Set(specDep))
info.dependencies.updateAt(specDep, Set(exprDep))
})
spec
.copy(
@ -558,8 +651,15 @@ case object DataflowAnalysis extends IRPass {
): IR.CallArgument = {
argument match {
case spec @ IR.CallArgument.Specified(name, value, _, _, _, _) =>
info.updateAt(asStatic(value), Set(asStatic(spec)))
name.foreach(name => info.updateAt(asStatic(name), Set(asStatic(spec))))
val specDep = asStatic(spec)
val valueDep = asStatic(value)
info.dependents.updateAt(valueDep, Set(specDep))
info.dependencies.updateAt(specDep, Set(valueDep))
name.foreach(name => {
val nameDep = asStatic(name)
info.dependents.updateAt(nameDep, Set(specDep))
info.dependencies.updateAt(specDep, Set(nameDep))
})
spec
.copy(
@ -573,28 +673,31 @@ case object DataflowAnalysis extends IRPass {
/** Storage for dependency information.
*
* @param dependencies storage for the direct dependencies between program
* components
* It maps from an expression to other expressions based on some relationship
* between them.
*
* @param mapping storage for the direct mapping between program components
*/
sealed case class DependencyInfo(
dependencies: mutable.Map[DependencyInfo.Type, Set[DependencyInfo.Type]] =
sealed case class DependencyMapping(
mapping: mutable.Map[DependencyInfo.Type, Set[DependencyInfo.Type]] =
mutable.Map()
) extends IRPass.Metadata {
override val metadataName: String = "DataflowAnalysis.Dependencies"
) {
/** Returns the set of all dependents for the provided key.
/** Returns the set of all program component associated with the provided
* key.
*
* Please note that the result set contains not just the _direct_
* dependents of the key, but _all_ dependents of the key.
* associations with the key, but also the _indirect_ associations with the
* key.
*
* @param key the key to get the dependents of
* @return the set of all dependencies on `key`
* @param key the key to get the associated components of
* @return the set of all components associated with `key`
* @throws NoSuchElementException when `key` does not exist in the
* dependencies mapping
*/
@throws[NoSuchElementException]
def apply(key: DependencyInfo.Type): Set[DependencyInfo.Type] = {
if (dependencies.contains(key)) {
if (mapping.contains(key)) {
get(key) match {
case Some(deps) => deps
case None => throw new NoSuchElementException
@ -604,18 +707,20 @@ case object DataflowAnalysis extends IRPass {
}
}
/** Obtains the _direct_ dependents of a given node in the IR.
/** Obtains the program components _directly_ associated with a given node
* in the IR.
*
* Please note that this does _not_ return the transitive closure of all
* dependents of the node.
* associations with the node.
*
* @param key the key to get the dependents of
* @return the set of the _direct_ dependencies on `key`, if it exists
* @param key the key to get the associated components of
* @return the set of the components directly associated with `key`, if it
* exists
*/
def getDirect(
key: DependencyInfo.Type
): Option[Set[DependencyInfo.Type]] = {
dependencies.get(key)
mapping.get(key)
}
/** Obtains the external identifiers of the _direct_ dependents of a given
@ -631,13 +736,15 @@ case object DataflowAnalysis extends IRPass {
getDirect(key).map(_.flatMap(_.externalId))
}
/** Safely gets the set of all dependents for the provided key.
/** Safely gets the set of all program components associated with the
* provided key.
*
* Please note that the result set contains not just the _direct_
* dependents of the key, but _all_ dependents of the key.
* Please note that the result set contains not just the components that
* are directly associated with the key, but all components associated with
* the key
*
* @param key the key to get the dependents of
* @return the set of all dependencies on `key`, if key exists
* @param key the key to get the associations of
* @return the set of all associations with `key`, if key exists
*/
def get(key: DependencyInfo.Type): Option[Set[DependencyInfo.Type]] = {
val visited = mutable.Set[DependencyInfo.Type]()
@ -646,7 +753,7 @@ case object DataflowAnalysis extends IRPass {
if (!visited.contains(key)) {
visited += key
dependencies.get(key) match {
mapping.get(key) match {
case Some(deps) => deps ++ deps.map(go).reduceLeft(_ ++ _)
case None => Set()
}
@ -655,61 +762,61 @@ case object DataflowAnalysis extends IRPass {
}
}
if (dependencies.contains(key)) {
if (mapping.contains(key)) {
Some(go(key))
} else {
None
}
}
/** Safely gets the external identifiers for all dependents of the provided
* key.
/** Safely gets the external identifiers for all program component
* associated with the provided key.
*
* Please note that the result set contains not just the _direct_
* dependents of the key, but all dependents of the key.
* Please note that the result set contains not just the components that
* are directly associated with the key, but all associations with the key.
*
* @param key the key from which to get the external identifiers of its
* dependents
* @return the set of all external identifiers of dependents on `key`, if
* it exists
* associated program components
* @return the set of all external identifiers of program components
* associated with `key`, if it exists
*/
def getExternal(key: DependencyInfo.Type): Option[Set[IR.ExternalId]] = {
get(key).map(_.flatMap(_.externalId))
}
/** Executes an update on the dependency information.
/** Executes an update on the association information.
*
* @param key the key to update the dependents for
* @param dependents the updated dependents for `key`
* @param key the key to update the associations for
* @param newDependents the updated associations for `key`
*/
def update(
key: DependencyInfo.Type,
dependents: Set[DependencyInfo.Type]
newDependents: Set[DependencyInfo.Type]
): Unit =
dependencies(key) = dependents
mapping(key) = newDependents
/** Updates the dependents for the provided key, or creates them if they do
* not already exist.
/** Updates the associations for the provided key, or creates them if they
* do not already exist.
*
* @param key the key to add or update dependents for
* @param dependents the new dependents information for `key`
* @param key the key to add or update associations for
* @param newDependents the new associations information for `key`
*/
def updateAt(
key: DependencyInfo.Type,
dependents: Set[DependencyInfo.Type]
newDependents: Set[DependencyInfo.Type]
): Unit = {
if (dependencies.contains(key)) {
dependencies(key) ++= dependents
if (mapping.contains(key)) {
mapping(key) ++= newDependents
} else {
dependencies(key) = dependents
mapping(key) = newDependents
}
}
/** Updates the dependents for the provided keys, or creates them if they do
* not already exist.
/** Updates the associations for the provided keys, or creates them if they
* do not already exist.
*
* @param keys the keys to add or update dependents for
* @param dependents the new dependents information for each `key` in
* @param keys the keys to add or update assocuations for
* @param dependents the new associations information for each `key` in
* `keys`
*/
def updateAt(
@ -719,14 +826,14 @@ case object DataflowAnalysis extends IRPass {
/** Combines two dependency information containers.
*
* @param that the other contaoner to combine with `this`
* @param that the other container to combine with `this`
* @return the result of combining `this` and `that`
*/
def ++(that: DependencyInfo): DependencyInfo = {
val combinedModule = new DependencyInfo(this.dependencies)
def ++(that: DependencyMapping): DependencyMapping = {
val combinedModule = new DependencyMapping(this.mapping)
for ((key, value) <- that.dependencies) {
combinedModule.dependencies.get(key) match {
for ((key, value) <- that.mapping) {
combinedModule.mapping.get(key) match {
case Some(xs) => combinedModule(key) = value ++ xs
case None => combinedModule(key) = value
}
@ -734,6 +841,34 @@ case object DataflowAnalysis extends IRPass {
combinedModule
}
}
/** A representation of dependency information for dataflow analysis.
*
* @param dependents information on the dependents of program components,
* mapping from a component to the components that depend
* on it
* @param dependencies information on the dependencies of program components,
* mapping from a component to the components that it
* depends on
*/
sealed case class DependencyInfo(
dependents: DependencyMapping = DependencyMapping(),
dependencies: DependencyMapping = DependencyMapping()
) extends IRPass.Metadata {
override val metadataName: String = "DataflowAnalysis.DependencyInfo"
/** Combines two dependency information containers.
*
* @param that the other container to combine with `this`
* @return the result of combining `this` and `that`
*/
def ++(that: DependencyInfo): DependencyInfo = {
DependencyInfo(
dependents = this.dependents ++ that.dependents,
dependencies = that.dependencies ++ that.dependencies
)
}
override def duplicate(): Option[IRPass.Metadata] = None
}

View File

@ -45,7 +45,15 @@ case object DemandAnalysis extends IRPass {
): IR.Module = {
ir.copy(bindings =
ir.bindings.map(t =>
t.mapExpressions(runExpression(_, InlineContext(moduleContext.module)))
t.mapExpressions(
runExpression(
_,
InlineContext(
moduleContext.module,
compilerConfig = moduleContext.compilerConfig
)
)
)
)
)
}

View File

@ -73,7 +73,8 @@ case object LambdaShorthandToLambda extends IRPass {
_,
InlineContext(
moduleContext.module,
freshNameSupply = moduleContext.freshNameSupply
freshNameSupply = moduleContext.freshNameSupply,
compilerConfig = moduleContext.compilerConfig
)
)
)

View File

@ -47,7 +47,13 @@ case object OperatorToFunction extends IRPass {
case asc: IR.Type.Ascription => asc
case a =>
a.mapExpressions(
runExpression(_, new InlineContext(moduleContext.module))
runExpression(
_,
new InlineContext(
moduleContext.module,
compilerConfig = moduleContext.compilerConfig
)
)
)
}
ir.copy(bindings = new_bindings)

View File

@ -49,7 +49,8 @@ case object SectionsToBinOp extends IRPass {
_,
new InlineContext(
moduleContext.module,
freshNameSupply = moduleContext.freshNameSupply
freshNameSupply = moduleContext.freshNameSupply,
compilerConfig = moduleContext.compilerConfig
)
)
)

View File

@ -46,8 +46,16 @@ case object UnusedBindings extends IRPass {
override def runModule(
ir: IR.Module,
moduleContext: ModuleContext
): IR.Module = if (!moduleContext.noWarnings) {
ir.mapExpressions(runExpression(_, InlineContext(moduleContext.module)))
): IR.Module = if (moduleContext.compilerConfig.warningsEnabled) {
ir.mapExpressions(
runExpression(
_,
InlineContext(
moduleContext.module,
compilerConfig = moduleContext.compilerConfig
)
)
)
} else ir
/** Lints an arbitrary expression.
@ -61,7 +69,7 @@ case object UnusedBindings extends IRPass {
override def runExpression(
ir: IR.Expression,
inlineContext: InlineContext
): IR.Expression = if (!inlineContext.noWarnings) {
): IR.Expression = if (inlineContext.compilerConfig.warningsEnabled) {
ir.transformExpressions {
case binding: IR.Expression.Binding => lintBinding(binding, inlineContext)
case function: IR.Function => lintFunction(function, inlineContext)

View File

@ -56,7 +56,11 @@ case object ApplicationSaturation extends IRPass {
ir.mapExpressions(
runExpression(
_,
new InlineContext(moduleContext.module, passConfiguration = passConfig)
new InlineContext(
moduleContext.module,
passConfiguration = passConfig,
compilerConfig = moduleContext.compilerConfig
)
)
)
}

View File

@ -81,7 +81,8 @@ case object LambdaConsolidate extends IRPass {
_,
new InlineContext(
moduleContext.module,
freshNameSupply = moduleContext.freshNameSupply
freshNameSupply = moduleContext.freshNameSupply,
compilerConfig = moduleContext.compilerConfig
)
)
)

View File

@ -11,7 +11,8 @@ import org.enso.compiler.pass.resolve.ModuleAnnotations.Annotations
case object ExpressionAnnotations extends IRPass {
val tailCallName = "@Tail_Call"
val builtinMethodName = "@Builtin_Method"
val knownAnnotations = Seq(tailCallName, builtinMethodName)
val autoParallelName = "@Auto_Parallel"
val knownAnnotations = Seq(tailCallName, builtinMethodName, autoParallelName)
/** The type of the metadata object that the pass writes to the IR. */
override type Metadata = Annotations

View File

@ -63,7 +63,8 @@ case object IgnoredBindings extends IRPass {
_,
InlineContext(
moduleContext.module,
freshNameSupply = moduleContext.freshNameSupply
freshNameSupply = moduleContext.freshNameSupply,
compilerConfig = moduleContext.compilerConfig
)
)
)
@ -87,7 +88,9 @@ case object IgnoredBindings extends IRPass {
)
)
resolveExpression(ir, freshNameSupply)
if (inlineContext.compilerConfig.warningsEnabled) {
resolveExpression(ir, freshNameSupply)
} else ir
}
// === Pass Internals =======================================================

View File

@ -3,6 +3,7 @@ package org.enso.compiler.phase
import org.enso.compiler.Passes
import org.enso.compiler.codegen.AstToIr
import org.enso.compiler.context.{FreshNameSupply, ModuleContext}
import org.enso.compiler.data.CompilerConfig
import org.enso.interpreter.runtime.Module
import org.enso.interpreter.runtime.Module.CompilationStage
import org.enso.syntax.text.Parser
@ -37,7 +38,7 @@ object BuiltinsIrBuilder {
val moduleContext = ModuleContext(
module = module,
freshNameSupply = Some(freshNameSupply),
noWarnings = true
compilerConfig = CompilerConfig(warningsEnabled = false)
)
val parsedAst = Parser().runWithIds(module.getSource.getCharacters.toString)
val initialIr = AstToIr.translate(parsedAst)

View File

@ -220,7 +220,13 @@ class EnsureCompiledJob(protected val files: Iterable[File])
ctx: RuntimeContext
): CompilationStatus = {
val pass = GatherDiagnostics
.runModule(module.getIr, ModuleContext(module))
.runModule(
module.getIr,
ModuleContext(
module,
compilerConfig = ctx.executionService.getContext.getCompilerConfig
)
)
.unsafeGetMetadata(
GatherDiagnostics,
"No diagnostics metadata right after the gathering pass."

View File

@ -3,6 +3,7 @@ package org.enso.compiler.test
import org.enso.compiler.codegen.AstToIr
import org.enso.compiler.context.{FreshNameSupply, InlineContext, ModuleContext}
import org.enso.compiler.core.IR
import org.enso.compiler.data.CompilerConfig
import org.enso.compiler.pass.{PassConfiguration, PassManager}
import org.enso.syntax.text.{AST, Parser}
import org.scalatest.matchers.should.Matchers
@ -238,12 +239,14 @@ trait CompilerRunner {
def buildModuleContext(
moduleName: QualifiedName = QualifiedName.simpleName("Test_Module"),
freshNameSupply: Option[FreshNameSupply] = None,
passConfiguration: Option[PassConfiguration] = None
passConfiguration: Option[PassConfiguration] = None,
compilerConfig: CompilerConfig = defaultConfig
): ModuleContext = {
ModuleContext(
module = Module.empty(moduleName),
freshNameSupply = freshNameSupply,
passConfiguration = passConfiguration
passConfiguration = passConfiguration,
compilerConfig = compilerConfig
)
}
@ -260,7 +263,8 @@ trait CompilerRunner {
localScope: Option[LocalScope] = None,
isInTailPosition: Option[Boolean] = None,
freshNameSupply: Option[FreshNameSupply] = None,
passConfiguration: Option[PassConfiguration] = None
passConfiguration: Option[PassConfiguration] = None,
compilerConfig: CompilerConfig = defaultConfig
): InlineContext = {
val mod = Module.empty(QualifiedName.simpleName("Test_Module"))
mod.unsafeBuildIrStub()
@ -269,7 +273,10 @@ trait CompilerRunner {
freshNameSupply = freshNameSupply,
passConfiguration = passConfiguration,
localScope = localScope,
isInTailPosition = isInTailPosition
isInTailPosition = isInTailPosition,
compilerConfig = compilerConfig
)
}
val defaultConfig: CompilerConfig = CompilerConfig()
}

View File

@ -8,7 +8,7 @@ import org.enso.compiler.pass.analyse.{AliasAnalysis, BindingAnalysis}
import org.enso.compiler.pass.desugar._
import org.enso.compiler.pass.lint.ShadowedPatternFields
import org.enso.compiler.pass.optimise.UnreachableMatchBranches
import org.enso.compiler.pass.resolve.{DocumentationComments, ExpressionAnnotations, IgnoredBindings, MethodDefinitions, ModuleAnnotations, ModuleThisToHere, TypeFunctions, TypeSignatures}
import org.enso.compiler.pass.resolve._
class PassesTest extends CompilerTest {
@ -35,7 +35,7 @@ class PassesTest extends CompilerTest {
// === The Tests ============================================================
"Compiler pass ordering slicing" should {
val passes = new Passes
val passes = new Passes(defaultConfig)
"get the precursors of a given pass" in {
passes.getPrecursors(AliasAnalysis).map(_.passes) shouldEqual Some(

View File

@ -1,7 +1,5 @@
package org.enso.compiler.test.context
import java.util.UUID
import org.enso.compiler.Passes
import org.enso.compiler.context.{
ChangesetBuilder,
@ -16,9 +14,11 @@ import org.enso.interpreter.runtime.scope.LocalScope
import org.enso.text.buffer.Rope
import org.enso.text.editing.model.{Position, Range, TextEdit}
import java.util.UUID
class ChangesetBuilderTest extends CompilerTest {
implicit val passManager: PassManager = new Passes().passManager
implicit val passManager: PassManager = new Passes(defaultConfig).passManager
"DiffChangeset" should {

View File

@ -17,7 +17,7 @@ import org.enso.polyglot.data.Tree
class SuggestionBuilderTest extends CompilerTest {
implicit val passManager: PassManager = new Passes().passManager
implicit val passManager: PassManager = new Passes(defaultConfig).passManager
private val Module = QualifiedName(List("Unnamed"), "Test")
private val ModuleAtomNode = Tree.Node(

View File

@ -46,7 +46,7 @@ class PassManagerTest extends CompilerTest {
UnusedBindings
)
val validOrdering: List[PassGroup] = (new Passes).passOrdering
val validOrdering: List[PassGroup] = (new Passes(defaultConfig)).passOrdering
val passConfiguration: PassConfiguration = new PassConfiguration()

View File

@ -16,7 +16,7 @@ class AliasAnalysisTest extends CompilerTest {
// === Utilities ============================================================
val passes = new Passes
val passes = new Passes(defaultConfig)
/** The passes that need to be run before the alias analysis pass. */
val precursorPasses: PassGroup = passes.getPrecursors(AliasAnalysis).get

View File

@ -0,0 +1,238 @@
package org.enso.compiler.test.pass.analyse
import org.enso.compiler.Passes
import org.enso.compiler.context.{FreshNameSupply, ModuleContext}
import org.enso.compiler.core.IR
import org.enso.compiler.core.IR.Module.Scope.Definition.Method
import org.enso.compiler.data.CompilerConfig
import org.enso.compiler.pass.PassConfiguration.ToPair
import org.enso.compiler.pass.analyse.{AliasAnalysis, AutomaticParallelism}
import org.enso.compiler.pass.optimise.ApplicationSaturation
import org.enso.compiler.pass.{PassConfiguration, PassGroup, PassManager}
import org.enso.compiler.test.CompilerTest
import scala.annotation.unused
class AutomaticParallelismTest extends CompilerTest {
// === Test Setup ===========================================================
val passes = new Passes(CompilerConfig(autoParallelismEnabled = true))
val precursorPasses: PassGroup =
passes.getPrecursors(AutomaticParallelism).get
val passConfig: PassConfiguration = PassConfiguration(
AliasAnalysis -->> AliasAnalysis.Configuration(),
ApplicationSaturation -->> ApplicationSaturation.Configuration()
)
implicit val passManager: PassManager =
new PassManager(List(precursorPasses), passConfig)
implicit class AnalyseModule(ir: IR.Module) {
def analyse(implicit ctx: ModuleContext): IR.Module =
AutomaticParallelism.runModule(ir, ctx)
}
def mkModuleContext: ModuleContext = {
buildModuleContext(freshNameSupply = Some(new FreshNameSupply))
}
// === The Tests ============================================================
"Successful parallelism analysis" should {
implicit val moduleContext: ModuleContext = mkModuleContext
@unused val code =
"""
|fn f g =
| x = File.read "foo"
| y = File.read "bar"
| a = x.length
| b = y.length
|
| @Auto_Parallel a.n b
|""".stripMargin.preprocessModule.analyse
"determine the separated flows" in {
pending
}
"inline the flows" in {
pending
}
"associate correct metadata with the block" in {
pending
}
"work in nested blocks" in {
pending
}
"leave unrelated things untouched" in {
pending
}
"work when the result of the parallel call is later used" in {
pending
}
"work with named argument applications" in {
pending
}
}
"Failed parallelism analysis" should {
pending
"raise a warning when there aren't enough arguments to parallelize" in {
implicit val moduleContext: ModuleContext = mkModuleContext
val ir =
"""
|fn f g =
| x = File.read "x"
| @Auto_Parallel x.foo
|""".stripMargin.preprocessModule.analyse
val defn = ir.bindings.head.asInstanceOf[Method.Explicit]
val parApp = defn.body
.asInstanceOf[IR.Function.Lambda]
.body
.asInstanceOf[IR.Expression.Block]
.returnValue
.asInstanceOf[IR.Application.Prefix]
val diag = parApp.diagnostics.collect {
case d: IR.Warning.FailedParallelism => d
}
diag.length shouldEqual 1
val expected = "Cannot parallelize an application with 1 arguments."
diag.head.reason shouldEqual expected
}
"raise a warning when the incoming flows are not distinct" in {
implicit val moduleContext: ModuleContext = mkModuleContext
val ir =
"""
|fn f g =
| x = File.read "x"
| y = File.read "y"
| a = x.length
| b = y + a
| @Auto_Parallel Table.foo a b
|""".stripMargin.preprocessModule.analyse
val defn = ir.bindings.head.asInstanceOf[Method.Explicit]
val parApp = defn.body
.asInstanceOf[IR.Function.Lambda]
.body
.asInstanceOf[IR.Expression.Block]
.returnValue
.asInstanceOf[IR.Application.Prefix]
val diag = parApp.diagnostics.collect {
case d: IR.Warning.FailedParallelism => d
}
diag.length shouldEqual 1
val expected =
"Arguments to the parallel call are not distinct computations."
diag.head.reason shouldEqual expected
}
"raise a warning when the parallel call depends on input arguments" in {
implicit val moduleContext: ModuleContext = mkModuleContext
val ir =
"""
|fn f g =
| x = File.read "x"
| y = File.read "y"
| a = f x
| b = y.length
| @Auto_Parallel Table.foo a b
|""".stripMargin.preprocessModule.analyse
val defn = ir.bindings.head.asInstanceOf[Method.Explicit]
val parApp = defn.body
.asInstanceOf[IR.Function.Lambda]
.body
.asInstanceOf[IR.Expression.Block]
.returnValue
.asInstanceOf[IR.Application.Prefix]
val diag = parApp.diagnostics.collect {
case d: IR.Warning.FailedParallelism => d
}
diag.length shouldEqual 1
val expected = "Auto-parallel calls cannot depend on input arguments " +
"as these are not statically known."
diag.head.reason shouldEqual expected
}
"raise a warning when an intermediary is used outside the streams" in {
implicit val moduleContext: ModuleContext = mkModuleContext
val ir =
"""
|fn f g =
| x = File.read "x"
| y = File.read "y"
| a = x.length
| b = y.length
| z = @Auto_Parallel Table.foo a b
| z y
|""".stripMargin.preprocessModule.analyse
val defn = ir.bindings.head.asInstanceOf[Method.Explicit]
val parApp = defn.body
.asInstanceOf[IR.Function.Lambda]
.body
.asInstanceOf[IR.Expression.Block]
.expressions(4)
.asInstanceOf[IR.Expression.Binding]
.expression
.asInstanceOf[IR.Application.Prefix]
val diag = parApp.diagnostics.collect {
case d: IR.Warning.FailedParallelism => d
}
diag.length shouldEqual 1
val expected = "Cannot inline the dependencies of the incoming flows. " +
"They are used in more than one place."
diag.head.reason shouldEqual expected
}
"raise a warning when dependencies are used outside the @Auto_Parallel call" in {
implicit val moduleContext: ModuleContext = mkModuleContext
val ir =
"""
|fn f g =
| x = File.read "x"
| y = File.read "y"
| a = x.length
| b = y.length
| f b
| z = @Auto_Parallel Table.foo a b
| f g
|""".stripMargin.preprocessModule.analyse
val defn = ir.bindings.head.asInstanceOf[Method.Explicit]
val parApp = defn.body
.asInstanceOf[IR.Function.Lambda]
.body
.asInstanceOf[IR.Expression.Block]
.expressions(5)
.asInstanceOf[IR.Expression.Binding]
.expression
.asInstanceOf[IR.Application.Prefix]
val diag = parApp.diagnostics.collect {
case d: IR.Warning.FailedParallelism => d
}
diag.length shouldEqual 1
val expected = "Cannot inline the dependencies of the incoming flows. " +
"They are used in more than one place."
diag.head.reason shouldEqual expected
}
}
}

View File

@ -18,7 +18,7 @@ class BindingAnalysisTest extends CompilerTest {
freshNameSupply = Some(new FreshNameSupply)
)
val passes = new Passes
val passes = new Passes(defaultConfig)
val precursorPasses: PassGroup = passes.getPrecursors(BindingAnalysis).get

View File

@ -13,7 +13,7 @@ class DemandAnalysisTest extends CompilerTest {
// === Test Setup ===========================================================
val passes = new Passes
val passes = new Passes(defaultConfig)
/** The passes that must be run before the demand analysis pass. */
val precursorPasses: PassGroup = passes.getPrecursors(DemandAnalysis).get

View File

@ -111,7 +111,7 @@ class GatherDiagnosticsTest extends CompilerTest {
"avoid duplication" in {
implicit val passManager: PassManager =
new Passes().passManager
new Passes(defaultConfig).passManager
implicit val moduleContext: ModuleContext =
buildModuleContext(freshNameSupply = Some(new FreshNameSupply))

View File

@ -36,7 +36,7 @@ class TailCallTest extends CompilerTest {
freshNameSupply = Some(new FreshNameSupply)
)
val passes = new Passes
val passes = new Passes(defaultConfig)
val precursorPasses: PassGroup = passes.getPrecursors(TailCall).get

View File

@ -13,7 +13,7 @@ class UndefinedVariablesTest extends CompilerTest {
// === Test Setup ===========================================================
val passes = new Passes
val passes = new Passes(defaultConfig)
/** The passes that must be run before the demand analysis pass. */
val precursorPasses: PassGroup = passes.getPrecursors(UndefinedVariables).get

View File

@ -13,7 +13,7 @@ class ComplexTypeTest extends CompilerTest {
// === Test Setup ===========================================================
val passes = new Passes
val passes = new Passes(defaultConfig)
val precursorPasses: PassGroup = passes.getPrecursors(ComplexType).get

View File

@ -11,7 +11,7 @@ class FunctionBindingTest extends CompilerTest {
// === Test Setup ===========================================================
val passes = new Passes
val passes = new Passes(defaultConfig)
val precursorPasses: PassGroup = passes.getPrecursors(FunctionBinding).get
val passConfig: PassConfiguration = PassConfiguration()

View File

@ -12,7 +12,7 @@ class GenerateMethodBodiesTest extends CompilerTest {
// === Test Setup ===========================================================
val passes = new Passes
val passes = new Passes(defaultConfig)
implicit val ctx: ModuleContext = buildModuleContext()

View File

@ -11,7 +11,7 @@ class LambdaShorthandToLambdaTest extends CompilerTest {
// === Test Setup ===========================================================
val passes = new Passes
val passes = new Passes(defaultConfig)
val precursorPasses: PassGroup =
passes.getPrecursors(LambdaShorthandToLambda).get

View File

@ -16,7 +16,7 @@ class MainImportAndExportTest extends CompilerTest {
freshNameSupply = Some(new FreshNameSupply)
)
val passes = new Passes
val passes = new Passes(defaultConfig)
val precursorPasses: PassGroup =
passes.getPrecursors(MainImportAndExport).get

View File

@ -12,7 +12,7 @@ class NestedPatternMatchTest extends CompilerTest {
// === Test Setup ===========================================================
val passes = new Passes
val passes = new Passes(defaultConfig)
val precursorPasses: PassGroup =
passes.getPrecursors(NestedPatternMatch).get

View File

@ -11,7 +11,7 @@ class SectionsToBinOpTest extends CompilerTest {
// === Test Configuration ===================================================
val passes = new Passes
val passes = new Passes(defaultConfig)
val precursorPasses: PassGroup = passes.getPrecursors(SectionsToBinOp).get

View File

@ -12,7 +12,7 @@ class ShadowedPatternFieldsTest extends CompilerTest {
// === Test Setup ===========================================================
val passes = new Passes
val passes = new Passes(defaultConfig)
val precursorPasses: PassGroup =
passes.getPrecursors(ShadowedPatternFields).get

View File

@ -17,7 +17,7 @@ class UnusedBindingsTest extends CompilerTest with Inside {
// === Test Setup ===========================================================
val passes = new Passes
val passes = new Passes(defaultConfig)
val precursorPasses: PassGroup = passes.getPrecursors(UnusedBindings).get

View File

@ -53,7 +53,7 @@ class ApplicationSaturationTest extends CompilerTest {
)
)
val passes: Passes = new Passes
val passes: Passes = new Passes(defaultConfig)
val precursorPasses = passes.getPrecursors(TailCall).get

View File

@ -13,7 +13,7 @@ class LambdaConsolidateTest extends CompilerTest {
// === Test Setup ===========================================================
val passes = new Passes
val passes = new Passes(defaultConfig)
val precursorPasses: PassGroup =
passes.getPrecursors(LambdaConsolidate).get

View File

@ -12,7 +12,7 @@ class UnreachableMatchBranchesTest extends CompilerTest {
// === Test Setup ===========================================================
val passes = new Passes
val passes = new Passes(defaultConfig)
val precursorPasses: PassGroup =
passes.getPrecursors(UnreachableMatchBranches).get

View File

@ -207,7 +207,7 @@ class DocumentationCommentsTest extends CompilerTest with Inside {
"Documentation" should {
"be preserved after rewriting" in {
implicit val passManager: PassManager =
new Passes().passManager
new Passes(defaultConfig).passManager
implicit val moduleContext: ModuleContext =
buildModuleContext(freshNameSupply = Some(new FreshNameSupply))
@ -221,7 +221,7 @@ class DocumentationCommentsTest extends CompilerTest with Inside {
"be preserved after rewriting for all entities" in {
implicit val passManager: PassManager =
new Passes().passManager
new Passes(defaultConfig).passManager
implicit val moduleContext: ModuleContext =
buildModuleContext(freshNameSupply = Some(new FreshNameSupply))

View File

@ -3,6 +3,7 @@ package org.enso.compiler.test.pass.resolve
import org.enso.compiler.Passes
import org.enso.compiler.context.{FreshNameSupply, InlineContext, ModuleContext}
import org.enso.compiler.core.IR
import org.enso.compiler.core.IR.Name
import org.enso.compiler.pass.resolve.ExpressionAnnotations
import org.enso.compiler.pass.{PassConfiguration, PassGroup, PassManager}
import org.enso.compiler.test.CompilerTest
@ -21,7 +22,7 @@ class ExpressionAnnotationsTest extends CompilerTest {
freshNameSupply = Some(new FreshNameSupply)
)
val passes = new Passes
val passes = new Passes(defaultConfig)
val precursorPasses: PassGroup =
passes.getPrecursors(ExpressionAnnotations).get
@ -64,6 +65,7 @@ class ExpressionAnnotationsTest extends CompilerTest {
| @Tail_Call
| @Unknown_Annotation foo bar baz
| @Builtin_Method "myBuiltin"
| @Auto_Parallel a.f bar baz
| foo @Tail_Call
| foo (@Tail_Call bar baz)
|""".stripMargin.preprocessModule.analyse
@ -94,13 +96,24 @@ class ExpressionAnnotationsTest extends CompilerTest {
}
"associate the annotation with the annotated definition" in {
val correctDef = items.expressions(2).asInstanceOf[IR.Literal.Text]
correctDef.text shouldEqual "myBuiltin"
correctDef
val builtinDef = items.expressions(2).asInstanceOf[IR.Literal.Text]
builtinDef.text shouldEqual "myBuiltin"
builtinDef
.unsafeGetMetadata(ExpressionAnnotations, "")
.annotations
.head
.name shouldEqual "@Builtin_Method"
.name shouldEqual ExpressionAnnotations.builtinMethodName
val parallelDef = items.expressions(3).asInstanceOf[IR.Application.Prefix]
parallelDef.function shouldBe a[Name.Literal]
val fn = parallelDef.function.asInstanceOf[Name.Literal]
fn.name shouldEqual "f"
fn.isMethod shouldBe true
parallelDef
.unsafeGetMetadata(ExpressionAnnotations, "")
.annotations
.head
.name shouldEqual ExpressionAnnotations.autoParallelName
val correct = items.returnValue
.asInstanceOf[IR.Application.Prefix]
@ -119,7 +132,7 @@ class ExpressionAnnotationsTest extends CompilerTest {
"create an error on a misplaced annotation" in {
val misplaced = items
.expressions(3)
.expressions(4)
.asInstanceOf[IR.Application.Prefix]
.arguments(0)
.value

View File

@ -13,7 +13,7 @@ class IgnoredBindingsTest extends CompilerTest {
// === Test Setup ===========================================================
val passes = new Passes
val passes = new Passes(defaultConfig)
val precursorPasses: PassGroup = passes.getPrecursors(IgnoredBindings).get

View File

@ -18,7 +18,7 @@ class MethodDefinitionsTest extends CompilerTest {
freshNameSupply = Some(new FreshNameSupply)
)
val passes = new Passes
val passes = new Passes(defaultConfig)
val precursorPasses: PassGroup =
passes.getPrecursors(MethodDefinitions).get
@ -52,7 +52,7 @@ class MethodDefinitionsTest extends CompilerTest {
val ir =
"""
|type Foo a b c
|
|
|Foo.my_method a b c = a + b + c
|
|my_method = 10

View File

@ -12,7 +12,7 @@ class ModuleAnnotationsTest extends CompilerTest {
// === Test Setup ===========================================================
val passes = new Passes
val passes = new Passes(defaultConfig)
val precursorPasses: PassGroup = passes.getPrecursors(ModuleAnnotations).get

View File

@ -19,7 +19,7 @@ class ModuleThisToHereTest extends CompilerTest {
freshNameSupply = Some(new FreshNameSupply)
)
val passes = new Passes
val passes = new Passes(defaultConfig)
val precursorPasses: PassGroup =
passes.getPrecursors(ModuleThisToHere).get

View File

@ -11,7 +11,7 @@ class OverloadsResolutionTest extends CompilerTest {
// === Test Setup ===========================================================
val passes = new Passes
val passes = new Passes(defaultConfig)
val precursorPasses: PassGroup =
passes.getPrecursors(OverloadsResolution).get

View File

@ -24,7 +24,7 @@ class PatternsTest extends CompilerTest {
freshNameSupply = Some(new FreshNameSupply)
)
val passes = new Passes
val passes = new Passes(defaultConfig)
val precursorPasses: PassGroup =
passes.getPrecursors(Patterns).get

View File

@ -15,7 +15,7 @@ class SuspendedArgumentsTest extends CompilerTest {
// === Test Setup ===========================================================
val passes = new Passes
val passes = new Passes(defaultConfig)
val precursorPasses: PassGroup =
passes.getPrecursors(SuspendedArguments).get

View File

@ -11,7 +11,7 @@ class TypeFunctionsTest extends CompilerTest {
// === Test Setup ===========================================================
val passes = new Passes
val passes = new Passes(defaultConfig)
val precursorPasses: PassGroup = passes.getPrecursors(TypeFunctions).get

View File

@ -15,7 +15,7 @@ class TypeSignaturesTest extends CompilerTest {
// === Test Setup ===========================================================
val passes = new Passes
val passes = new Passes(defaultConfig)
val precursorPasses: PassGroup = passes.getPrecursors(TypeSignatures).get

View File

@ -23,7 +23,7 @@ class UppercaseNamesTest extends CompilerTest {
freshNameSupply = Some(new FreshNameSupply)
)
val passes = new Passes
val passes = new Passes(defaultConfig)
val group1 = passes.moduleDiscoveryPasses
val group2 = new PassGroup(

View File

@ -19,7 +19,7 @@ class VectorLiteralsTest extends CompilerTest {
freshNameSupply = Some(new FreshNameSupply)
)
val passes = new Passes
val passes = new Passes(defaultConfig)
val precursorPasses: PassGroup =
passes.getPrecursors(VectorLiterals).get