-
Notifications
You must be signed in to change notification settings - Fork 380
/
Copy pathSeparate.idr
293 lines (250 loc) · 9.86 KB
/
Separate.idr
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
module Compiler.Separate
import public Core.FC
import public Core.Name
import public Core.Name.Namespace
import public Core.CompileExpr
import public Compiler.LambdaLift
import public Compiler.VMCode
import public Libraries.Data.Graph
import public Libraries.Data.SortedMap
import public Libraries.Data.SortedSet
import public Libraries.Data.StringMap
import Core.Hash
import Core.TT
import Data.List
import Data.List1
import Data.Vect
import Data.Maybe
%default covering
-- Compilation unit IDs are intended to be opaque,
-- just to be able to express dependencies via keys in a map and such.
export
record CompilationUnitId where
constructor CUID
int : Int
export
Eq CompilationUnitId where
CUID x == CUID y = x == y
export
Ord CompilationUnitId where
compare (CUID x) (CUID y) = compare x y
export
Hashable CompilationUnitId where
hashWithSalt h (CUID int) = hashWithSalt h int
||| A compilation unit is a set of namespaces.
|||
||| The record is parameterised by the type of the definition,
||| which makes it reusable for various IRs provided by getCompileData.
public export
record CompilationUnit def where
constructor MkCompilationUnit
||| Unique identifier of a compilation unit within a CompilationUnitInfo record.
id : CompilationUnitId
||| Namespaces contained within the compilation unit.
namespaces : List1 Namespace
||| Other units that this unit depends on.
dependencies : SortedSet CompilationUnitId
||| The definitions belonging into this compilation unit.
definitions : List (Name, def)
export
Hashable def => Hashable (CompilationUnit def) where
hashWithSalt h cu =
h `hashWithSalt` Prelude.toList cu.dependencies
`hashWithSalt` cu.definitions
private
getNS : Name -> Namespace
getNS (NS ns _) = ns
getNS _ = emptyNS
||| Group definitions by namespace.
private
splitByNS : List (Name, def) -> List (Namespace, List (Name, def))
splitByNS = SortedMap.toList . foldl addOne SortedMap.empty
where
addOne
: SortedMap Namespace (List (Name, def))
-> (Name, def)
-> SortedMap Namespace (List (Name, def))
addOne nss ndef@(n, _) =
SortedMap.mergeWith
(++)
(SortedMap.singleton (getNS n) [ndef])
nss
public export
interface HasNamespaces a where
||| Return the set of namespaces mentioned within
nsRefs : a -> SortedSet Namespace
-- For now, we have instances only for NamedDef, LiftedDef and VMDef.
-- For other IR representations, we'll have to add more instances.
-- This is not hard, just a bit of tedious mechanical work.
mutual
export
HasNamespaces NamedCExp where
nsRefs (NmLocal fc n) = SortedSet.empty
nsRefs (NmRef fc n) = SortedSet.singleton $ getNS n
nsRefs (NmLam fc n rhs) = nsRefs rhs
nsRefs (NmLet fc n val rhs) = nsRefs val <+> nsRefs rhs
nsRefs (NmApp fc f args) = nsRefs f <+> concatMap nsRefs args
nsRefs (NmCon fc cn ci tag args) = concatMap nsRefs args
nsRefs (NmForce fc reason rhs) = nsRefs rhs
nsRefs (NmDelay fc reason rhs) = nsRefs rhs
nsRefs (NmErased fc) = SortedSet.empty
nsRefs (NmPrimVal fc x) = SortedSet.empty
nsRefs (NmOp fc op args) = concatMap nsRefs args
nsRefs (NmExtPrim fc n args) = concatMap nsRefs args
nsRefs (NmConCase fc scrut alts mbDflt) =
nsRefs scrut <+> concatMap nsRefs alts <+> concatMap nsRefs mbDflt
nsRefs (NmConstCase fc scrut alts mbDflt) =
nsRefs scrut <+> concatMap nsRefs alts <+> concatMap nsRefs mbDflt
nsRefs (NmCrash fc msg) = SortedSet.empty
export
HasNamespaces NamedConAlt where
nsRefs (MkNConAlt n ci tag args rhs) = nsRefs rhs
export
HasNamespaces NamedConstAlt where
nsRefs (MkNConstAlt c rhs) = nsRefs rhs
export
HasNamespaces NamedDef where
nsRefs (MkNmFun argNs rhs) = nsRefs rhs
nsRefs (MkNmCon tag arity nt) = SortedSet.empty
nsRefs (MkNmForeign ccs fargs rty) = SortedSet.empty
nsRefs (MkNmError rhs) = nsRefs rhs
mutual
export
HasNamespaces (Lifted vars) where
nsRefs (LLocal fc prf) = SortedSet.empty
nsRefs (LAppName fc reason n args) =
SortedSet.singleton (getNS n) <+> concatMap nsRefs args
nsRefs (LUnderApp fc n missing args) =
SortedSet.singleton (getNS n) <+> concatMap nsRefs args
nsRefs (LApp fc reason f args) = nsRefs f <+> nsRefs args
nsRefs (LLet fc n val rhs) = nsRefs val <+> nsRefs rhs
nsRefs (LCon fc cn ci tag args) = concatMap nsRefs args
nsRefs (LOp fc reason op args) = concatMap nsRefs args
nsRefs (LExtPrim fc reason n args) = concatMap nsRefs args
nsRefs (LConCase fc scrut alts mbDflt) =
nsRefs scrut <+> concatMap nsRefs alts <+> concatMap nsRefs mbDflt
nsRefs (LConstCase fc scrut alts mbDflt) =
nsRefs scrut <+> concatMap nsRefs alts <+> concatMap nsRefs mbDflt
nsRefs (LPrimVal fc x) = SortedSet.empty
nsRefs (LErased fc) = SortedSet.empty
nsRefs (LCrash fc msg) = SortedSet.empty
export
HasNamespaces (LiftedConAlt vars) where
nsRefs (MkLConAlt n ci tag args rhs) = nsRefs rhs
export
HasNamespaces (LiftedConstAlt vars) where
nsRefs (MkLConstAlt c rhs) = nsRefs rhs
export
HasNamespaces LiftedDef where
nsRefs (MkLFun args scope rhs) = nsRefs rhs
nsRefs (MkLCon tag arity nt) = SortedSet.empty
nsRefs (MkLForeign ccs fargs rty) = SortedSet.empty
nsRefs (MkLError rhs) = nsRefs rhs
export
HasNamespaces VMInst where
nsRefs (DECLARE x) = empty
nsRefs START = empty
nsRefs (ASSIGN x y) = empty
nsRefs (MKCON x tag args) = either (const empty) (singleton . getNS) tag
nsRefs (MKCLOSURE x n missing args) = singleton $ getNS n
nsRefs (MKCONSTANT x y) = empty
nsRefs (APPLY x f a) = empty
nsRefs (CALL x tailpos n args) = singleton $ getNS n
nsRefs (OP x y xs) = empty
nsRefs (EXTPRIM x n xs) = singleton $ getNS n
nsRefs (CASE x alts def) =
maybe empty (concatMap nsRefs) def <+>
concatMap ((concatMap nsRefs) . snd) alts <+>
concatMap ((either (const empty) (singleton . getNS)) . fst) alts
nsRefs (CONSTCASE x alts def) =
maybe empty (concatMap nsRefs) def <+>
concatMap ((concatMap nsRefs) . snd) alts
nsRefs (PROJECT x value pos) = empty
nsRefs (NULL x) = empty
nsRefs (ERROR x) = empty
export
HasNamespaces VMDef where
nsRefs (MkVMFun args is) = concatMap nsRefs is
nsRefs (MkVMForeign _ _ _) = empty
nsRefs (MkVMError is) = concatMap nsRefs is
-- a slight hack for convenient use with CompileData.namedDefs
export
HasNamespaces a => HasNamespaces (FC, a) where
nsRefs (_, x) = nsRefs x
-- another slight hack for convenient use with CompileData.namedDefs
export
Hashable def => Hashable (FC, def) where
-- ignore FC in hash, like everywhere else
hashWithSalt h (fc, x) = hashWithSalt h x
||| Output of the codegen separation algorithm.
||| Should contain everything you need in a separately compiling codegen.
public export
record CompilationUnitInfo def where
constructor MkCompilationUnitInfo
||| Compilation units computed from the given definitions,
||| ordered topologically, starting from units depending on no other unit.
compilationUnits : List (CompilationUnit def)
||| Mapping from ID to CompilationUnit.
byId : SortedMap CompilationUnitId (CompilationUnit def)
||| Maps each namespace to the compilation unit that contains it.
namespaceMap : SortedMap Namespace CompilationUnitId
||| Group the given definitions into compilation units for separate code generation.
export
getCompilationUnits : HasNamespaces def => List (Name, def) -> CompilationUnitInfo def
getCompilationUnits {def} defs =
let
-- Definitions grouped by namespace.
defsByNS : SortedMap Namespace (List (Name, def))
= SortedMap.fromList $ splitByNS defs
-- Mapping from a namespace to all namespaces mentioned within.
-- Represents graph edges pointing in that direction.
nsDeps : SortedMap Namespace (SortedSet Namespace)
= foldl (SortedMap.mergeWith SortedSet.union) SortedMap.empty
[ SortedMap.singleton (getNS n) (SortedSet.delete (getNS n) (nsRefs d))
| (n, d) <- defs
]
-- Strongly connected components of the NS dep graph,
-- ordered by output degree ascending.
--
-- Each SCC will become a compilation unit.
components : List (List1 Namespace)
= List.reverse $ tarjan nsDeps -- tarjan generates reverse toposort
-- Maps a namespace to the compilation unit that contains it.
nsMap : SortedMap Namespace CompilationUnitId
= SortedMap.fromList [(ns, cuid) | (cuid, nss) <- withCUID components, ns <- List1.forget nss]
-- List of all compilation units, ordered by number of dependencies, ascending.
units : List (CompilationUnit def)
= [mkUnit nsDeps nsMap defsByNS cuid nss | (cuid, nss) <- withCUID components]
in MkCompilationUnitInfo
{ compilationUnits = units
, byId = SortedMap.fromList [(unit.id, unit) | unit <- units]
, namespaceMap = nsMap
}
where
withCUID : List a -> List (CompilationUnitId, a)
withCUID xs = [(CUID $ cast i, x) | (i, x) <- zip [0..length xs] xs]
||| Wrap all information in a compilation unit record.
mkUnit :
SortedMap Namespace (SortedSet Namespace)
-> SortedMap Namespace CompilationUnitId
-> SortedMap Namespace (List (Name, def))
-> CompilationUnitId -> List1 Namespace -> CompilationUnit def
mkUnit nsDeps nsMap defsByNS cuid nss =
MkCompilationUnit
{ id = cuid
, namespaces = nss
, dependencies = SortedSet.delete cuid dependencies
, definitions = definitions
}
where
dependencies : SortedSet CompilationUnitId
dependencies = SortedSet.fromList $ do
ns <- List1.forget nss -- NS contained within
depsNS <- Prelude.toList $ -- NS we depend on
fromMaybe SortedSet.empty $
SortedMap.lookup ns nsDeps
case SortedMap.lookup depsNS nsMap of
Nothing => []
Just depCUID => [depCUID]
definitions : List (Name, def)
definitions = concat [fromMaybe [] $ SortedMap.lookup ns defsByNS | ns <- nss]