mirror of
https://github.com/enso-org/enso.git
synced 2025-01-09 01:26:59 +03:00
Misc compiler enhancements (#1748)
This commit is contained in:
parent
f34f8be895
commit
80eff9c017
@ -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
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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),
|
||||
|
@ -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. */
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
)
|
||||
|
@ -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 ===============================================================
|
||||
|
@ -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
|
||||
)
|
@ -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
|
||||
* `@Auto_Parallel` 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
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
@ -73,7 +73,8 @@ case object LambdaShorthandToLambda extends IRPass {
|
||||
_,
|
||||
InlineContext(
|
||||
moduleContext.module,
|
||||
freshNameSupply = moduleContext.freshNameSupply
|
||||
freshNameSupply = moduleContext.freshNameSupply,
|
||||
compilerConfig = moduleContext.compilerConfig
|
||||
)
|
||||
)
|
||||
)
|
||||
|
@ -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)
|
||||
|
@ -49,7 +49,8 @@ case object SectionsToBinOp extends IRPass {
|
||||
_,
|
||||
new InlineContext(
|
||||
moduleContext.module,
|
||||
freshNameSupply = moduleContext.freshNameSupply
|
||||
freshNameSupply = moduleContext.freshNameSupply,
|
||||
compilerConfig = moduleContext.compilerConfig
|
||||
)
|
||||
)
|
||||
)
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
@ -81,7 +81,8 @@ case object LambdaConsolidate extends IRPass {
|
||||
_,
|
||||
new InlineContext(
|
||||
moduleContext.module,
|
||||
freshNameSupply = moduleContext.freshNameSupply
|
||||
freshNameSupply = moduleContext.freshNameSupply,
|
||||
compilerConfig = moduleContext.compilerConfig
|
||||
)
|
||||
)
|
||||
)
|
||||
|
@ -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
|
||||
|
@ -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 =======================================================
|
||||
|
@ -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)
|
||||
|
@ -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."
|
||||
|
@ -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()
|
||||
}
|
||||
|
@ -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(
|
||||
|
@ -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 {
|
||||
|
||||
|
@ -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(
|
||||
|
@ -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()
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
@ -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
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -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
|
||||
|
@ -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))
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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()
|
||||
|
@ -12,7 +12,7 @@ class GenerateMethodBodiesTest extends CompilerTest {
|
||||
|
||||
// === Test Setup ===========================================================
|
||||
|
||||
val passes = new Passes
|
||||
val passes = new Passes(defaultConfig)
|
||||
|
||||
implicit val ctx: ModuleContext = buildModuleContext()
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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))
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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(
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user