summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDominik Peteler <haskell+gitlab@with-h.at>2022-09-27 23:45:29 +0200
committerDominik Peteler <haskell+gitlab@with-h.at>2022-09-27 23:45:29 +0200
commitc1c39536ffdd64bc55faf6abbea942cd31f7d188 (patch)
tree52915139f0e44569f1fcb89bbedd0f5d2477b05e
parenta3e5360a3b9d45e1a9a0db0ba8dd1d1c902399b3 (diff)
downloadhaskell-wip/21611-move-corem.tar.gz
Extended Note "The architecture of the Core optimizer"wip/21611-move-corem
-rw-r--r--compiler/GHC/Driver/Core/Opt.hs80
1 files changed, 65 insertions, 15 deletions
diff --git a/compiler/GHC/Driver/Core/Opt.hs b/compiler/GHC/Driver/Core/Opt.hs
index f93d222e81..963a784544 100644
--- a/compiler/GHC/Driver/Core/Opt.hs
+++ b/compiler/GHC/Driver/Core/Opt.hs
@@ -129,8 +129,12 @@ Note [The architecture of the Core optimizer]
Conceptually the Core optimizer consists of two stages:
- 1. **The planning stage**: where we take the user-specified input (currently in `DynFlags`) and produce a domain-specific configuration for just this pipeline stage (a `[CoreToDo]` value). This configuration is the plan.
- 2. **The execution stage**: where we we take a Core program (a `ModGuts`) and the configuration (a `[CoreToDo]`) and optimize that program according to the `[CoreToDo]` plan, producing a new `ModGuts`.
+ 1. **The planning stage**: where we take the user-specified input (currently in
+ `DynFlags`) and produce a domain-specific configuration for just this
+ pipeline stage (a `[CoreToDo]` value). This configuration is the plan.
+ 2. **The execution stage**: where we we take a Core program (a `ModGuts`) and
+ the configuration (a `[CoreToDo]`) and optimize that program according to
+ the `[CoreToDo]` plan, producing a new `ModGuts`.
This division is mirrored in the interface of the different optimizations. For
each of those optimzations we have
@@ -151,19 +155,65 @@ question and derives the configuration for each pass from the session's state
`GHC.Driver.Config.Core.Opt` as well. The `CoreToDo` type that is finally used
to wrap this configuration value is a sum type enumerating all the optimizations
available in GHC.
-
-The entrypoint of the second stage are the `optimizeCore*` functions found in
-GHC.Driver.Core.Opt. These functions is part of the Application Layer and
-utilize the `runCorePasses` function from `GHC.Core.Opt` which is the
-counterpart of these functions in the Domain Layer. In other words, while the
-`optimizeCore*` know about `HscEnv` and are therefore bound to a concrete
-driver, `runCorePasses` is more independent as it is a component of its own.
-
-`runCorePasses` is essentially an interpreter for the `CoreToDo`s constructed in
-the planning phase. It calls the entrypoints of the passes with their respective
-configurations as arguments as well as some execution context like the unit
-environment, the rules and the type family instance in scope, and most notably
-the module we wish to compile (`ModGuts`).
+As an example suppose we have a `DynFlags` value "dflags" with
+ gopt Opt_FullLaziness dflags == True
+ floatLamArgs dflags == Just 42
+
+If we pass that dflags value to the `getCoreToDo` planning function we receive a
+list like
+ [ ...
+ , CoreDoFloatOutwards FloatOutSwitches
+ { floatOutLambdas = Just 0
+ , floatOutConstants = True
+ , floatOutOverSatApps = False
+ , floatToTopLevelOnly = False
+ }
+ , ...
+ , CoreDoFloatOutwards FloatOutSwitches
+ { floatOutLambdas = Just 42
+ , floatOutConstants = True
+ , floatOutOverSatApps = True
+ , floatToTopLevelOnly = False
+ }
+ , ...
+ ]
+
+This plan contains two "instructions" to perform two FloatOut optimization
+passes with the given configuration. Note that these configurations draw their
+values from dflags and the planning function `getCoreToDo` alone and are
+independent of the modules they might be applied to.
+
+The entrypoint of the second stage is the `runCorePasses` function found in
+GHC.Core.Opt. It is essentially an interpreter for the `CoreToDo`s constructed
+in the planning stage. It calls the entrypoints of the passes with their
+respective configurations as arguments as well as some execution context like
+the unit environment, the rules and the type family instance in scope, and most
+notably the module we wish to compile (`ModGuts`).
+Feeding the list with the two FloatOut configurations above to that function
+would result in two calls of
+ ...
+ floatOutwards logger
+ (FloatOutSwitches { floatOutLambdas = Just 0, ... }) us binds
+ ...
+ floatOutwards logger
+ (FloatOutSwitches { floatOutLambdas = Just 42, ... }) us binds'
+ ...
+where the Logger "logger" and the unique supply "us" are supplied by the
+execution context of the interpreter and binds and binds' are the Core bindings
+of the `ModGuts` being optimized.
+
+The Core Optimizer itself is invoked by calling one of the `optimizeCore*`
+functions found in GHC.Driver.Core.Opt. These functions is part of the
+Application Layer and utilize both the `getCoreToDo` function -- which is part
+of the Application Layer as well -- and the `runCorePasses` function which is
+part of the Domain Layer. Here, the terms "Application Layer" and "Domain Layer"
+are terms borrowed from Domain Driven Design and its application in the context
+of GHC is layed out in the "Modularizing GHC" paper
+(https://hsyl20.fr/home/posts/2022-05-03-modularizing-ghc-paper.html).
+In other words, while the `optimizeCore*` and `getCoreToDo` know about `HscEnv`
+and `DynFlags` and are therefore bound to a concrete driver, `runCorePasses` is
+more independent as it is a component of its own. As such it could be used by a
+different driver that produces a plan in a completely different way.
A similar split in functionality is done for the Core Linting: After each pass
we may check the sanity of the resulting Core running a so-called EndPass check.