c0ccadfbecaa9673906f4e639d1adc4713733a15
[ghc-hetmet.git] / compiler / cmm / cmm-notes
1 More notes (May 11)\r
2 ~~~~~~~~~~~~~~~~~~~\r
3 In CmmNode, consider spliting CmmCall into two: call and jump\r
4 \r
5 Notes on new codegen (Aug 10)\r
6 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\r
7 \r
8 Things to do:\r
9  - We insert spills for variables before the stack check! This is the reason for\r
10    some fishy code in StgCmmHeap.entryHeapCheck where we are doing some strange\r
11         things to fix up the stack pointer before GC calls/jumps.\r
12 \r
13         The reason spills are inserted before the sp check is that at the entry to a\r
14         function we always store the parameters passed in registers to local variables.\r
15         The spill pass simply inserts spills at variable definitions. We instead should\r
16         sink the spills so that we can avoid spilling them on branches that never\r
17         reload them.\r
18 \r
19         This will fix the spill before stack check problem but only really as a side\r
20         effect. A 'real fix' probably requires making the spiller know about sp checks.\r
21 \r
22    EZY: I don't understand this comment. David Terei, can you clarify?\r
23 \r
24  - Proc points pass all arguments on the stack, adding more code and\r
25    slowing down things a lot. We either need to fix this or even better\r
26    would be to get rid of proc points.\r
27 \r
28  - CmmInfo.cmmToRawCmm uses Old.Cmm, so it is called after converting Cmm.Cmm to\r
29    Old.Cmm. We should abstract it to work on both representations, it needs only to\r
30    convert a CmmInfoTable to [CmmStatic].\r
31 \r
32  - The MkGraph currenty uses a different semantics for <*> than Hoopl. Maybe\r
33    we could convert codeGen/StgCmm* clients to the Hoopl's semantics?\r
34    It's all deeply unsatisfactory.\r
35 \r
36  - Improve performance of Hoopl.\r
37 \r
38    A nofib comparison of -fasm vs -fnewcodegen nofib compilation parameters\r
39    (using the same ghc-cmm branch +libraries compiled by the old codegenerator)\r
40    is at http://fox.auryn.cz/msrc/0517_hoopl/32bit.oldghcoldgen.oldghchoopl.txt\r
41    - the code produced is 10.9% slower, the compilation is +118% slower!\r
42 \r
43    The same comparison with ghc-head with zip representation is at\r
44    http://fox.auryn.cz/msrc/0517_hoopl/32bit.oldghcoldgen.oldghczip.txt\r
45    - the code produced is 11.7% slower, the compilation is +78% slower.\r
46 \r
47    When compiling nofib, ghc-cmm + libraries compiled with -fnew-codegen\r
48    is 23.7% slower (http://fox.auryn.cz/msrc/0517_hoopl/32bit.oldghcoldgen.hooplghcoldgen.txt).\r
49    When compiling nofib, ghc-head + libraries compiled with -fnew-codegen\r
50    is 31.4% slower (http://fox.auryn.cz/msrc/0517_hoopl/32bit.oldghcoldgen.zipghcoldgen.txt).\r
51 \r
52    So we generate a bit better code, but it takes us longer!\r
53 \r
54    EZY: Also importantly, Hoopl uses dramatically more memory than the\r
55    old code generator.\r
56 \r
57  - Are all blockToNodeList and blockOfNodeList really needed? Maybe we could\r
58    splice blocks instead?\r
59 \r
60    In the CmmContFlowOpt.blockConcat, using Dataflow seems too clumsy. Still,\r
61    a block catenation function would be probably nicer than blockToNodeList\r
62    / blockOfNodeList combo.\r
63 \r
64  - lowerSafeForeignCall seems too lowlevel. Just use Dataflow. After that\r
65    delete splitEntrySeq from HooplUtils.\r
66 \r
67  - manifestSP seems to touch a lot of the graph representation. It is\r
68    also slow for CmmSwitch nodes O(block_nodes * switch_statements).\r
69    Maybe rewrite manifestSP to use Dataflow?\r
70 \r
71  - Sort out Label, LabelMap, LabelSet versus BlockId, BlockEnv, BlockSet\r
72    dichotomy. Mostly this means global replace, but we also need to make\r
73    Label an instance of Outputable (probably in the Outputable module).\r
74 \r
75  - NB that CmmProcPoint line 283 has a hack that works around a GADT-related\r
76    bug in 6.10.\r
77 \r
78  - SDM (2010-02-26) can we remove the Foreign constructor from Convention?\r
79    Reason: we never generate code for a function with the Foreign\r
80    calling convention, and the code for calling foreign calls is generated\r
81 \r
82  - AsmCodeGen has a generic Cmm optimiser; move this into new pipeline\r
83    EZY (2011-04-16): The mini-inliner has been generalized and ported,\r
84    but the constant folding and other optimizations need to still be\r
85    ported.\r
86 \r
87  - AsmCodeGen has post-native-cg branch eliminator (shortCutBranches);\r
88    we ultimately want to share this with the Cmm branch eliminator.\r
89 \r
90  - At the moment, references to global registers like Hp are "lowered" \r
91    late (in CgUtils.fixStgRegisters). We should do this early, in the\r
92         new native codegen, much in the way that we lower calling conventions.\r
93         Might need to be a bit sophisticated about aliasing.\r
94 \r
95  - Question: currently we lift procpoints to become separate\r
96    CmmProcs.  Do we still want to do this?\r
97     \r
98    NB: and advantage of continuing to do this is that\r
99    we can do common-proc elimination!\r
100 \r
101  - Move to new Cmm rep:\r
102      * Make native CG consume New Cmm; \r
103      * Convert Old Cmm->New Cmm to keep old path alive\r
104      * Produce New Cmm when reading in .cmm files\r
105 \r
106  - Consider module names\r
107 \r
108  - Top-level SRT threading is a bit ugly\r
109 \r
110  - Add type/newtype for CmmModule = [CmmGroup]    -- A module\r
111                         CmmGroup  = [CmmTop]      -- A .o file\r
112                         CmmTop    = Proc | Data   -- A procedure or data\r
113 \r
114  - This is a *change*: currently a CmmGroup is one function's-worth of code\r
115    regardless of SplitObjs.   Question: can we *always* generate M.o if there\r
116    is just one element in the list (rather than M/M1.o, M/M2.o etc)\r
117 \r
118    One SRT per group.\r
119 \r
120  - See "CAFs" below; we want to totally refactor the way SRTs are calculated\r
121 \r
122  - Pull out Areas into its own module\r
123    Parameterise AreaMap (note there are type synonyms in CmmStackLayout!)\r
124    Add ByteWidth = Int\r
125    type SubArea    = (Area, ByteOff, ByteWidth) \r
126    ByteOff should not be defined in SMRep -- that is too high up the hierarchy\r
127    \r
128  - SMRep should not be imported by any module in cmm/!  Make it so.\r
129         -- ByteOff etc   ==>  CmmExpr\r
130         -- rET_SMALL etc ==> CmmInfo\r
131    Check that there are no other imports from codeGen in cmm/\r
132 \r
133  - If you eliminate a label by branch chain elimination,\r
134    what happens if there's an Area associated with that label?\r
135 \r
136  - Think about a non-flattened representation?\r
137 \r
138  - LastCall: \r
139     * Use record fields for LastCall!\r
140     * cml_ret_off should be a ByteOff\r
141     * Split into \r
142          LastCall (which has a successor) and\r
143          LastJump (which does not, includes return?)\r
144            - does not have cml_cont, cml_ret_args, cml_ret_off\r
145          LastForeignCall \r
146            - safe! \r
147            - expands into save/MidForeignCall/restore/goto\r
148            - like any LastCall, target of the call gets an info table\r
149 \r
150  - JD: remind self of what goes wrong if you turn off the \r
151    liveness of the update frame\r
152 \r
153  - Garbage-collect http://hackage.haskell.org/trac/ghc/wiki/Commentary/Compiler/CPS\r
154    moving good stuff into \r
155    http://hackage.haskell.org/trac/ghc/wiki/Commentary/Compiler/NewCodeGenPipeline\r
156 \r
157 \r
158  - We believe that all of CmmProcPoint.addProcPointProtocols is dead.  What\r
159    goes wrong if we simply never call it?\r
160 \r
161  - Something fishy in CmmStackLayout.hs\r
162    * In particular, 'getAreaSize' returns an AreaMap, but we *know* the width of\r
163         LocalRegs, so it'd be better to return FiniteMap AreaId ByteWidth\r
164    * setSuccSPs looks fishy.  Rather than lookin in procPoints, it could\r
165         just lookup the block in areaSize which, after all, has a binding\r
166         for precisely successors of calls.  All other blocks (including proc\r
167         points that are not successors of a call, we think) can be treated\r
168         uniformly: zero-size Area, and use inSP.\r
169 \r
170 \r
171  - Currently AsmCodeGen top level calls AsmCodeGen.cmmToCmm, which is a small\r
172    C-- optimiser.  It has quite a lot of boilerplate folding code in AsmCodeGen\r
173    (cmmBlockConFold, cmmStmtConFold, cmmExprConFold), before calling out to\r
174    CmmOpt.  ToDo: see what optimisations are being done; and do them before\r
175    AsmCodeGen.\r
176 \r
177  - Modularise the CPS pipeline; instead of ...; A;B;C; ...\r
178                                 use  ..; ABC; ....\r
179 \r
180  - Most of HscMain.tryNewCodeGen does not belong in HscMain.  Instead\r
181         if new_cg then\r
182              StgCmm.codeGen\r
183              processCmm  [including generating "raw" cmm]\r
184         else\r
185              CodeGen.codeGen\r
186              cmmToRawCmm\r
187 \r
188 \r
189  - If we stick CAF and stack liveness info on a LastCall node (not LastRet/Jump)\r
190    then all CAF and stack liveness stuff be completed before we split\r
191    into separate C procedures.\r
192 \r
193    Short term:\r
194      compute and attach liveness into to LastCall\r
195      right at end, split, cvt to old rep\r
196      [must split before cvt, because old rep is not expressive enough]\r
197 \r
198    Longer term: \r
199      when old rep disappears, \r
200      move the whole splitting game into the C back end *only*\r
201          (guided by the procpoint set)\r
202 \r
203 ----------------------------------------------------\r
204         Modules in cmm/\r
205 ----------------------------------------------------\r
206 \r
207 -------- Testing stuff ------------\r
208 HscMain.optionallyConvertAndOrCPS\r
209         testCmmConversion\r
210 DynFlags:  -fconvert-to-zipper-and-back, -frun-cpsz\r
211 \r
212 -------- Moribund stuff ------------\r
213 OldCmm.hs      Definition of flowgraph of old representation\r
214 OldCmmUtil.hs  Utilites that operates mostly on on CmmStmt\r
215 OldPprCmm.hs   Pretty print for CmmStmt, GenBasicBlock and ListGraph\r
216 CmmCvt.hs      Conversion between old and new Cmm reps\r
217 CmmOpt.hs      Hopefully-redundant optimiser\r
218 \r
219 -------- Stuff to keep ------------\r
220 CmmCPS.hs                 Driver for new pipeline\r
221 \r
222 CmmLive.hs                Liveness analysis, dead code elim\r
223 CmmProcPoint.hs           Identifying and splitting out proc-points\r
224 \r
225 CmmSpillReload.hs         Save and restore across calls\r
226 \r
227 CmmCommonBlockElim.hs     Common block elim\r
228 CmmContFlowOpt.hs         Other optimisations (branch-chain, merging)\r
229 \r
230 CmmBuildInfoTables.hs     New info-table \r
231 CmmStackLayout.hs         and stack layout \r
232 CmmCallConv.hs\r
233 CmmInfo.hs                Defn of InfoTables, and conversion to exact byte layout\r
234 \r
235 ---------- Cmm data types --------------\r
236 Cmm.hs              Cmm instantiations of dataflow graph framework\r
237 MkGraph.hs          Interface for building Cmm for codeGen/Stg*.hs modules\r
238 \r
239 CmmDecl.hs          Shared Cmm types of both representations\r
240 CmmExpr.hs          Type of Cmm expression\r
241 CmmType.hs          Type of Cmm types and their widths\r
242 CmmMachOp.hs        MachOp type and accompanying utilities\r
243 \r
244 CmmUtils.hs\r
245 CmmLint.hs\r
246 \r
247 PprC.hs             Pretty print Cmm in C syntax\r
248 PprCmm.hs           Pretty printer for CmmGraph.\r
249 PprCmmDecl.hs       Pretty printer for common Cmm types.\r
250 PprCmmExpr.hs       Pretty printer for Cmm expressions.\r
251 \r
252 CLabel.hs           CLabel\r
253 BlockId.hs          BlockId, BlockEnv, BlockSet\r
254 \r
255 ----------------------------------------------------\r
256       Top-level structure\r
257 ----------------------------------------------------\r
258 \r
259 * New codgen called in HscMain.hscGenHardCode, by calling HscMain.tryNewCodeGen, \r
260   enabled by -fnew-codegen (Opt_TryNewCodeGen)\r
261 \r
262   THEN it calls CmmInfo.cmmToRawCmm to lay out the details of info tables\r
263       type Cmm    = GenCmm CmmStatic CmmInfo     (ListGraph CmmStmt)\r
264       type RawCmm = GenCmm CmmStatic [CmmStatic] (ListGraph CmmStmt)\r
265 \r
266 * HscMain.tryNewCodeGen\r
267     - STG->Cmm:    StgCmm.codeGen (new codegen)\r
268     - Optimise:    CmmContFlowOpt (simple optimisations, very self contained)\r
269     - Cps convert: CmmCPS.protoCmmCPS \r
270     - Optimise:    CmmContFlowOpt again\r
271     - Convert:     CmmCvt.cmmOfZgraph (convert to old rep) very self contained\r
272 \r
273 * StgCmm.hs  The new STG -> Cmm conversion code generator\r
274   Lots of modules StgCmmXXX\r
275 \r
276 \r
277 ----------------------------------------------------\r
278       CmmCPS.protoCmmCPS   The new pipeline\r
279 ----------------------------------------------------\r
280 \r
281 CmmCPS.protoCmmCPS:\r
282    1. Do cpsTop for each procedures separately\r
283    2. Build SRT representation; this spans multiple procedures\r
284         (unless split-objs)\r
285 \r
286 cpsTop:\r
287   * CmmCommonBlockElim.elimCommonBlocks:\r
288         eliminate common blocks \r
289 \r
290   * CmmProcPoint.minimalProcPointSet\r
291         identify proc-points\r
292         no change to graph\r
293 \r
294   * CmmProcPoint.addProcPointProtocols\r
295         something to do with the MA optimisation\r
296         probably entirely unnecessary\r
297 \r
298   * Spill and reload:\r
299      - CmmSpillReload.dualLivenessWithInsertion\r
300        insert spills/reloads across \r
301            LastCalls, and \r
302            Branches to proc-points\r
303      Now sink those reloads (and other instructions):\r
304      - CmmSpillReload.rewriteAssignments\r
305      - CmmSpillReload.removeDeadAssignmentsAndReloads\r
306 \r
307   * CmmStackLayout.stubSlotsOnDeath\r
308         debug only: zero out dead slots when they die\r
309 \r
310   * Stack layout\r
311      - CmmStackLayout.lifeSlotAnal: \r
312        find which sub-areas are live on entry to each block\r
313 \r
314      - CmmStackLayout.layout\r
315        Lay out the stack, returning an AreaMap\r
316          type AreaMap = FiniteMap Area ByteOff\r
317           -- Byte offset of the oldest byte of the Area, \r
318           -- relative to the oldest byte of the Old Area\r
319 \r
320      - CmmStackLayout.manifestSP\r
321        Manifest the stack pointer\r
322 \r
323    * Split into separate procedures\r
324       - CmmProcPoint.procPointAnalysis\r
325         Given set of proc points, which blocks are reachable from each\r
326         Claim: too few proc-points => code duplication, but program still works??\r
327 \r
328       - CmmProcPoint.splitAtProcPoints\r
329         Using this info, split into separate procedures\r
330 \r
331       - CmmBuildInfoTables.setInfoTableStackMap\r
332         Attach stack maps to each info table\r
333 \r
334 \r
335 ----------------------------------------------------\r
336         Proc-points\r
337 ----------------------------------------------------\r
338 \r
339 Consider this program, which has a diamond control flow, \r
340 with a call on one branch\r
341  fn(p,x) {\r
342         h()\r
343         if b then { ... f(x) ...; q=5; goto J }\r
344              else { ...; q=7; goto J }\r
345      J: ..p...q...\r
346   }\r
347 then the join point J is a "proc-point".  So, is 'p' passed to J\r
348 as a parameter?  Or, if 'p' was saved on the stack anyway, perhaps\r
349 to keep it alive across the call to h(), maybe 'p' gets communicated\r
350 to J that way. This is an awkward choice.  (We think that we currently\r
351 never pass variables to join points via arguments.)\r
352 \r
353 Furthermore, there is *no way* to pass q to J in a register (other\r
354 than a parameter register).\r
355 \r
356 What we want is to do register allocation across the whole caboodle.\r
357 Then we could drop all the code that deals with the above awkward\r
358 decisions about spilling variables across proc-points.\r
359 \r
360 Note that J doesn't need an info table.\r
361 \r
362 What we really want is for each LastCall (not LastJump/Ret) \r
363 to have an info table.   Note that ProcPoints that are not successors\r
364 of calls don't need an info table.\r
365 \r
366 Figuring out proc-points\r
367 ~~~~~~~~~~~~~~~~~~~~~~~~\r
368 Proc-points are identified by\r
369 CmmProcPoint.minimalProcPointSet/extendPPSet Although there isn't\r
370 that much code, JD thinks that it could be done much more nicely using\r
371 a dominator analysis, using the Dataflow Engine.\r
372 \r
373 ----------------------------------------------------\r
374                 CAFs\r
375 ----------------------------------------------------\r
376 \r
377 * The code for a procedure f may refer to either the *closure* \r
378   or the *entry point* of another top-level procedure g.  \r
379   If f is live, then so is g.  f's SRT must include g's closure.\r
380 \r
381 * The CLabel for the entry-point/closure reveals whether g is \r
382   a CAF (or refers to CAFs).  See the IdLabel constructor of CLabel.\r
383 \r
384 * The CAF-ness of the original top-level defininions is figured out\r
385   (by TidyPgm) before we generate C--.  This CafInfo is only set for\r
386   top-level Ids; nested bindings stay with MayHaveCafRefs.\r
387 \r
388 * Currently an SRT contains (only) pointers to (top-level) closures.\r
389 \r
390 * Consider this Core code\r
391         f = \x -> let g = \y -> ...x...y...h1...\r
392                   in ...h2...g...\r
393   and suppose that h1, h2 have IdInfo of MayHaveCafRefs.\r
394   Therefore, so will f,  But g will not (since it's nested).\r
395 \r
396   This generates C-- roughly like this:\r
397      f_closure: .word f_entry\r
398      f_entry() [info-tbl-for-f] { ...jump g_entry...jump h2... }\r
399      g_entry() [info-tbl-for-g] { ...jump h1... }\r
400 \r
401   Note that there is no top-level closure for g (only an info table).\r
402   This fact (whether or not there is a top-level closure) is recorded\r
403   in the InfoTable attached to the CmmProc for f, g\r
404   INVARIANT: \r
405      Any out-of-Group references to an IdLabel goes to\r
406      a Proc whose InfoTable says "I have a top-level closure".\r
407   Equivalently: \r
408      A CmmProc whose InfoTable says "I do not have a top-level\r
409      closure" is referred to only from its own Group.\r
410 \r
411 * So:   info-tbl-for-f must have an SRT that keeps h1,h2 alive\r
412         info-tbl-for-g must have an SRT that keeps h1 (only) alive\r
413 \r
414   But if we just look for the free CAF refs, we get:\r
415         f   h2 (only)\r
416         g   h1\r
417 \r
418   So we need to do a transitive closure thing to flesh out \r
419   f's keep-alive refs to include h1.\r
420 \r
421 * The SRT info is the C_SRT field of Cmm.ClosureTypeInfo in a\r
422   CmmInfoTable attached to each CmmProc.  CmmCPS.toTops actually does\r
423   the attaching, right at the end of the pipeline.  The C_SRT part\r
424   gives offsets within a single, shared table of closure pointers.\r
425 \r
426 * DECIDED: we can generate SRTs based on the final Cmm program\r
427   without knowledge of how it is generated.\r
428 \r
429 ----------------------------------------------------\r
430                 Foreign calls\r
431 ----------------------------------------------------\r
432 \r
433 See Note [Foreign calls] in CmmNode!  This explains that a safe\r
434 foreign call must do this:\r
435   save thread state\r
436   push info table (on thread stack) to describe frame\r
437   make call (via C stack)\r
438   pop info table\r
439   restore thread state\r
440 and explains why this expansion must be done late in the day.\r
441 \r
442 Hence, \r
443   - Every foreign call is represented as a middle node\r
444 \r
445   - *Unsafe* foreign calls are simply "fat machine instructions"\r
446       and are passed along to the native code generator\r
447 \r
448   - *Safe* foreign calls are "lowered" to unsafe calls by wrapping\r
449       them in the above save/restore sequence. This step is done\r
450       very late in the pipeline, just before handing to the native\r
451       code gen.   \r
452   \r
453       This lowering is done by BuildInfoTables.lowerSafeForeignCalls\r
454 \r
455 \r
456 NEW PLAN for foreign calls:\r
457   - Unsafe foreign calls remain as a middle node (fat machine instruction)\r
458     Even the parameter passing is not lowered (just as machine instrs\r
459     get arguments).\r
460 \r
461   - Initially, safe foreign calls appear as LastCalls with \r
462         \r
463 \r
464 ----------------------------------------------------\r
465                 Cmm representations\r
466 ----------------------------------------------------\r
467 \r
468 * CmmDecl.hs\r
469      The type [GenCmm d h g] represents a whole module, \r
470         ** one list element per .o file **\r
471         Without SplitObjs, the list has exactly one element\r
472 \r
473      newtype GenCmm d h g = Cmm [GenCmmTop d h g]  -- A whole .o file\r
474      data GenCmmTop d h g\r
475          = CmmProc h g           -- One procedure, graph d\r
476          | CmmData <stuff> [d]   -- Initialised data, items d\r
477 \r
478   Old and new piplines use different representations\r
479         (CmmCvt.hs converts between the two)\r
480 \r
481 \r
482 -------------\r
483 OLD BACK END representations (OldCmm.hs):  \r
484       type Cmm = GenCmm CmmStatic CmmInfo (ListGraph CmmStmt)\r
485                                 -- A whole module\r
486       newtype ListGraph i = ListGraph [GenBasicBlock i]\r
487 \r
488       data CmmStmt = Assign | Store | Return etc -- OLD BACK END ONLY\r
489 \r
490 \r
491    Once the info tables are laid out, we replace CmmInfo with [CmmStatic]\r
492       type RawCmm    = GenCmm CmmStatic [CmmStatic] (ListGraph CmmStmt)\r
493    which represents the info tables as data, that should \r
494    immediately precede the code\r
495   \r
496 -------------\r
497 NEW BACK END representations \r
498 * Uses Hoopl library, a zero-boot package\r
499 * CmmNode defines a node of a flow graph.\r
500 * Cmm defines CmmGraph, CmmTop, Cmm\r
501    - CmmGraph is a closed/closed graph + an entry node.\r
502 \r
503        data CmmGraph = CmmGraph { g_entry :: BlockId\r
504                                 , g_graph :: Graph CmmNode C C }\r
505 \r
506    - CmmTop is a top level chunk, specialization of GenCmmTop from CmmDecl.hs\r
507        with CmmGraph as a flow graph.\r
508    - Cmm is a collection of CmmTops.\r
509 \r
510        type Cmm          = GenCmm    CmmStatic CmmTopInfo CmmGraph\r
511        type CmmTop       = GenCmmTop CmmStatic CmmTopInfo CmmGraph\r
512 \r
513    - CmmTop uses CmmTopInfo, which is a CmmInfoTable and CmmStackInfo\r
514 \r
515        data CmmTopInfo   = TopInfo {info_tbl :: CmmInfoTable, stack_info :: CmmStackInfo}\r
516 \r
517    - CmmStackInfo\r
518 \r
519        data CmmStackInfo = StackInfo {arg_space :: ByteOff, updfr_space :: Maybe ByteOff}\r
520 \r
521          * arg_space = SP offset on entry\r
522          * updfr_space space = SP offset on exit\r
523        Once the staci is manifested, we could drom CmmStackInfo, ie. get\r
524          GenCmm CmmStatic CmmInfoTable CmmGraph, but we do not do that currently.\r
525 \r
526 \r
527 * MkGraph.hs: smart constructors for Cmm.hs\r
528   Beware, the CmmAGraph defined here does not use AGraph from Hoopl,\r
529   as CmmAGraph can be opened or closed at exit, See the notes in that module.\r
530 \r
531 -------------\r
532 * SHARED stuff\r
533   CmmDecl.hs - GenCmm and GenCmmTop types\r
534   CmmExpr.hs - defines the Cmm expression types\r
535              - CmmExpr, CmmReg, CmmLit, LocalReg, GlobalReg\r
536              - Area, AreaId etc     (separate module?)\r
537   CmmType.hs - CmmType, Width etc   (saparate module?)\r
538   CmmMachOp.hs - MachOp and CallishMachOp types\r
539 \r
540   BlockId.hs defines  BlockId, BlockEnv, BlockSet\r
541 -------------\r