Skip to content

Decouple dynflags in Cmm

doyougnu requested to merge doyougnu/ghc:wip/Cmm-decouple-dynflags into master

What

Continuing to decouple DynFlags, see #20730 for project plan and status

Adds two modules:

  • Driver.Config.Cmm: module that defines initCmmConfig :: DynFlags -> CmmConfig
  • Cmm.Config: defines the data type CmmConfig

Refactors instances of DynFlags in the compiler/GHC/Cmm to use CmmConfig instead of DynFlags.

This MR does not change Cmm/Parser.y My reasoning is that since parsers are necessarily external facing DynFlags is an acceptable input, but I do not know how this integrates with the rest of the compiler.

Other things

  • You'll notice that CmmConfig has a field cmmNCGPayload due to oneSRT in Cmm.Info.Build:
-- | Build an SRT for a set of blocks
oneSRT
  :: CmmConfig
  -> LabelMap CLabel            -- which blocks are static function entry points
  -> [SomeLabel]                -- blocks in this set
  -> [CAFLabel]                 -- labels for those blocks
  -> Bool                       -- True <=> this SRT is for a CAF
  -> Set CAFLabel               -- SRT for this set
  -> Set CLabel                 -- Static data labels in this group
  -> StateT ModuleSRTInfo UniqSM
       ( [CmmDeclSRTs]                -- SRT objects we built
       , [(Label, CLabel)]            -- SRT fields for these blocks' itbls
       , [(Label, [SRTEntry])]        -- SRTs to attach to static functions
       , Bool                         -- Whether the group has CAF references
       )

oneSRT cfg staticFuns lbls caf_lbls isCAF cafs static_data = do
  topSRT <- get

  let
    this_mod = thisModule topSRT
    config   = cmmNCGPayload cfg this_mod             -- <---- used right here
    profile  = cmmProfile cfg
    platform = profilePlatform profile
    srtMap   = moduleSRTMap topSRT

And in the initCmmConfig function this field is populated with a PAP:

initCmmConfig :: DynFlags -> CmmConfig
initCmmConfig dflags = CmmConfig
  { cmmPlatform          = targetPlatform               dflags
  , cmmBackend           = backend                      dflags
  , cmmProfile           = targetProfile                dflags
  , cmmPositionInd       = positionIndependent          dflags
  , cmmOptControlFlow    = gopt Opt_CmmControlFlow      dflags
  , cmmOptDoLinting      = gopt Opt_DoCmmLinting        dflags
  , cmmOptElimCommonBlks = gopt Opt_CmmElimCommonBlocks dflags
  , cmmOptSink           = gopt Opt_CmmSink             dflags
  , cmmDebugLevel        = debugLevel                   dflags
  , cmmNCGPayload        = initNCGConfig                dflags         -- <----- partially applied right here
  }

This is a design decision and I'm open to suggestions.

What I chose was to allow a pass through field that curries DynFlags for the native code generator. This design maintains the design goal of only having initPhase functions input DynFlags and then passdown information to subsequent phases via handshakes, but also allows CmmConfig to not know anything about the DynFlags required by the native code generator. I think these are both advantages of the approach

The disadvantages is that we are smuggling in DynFlags via a PAP, but only this time we do not have type information that this is occurring. Similarly, if some downstream code needed to change a field in the hidden DynFlags then it can no longer do that because the partially applied DynFlags is read only now.

Other approaches might be to hold NCGConfig itself, however I'm unsure how this would be possible since NCGConfig is parameterized by Module, hence the PAP. Or we might mirror all of NCGConfig in a new type, but then this breaks separation of concerns and couples CmmConfig to NCGConfig.

Edited by doyougnu

Merge request reports